From a3a4c6b5ea4a38c3f885ebb84e755ec20a396662 Mon Sep 17 00:00:00 2001
From: Eliran Sapir <114778791+sapireli@users.noreply.github.com>
Date: Thu, 7 May 2026 05:13:00 -0400
Subject: [PATCH 1/2] Fix Xtream section search across categories
---
.../features/with-selection.feature.spec.ts | 68 ++++++++++++++++++-
.../stores/features/with-selection.feature.ts | 6 +-
.../live-stream-layout.component.html | 2 +-
.../live-stream-layout.component.spec.ts | 31 ++++++++-
.../services/workspace-shell.facade.spec.ts | 65 ++++++++++++------
.../services/workspace-shell.facade.ts | 15 ++--
6 files changed, 150 insertions(+), 37 deletions(-)
diff --git a/libs/portal/xtream/data-access/src/lib/stores/features/with-selection.feature.spec.ts b/libs/portal/xtream/data-access/src/lib/stores/features/with-selection.feature.spec.ts
index d7676347c..ff5268eba 100644
--- a/libs/portal/xtream/data-access/src/lib/stores/features/with-selection.feature.spec.ts
+++ b/libs/portal/xtream/data-access/src/lib/stores/features/with-selection.feature.spec.ts
@@ -9,8 +9,37 @@ const TestSelectionStore = signalStore(
vod: 'ready',
series: 'ready',
},
- liveCategories: [],
- liveStreams: [],
+ liveCategories: [
+ {
+ id: 50,
+ category_id: '50',
+ category_name: 'News',
+ type: 'live',
+ },
+ {
+ id: 60,
+ category_id: '60',
+ category_name: 'Sports',
+ type: 'live',
+ },
+ ],
+ liveStreams: [
+ {
+ xtream_id: 201,
+ category_id: '50',
+ title: 'World News',
+ },
+ {
+ xtream_id: 202,
+ category_id: '60',
+ title: 'World Sports',
+ },
+ {
+ xtream_id: 203,
+ category_id: '60',
+ title: 'Match Day',
+ },
+ ],
vodCategories: [
{
id: 10,
@@ -175,7 +204,7 @@ describe('withSelection', () => {
).toEqual(['Stargate SG-1', 'Stargate Atlantis']);
});
- it('keeps VOD search scoped to the selected category', () => {
+ it('keeps VOD category route search scoped to the selected category', () => {
store.setSelectedContentType('vod');
store.setSelectedCategory(10);
@@ -185,4 +214,37 @@ describe('withSelection', () => {
store.selectItemsFromSelectedCategory().map((item) => item.title)
).toEqual(['First']);
});
+
+ it('keeps series category route search scoped to the selected category', () => {
+ store.setSelectedContentType('series');
+ store.setSelectedCategory(30);
+
+ store.setCategorySearchTerm('stargate');
+
+ expect(
+ store.selectItemsFromSelectedCategory().map((item) => item.title)
+ ).toEqual(['Stargate SG-1']);
+ });
+
+ it('keeps live category route search scoped to the selected category', () => {
+ store.setSelectedContentType('live');
+ store.setSelectedCategory(50);
+
+ store.setCategorySearchTerm('world');
+
+ expect(
+ store.selectItemsFromSelectedCategory().map((item) => item.title)
+ ).toEqual(['World News']);
+ });
+
+ it('filters all live items across categories when no category is selected', () => {
+ store.setSelectedContentType('live');
+ store.setSelectedCategory(null);
+
+ store.setCategorySearchTerm('world');
+
+ expect(
+ store.selectItemsFromSelectedCategory().map((item) => item.title)
+ ).toEqual(['World News', 'World Sports']);
+ });
});
diff --git a/libs/portal/xtream/data-access/src/lib/stores/features/with-selection.feature.ts b/libs/portal/xtream/data-access/src/lib/stores/features/with-selection.feature.ts
index fe2fcfb33..7157306ee 100644
--- a/libs/portal/xtream/data-access/src/lib/stores/features/with-selection.feature.ts
+++ b/libs/portal/xtream/data-access/src/lib/stores/features/with-selection.feature.ts
@@ -276,19 +276,19 @@ export function withSelection() {
: sortedContent();
filtered = filterBySearchTerm(filtered, searchTerm);
- return categoryId
+ return categoryId || searchTerm
? sortByMode(filtered, sortMode, categoryType)
: filtered;
}
if (!categoryId) {
- return sortedContent();
+ return filterBySearchTerm(sortedContent(), searchTerm);
}
const filtered = content.filter(
(item) => Number(item.category_id) === categoryId
);
- return filtered;
+ return filterBySearchTerm(filtered, searchTerm);
});
return {
diff --git a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html
index fcdfe651f..9a2fe6cce 100644
--- a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html
+++ b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html
@@ -92,7 +92,7 @@
}
@if (!selectedCategoryId()) {
- @if (isSelectedTypeContentLoading()) {
+ @if (isSelectedTypeContentLoading() || workspaceSearchTerm()) {
{
let fixture: ComponentFixture;
let component: LiveStreamLayoutComponent;
+ let routeQueryParamMap: BehaviorSubject<
+ ReturnType
+ >;
const fixedNow = new Date('2026-04-05T12:00:00.000Z');
const sampleChannel = {
@@ -188,6 +192,7 @@ describe('LiveStreamLayoutComponent', () => {
selectedContentType.set('live');
selectedItem.set(sampleChannel);
currentPlaylist.set(playlist);
+ routeQueryParamMap = new BehaviorSubject(convertToParamMap({}));
await TestBed.configureTestingModule({
imports: [LiveStreamLayoutComponent, NoopAnimationsModule],
@@ -199,11 +204,11 @@ describe('LiveStreamLayoutComponent', () => {
data: {},
queryParamMap: convertToParamMap({}),
},
- queryParamMap: of(convertToParamMap({})),
+ queryParamMap: routeQueryParamMap.asObservable(),
pathFromRoot: [
{
snapshot: {
- data: {},
+ data: { layout: 'workspace' },
},
},
],
@@ -343,6 +348,26 @@ describe('LiveStreamLayoutComponent', () => {
).toBeNull();
});
+ it('shows the cross-category live channel list while searching from the live root', () => {
+ selectedCategoryId.set(null);
+ selectedTypeContentLoading.set(false);
+ routeQueryParamMap.next(convertToParamMap({ q: 'world' }));
+
+ fixture.detectChanges();
+
+ const list = fixture.debugElement.query(
+ By.directive(StubPortalChannelsListComponent)
+ );
+
+ expect(list).not.toBeNull();
+ expect(list.componentInstance.searchTermInput() as string).toBe(
+ 'world'
+ );
+ expect(
+ fixture.nativeElement.querySelector('app-portal-empty-state')
+ ).toBeNull();
+ });
+
it('renders the current EPG program in the collapsible panel summary', () => {
epgItems.set([
buildEpgItem(
diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/services/workspace-shell.facade.spec.ts b/libs/workspace/shell/feature/src/lib/workspace-shell/services/workspace-shell.facade.spec.ts
index 0f569b249..864202a55 100644
--- a/libs/workspace/shell/feature/src/lib/workspace-shell/services/workspace-shell.facade.spec.ts
+++ b/libs/workspace/shell/feature/src/lib/workspace-shell/services/workspace-shell.facade.spec.ts
@@ -17,10 +17,7 @@ import {
} from '@iptvnator/portal/shared/util';
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
-import {
- PlaylistsService,
- SettingsStore,
-} from 'services';
+import { PlaylistsService, SettingsStore } from 'services';
import { PlaylistMeta } from 'shared-interfaces';
import {
WorkspaceStartupPreferencesService,
@@ -285,7 +282,8 @@ describe('WorkspaceShellFacade', () => {
params?: Record
) => {
if (
- key === 'WORKSPACE.SHELL.XTREAM_IMPORT_PROGRESS' &&
+ key ===
+ 'WORKSPACE.SHELL.XTREAM_IMPORT_PROGRESS' &&
params
) {
return `${params.type} imported: ${params.current} / ${params.total}`;
@@ -325,7 +323,9 @@ describe('WorkspaceShellFacade', () => {
});
it('routes dashboard search Enter into the active Xtream playlist search', () => {
- const xtreamStore = TestBed.inject(XtreamStore) as unknown as MockXtreamStore;
+ const xtreamStore = TestBed.inject(
+ XtreamStore
+ ) as unknown as MockXtreamStore;
facade.currentUrl.set('/workspace/dashboard');
router.navigate.mockClear();
@@ -342,7 +342,9 @@ describe('WorkspaceShellFacade', () => {
});
it('uses a loading label for remote Xtream fetch phases', () => {
- const xtreamStore = TestBed.inject(XtreamStore) as unknown as MockXtreamStore;
+ const xtreamStore = TestBed.inject(
+ XtreamStore
+ ) as unknown as MockXtreamStore;
const xtreamImport = TestBed.inject(WorkspaceShellXtreamImportService);
xtreamStore.currentImportPhase.set('loading-categories');
@@ -446,7 +448,9 @@ describe('WorkspaceShellFacade', () => {
});
it('builds a type-aware xtream import progress label', () => {
- const xtreamStore = TestBed.inject(XtreamStore) as unknown as MockXtreamStore;
+ const xtreamStore = TestBed.inject(
+ XtreamStore
+ ) as unknown as MockXtreamStore;
const xtreamImport = TestBed.inject(WorkspaceShellXtreamImportService);
xtreamStore.activeImportContentType.set('vod');
@@ -486,9 +490,12 @@ describe('WorkspaceShellFacade', () => {
facade.onSearchEnter('');
TestBed.flushEffects();
- expect(router.navigateByUrl).toHaveBeenCalledWith('/workspace/sources', {
- replaceUrl: true,
- });
+ expect(router.navigateByUrl).toHaveBeenCalledWith(
+ '/workspace/sources',
+ {
+ replaceUrl: true,
+ }
+ );
});
it('navigates to the global favorites route', () => {
@@ -513,7 +520,9 @@ describe('WorkspaceShellFacade', () => {
);
expect(storeDispatch).toHaveBeenCalled();
expect(router.navigateByUrl).toHaveBeenCalledWith(
- expect.stringMatching(/^\/workspace\/stalker\/pl-1\/recent\?refresh=/),
+ expect.stringMatching(
+ /^\/workspace\/stalker\/pl-1\/recent\?refresh=/
+ ),
{
replaceUrl: true,
}
@@ -533,7 +542,9 @@ describe('WorkspaceShellFacade', () => {
});
it('applies q to Xtream category search on vod routes', () => {
- const xtreamStore = TestBed.inject(XtreamStore) as unknown as MockXtreamStore;
+ const xtreamStore = TestBed.inject(
+ XtreamStore
+ ) as unknown as MockXtreamStore;
facade.currentUrl.set('/workspace/xtreams/pl-1/vod?q=neo');
(facade as { syncSearchFromRoute: () => void }).syncSearchFromRoute();
@@ -543,6 +554,19 @@ describe('WorkspaceShellFacade', () => {
expect(xtreamStore.setSearchTerm).not.toHaveBeenCalled();
});
+ it('applies q to Xtream category search on live routes', () => {
+ const xtreamStore = TestBed.inject(
+ XtreamStore
+ ) as unknown as MockXtreamStore;
+
+ facade.currentUrl.set('/workspace/xtreams/pl-1/live?q=world');
+ (facade as { syncSearchFromRoute: () => void }).syncSearchFromRoute();
+ TestBed.flushEffects();
+
+ expect(xtreamStore.setCategorySearchTerm).toHaveBeenCalledWith('world');
+ expect(xtreamStore.setSearchTerm).not.toHaveBeenCalled();
+ });
+
it('enables local-filter search on playlist favorites routes', () => {
facade.currentUrl.set('/workspace/playlists/pl-1/favorites?q=news');
(facade as { syncSearchFromRoute: () => void }).syncSearchFromRoute();
@@ -586,9 +610,9 @@ describe('WorkspaceShellFacade', () => {
});
it('persists the last restorable route from navigation events', () => {
- expect(startupPreferences.persistLastRestorablePath).toHaveBeenCalledWith(
- '/workspace/xtreams/pl-1/vod'
- );
+ expect(
+ startupPreferences.persistLastRestorablePath
+ ).toHaveBeenCalledWith('/workspace/xtreams/pl-1/vod');
});
it('uses the translated recent scope label on the global recent route', () => {
@@ -689,9 +713,9 @@ describe('WorkspaceShellFacade', () => {
expect(commands[0]?.id).toBe('clear-current-favorites');
expect(commands[0]?.group).toBe('view');
- expect(commands.some((command) => command.id === 'playlist-search')).toBe(
- false
- );
+ expect(
+ commands.some((command) => command.id === 'playlist-search')
+ ).toBe(false);
unregister();
});
@@ -701,8 +725,7 @@ describe('WorkspaceShellFacade', () => {
open: jest.Mock;
};
dialog.open.mockReturnValueOnce({
- afterClosed: () =>
- of({ commandId: 'open-settings', query: '' }),
+ afterClosed: () => of({ commandId: 'open-settings', query: '' }),
});
facade.openCommandPalette();
diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/services/workspace-shell.facade.ts b/libs/workspace/shell/feature/src/lib/workspace-shell/services/workspace-shell.facade.ts
index 90fbbd6bb..9aa49cb8e 100644
--- a/libs/workspace/shell/feature/src/lib/workspace-shell/services/workspace-shell.facade.ts
+++ b/libs/workspace/shell/feature/src/lib/workspace-shell/services/workspace-shell.facade.ts
@@ -576,7 +576,11 @@ export class WorkspaceShellFacade {
return;
}
- if (section === 'vod' || section === 'series') {
+ if (
+ section === 'vod' ||
+ section === 'series' ||
+ section === 'live'
+ ) {
this.xtreamStore.setCategorySearchTerm(term);
}
});
@@ -617,8 +621,8 @@ export class WorkspaceShellFacade {
}
openActiveExternalSessionTarget(): void {
- const playlistId = this.externalPlaybackSession()?.contentInfo
- ?.playlistId;
+ const playlistId =
+ this.externalPlaybackSession()?.contentInfo?.playlistId;
if (!playlistId) return;
const playlist = this.playlists().find((p) => p._id === playlistId);
@@ -792,7 +796,8 @@ export class WorkspaceShellFacade {
}
private readonly commandBuilderActions: CommandBuilderActions = {
- openPlaylistSearch: (query) => this.openPlaylistSearchFromPalette(query),
+ openPlaylistSearch: (query) =>
+ this.openPlaylistSearchFromPalette(query),
refreshCurrentPlaylist: () => this.refreshCurrentPlaylist(),
openPlaylistInfo: () => this.openPlaylistInfo(),
openAccountInfo: () => this.openAccountInfo(),
@@ -806,7 +811,6 @@ export class WorkspaceShellFacade {
: this.workspaceActions.openAddPlaylistDialog(),
};
-
private openPlaylistSearchFromPalette(query: string): void {
const effectiveContext =
this.dashboardXtreamContext() ?? this.currentContext();
@@ -905,5 +909,4 @@ export class WorkspaceShellFacade {
): string {
return this.translate.instant(key, params);
}
-
}
From 6efa8b90729430d25a470d4b81a0856113fdc4bf Mon Sep 17 00:00:00 2001
From: 4gray
Date: Fri, 8 May 2026 21:05:10 +0200
Subject: [PATCH 2/2] fix(xtream): preserve live search playback sidebar
Entire-Checkpoint: f957cd9849e0
---
apps/electron-backend-e2e/src/search.e2e.ts | 184 +++++++++++++++++-
.../live-stream-layout.component.html | 36 ++--
.../live-stream-layout.component.spec.ts | 36 +++-
.../live-stream-layout.component.ts | 9 +-
4 files changed, 236 insertions(+), 29 deletions(-)
diff --git a/apps/electron-backend-e2e/src/search.e2e.ts b/apps/electron-backend-e2e/src/search.e2e.ts
index 291dd7175..653acae96 100644
--- a/apps/electron-backend-e2e/src/search.e2e.ts
+++ b/apps/electron-backend-e2e/src/search.e2e.ts
@@ -206,7 +206,10 @@ test.describe('Electron Workspace Search', () => {
await fillWorkspaceSearch(app.mainWindow, sample.groupTitle);
- await expectPathname(app.mainWindow, /\/workspace\/global-favorites$/);
+ await expectPathname(
+ app.mainWindow,
+ /\/workspace\/global-favorites$/
+ );
await expectQueryParam(app.mainWindow, 'q', sample.groupTitle);
await expectWorkspaceSearchScope(
app.mainWindow,
@@ -411,6 +414,134 @@ test.describe('Electron Workspace Search', () => {
}
});
+ test('@search @xtream filters VOD and series root views across categories', async ({
+ dataDir,
+ request,
+ }) => {
+ await resetMockServers(request, ['xtream']);
+ const vodSample = await fetchXtreamVodFixture(request);
+ const seriesSample = await fetchXtreamSeriesFixture(request);
+
+ const app = await launchElectronApp(dataDir);
+
+ try {
+ await addXtreamPortal(app.mainWindow, {
+ password: xtreamSearchPassword,
+ username: xtreamSearchUsername,
+ });
+ await waitForXtreamWorkspaceReady(app.mainWindow);
+
+ await expectXtreamRootCatalogSearch(app.mainWindow, {
+ pathPattern: /\/workspace\/xtreams\/[^/]+\/vod$/,
+ sample: vodSample,
+ sectionLabel: 'Movies',
+ });
+ await expect(
+ app.mainWindow.locator(headerSearchSelector)
+ ).toHaveValue(vodSample.targetTitle);
+
+ await fillWorkspaceSearch(app.mainWindow, '');
+ await openWorkspaceSection(app.mainWindow, 'Series');
+
+ await expectXtreamRootCatalogSearch(app.mainWindow, {
+ pathPattern: /\/workspace\/xtreams\/[^/]+\/series$/,
+ sample: seriesSample,
+ sectionLabel: 'Series',
+ });
+ await expect(
+ app.mainWindow.locator(headerSearchSelector)
+ ).toHaveValue(seriesSample.targetTitle);
+ } finally {
+ await closeElectronApp(app);
+ }
+ });
+
+ test('@search @xtream filters live root and category scopes while preserving playback sidebar', async ({
+ dataDir,
+ request,
+ }) => {
+ await resetMockServers(request, ['xtream']);
+ const sample = await fetchXtreamLiveFixture(request);
+
+ const app = await launchElectronApp(dataDir);
+
+ try {
+ await addXtreamPortal(app.mainWindow, {
+ password: xtreamSearchPassword,
+ username: xtreamSearchUsername,
+ });
+ await waitForXtreamWorkspaceReady(app.mainWindow);
+ await openWorkspaceSection(app.mainWindow, 'Live TV');
+ await expectPathname(
+ app.mainWindow,
+ /\/workspace\/xtreams\/[^/]+\/live$/
+ );
+
+ await fillWorkspaceSearch(app.mainWindow, sample.targetTitle);
+
+ await expectPathname(
+ app.mainWindow,
+ /\/workspace\/xtreams\/[^/]+\/live$/
+ );
+ await expectQueryParam(app.mainWindow, 'q', sample.targetTitle);
+ await expectWorkspaceSearchScope(
+ app.mainWindow,
+ 'Live TV / All Items'
+ );
+ await expect(
+ liveChannelSidebar(app.mainWindow)
+ .locator('[data-test-id="channel-item"]')
+ .first()
+ ).toBeVisible({ timeout: 20000 });
+ await expect(
+ channelItemByTitle(app.mainWindow, sample.targetTitle).first()
+ ).toBeVisible({ timeout: 20000 });
+ await expect(
+ channelItemByTitle(app.mainWindow, sample.controlTitle)
+ ).toHaveCount(0);
+
+ await channelItemByTitle(app.mainWindow, sample.targetTitle)
+ .first()
+ .click();
+
+ await expect(
+ app.mainWindow.locator(
+ 'app-live-stream-layout .content-container .video-player'
+ )
+ ).toBeVisible({ timeout: 20000 });
+ await expect(
+ liveChannelSidebar(app.mainWindow)
+ .locator('[data-test-id="channel-item"]')
+ .first()
+ ).toBeVisible();
+ await expect(
+ channelItemByTitle(app.mainWindow, sample.targetTitle).first()
+ ).toHaveClass(/(^|\s)active(\s|$)/);
+
+ await fillWorkspaceSearch(app.mainWindow, '');
+ await clickCategoryByNameExact(app.mainWindow, sample.categoryName);
+ await fillWorkspaceSearch(app.mainWindow, sample.targetTitle);
+
+ await expectPathname(
+ app.mainWindow,
+ /\/workspace\/xtreams\/[^/]+\/live(?:\/[^/]+)?$/
+ );
+ await expectQueryParam(app.mainWindow, 'q', sample.targetTitle);
+ await expectWorkspaceSearchScope(
+ app.mainWindow,
+ `Live TV / ${sample.categoryName}`
+ );
+ await expect(
+ channelItemByTitle(app.mainWindow, sample.targetTitle).first()
+ ).toBeVisible({ timeout: 20000 });
+ await expect(
+ channelItemByTitle(app.mainWindow, sample.controlTitle)
+ ).toHaveCount(0);
+ } finally {
+ await closeElectronApp(app);
+ }
+ });
+
test('@search @xtream promotes dashboard header search into advanced playlist search', async ({
dataDir,
request,
@@ -561,7 +692,10 @@ test.describe('Electron Workspace Search', () => {
);
await openWorkspaceSection(app.mainWindow, 'Favorites');
- await expectPathname(app.mainWindow, /\/workspace\/global-favorites$/);
+ await expectPathname(
+ app.mainWindow,
+ /\/workspace\/global-favorites$/
+ );
await expect(
contentCardByTitle(
app.mainWindow,
@@ -801,7 +935,10 @@ test.describe('Electron Workspace Search', () => {
await toggleFavoriteForChannel(app.mainWindow, sample.controlTitle);
await openWorkspaceSection(app.mainWindow, 'Favorites');
- await expectPathname(app.mainWindow, /\/workspace\/global-favorites$/);
+ await expectPathname(
+ app.mainWindow,
+ /\/workspace\/global-favorites$/
+ );
await expect(
channelItemByTitle(app.mainWindow, sample.targetTitle).first()
).toBeVisible({ timeout: 20000 });
@@ -911,6 +1048,15 @@ async function fetchXtreamVodFixture(
});
}
+async function fetchXtreamLiveFixture(
+ request: APIRequestContext
+): Promise {
+ return fetchXtreamFixture(request, {
+ categoriesAction: 'get_live_categories',
+ itemsAction: 'get_live_streams',
+ });
+}
+
async function fetchXtreamSeriesFixture(
request: APIRequestContext
): Promise {
@@ -1262,6 +1408,10 @@ function xtreamSearchResultCards(page: Page) {
return page.locator('app-search-results .results-grid .content-card');
}
+function liveChannelSidebar(page: Page) {
+ return page.locator('app-live-stream-layout .sidebar');
+}
+
function gridListCardByTitle(page: Page, title: string) {
return page.locator('.category-content-layout mat-card').filter({
has: page.locator('.title', {
@@ -1329,6 +1479,34 @@ async function resolveVisibleXtreamTitles(
};
}
+async function expectXtreamRootCatalogSearch(
+ page: Page,
+ options: {
+ pathPattern: RegExp;
+ sample: XtreamVodFixture;
+ sectionLabel: string;
+ }
+): Promise {
+ await openWorkspaceSection(page, options.sectionLabel);
+ await expectPathname(page, options.pathPattern);
+ await fillWorkspaceSearch(page, options.sample.targetTitle);
+
+ await expectPathname(page, options.pathPattern);
+ await expectQueryParam(page, 'q', options.sample.targetTitle);
+ await expectWorkspaceSearchScope(
+ page,
+ `${options.sectionLabel} / All Items`
+ );
+ const contentLayout = page.locator('.category-content-layout');
+ await expect(contentLayout).toContainText(
+ flexibleTextPattern(options.sample.targetTitle),
+ { timeout: 20000 }
+ );
+ await expect(contentLayout).not.toContainText(
+ flexibleTextPattern(options.sample.controlTitle)
+ );
+}
+
async function addCurrentDetailToFavorites(page: Page): Promise {
const addButton = page
.locator('button.favorite-btn')
diff --git a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html
index 9a2fe6cce..edf388a22 100644
--- a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html
+++ b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html
@@ -1,7 +1,7 @@
@let isEmbeddedPlayer = usesEmbeddedPlayer();
@let streamUrl = activeStreamUrl();
-@if (selectedCategoryId()) {
+@if (showLiveChannelSidebar()) {