diff --git a/libs/m3u-state/src/lib/actions.ts b/libs/m3u-state/src/lib/actions.ts index 2ebc8a8d7..7d86d663f 100644 --- a/libs/m3u-state/src/lib/actions.ts +++ b/libs/m3u-state/src/lib/actions.ts @@ -23,7 +23,6 @@ export const PlaylistActions = createActionGroup({ positionUpdates: { id: string; changes: { position: number } }[]; }>(), 'Remove All Playlists': emptyProps(), - 'Set Current Playlist Id': props<{ playlistId: string | undefined }>(), 'Handle Adding Playlist By Url': props<{ isTemporary: boolean; playlist: Playlist; diff --git a/libs/m3u-state/src/lib/effects.ts b/libs/m3u-state/src/lib/effects.ts index 6778a79a8..c9895974b 100644 --- a/libs/m3u-state/src/lib/effects.ts +++ b/libs/m3u-state/src/lib/effects.ts @@ -268,30 +268,18 @@ export class PlaylistEffects { PlaylistActions.addPlaylist, PlaylistActions.handleAddingPlaylistByUrl ), + tap((action) => { + if ('isTemporary' in action && action.isTemporary) { + return; + } + + this.navigateToPlaylist(action.playlist); + }), switchMap((action) => { if ('isTemporary' in action && action.isTemporary) { return EMPTY; } return this.playlistsService.addPlaylist(action.playlist); - }), - map((playlist: Playlist) => { - if (playlist.serverUrl) { - this.router.navigate([ - '/workspace/xtreams/', - playlist._id, - ]); - } else if (playlist.macAddress) { - this.router.navigate([ - '/workspace/stalker/', - playlist._id, - ]); - } else { - this.router.navigate([ - '/workspace/playlists/', - playlist._id, - ]); - } - return playlist; }) ); }, @@ -375,4 +363,18 @@ export class PlaylistEffects { }) ); }); + + private navigateToPlaylist(playlist: Playlist): void { + if (playlist.serverUrl) { + void this.router.navigate(['/workspace', 'xtreams', playlist._id]); + return; + } + + if (playlist.macAddress) { + void this.router.navigate(['/workspace', 'stalker', playlist._id]); + return; + } + + void this.router.navigate(['/workspace', 'playlists', playlist._id]); + } } diff --git a/libs/m3u-state/src/lib/reducers/favorites.reducers.ts b/libs/m3u-state/src/lib/reducers/favorites.reducers.ts index 9b58df853..4e83e0947 100644 --- a/libs/m3u-state/src/lib/reducers/favorites.reducers.ts +++ b/libs/m3u-state/src/lib/reducers/favorites.reducers.ts @@ -4,27 +4,28 @@ import { PlaylistState } from '../state'; export const favoritesReducers = [ on(FavoritesActions.updateFavorites, (state, action): PlaylistState => { - let favorites; - const { channel } = action; - const playlistFavorites = - state.playlists.entities[state.playlists.selectedId]?.favorites; - if (playlistFavorites?.includes(channel.url)) { - favorites = [ - ...(playlistFavorites ?? []).filter( - (url: string) => url !== channel.url - ), - ]; - } else { - favorites = [...(playlistFavorites ?? []), channel.url]; + const selectedId = state.playlists.selectedId; + const playlist = state.playlists.entities[selectedId]; + if (!selectedId || !playlist) { + return state; } + + const { channel } = action; + const playlistFavorites = (playlist.favorites ?? []).filter( + (favorite): favorite is string => typeof favorite === 'string' + ); + const favorites = playlistFavorites.includes(channel.url) + ? playlistFavorites.filter((url) => url !== channel.url) + : [...playlistFavorites, channel.url]; + return { ...state, playlists: { ...state.playlists, entities: { ...state.playlists.entities, - [state.playlists.selectedId]: { - ...state.playlists.entities[state.playlists.selectedId], + [selectedId]: { + ...playlist, favorites, }, }, @@ -32,6 +33,12 @@ export const favoritesReducers = [ }; }), on(FavoritesActions.setFavorites, (state, action): PlaylistState => { + const selectedId = state.playlists.selectedId; + const playlist = state.playlists.entities[selectedId]; + if (!selectedId || !playlist) { + return state; + } + const { channelIds } = action; return { ...state, @@ -39,8 +46,8 @@ export const favoritesReducers = [ ...state.playlists, entities: { ...state.playlists.entities, - [state.playlists.selectedId]: { - ...state.playlists.entities[state.playlists.selectedId], + [selectedId]: { + ...playlist, favorites: channelIds, }, }, diff --git a/libs/m3u-state/src/lib/reducers/index.ts b/libs/m3u-state/src/lib/reducers/index.ts index dbf3afd18..1eb959974 100644 --- a/libs/m3u-state/src/lib/reducers/index.ts +++ b/libs/m3u-state/src/lib/reducers/index.ts @@ -28,5 +28,3 @@ export const selectCurrentEpgProgramReducer = (state: PlaylistState) => export const selectChannelsReducer = (state: PlaylistState) => state.channels; export const selectPlaylistsLoadingFlagReducer = (state: PlaylistState) => state.playlists?.allPlaylistsLoaded; -export const selectCurrentPlaylistIdReducer = (state: PlaylistState) => - state.currentPlaylistId; diff --git a/libs/m3u-state/src/lib/reducers/playlist.reducers.ts b/libs/m3u-state/src/lib/reducers/playlist.reducers.ts index 44405dd23..4703ca092 100644 --- a/libs/m3u-state/src/lib/reducers/playlist.reducers.ts +++ b/libs/m3u-state/src/lib/reducers/playlist.reducers.ts @@ -15,12 +15,19 @@ export const playlistReducers = [ }; }), on(PlaylistActions.removePlaylist, (state, action): PlaylistState => { + const playlists = playlistsAdapter.removeOne( + action.playlistId, + state.playlists + ); return { ...state, - playlists: playlistsAdapter.removeOne( - action.playlistId, - state.playlists - ), + playlists: { + ...playlists, + selectedId: + state.playlists.selectedId === action.playlistId + ? '' + : playlists.selectedId, + }, }; }), on(PlaylistActions.updatePlaylist, (state, action): PlaylistState => { @@ -137,20 +144,15 @@ export const playlistReducers = [ }; }), on(PlaylistActions.removeAllPlaylists, (state): PlaylistState => { + const playlists = playlistsAdapter.removeAll(state.playlists); return { ...state, - playlists: playlistsAdapter.removeAll(state.playlists), + playlists: { + ...playlists, + selectedId: '', + }, }; }), - on( - PlaylistActions.setCurrentPlaylistId, - (state, { playlistId }): PlaylistState => { - return { - ...state, - currentPlaylistId: playlistId, - }; - } - ), on( PlaylistActions.handleAddingPlaylistByUrl, (state, action): PlaylistState => { diff --git a/libs/m3u-state/src/lib/selectors.spec.ts b/libs/m3u-state/src/lib/selectors.spec.ts new file mode 100644 index 000000000..09292edef --- /dev/null +++ b/libs/m3u-state/src/lib/selectors.spec.ts @@ -0,0 +1,29 @@ +import { PlaylistMeta } from 'shared-interfaces'; +import { selectActivePlaylist } from './selectors'; + +describe('selectors', () => { + const playlistOne = { + _id: 'playlist-1', + title: 'Playlist One', + } as PlaylistMeta; + const playlistTwo = { + _id: 'playlist-2', + title: 'Playlist Two', + } as PlaylistMeta; + const entities = { + 'playlist-1': playlistOne, + 'playlist-2': playlistTwo, + }; + + describe('selectActivePlaylist', () => { + it('resolves the active playlist from entity state and selected id', () => { + expect(selectActivePlaylist.projector(entities, 'playlist-2')).toBe( + playlistTwo + ); + }); + + it('returns null when there is no active playlist id', () => { + expect(selectActivePlaylist.projector(entities, '')).toBeNull(); + }); + }); +}); diff --git a/libs/m3u-state/src/lib/selectors.ts b/libs/m3u-state/src/lib/selectors.ts index dc7a8cdad..b63a481a0 100644 --- a/libs/m3u-state/src/lib/selectors.ts +++ b/libs/m3u-state/src/lib/selectors.ts @@ -1,9 +1,7 @@ -import { EntityState } from '@ngrx/entity'; -import { createFeatureSelector, createSelector, Selector } from '@ngrx/store'; -import { Playlist, PlaylistMeta } from 'shared-interfaces'; +import { createFeatureSelector, createSelector } from '@ngrx/store'; +import { Playlist } from 'shared-interfaces'; import * as fromPlaylistMetaState from './playlists.state'; import * as fromPlaylistState from './reducers'; -import { selectRouteParam } from './router.selectors'; import { PlaylistState } from './state'; export const selectPlaylistState = @@ -24,11 +22,6 @@ export const selectCurrentEpgProgram = createSelector( fromPlaylistState.selectCurrentEpgProgramReducer ); -export const selectCurrentPlaylistId = createSelector( - selectPlaylistState, - fromPlaylistState.selectCurrentPlaylistIdReducer -); - export const selectChannels = createSelector( selectPlaylistState, fromPlaylistState.selectChannelsReducer @@ -52,41 +45,15 @@ export const selectAllPlaylistsMeta = createSelector( export const selectActiveTypeFilters = createSelector( selectPlaylistsMetaState, - fromPlaylistMetaState.getPlaylistMetaEntities, (state) => state.selectedFilters ); export const selectPlaylistEntity = (id: string) => - createSelector( - selectPlaylistsMetaState, - fromPlaylistMetaState.getPlaylistMetaEntities, - (data) => { - return data.entities[id]; - } - ); + createSelector(selectPlaylistEntities, (entities) => entities[id]); export const selectActivePlaylistId = createSelector( selectPlaylistsMetaState, - fromPlaylistMetaState.getPlaylistMetaEntities, - (data) => data.selectedId -); - -export const selectPlaylistTitle = createSelector( - selectPlaylistsMetaState, - fromPlaylistMetaState.getPlaylistMetaEntities, - selectCurrentPlaylistId, - (data) => { - if ( - data.entities && - data.selectedId && - data.entities[data.selectedId] - ) { - return ( - data.entities[data.selectedId]?.title || - data.entities[data.selectedId]?.filename - ); - } else return 'Untitled playlist'; - } + (state) => state.selectedId ); export const selectPlaylistEntities = createSelector( @@ -94,18 +61,23 @@ export const selectPlaylistEntities = createSelector( fromPlaylistMetaState.getPlaylistMetaEntities ); -export const selectCurrentPlaylist = createSelector( +export const selectActivePlaylist = createSelector( selectPlaylistEntities, - selectRouteParam('id'), - selectCurrentPlaylistId, - (entities, id, currentPlaylistId) => { - if (entities) { - return entities[id!] || entities[currentPlaylistId!]; + selectActivePlaylistId, + (entities, activePlaylistId) => { + if (!entities || !activePlaylistId) { + return null; } - return null; + + return entities[activePlaylistId] ?? null; } ); +export const selectPlaylistTitle = createSelector( + selectActivePlaylist, + (playlist) => playlist?.title || playlist?.filename || 'Untitled playlist' +); + export const selectPlaylistById = (id: string) => createSelector(selectPlaylistEntities, (entities) => { if (entities) { @@ -114,32 +86,10 @@ export const selectPlaylistById = (id: string) => return null; }); -export const selectActivePlaylist = createSelector( - selectPlaylistsMetaState, - (state) => { - if (state.entities && state.selectedId !== '') { - return state.entities[state.selectedId] as Playlist; - } - return null; - } -); - export const selectFavorites = createSelector( - selectPlaylistsMetaState, - fromPlaylistMetaState.getPlaylistMetaEntities, - fromPlaylistState.selectPlaylistId as unknown as Selector< - EntityState, - string - >, - (data) => { - if ( - data.entities && - data.selectedId && - data.entities[data.selectedId] - ) { - return (data.entities[data.selectedId]?.favorites || []).filter( - (f): f is string => typeof f === 'string' - ); - } else return []; - } + selectActivePlaylist, + (playlist) => + (playlist?.favorites || []).filter( + (favorite): favorite is string => typeof favorite === 'string' + ) ); diff --git a/libs/m3u-state/src/lib/state.ts b/libs/m3u-state/src/lib/state.ts index 368d7a080..4093adc95 100644 --- a/libs/m3u-state/src/lib/state.ts +++ b/libs/m3u-state/src/lib/state.ts @@ -9,7 +9,6 @@ export interface PlaylistState { epgAvailable: boolean; channels: Channel[]; // TODO: use entity store playlists: PlaylistMetaState; - currentPlaylistId: string | undefined; } export const initialState: PlaylistState = { @@ -18,5 +17,4 @@ export const initialState: PlaylistState = { epgAvailable: false, channels: [], playlists: initialPlaylistMetaState, - currentPlaylistId: undefined, }; diff --git a/libs/playlist/m3u/feature-player/src/index.ts b/libs/playlist/m3u/feature-player/src/index.ts index 8daef9c5a..c57131edf 100644 --- a/libs/playlist/m3u/feature-player/src/index.ts +++ b/libs/playlist/m3u/feature-player/src/index.ts @@ -1,2 +1,3 @@ export * from './lib/m3u-collection-route/m3u-collection-route.component'; +export * from './lib/m3u-workspace-route-session.service'; export * from './lib/video-player/video-player.component';