fix(dashboard): read M3U favorites after queued favorite writes

getM3uFavoriteChannels could query SQLite before a just-toggled favorite
was written: updateFavorites is serialized per playlist and can land late,
while the dashboard reads as soon as the store changes and never reloads
because playlistFavoritesReloadKey is unchanged. Await the playlist's write
queue tail (which never rejects) before reading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-09-29 20:56:01 +02:00
1 parent 28b022ff48
commit 5e9e77d8db
3 files changed
+54

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
---
type: fix
area: dashboard
---
An M3U channel added to favorites right before opening the dashboard now
appears in the favorites rail immediately, instead of only after the next
reload.
@@ -345,6 +345,49 @@ describe('PlaylistsService', () => {
expect(electron.dbGetAppPlaylists).not.toHaveBeenCalled();
});
it('reads resolved M3U favorites only after a queued favorites write lands', async () => {
let storedFavorites: string[] = [];
let releaseWrite!: () => void;
const writeGate = new Promise<void>((resolve) => {
releaseWrite = resolve;
});
const electron = {
dbGetAppPlaylist: jest.fn(async () => {
await writeGate;
return { _id: 'playlist-1', favorites: storedFavorites };
}),
dbGetAppPlaylistFavoriteChannels: jest.fn(async () =>
storedFavorites.map((id) => ({ id }))
),
dbGetAppPlaylists: jest.fn(async () => []),
dbGetAppState: jest.fn(async (key: string) =>
key === SQLITE_PLAYLIST_MIGRATION_FLAG ||
key === STALKER_PLAYLIST_METADATA_MIGRATION_FLAG
? '1'
: null
),
dbSetAppState: jest.fn(),
dbUpsertAppPlaylist: jest.fn(async (playlist: Playlist) => {
storedFavorites = playlist.favorites as string[];
}),
dbUpsertAppPlaylists: jest.fn(),
};
testWindow.electron = electron;
const service = createService();
const write = firstValueFrom(
service.updateFavorites('playlist-1', ['channel-1'])
);
const read = firstValueFrom(
service.getM3uFavoriteChannels('playlist-1')
);
await new Promise((resolve) => setTimeout(resolve));
releaseWrite();
await write;
await expect(read).resolves.toEqual([{ id: 'channel-1' }]);
});
it('falls back from resolved M3U favorites when SQLite playlist migration is incomplete', async () => {
const electron = {
dbGetAppPlaylist: jest.fn(),
@@ -1092,6 +1092,9 @@ export class PlaylistsService {
return from(
(async () => {
// A favorite toggled just before this read may still be queued;
// the queue tail never rejects.
await this.playlistWriteQueues.get(playlistId);
const alreadyMigrated = await electron.dbGetAppState(
SQLITE_PLAYLIST_MIGRATION_FLAG
);