diff --git a/libs/portal/shared/util/src/lib/navigation/portal-rail-links.spec.ts b/libs/portal/shared/util/src/lib/navigation/portal-rail-links.spec.ts index b04195703..7823ee148 100644 --- a/libs/portal/shared/util/src/lib/navigation/portal-rail-links.spec.ts +++ b/libs/portal/shared/util/src/lib/navigation/portal-rail-links.spec.ts @@ -1,7 +1,7 @@ import { buildPortalRailLinks } from './portal-rail-links'; describe('buildPortalRailLinks', () => { - it('builds Xtream links with scoped tooltip labels on Electron', () => { + it('builds Xtream links with scoped tooltip labels when downloads are supported', () => { const links = buildPortalRailLinks({ provider: 'xtreams', playlistId: 'xtream-1', diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/services/helpers/workspace-shell-command-builders.ts b/libs/workspace/shell/feature/src/lib/workspace-shell/services/helpers/workspace-shell-command-builders.ts index 281baa36f..52ea24360 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-shell/services/helpers/workspace-shell-command-builders.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-shell/services/helpers/workspace-shell-command-builders.ts @@ -29,7 +29,7 @@ export interface CommandBuilderContext { hasActivePlaylist: boolean; hasXtreamPlaylists: boolean; canRefreshPlaylist: boolean; - isElectron: boolean; + supportsDownloads: boolean; showDashboard: boolean; translate: TranslateFn; router: Router; @@ -139,8 +139,13 @@ export function getPlaylistCommandDefinitions( export function getGlobalCommandDefinitions( ctx: CommandBuilderContext ): WorkspaceCommandContribution[] { - const { route, hasXtreamPlaylists, isElectron, showDashboard, actions } = - ctx; + const { + route, + hasXtreamPlaylists, + supportsDownloads, + showDashboard, + actions, + } = ctx; return [ { @@ -185,7 +190,7 @@ export function getGlobalCommandDefinitions( descriptionKey: 'WORKSPACE.SHELL.COMMANDS.OPEN_DOWNLOADS_DESCRIPTION', priority: 40, - visible: isElectron && route.kind !== 'downloads', + visible: supportsDownloads && route.kind !== 'downloads', run: () => actions.openDownloadsShortcut(), }, { 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 11c2b7507..f282beb3d 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 @@ -18,6 +18,7 @@ import { import { StalkerStore } from '@iptvnator/portal/stalker/data-access'; import { XtreamStore } from '@iptvnator/portal/xtream/data-access'; import { + DownloadsService, PlaylistsService, RuntimeCapabilitiesService, SettingsStore, @@ -129,6 +130,7 @@ describe('WorkspaceShellFacade', () => { typeof signal >; let playlistsSignal: ReturnType>; + let downloadsActiveCountSignal: ReturnType>; let refreshPreparationSignal: ReturnType< typeof signal >; @@ -142,6 +144,7 @@ describe('WorkspaceShellFacade', () => { let runtime: { isElectron: boolean; isMacOS: boolean; + supportsDownloads: boolean; }; beforeEach(() => { @@ -149,6 +152,7 @@ describe('WorkspaceShellFacade', () => { runtime = { isElectron: true, isMacOS: true, + supportsDownloads: true, }; activePlaylistSignal = signal({ @@ -161,6 +165,7 @@ describe('WorkspaceShellFacade', () => { { _id: 'pl-1', serverUrl: 'http://example.com' }, { _id: 'pl-2', macAddress: '00:11:22:33' }, ]); + downloadsActiveCountSignal = signal(0); refreshPreparationSignal = signal( null ); @@ -273,6 +278,12 @@ describe('WorkspaceShellFacade', () => { provide: PlaylistsService, useValue: playlistsService, }, + { + provide: DownloadsService, + useValue: { + activeCount: downloadsActiveCountSignal, + }, + }, { provide: MatDialog, useValue: { @@ -685,6 +696,20 @@ describe('WorkspaceShellFacade', () => { expect(commands.every((command) => command.enabled)).toBe(true); }); + it('hides the downloads command when downloads are unsupported', () => { + (facade as unknown as { supportsDownloads: boolean }).supportsDownloads = + false; + activePlaylistSignal.set(null); + playlistsSignal.set([]); + facade.currentUrl.set('/workspace/dashboard'); + + const commands = facade.commandPaletteCommands(); + + expect(commands.map((command) => command.id)).not.toContain( + 'open-downloads' + ); + }); + it('includes M3U navigation, playlist actions, and Multi-EPG on playlist routes', () => { const headerContext = TestBed.inject(WorkspaceHeaderContextService); 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 0d874a50f..ea1bb8433 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 @@ -100,7 +100,7 @@ export class WorkspaceShellFacade { private readonly runtime = inject(RuntimeCapabilitiesService); private readonly downloadsService = inject(DownloadsService); readonly hasActiveDownloads = computed( - () => this.isElectron && this.downloadsService.activeCount() > 0 + () => this.supportsDownloads && this.downloadsService.activeCount() > 0 ); private readonly languageTick = toSignal( this.translate.onLangChange.pipe(startWith(null)), @@ -146,6 +146,11 @@ export class WorkspaceShellFacade { get isMacOS(): boolean { return this.runtime.isMacOS; } + + get supportsDownloads(): boolean { + return this.runtime.supportsDownloads; + } + readonly currentUrl = signal(this.router.url); readonly currentRoute = computed(() => parseWorkspaceShellRoute(this.currentUrl()) @@ -437,7 +442,7 @@ export class WorkspaceShellFacade { buildPortalRailLinks({ provider: context.provider, playlistId: context.playlistId, - supportsDownloads: this.isElectron, + supportsDownloads: this.supportsDownloads, workspace: true, }).primary, context.provider, @@ -456,7 +461,7 @@ export class WorkspaceShellFacade { buildPortalRailLinks({ provider: context.provider, playlistId: context.playlistId, - supportsDownloads: this.isElectron, + supportsDownloads: this.supportsDownloads, workspace: true, }).secondary.filter((link) => link.section !== 'downloads'), context.provider, @@ -801,7 +806,7 @@ export class WorkspaceShellFacade { (playlist) => !!playlist.serverUrl ), canRefreshPlaylist: this.canRefreshPlaylist(), - isElectron: this.isElectron, + supportsDownloads: this.supportsDownloads, showDashboard: this.showDashboard(), translate: (key, params) => this.translateText(key, params), router: this.router,