fix(xtream): keep category management available

Fixes #913
This commit is contained in:
4gray committed 2026-05-09 18:34:57 +02:00
1 parent 134b5fb779
commit aead89e3e6
3 files changed
+102 -10

No files matched your search

@@ -1,7 +1,6 @@
import { Locator, Page } from '@playwright/test';
import {
addXtreamPortal,
clickCategoryById,
closeElectronApp,
expect,
launchElectronApp,
@@ -205,6 +204,86 @@ test.describe('Electron Xtream Category Management', () => {
await closeElectronApp(app);
}
});
test('allows restoring live categories after every category is hidden', async ({
dataDir,
request,
}) => {
await resetMockServers(request, ['xtream']);
const portalName = 'All Hidden Categories Xtream';
const app = await launchElectronApp(dataDir);
try {
await addXtreamPortal(app.mainWindow, {
name: portalName,
});
await waitForXtreamWorkspaceReady(app.mainWindow);
await openWorkspaceSection(app.mainWindow, 'Live TV');
await app.mainWindow.waitForURL(
/\/workspace\/xtreams\/[^/]+\/live/
);
await expect
.poll(() => readVisibleSidebarCategoryNames(app.mainWindow), {
timeout: 15000,
})
.not.toEqual([]);
const initialCategoryNames = await readVisibleSidebarCategoryNames(
app.mainWindow
);
let dialog = await openManageCategoriesDialog(app.mainWindow);
await dialog
.getByRole('button', {
name: 'Deselect All',
exact: true,
})
.click();
await expect(
dialog.locator('.category-item mat-checkbox input:checked')
).toHaveCount(0);
await dialog
.getByRole('button', { name: 'Save', exact: true })
.click();
await app.mainWindow.waitForSelector('mat-dialog-container', {
state: 'detached',
});
await expect
.poll(() => readVisibleSidebarCategoryNames(app.mainWindow), {
timeout: 15000,
})
.toEqual([]);
await expect(
app.mainWindow.getByRole('button', {
name: 'Manage categories',
exact: true,
})
).toBeEnabled();
dialog = await openManageCategoriesDialog(app.mainWindow);
await dialog
.getByRole('button', { name: 'Select All', exact: true })
.click();
await expect(
dialog.locator('.category-item mat-checkbox input:checked')
).toHaveCount(initialCategoryNames.length);
await dialog
.getByRole('button', { name: 'Save', exact: true })
.click();
await app.mainWindow.waitForSelector('mat-dialog-container', {
state: 'detached',
});
await expectVisibleSidebarCategoryNames(
app.mainWindow,
initialCategoryNames
);
} finally {
await closeElectronApp(app);
}
});
});
async function openManageCategoriesDialog(page: Page) {
@@ -266,6 +345,10 @@ async function expectVisibleSidebarCategoryNames(
.toEqual(expectedNames);
} catch (error) {
const actualNames = await readVisibleSidebarCategoryNames(page);
if (stringArraysEqual(actualNames, expectedNames)) {
return;
}
throw new Error(
`Expected visible sidebar categories ${JSON.stringify(
expectedNames
@@ -300,6 +383,13 @@ async function readVisibleSidebarCategoryNames(page: Page): Promise<string[]> {
return actualNames;
}
function stringArraysEqual(left: string[], right: string[]): boolean {
return (
left.length === right.length &&
left.every((value, index) => value === right[index])
);
}
async function pickSidebarCategory(
page: Page
): Promise<{ id: string; itemCount: number; name: string }> {
+11 -8
View File
@@ -66,14 +66,12 @@ ALTER TABLE categories ADD COLUMN hidden INTEGER DEFAULT 0
**Integration Points**
- `xtream-main-container.component.ts` - Movies & Series sections
- `live-stream-layout.component.ts` - Live TV section
Both components:
1. Add a "tune" icon button in the sidebar header
2. Open the dialog with playlist ID and content type
3. Call `xtreamStore.reloadCategories()` after dialog closes with changes
- `libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-context-panel.component.ts`
renders the tune icon button for Xtream Live TV, Movies, and Series sidebars.
- The workspace context panel lazy-loads `CategoryManagementDialogComponent`
from `@iptvnator/portal/xtream/feature`, opens it with playlist ID, content
type, and item counts, then calls `xtreamStore.reloadCategories()` after a
successful dialog save.
### Store
@@ -87,6 +85,11 @@ Added `reloadCategories()` method to refresh categories from database after visi
- **Persistence**: Visibility settings survive playlist refresh (see below)
- **Per-playlist, per-type**: Categories are managed per playlist and per content type (live/movies/series)
- **No content deletion**: Hiding a category only affects sidebar visibility; the category and its content remain in the database
- **All-hidden recovery**: Once the selected Xtream type is loaded, the manage
categories button remains available even if every visible category has been
hidden. The sidebar category list is filtered, but the dialog reads all
categories through `getAllXtreamCategories()` so users can select categories
again.
### Visibility Preservation During Refresh
@@ -101,7 +101,6 @@ export class WorkspaceContextPanelComponent {
readonly canManageXtreamCategories = computed(
() =>
this.isXtreamCategories() &&
this.xtreamCategories().length > 0 &&
this.xtreamSelectedTypeCountsReady()
);
readonly xtreamStatusText = computed(() => {