fix(settings): match noncanonical PWA Xtream category ids against their locks

The PWA data source compared raw provider category ids such as "009"
with locks stored as numbers, so such a category stayed visible while
locked. Both sides are now compared in canonical numeric form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-09-27 16:05:59 +02:00
1 parent 8d11f674cf
commit b078526bef
3 files changed
+36 -3

No files matched your search

+3 -1
View File
@@ -276,7 +276,9 @@ so a cancelled or refused PIN leaves it showing the real state.
(`showChannelViews`) and the groups view keeps its rail instead of the
generic empty state.
- **Xtream (PWA):** `PwaXtreamDataSource` drops withheld categories,
streams and search hits at read time; the same reloads apply.
streams and search hits at read time; the same reloads apply. Provider
category ids are compared in canonical numeric form (`"009"` matches the
stored lock `9`), as the lock editor stores them with `Number`.
- **Warm-cache detection (Electron):** the filtered category/content reads
can be empty while the offline cache is complete (every category locked),
so `ElectronXtreamDataSource` confirms an empty read with the unfiltered
@@ -98,6 +98,25 @@ describe('PwaXtreamDataSource', () => {
expect(unlocked).toHaveLength(2);
});
it('matches a noncanonical provider category id against its numeric lock', async () => {
apiService.getStreams.mockResolvedValue([
{ stream_id: 1, name: 'Family film', category_id: '5' },
{ stream_id: 2, name: 'Family after dark', category_id: '009' },
]);
parentalLock.active.mockReturnValue(true);
parentalLock.lockedXtreamIds.mockImplementation((_id, type) =>
type === 'movies' ? [9] : []
);
const content = await dataSource.getContent(
'playlist-1',
credentials,
'movie'
);
expect(content.map((item) => item.name)).toEqual(['Family film']);
});
it('withholds everything while the lock store cannot be read', async () => {
apiService.getStreams.mockResolvedValue([
{ stream_id: 1, name: 'Family film', category_id: '5' },
@@ -391,7 +391,8 @@ export class PwaXtreamDataSource implements IXtreamDataSource {
return withheld.size === 0
? categories
: categories.filter(
(category) => !withheld.has(String(category.category_id))
(category) =>
!withheld.has(canonicalCategoryId(category.category_id))
);
}
@@ -404,7 +405,7 @@ export class PwaXtreamDataSource implements IXtreamDataSource {
return withheld.size === 0
? items
: items.filter(
(item) => !withheld.has(String(item.category_id ?? ''))
(item) => !withheld.has(canonicalCategoryId(item.category_id))
);
}
@@ -1585,3 +1586,14 @@ export class PwaXtreamDataSource implements IXtreamDataSource {
this.contentCache.clear();
}
}
/**
* The lock store keys provider category ids as numbers (the lock editor
* converts with `Number`), so a noncanonical provider id such as `"001"`
* must be compared as `"1"`. Non-numeric and empty ids stay as they are.
*/
function canonicalCategoryId(id: string | number | null | undefined): string {
const raw = String(id ?? '').trim();
const numeric = Number(raw);
return raw !== '' && Number.isFinite(numeric) ? String(numeric) : raw;
}