feat(stalker): reflect persisted connections in state

This commit is contained in:
4gray committed 2026-07-27 10:50:01 +02:00
1 parent cede2c6bb3
commit 57e108b8cb
3 files changed
+76 -11

No files matched your search

+7 -1
View File
@@ -1,5 +1,10 @@
import { createActionGroup, emptyProps, props } from '@ngrx/store';
import { Channel, EpgProgram, Playlist, PlaylistMeta } from '@iptvnator/shared/interfaces';
import {
Channel,
EpgProgram,
Playlist,
PlaylistMeta,
} from '@iptvnator/shared/interfaces';
export const PlaylistActions = createActionGroup({
source: 'Playlists',
@@ -7,6 +12,7 @@ export const PlaylistActions = createActionGroup({
'Load Playlists': emptyProps(),
'Load Playlists Success': props<{ playlists: PlaylistMeta[] }>(),
'Add Playlist': props<{ playlist: Playlist }>(),
'Stalker Connection Persisted': props<{ playlist: Playlist }>(),
'Add Many Playlists': props<{ playlists: Playlist[] }>(),
'Remove Playlist': props<{ playlistId: string }>(),
'Update Playlist Meta': props<{ playlist: PlaylistMeta }>(),
@@ -160,9 +160,9 @@ describe('playlistReducers', () => {
})
);
expect(
nextState.playlists.entities['playlist-1']?.autoRefresh
).toBe(true);
expect(nextState.playlists.entities['playlist-1']?.autoRefresh).toBe(
true
);
});
it('keeps autoRefresh disabled on playlist refresh when the existing playlist has it disabled', () => {
@@ -194,8 +194,57 @@ describe('playlistReducers', () => {
})
);
expect(
nextState.playlists.entities['playlist-1']?.autoRefresh
).toBe(false);
expect(nextState.playlists.entities['playlist-1']?.autoRefresh).toBe(
false
);
});
it('reflects an atomically persisted Stalker connection without triggering another write', () => {
const existingPlaylist = {
_id: 'stalker-1',
autoRefresh: false,
count: 0,
importDate: '2026-07-27T00:00:00.000Z',
macAddress: '00:1A:79:AA:BB:CC',
portalUrl: 'https://old.example/server/load.php',
stalkerToken: 'legacy-token',
title: 'Old Portal',
} as PlaylistMeta;
const persistedPlaylist = {
...existingPlaylist,
portalUrl: 'https://new.example/server/load.php',
stalkerLandingUrl: 'https://new.example/c/',
stalkerRequestRecipe: 'full-session',
stalkerSourceUrl: 'https://new.example/c/',
title: 'Connected Portal',
} as Playlist;
delete persistedPlaylist.stalkerToken;
const state = {
...initialState,
playlists: playlistsAdapter.addOne(
existingPlaylist,
initialState.playlists
),
};
const nextState = reducer(
state,
PlaylistActions.stalkerConnectionPersisted({
playlist: persistedPlaylist,
})
);
expect(nextState.playlists.entities['stalker-1']).toEqual(
expect.objectContaining({
portalUrl: 'https://new.example/server/load.php',
stalkerLandingUrl: 'https://new.example/c/',
stalkerRequestRecipe: 'full-session',
stalkerSourceUrl: 'https://new.example/c/',
title: 'Connected Portal',
})
);
expect(nextState.playlists.entities['stalker-1']).not.toHaveProperty(
'stalkerToken'
);
});
});
@@ -39,11 +39,9 @@ export const playlistReducers = [
detectedEpgUrls:
action.playlist.detectedEpgUrls ??
currentPlaylist?.detectedEpgUrls,
enabledEpgUrls:
action.playlist.epgUrls ?? currentPlaylist?.epgUrls,
enabledEpgUrls: action.playlist.epgUrls ?? currentPlaylist?.epgUrls,
manualEpgUrls:
action.playlist.manualEpgUrls ??
currentPlaylist?.manualEpgUrls,
action.playlist.manualEpgUrls ?? currentPlaylist?.manualEpgUrls,
disabledEpgUrls:
action.playlist.disabledEpgUrls ??
currentPlaylist?.disabledEpgUrls,
@@ -86,6 +84,18 @@ export const playlistReducers = [
),
};
}),
on(
PlaylistActions.stalkerConnectionPersisted,
(state, action): PlaylistState => {
return {
...state,
playlists: playlistsAdapter.setOne(
action.playlist,
state.playlists
),
};
}
),
on(PlaylistActions.addManyPlaylists, (state, action): PlaylistState => {
return {
...state,