diff --git a/libs/m3u-state/src/lib/actions.ts b/libs/m3u-state/src/lib/actions.ts index b2dd25083..2ebc8a8d7 100644 --- a/libs/m3u-state/src/lib/actions.ts +++ b/libs/m3u-state/src/lib/actions.ts @@ -1,140 +1,70 @@ -import { createAction, props } from '@ngrx/store'; +import { createActionGroup, emptyProps, props } from '@ngrx/store'; import { Channel, EpgProgram, Playlist, PlaylistMeta } from 'shared-interfaces'; -const STORE_KEY = '[GLOBAL STORE]'; -const PLAYLISTS_STORE_KEY = '[PLAYLISTS STORE]'; +export const PlaylistActions = createActionGroup({ + source: 'Playlists', + events: { + 'Load Playlists': emptyProps(), + 'Load Playlists Success': props<{ playlists: PlaylistMeta[] }>(), + 'Add Playlist': props<{ playlist: Playlist }>(), + 'Add Many Playlists': props<{ playlists: Playlist[] }>(), + 'Remove Playlist': props<{ playlistId: string }>(), + 'Update Playlist Meta': props<{ playlist: PlaylistMeta }>(), + 'Update Playlist': props<{ playlist: Playlist; playlistId: string }>(), + 'Update Many Playlists': props<{ playlists: Playlist[] }>(), + 'Parse Playlist': props<{ + uploadType: 'FILE' | 'URL' | 'TEXT'; + playlist: string; + title: string; + path?: string; + }>(), + 'Set Active Playlist': props<{ playlistId: string }>(), + 'Update Playlist Positions': props<{ + 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; + }>(), + }, +}); -export const loadPlaylists = createAction( - `${PLAYLISTS_STORE_KEY} Load playlists from db` -); +export const ChannelActions = createActionGroup({ + source: 'Channels', + events: { + 'Set Channels': props<{ channels: Channel[] }>(), + 'Set Active Channel': props<{ channel: Channel }>(), + 'Set Active Channel Success': props<{ channel: Channel }>(), + 'Reset Active Channel': emptyProps(), + 'Set Adjacent Channel As Active': props<{ + direction: 'next' | 'previous'; + }>(), + }, +}); -export const loadPlaylistsSuccess = createAction( - `${PLAYLISTS_STORE_KEY} Successfully loaded playlists from db`, - props<{ playlists: PlaylistMeta[] }>() -); +export const EpgActions = createActionGroup({ + source: 'EPG', + events: { + 'Set Active Epg Program': props<{ program: EpgProgram }>(), + 'Set Current Epg Program': props<{ program: EpgProgram }>(), + 'Reset Active Epg Program': emptyProps(), + 'Set Epg Available Flag': props<{ value: boolean }>(), + }, +}); -export const addPlaylist = createAction( - `${PLAYLISTS_STORE_KEY} Add new playlist`, - props<{ playlist: Playlist }>() -); +export const FavoritesActions = createActionGroup({ + source: 'Favorites', + events: { + 'Update Favorites': props<{ channel: Channel }>(), + 'Set Favorites': props<{ channelIds: string[] }>(), + }, +}); -export const addManyPlaylists = createAction( - `${PLAYLISTS_STORE_KEY} Add many playlists`, - props<{ playlists: Playlist[] }>() -); - -export const removePlaylist = createAction( - `${PLAYLISTS_STORE_KEY} Remove playlist by id`, - props<{ playlistId: string }>() -); - -export const updatePlaylistMeta = createAction( - `${PLAYLISTS_STORE_KEY} update playlist meta`, - props<{ playlist: PlaylistMeta }>() -); - -export const updatePlaylist = createAction( - `${PLAYLISTS_STORE_KEY} update playlist`, - props<{ playlist: Playlist; playlistId: string }>() -); - -export const updateManyPlaylists = createAction( - `${PLAYLISTS_STORE_KEY} Update many playlists (auto-update mechanism)`, - props<{ playlists: Playlist[] }>() -); - -export const parsePlaylist = createAction( - `${PLAYLISTS_STORE_KEY} parse playlist`, - props<{ - uploadType: 'FILE' | 'URL' | 'TEXT'; - playlist: string; - title: string; - path?: string; - }>() -); - -export const setActivePlaylist = createAction( - `${PLAYLISTS_STORE_KEY} set active playlist`, - props<{ playlistId: string }>() -); - -export const updateFavorites = createAction( - `${STORE_KEY} Add/remove provided channel to the favorites`, - props<{ channel: Channel }>() -); - -export const setFavorites = createAction( - `${STORE_KEY} Set favorites`, - props<{ channelIds: string[] }>() -); - -export const setActiveChannel = createAction( - `${STORE_KEY} Set active channel`, - props<{ channel: Channel }>() -); - -export const resetActiveChannel = createAction( - `${STORE_KEY} Reset active channel` -); - -export const setActiveChannelSuccess = createAction( - `${STORE_KEY} Set active channel success`, - props<{ channel: Channel }>() -); - -export const setActiveEpgProgram = createAction( - `${STORE_KEY} Sets the given timestamp for the epg program`, - props<{ program: EpgProgram }>() -); - -export const setCurrentEpgProgram = createAction( - `${STORE_KEY} Updates the active epg program for the active channel`, - props<{ program: EpgProgram }>() -); - -export const resetActiveEpgProgram = createAction( - `${STORE_KEY} Reset active epg program` -); - -export const setEpgAvailableFlag = createAction( - `${STORE_KEY} Reset active epg program`, - props<{ value: boolean }>() -); - -export const setChannels = createAction( - `${STORE_KEY} Set channels`, - props<{ channels: Channel[] }>() -); - -export const updatePlaylistPositions = createAction( - `${STORE_KEY} Update playlist positions`, - props<{ - positionUpdates: { id: string; changes: { position: number } }[]; - }>() -); - -export const removeAllPlaylists = createAction( - `${STORE_KEY} Remove all playlists` -); - -export const setAdjacentChannelAsActive = createAction( - `${STORE_KEY} Set adjacent channel as active`, - props<{ - direction: 'next' | 'previous'; - }>() -); - -export const setSelectedFilters = createAction( - `${STORE_KEY} Set selected filters`, - props<{ selectedFilters: string[] }>() -); - -export const setCurrentPlaylistId = createAction( - `${STORE_KEY} Set current playlist id`, - props<{ playlistId: string | undefined }>() -); - -export const handleAddingPlaylistByUrl = createAction( - `${PLAYLISTS_STORE_KEY} Handle adding playlist by URL`, - props<{ isTemporary: boolean; playlist: Playlist }>() -); +export const FilterActions = createActionGroup({ + source: 'Filters', + events: { + 'Set Selected Filters': props<{ selectedFilters: string[] }>(), + }, +}); diff --git a/libs/m3u-state/src/lib/effects.ts b/libs/m3u-state/src/lib/effects.ts index 749d91b05..dadeb30e9 100644 --- a/libs/m3u-state/src/lib/effects.ts +++ b/libs/m3u-state/src/lib/effects.ts @@ -24,7 +24,12 @@ import { STORE_KEY, VideoPlayer, } from 'shared-interfaces'; -import * as PlaylistActions from './actions'; +import { + PlaylistActions, + ChannelActions, + EpgActions, + FavoritesActions, +} from './actions'; import { selectActive, selectActivePlaylistId, @@ -47,7 +52,7 @@ export class PlaylistEffects { updateFavorites$ = createEffect( () => { return this.actions$.pipe( - ofType(PlaylistActions.updateFavorites), + ofType(FavoritesActions.updateFavorites), combineLatestWith( this.store.select(selectFavorites), this.store.select(selectActivePlaylistId) @@ -70,7 +75,7 @@ export class PlaylistEffects { setFavorites$ = createEffect( () => { return this.actions$.pipe( - ofType(PlaylistActions.setFavorites), + ofType(FavoritesActions.setFavorites), combineLatestWith(this.store.select(selectActivePlaylistId)), filter( ([, playlistId]) => @@ -92,7 +97,7 @@ export class PlaylistEffects { setActiveEpgProgram$ = createEffect( () => { return this.actions$.pipe( - ofType(PlaylistActions.setActiveEpgProgram), + ofType(EpgActions.setActiveEpgProgram), combineLatestWith(this.store.select(selectActive)), map(([, activeChannel]) => { firstValueFrom(this.storage.get(STORE_KEY.Settings)).then( @@ -135,7 +140,7 @@ export class PlaylistEffects { setActiveChannel$ = createEffect(() => { return this.actions$.pipe( - ofType(PlaylistActions.setActiveChannel), + ofType(ChannelActions.setActiveChannel), // Skip the effect entirely when channel is falsy filter((action) => !!action.channel), map((action) => { @@ -186,7 +191,7 @@ export class PlaylistEffects { } ); - return PlaylistActions.setActiveChannelSuccess({ + return ChannelActions.setActiveChannelSuccess({ channel: action.channel, }); }) @@ -400,7 +405,7 @@ export class PlaylistEffects { setAdjacentChannelAsActive$ = createEffect(() => { return this.actions$.pipe( - ofType(PlaylistActions.setAdjacentChannelAsActive), + ofType(ChannelActions.setAdjacentChannelAsActive), withLatestFrom( this.store.select(selectChannels), this.store.select(selectActive) @@ -419,7 +424,7 @@ export class PlaylistEffects { adjacentChannel = activeChannel; adjacentChannel = channels[index - 1]; } - return PlaylistActions.setActiveChannelSuccess({ + return ChannelActions.setActiveChannelSuccess({ channel: adjacentChannel!, }); }) diff --git a/libs/m3u-state/src/lib/reducers.ts b/libs/m3u-state/src/lib/reducers.ts index 4ddd0eb59..bb775cb93 100644 --- a/libs/m3u-state/src/lib/reducers.ts +++ b/libs/m3u-state/src/lib/reducers.ts @@ -1,292 +1,2 @@ -import { createReducer, on } from '@ngrx/store'; -import moment from 'moment'; -import { Channel } from 'shared-interfaces'; -import * as PlaylistActions from './actions'; -import { playlistsAdapter } from './playlists.state'; -import { PlaylistState, initialState } from './state'; - -export const playlistReducer = createReducer( - initialState, - on(PlaylistActions.setActiveEpgProgram, (state, action): PlaylistState => { - const { program } = action; - const from = moment(program.start, 'YYYYMMDDHHmm ZZ').unix(); - const now = moment(Date.now()).unix(); - const epgParams = `?utc=${from}&lutc=${now}`; - return { - ...state, - active: { ...state.active, epgParams } as Channel, - }; - }), - on( - PlaylistActions.resetActiveEpgProgram, - (state): PlaylistState => ({ - ...state, - active: { ...state.active, epgParams: '' } as Channel, - }) - ), - on( - PlaylistActions.setActiveChannelSuccess, - (state, action): PlaylistState => { - const { channel } = action; - return { - ...state, - active: { ...channel, epgParams: '' } as Channel, - }; - } - ), - on(PlaylistActions.resetActiveChannel, (state): PlaylistState => { - return { - ...state, - active: undefined, - }; - }), - on( - PlaylistActions.setCurrentEpgProgram, - (state, action): PlaylistState => ({ - ...state, - currentEpgProgram: action.program, - }) - ), - on( - PlaylistActions.setEpgAvailableFlag, - (state, action): PlaylistState => ({ - ...state, - epgAvailable: action.value, - }) - ), - on(PlaylistActions.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]; - } - return { - ...state, - playlists: { - ...state.playlists, - entities: { - ...state.playlists.entities, - [state.playlists.selectedId]: { - ...state.playlists.entities[state.playlists.selectedId], - favorites, - }, - }, - }, - }; - }), - on(PlaylistActions.setFavorites, (state, action): PlaylistState => { - const { channelIds } = action; - return { - ...state, - playlists: { - ...state.playlists, - entities: { - ...state.playlists.entities, - [state.playlists.selectedId]: { - ...state.playlists.entities[state.playlists.selectedId], - favorites: channelIds, - }, - }, - }, - }; - }), - on(PlaylistActions.loadPlaylistsSuccess, (state, action): PlaylistState => { - return { - ...state, - playlists: playlistsAdapter.addMany(action.playlists, { - ...state.playlists, - allPlaylistsLoaded: true, - }), - }; - }), - on(PlaylistActions.removePlaylist, (state, action): PlaylistState => { - return { - ...state, - playlists: playlistsAdapter.removeOne( - action.playlistId, - state.playlists - ), - }; - }), - on(PlaylistActions.updatePlaylist, (state, action): PlaylistState => { - return { - ...state, - playlists: playlistsAdapter.updateOne( - { - id: action.playlistId, - changes: { - ...action.playlist, - _id: action.playlistId, - updateDate: Date.now(), - count: action.playlist.playlist.items.length, - userAgent: action.playlist.userAgent, - favorites: - state.playlists.entities[action.playlistId] - ?.favorites ?? [], - }, - }, - state.playlists - ), - }; - }), - on(PlaylistActions.addPlaylist, (state, action): PlaylistState => { - return { - ...state, - playlists: playlistsAdapter.addOne( - action.playlist, - state.playlists - ), - }; - }), - on(PlaylistActions.addManyPlaylists, (state, action): PlaylistState => { - return { - ...state, - playlists: playlistsAdapter.addMany( - action.playlists, - state.playlists - ), - }; - }), - on(PlaylistActions.setActivePlaylist, (state, action): PlaylistState => { - return { - ...state, - playlists: { - ...state.playlists, - selectedId: action.playlistId, - }, - }; - }), - on( - PlaylistActions.updatePlaylistPositions, - (state, action): PlaylistState => { - return { - ...state, - playlists: playlistsAdapter.updateMany( - action.positionUpdates, - state.playlists - ), - }; - } - ), - on(PlaylistActions.updatePlaylistMeta, (state, action): PlaylistState => { - return { - ...state, - playlists: playlistsAdapter.updateOne( - { - id: action.playlist._id, - changes: { - title: action.playlist.title, - autoRefresh: action.playlist.autoRefresh || false, - userAgent: action.playlist.userAgent, - ...(action.playlist.serverUrl !== null - ? { serverUrl: action.playlist.serverUrl } - : {}), - ...(action.playlist.username !== null - ? { username: action.playlist.username } - : {}), - ...(action.playlist.password !== null - ? { password: action.playlist.password } - : {}), - ...(action.playlist.macAddress !== null - ? { macAddress: action.playlist.macAddress } - : {}), - ...(action.playlist.portalUrl !== null - ? { portalUrl: action.playlist.portalUrl } - : {}), - }, - }, - state.playlists - ), - }; - }), - on(PlaylistActions.setChannels, (state, action): PlaylistState => { - return { - ...state, - channels: action.channels, - }; - }), - on(PlaylistActions.updateManyPlaylists, (state, action): PlaylistState => { - return { - ...state, - playlists: playlistsAdapter.updateMany( - action.playlists.map((updatedPlaylist) => ({ - id: updatedPlaylist._id, - changes: { - ...updatedPlaylist, - updateDate: Date.now(), - }, - })), - state.playlists - ), - }; - }), - on(PlaylistActions.removeAllPlaylists, (state): PlaylistState => { - return { - ...state, - playlists: playlistsAdapter.removeAll(state.playlists), - }; - }), - on( - PlaylistActions.setSelectedFilters, - (state, { selectedFilters }): PlaylistState => { - return { - ...state, - playlists: { - ...state.playlists, - selectedFilters, - }, - }; - } - ), - on( - PlaylistActions.setCurrentPlaylistId, - (state, { playlistId }): PlaylistState => { - return { - ...state, - currentPlaylistId: playlistId, - }; - } - ), - on( - PlaylistActions.handleAddingPlaylistByUrl, - (state, action): PlaylistState => { - if (action.isTemporary) { - return { - ...state, - channels: action.playlist.playlist.items as Channel[], - }; - } else { - return { - ...state, - playlists: playlistsAdapter.addOne( - action.playlist, - state.playlists - ), - }; - } - } - ) -); - -export const selectPlaylists = (state: PlaylistState) => state.playlists; -export const selectPlaylistId = (state: PlaylistState) => - state.playlists?.selectedId; - -export const selectIsEpgAvailableReducer = (state: PlaylistState) => - state.epgAvailable; -export const selectActiveReducer = (state: PlaylistState) => state.active; -export const selectCurrentEpgProgramReducer = (state: PlaylistState) => - state.currentEpgProgram; -export const selectChannelsReducer = (state: PlaylistState) => state.channels; -export const selectPlaylistsLoadingFlagReducer = (state: PlaylistState) => - state.playlists?.allPlaylistsLoaded; -export const selectCurrentPlaylistIdReducer = (state: PlaylistState) => - state.currentPlaylistId; +// Re-export from the new modular reducers structure +export * from './reducers/index'; diff --git a/libs/m3u-state/src/lib/reducers/channel.reducers.ts b/libs/m3u-state/src/lib/reducers/channel.reducers.ts new file mode 100644 index 000000000..c4d1de1be --- /dev/null +++ b/libs/m3u-state/src/lib/reducers/channel.reducers.ts @@ -0,0 +1,29 @@ +import { on } from '@ngrx/store'; +import { Channel } from 'shared-interfaces'; +import { ChannelActions } from '../actions'; +import { PlaylistState } from '../state'; + +export const channelReducers = [ + on( + ChannelActions.setActiveChannelSuccess, + (state, action): PlaylistState => { + const { channel } = action; + return { + ...state, + active: { ...channel, epgParams: '' } as Channel, + }; + } + ), + on(ChannelActions.resetActiveChannel, (state): PlaylistState => { + return { + ...state, + active: undefined, + }; + }), + on(ChannelActions.setChannels, (state, action): PlaylistState => { + return { + ...state, + channels: action.channels, + }; + }), +]; diff --git a/libs/m3u-state/src/lib/reducers/epg.reducers.ts b/libs/m3u-state/src/lib/reducers/epg.reducers.ts new file mode 100644 index 000000000..e3f971eca --- /dev/null +++ b/libs/m3u-state/src/lib/reducers/epg.reducers.ts @@ -0,0 +1,39 @@ +import { on } from '@ngrx/store'; +import moment from 'moment'; +import { Channel } from 'shared-interfaces'; +import { EpgActions } from '../actions'; +import { PlaylistState } from '../state'; + +export const epgReducers = [ + on(EpgActions.setActiveEpgProgram, (state, action): PlaylistState => { + const { program } = action; + const from = moment(program.start, 'YYYYMMDDHHmm ZZ').unix(); + const now = moment(Date.now()).unix(); + const epgParams = `?utc=${from}&lutc=${now}`; + return { + ...state, + active: { ...state.active, epgParams } as Channel, + }; + }), + on( + EpgActions.resetActiveEpgProgram, + (state): PlaylistState => ({ + ...state, + active: { ...state.active, epgParams: '' } as Channel, + }) + ), + on( + EpgActions.setCurrentEpgProgram, + (state, action): PlaylistState => ({ + ...state, + currentEpgProgram: action.program, + }) + ), + on( + EpgActions.setEpgAvailableFlag, + (state, action): PlaylistState => ({ + ...state, + epgAvailable: action.value, + }) + ), +]; diff --git a/libs/m3u-state/src/lib/reducers/favorites.reducers.ts b/libs/m3u-state/src/lib/reducers/favorites.reducers.ts new file mode 100644 index 000000000..9b58df853 --- /dev/null +++ b/libs/m3u-state/src/lib/reducers/favorites.reducers.ts @@ -0,0 +1,50 @@ +import { on } from '@ngrx/store'; +import { FavoritesActions } from '../actions'; +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]; + } + return { + ...state, + playlists: { + ...state.playlists, + entities: { + ...state.playlists.entities, + [state.playlists.selectedId]: { + ...state.playlists.entities[state.playlists.selectedId], + favorites, + }, + }, + }, + }; + }), + on(FavoritesActions.setFavorites, (state, action): PlaylistState => { + const { channelIds } = action; + return { + ...state, + playlists: { + ...state.playlists, + entities: { + ...state.playlists.entities, + [state.playlists.selectedId]: { + ...state.playlists.entities[state.playlists.selectedId], + favorites: channelIds, + }, + }, + }, + }; + }), +]; diff --git a/libs/m3u-state/src/lib/reducers/filter.reducers.ts b/libs/m3u-state/src/lib/reducers/filter.reducers.ts new file mode 100644 index 000000000..a4533992c --- /dev/null +++ b/libs/m3u-state/src/lib/reducers/filter.reducers.ts @@ -0,0 +1,18 @@ +import { on } from '@ngrx/store'; +import { FilterActions } from '../actions'; +import { PlaylistState } from '../state'; + +export const filterReducers = [ + on( + FilterActions.setSelectedFilters, + (state, { selectedFilters }): PlaylistState => { + return { + ...state, + playlists: { + ...state.playlists, + selectedFilters, + }, + }; + } + ), +]; diff --git a/libs/m3u-state/src/lib/reducers/index.ts b/libs/m3u-state/src/lib/reducers/index.ts new file mode 100644 index 000000000..dbf3afd18 --- /dev/null +++ b/libs/m3u-state/src/lib/reducers/index.ts @@ -0,0 +1,32 @@ +import { createReducer } from '@ngrx/store'; +import { initialState, PlaylistState } from '../state'; +import { channelReducers } from './channel.reducers'; +import { epgReducers } from './epg.reducers'; +import { favoritesReducers } from './favorites.reducers'; +import { filterReducers } from './filter.reducers'; +import { playlistReducers } from './playlist.reducers'; + +export const playlistReducer = createReducer( + initialState, + ...epgReducers, + ...channelReducers, + ...playlistReducers, + ...favoritesReducers, + ...filterReducers +); + +// Selectors +export const selectPlaylists = (state: PlaylistState) => state.playlists; +export const selectPlaylistId = (state: PlaylistState) => + state.playlists?.selectedId; + +export const selectIsEpgAvailableReducer = (state: PlaylistState) => + state.epgAvailable; +export const selectActiveReducer = (state: PlaylistState) => state.active; +export const selectCurrentEpgProgramReducer = (state: PlaylistState) => + state.currentEpgProgram; +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 new file mode 100644 index 000000000..622cc037d --- /dev/null +++ b/libs/m3u-state/src/lib/reducers/playlist.reducers.ts @@ -0,0 +1,166 @@ +import { on } from '@ngrx/store'; +import { Channel } from 'shared-interfaces'; +import { PlaylistActions } from '../actions'; +import { playlistsAdapter } from '../playlists.state'; +import { PlaylistState } from '../state'; + +export const playlistReducers = [ + on(PlaylistActions.loadPlaylistsSuccess, (state, action): PlaylistState => { + return { + ...state, + playlists: playlistsAdapter.addMany(action.playlists, { + ...state.playlists, + allPlaylistsLoaded: true, + }), + }; + }), + on(PlaylistActions.removePlaylist, (state, action): PlaylistState => { + return { + ...state, + playlists: playlistsAdapter.removeOne( + action.playlistId, + state.playlists + ), + }; + }), + on(PlaylistActions.updatePlaylist, (state, action): PlaylistState => { + return { + ...state, + playlists: playlistsAdapter.updateOne( + { + id: action.playlistId, + changes: { + ...action.playlist, + _id: action.playlistId, + updateDate: Date.now(), + count: action.playlist.playlist.items.length, + userAgent: action.playlist.userAgent, + favorites: + state.playlists.entities[action.playlistId] + ?.favorites ?? [], + }, + }, + state.playlists + ), + }; + }), + on(PlaylistActions.addPlaylist, (state, action): PlaylistState => { + return { + ...state, + playlists: playlistsAdapter.addOne( + action.playlist, + state.playlists + ), + }; + }), + on(PlaylistActions.addManyPlaylists, (state, action): PlaylistState => { + return { + ...state, + playlists: playlistsAdapter.addMany( + action.playlists, + state.playlists + ), + }; + }), + on(PlaylistActions.setActivePlaylist, (state, action): PlaylistState => { + return { + ...state, + playlists: { + ...state.playlists, + selectedId: action.playlistId, + }, + }; + }), + on( + PlaylistActions.updatePlaylistPositions, + (state, action): PlaylistState => { + return { + ...state, + playlists: playlistsAdapter.updateMany( + action.positionUpdates, + state.playlists + ), + }; + } + ), + on(PlaylistActions.updatePlaylistMeta, (state, action): PlaylistState => { + return { + ...state, + playlists: playlistsAdapter.updateOne( + { + id: action.playlist._id, + changes: { + title: action.playlist.title, + autoRefresh: action.playlist.autoRefresh || false, + userAgent: action.playlist.userAgent, + ...(action.playlist.serverUrl !== null + ? { serverUrl: action.playlist.serverUrl } + : {}), + ...(action.playlist.username !== null + ? { username: action.playlist.username } + : {}), + ...(action.playlist.password !== null + ? { password: action.playlist.password } + : {}), + ...(action.playlist.macAddress !== null + ? { macAddress: action.playlist.macAddress } + : {}), + ...(action.playlist.portalUrl !== null + ? { portalUrl: action.playlist.portalUrl } + : {}), + }, + }, + state.playlists + ), + }; + }), + on(PlaylistActions.updateManyPlaylists, (state, action): PlaylistState => { + return { + ...state, + playlists: playlistsAdapter.updateMany( + action.playlists.map((updatedPlaylist) => ({ + id: updatedPlaylist._id, + changes: { + ...updatedPlaylist, + updateDate: Date.now(), + }, + })), + state.playlists + ), + }; + }), + on(PlaylistActions.removeAllPlaylists, (state): PlaylistState => { + return { + ...state, + playlists: playlistsAdapter.removeAll(state.playlists), + }; + }), + on( + PlaylistActions.setCurrentPlaylistId, + (state, { playlistId }): PlaylistState => { + return { + ...state, + currentPlaylistId: playlistId, + }; + } + ), + on( + PlaylistActions.handleAddingPlaylistByUrl, + (state, action): PlaylistState => { + if (action.isTemporary) { + return { + ...state, + channels: action.playlist.playlist.items as Channel[], + }; + } else { + return { + ...state, + playlists: playlistsAdapter.addOne( + action.playlist, + state.playlists + ), + }; + } + } + ), +];