mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
* fix(portals): preserve playlist ownership during detail handoffs * fix(portals): reload Stalker categories only for a held destination Review follow-ups (Greptile, Codex): resetCategories() reloaded the category resource, and the route session calls it on a portal switch before the destination is resolved and on teardown, so it asked the portal being left, and a failed destination lookup could let that answer repopulate the sidebar. resetCategories() now only clears; the session calls the new reloadCategories() after installing the destination, and only when a handoff had already put that playlist in the store (the owner, and so the resource params, did not change). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: 4gray <fourgray@proton.me> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
7.4 KiB
7.4 KiB
Stalker Store API Baseline
This is the compatibility baseline for refactoring libs/portal/stalker/data-access/src/lib/stalker.store.ts.
Goal: keep this public surface stable while splitting to feature stores.
Source of Truth
- Store implementation:
libs/portal/stalker/data-access/src/lib/stalker.store.ts - Baseline created on current branch state before feature-store extraction.
Public State Signals
Direct signal properties currently exposed by signalStore:
selectedContentType: 'vod' | 'itv' | 'radio' | 'series'selectedCategoryId: string | null | undefinedselectedVodId: string | undefinedselectedSerialId: string | undefinedselectedItvId: string | undefinedpage: numbersearchPhrase: stringcurrentPlaylist: PlaylistMeta | undefinedtotalCount: numberselectedItem: StalkerVodSource | null | undefinedvodCategories: StalkerCategoryItem[]seriesCategories: StalkerCategoryItem[]itvCategories: StalkerCategoryItem[]radioCategories: StalkerCategoryItem[]hasMoreChannels: booleanitvChannels: StalkerItvChannel[]radioChannels: StalkerItvChannel[]vodSeriesSeasons: StalkerVodSeriesSeason[]vodSeriesEpisodes: StalkerVodSeriesEpisode[]selectedVodSeriesSeasonId: string | undefined
Public Computed Selectors
getPaginatedContent: StalkerContentItem[]isPaginatedContentLoading: booleanisPaginatedContentFailed: unknowngetSerialSeasonsResource: StalkerSeason[]isSerialSeasonsLoading: booleangetVodSeriesSeasonsResource: StalkerVodSeriesSeason[]isVodSeriesSeasonsLoading: booleangetCategoryResource: StalkerCategoryItem[]isCategoryResourceLoading: booleanisCategoryResourceFailed: unknowngetSelectedCategoryName: string
Exposed Resources/Props
These are currently reachable on the store object and used internally by computed selectors:
getCategoryResource(computed selector with stable array output)categoryResource(internal resource)getContentResource(resource)serialSeasonsResource(resource)vodSeriesSeasonsResource(resource)
Removed:
makeStalkerRequest(...)— deleted with the endpoint-discovery work. It was production-dead (no caller outside its own spec) and carried a fourth private copy of the portal-mode branch. Every Stalker request goes throughexecuteStalkerRequest()(stores/utils/stalker-request.utils.ts), which owns mode routing plus the lazy portal repair; no facade alias is provided because reinstating one would reintroduce the drift the shared predicate exists to prevent.limit,setLimit(...)andgetTotalPages— deleted with the catalog pagination removal (#1392, #1395). Catalogs now accumulate server-paged results into one deduplicated list, sohasMoreContent(accumulated length vstotal_items) answers whatgetTotalPagesused to, and no caller reads a page size: the portal decides how large a page is. No facade alias is provided, because a survivinglimitwould advertise a client-side window that the append path does not honour.
During refactor:
- Keep compatibility for external callers that may read these directly.
- If moved/renamed internally, provide facade aliases.
Public Methods (Compatibility Contract)
setSelectedContentType(type: 'vod' | 'itv' | 'radio' | 'series'): voidsetSelectedCategory(id: string | number | null): voidsetSelectedSerialId(id: string): voidsetSelectedVodId(id: string): voidsetSelectedItvId(id: string): voidsetPage(page: number): voidsetCurrentPlaylist(playlist: PlaylistMeta | undefined): Promise<void>setSelectedItem(selectedItem: StalkerVodSource | null | undefined): voidclearSelectedItem(): voidsetCategories(type: 'vod' | 'series' | 'itv' | 'radio', categories: StalkerCategoryItem[]): voidresetCategories(): voidClears only; it starts no request (called on portal switches and route teardown).reloadCategories(): voidRefetches the current portal's categories after a reset that keeps the owner.setItvChannels(channels: StalkerItvChannel[]): voidsetRadioChannels(channels: StalkerItvChannel[]): voidsetSearchPhrase(phrase: string): voidfetchVodSeriesEpisodes(videoId: string, seasonId: string): Promise<StalkerVodSeriesEpisode[]>getSelectedCategory(): { id: string | number; name: string; type: 'vod' | 'itv' | 'radio' | 'series' }Backed bywithComputedfor compatibility, not bywithMethods.fetchLinkToPlay(portalUrl: string, macAddress: string, cmd: string, series?: number): Promise<string>getExpireDate(): Promise<string>resolveItvPlayback(item: StalkerPortalItem): Promise<ResolvedPortalPlayback>resolveRadioPlayback(item: StalkerPortalItem): Promise<ResolvedPortalPlayback>addToFavorites(item: any, onDone?: () => void): voidremoveFromFavorites(favoriteId: string, onDone?: () => void): voidfetchMovieFileId(movieId: string): Promise<string | null>createLinkToPlayVod(cmd?: string, title?: string, thumbnail?: string, episodeNum?: number, episodeId?: number, startTime?: number): Promise<void>addToRecentlyViewed(item: any): voidremoveFromRecentlyViewed(itemId: number, onComplete?: () => void): voidfetchChannelEpg(channelId: number | string, size?: number): Promise<EpgItem[]>
Current Consumers (Observed)
Top observed store API usage in app code:
currentPlaylist(16 references)setSelectedContentType(9)selectedItem(9)setSelectedItem(7)setSelectedCategory(6)createLinkToPlayVod(6)removeFromFavorites(5)setPage(4)addToFavorites(4)fetchChannelEpg(3)- plus lower-frequency calls for paging/resources/series/recent.
Consumer directories sampled:
libs/portal/stalker/**libs/portal/xtream/feature/**libs/portal/catalog/feature/**libs/ui/components/**
Invariants to Preserve During Refactor
- Selection IDs (
selectedVodId,selectedItvId) are synchronized insetSelectedItem. selectedSerialIdis set only whenselectedContentTypeisseries, and is cleared for every other content type.serialSeasonsResourcefires aget_ordered_list&type=seriesportal request on each change of that id, and it is the only episode source for aseriesselection — while VOD-context shapes (embeddedseries[], Ministrais_series) resolve their episodes elsewhere, so carrying the id there only wastes a request. The gate must stay on content type alone: gating it on item shape would leave a series-section item that happens to carryis_series/series[]with an empty episode list.setSelectedCategory(...)resetspageto0.getPaginatedContent()andgetCategoryResource()always return arrays, even when the underlying request fails.- Radio stores stations separately in
radioChannelsand falls back to a synthetic all-radio category when a portal does not support radio category responses. - Request failures must surface through
isPaginatedContentFailed()andisCategoryResourceFailed()rather than resource reads that throw. createLinkToPlayVod(...)continues to:- support episode playback metadata
- append recently viewed
- preserve external player payload shape
- Full-portal auth path continues through
StalkerSessionService. - Non-auth/simple path continues through
DataService.sendIpcEvent(STALKER_REQUEST, ...). - Resource-driven loading signals preserve existing names.