From 122a69047cb49cfa8bfd44e61973e9ab59ebffc0 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sat, 28 Mar 2026 15:37:08 +0100 Subject: [PATCH] feat: enhance global recent route handling with translation and import phase management --- .../services/workspace-shell.facade.spec.ts | 7 ++++ .../services/workspace-shell.facade.ts | 42 +++++++++++++++++++ .../workspace-shell.component.html | 18 ++++++++ .../workspace-shell.component.ts | 4 ++ .../workspace-shell-route.utils.spec.ts | 7 ++++ .../navigation/workspace-shell-route.utils.ts | 6 ++- 6 files changed, 83 insertions(+), 1 deletion(-) 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 a427b9b7f..ed71c3d8d 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 @@ -325,4 +325,11 @@ describe('WorkspaceShellFacade', () => { 'HOME.PLAYLISTS.GLOBAL_FAVORITES' ); }); + + it('uses the translated recent scope label on the global recent route', () => { + facade.currentUrl.set('/workspace/global-recent?q=news'); + (facade as { syncSearchFromRoute: () => void }).syncSearchFromRoute(); + + expect(facade.searchScopeLabel()).toBe('PORTALS.RECENTLY_VIEWED'); + }); }); 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 46327dda5..9bcb6d227 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 @@ -252,6 +252,7 @@ export class WorkspaceShellFacade { currentRoute.kind !== 'sources' && currentRoute.kind !== 'settings' && currentRoute.kind !== 'global-favorites' && + currentRoute.kind !== 'global-recent' && currentRoute.kind !== 'downloads' ) { return null; @@ -291,6 +292,39 @@ export class WorkspaceShellFacade { ); readonly xtreamImportCount = this.xtreamStore.getImportCount; readonly xtreamItemsToImport = this.xtreamStore.itemsToImport; + readonly xtreamImportPhase = this.xtreamStore.currentImportPhase; + readonly isCancellingXtreamImport = this.xtreamStore.isCancellingImport; + readonly canCancelXtreamImport = computed( + () => + this.isElectron && + this.xtreamStore.isImporting() && + this.xtreamStore.activeImportOperationIds().length > 0 && + !this.xtreamStore.isCancellingImport() + ); + readonly xtreamImportPhaseLabel = computed(() => { + this.languageTick(); + + switch (this.xtreamStore.currentImportPhase()) { + case 'preparing-content': + return this.translateText( + 'WORKSPACE.SHELL.XTREAM_IMPORT_PREPARING' + ); + case 'saving-content': + return this.translateText( + 'WORKSPACE.SHELL.XTREAM_IMPORT_SAVING' + ); + case 'restoring-favorites': + return this.translateText( + 'WORKSPACE.SHELL.XTREAM_IMPORT_RESTORING_FAVORITES' + ); + case 'restoring-recently-viewed': + return this.translateText( + 'WORKSPACE.SHELL.XTREAM_IMPORT_RESTORING_RECENT' + ); + default: + return ''; + } + }); readonly showXtreamImportOverlay = computed(() => { const context = this.currentContext(); const section = this.currentSection(); @@ -763,6 +797,10 @@ export class WorkspaceShellFacade { }); } + cancelXtreamImport(): void { + void this.xtreamStore.cancelImport(); + } + openAccountInfo(): void { if (!this.canOpenAccountInfo()) { return; @@ -1028,6 +1066,10 @@ export class WorkspaceShellFacade { return this.translateText('HOME.PLAYLISTS.GLOBAL_FAVORITES'); } + if (kind === 'global-recent') { + return this.translateText('PORTALS.RECENTLY_VIEWED'); + } + if (kind === 'downloads') { return this.translateText('WORKSPACE.SHELL.RAIL_DOWNLOADS'); } diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.html b/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.html index e709eb88f..7f28bc900 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.html +++ b/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.html @@ -3,6 +3,9 @@

Loading playlist...

+ @if (facade.xtreamImportPhaseLabel()) { +

{{ facade.xtreamImportPhaseLabel() }}

+ } @if ( facade.xtreamImportCount() !== 0 && facade.xtreamItemsToImport() !== 0 @@ -22,6 +25,21 @@ } @else { } + @if (facade.canCancelXtreamImport()) { + + }
} diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.ts b/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.ts index 85241f980..4949f524d 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.ts @@ -2,8 +2,10 @@ import { Component, inject, } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { RouterOutlet } from '@angular/router'; +import { TranslatePipe } from '@ngx-translate/core'; import { ExternalPlaybackDockComponent } from 'components'; import { WorkspaceShellContextSidebarComponent } from './components/workspace-shell-context-sidebar/workspace-shell-context-sidebar.component'; import { WorkspaceShellHeaderComponent } from './components/workspace-shell-header/workspace-shell-header.component'; @@ -14,8 +16,10 @@ import { WorkspaceShellFacade } from './services/workspace-shell.facade'; selector: 'app-workspace-shell', imports: [ ExternalPlaybackDockComponent, + MatButtonModule, MatProgressBarModule, RouterOutlet, + TranslatePipe, WorkspaceShellContextSidebarComponent, WorkspaceShellHeaderComponent, WorkspaceShellRailComponent, diff --git a/libs/workspace/shell/util/src/lib/navigation/workspace-shell-route.utils.spec.ts b/libs/workspace/shell/util/src/lib/navigation/workspace-shell-route.utils.spec.ts index 98d3949fc..9d20b4e5e 100644 --- a/libs/workspace/shell/util/src/lib/navigation/workspace-shell-route.utils.spec.ts +++ b/libs/workspace/shell/util/src/lib/navigation/workspace-shell-route.utils.spec.ts @@ -35,6 +35,13 @@ describe('workspace-shell-route.utils', () => { kind: 'global-favorites', }) ); + expect(parseWorkspaceShellRoute('/workspace/global-recent')).toEqual( + expect.objectContaining({ + kind: 'global-recent', + searchMode: 'local-filter', + usesQuerySearch: true, + }) + ); expect(parseWorkspaceShellRoute('/workspace/downloads')).toEqual( expect.objectContaining({ kind: 'downloads', diff --git a/libs/workspace/shell/util/src/lib/navigation/workspace-shell-route.utils.ts b/libs/workspace/shell/util/src/lib/navigation/workspace-shell-route.utils.ts index 55c5bf7cf..e01a7e17a 100644 --- a/libs/workspace/shell/util/src/lib/navigation/workspace-shell-route.utils.ts +++ b/libs/workspace/shell/util/src/lib/navigation/workspace-shell-route.utils.ts @@ -7,6 +7,7 @@ export type WorkspaceShellPageKind = | 'dashboard' | 'downloads' | 'global-favorites' + | 'global-recent' | 'portal' | 'settings' | 'sources' @@ -184,7 +185,8 @@ function resolveRouteSearchMode( if ( kind === 'sources' || kind === 'downloads' || - kind === 'global-favorites' + kind === 'global-favorites' || + kind === 'global-recent' ) { return 'local-filter'; } @@ -281,6 +283,8 @@ export function parseWorkspaceShellRoute(url: string): WorkspaceShellRoute { ? 'settings' : page === 'global-favorites' ? 'global-favorites' + : page === 'global-recent' + ? 'global-recent' : page === 'downloads' ? 'downloads' : 'unknown';