feat: enhance global recent route handling with translation and import phase management

This commit is contained in:
4gray committed 2026-03-28 15:37:08 +01:00
1 parent f58a5c1290
commit 122a69047c
6 files changed
+83 -1

No files matched your search

@@ -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');
});
});
@@ -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');
}
@@ -3,6 +3,9 @@
<div class="workspace-loading-overlay">
<div class="workspace-loading-overlay__card">
<h3>Loading playlist...</h3>
@if (facade.xtreamImportPhaseLabel()) {
<p>{{ facade.xtreamImportPhaseLabel() }}</p>
}
@if (
facade.xtreamImportCount() !== 0 &&
facade.xtreamItemsToImport() !== 0
@@ -22,6 +25,21 @@
} @else {
<mat-progress-bar mode="indeterminate" />
}
@if (facade.canCancelXtreamImport()) {
<button
mat-stroked-button
type="button"
[disabled]="facade.isCancellingXtreamImport()"
(click)="facade.cancelXtreamImport()"
>
{{
(facade.isCancellingXtreamImport()
? 'WORKSPACE.SHELL.XTREAM_IMPORT_CANCELLING'
: 'WORKSPACE.SHELL.XTREAM_IMPORT_CANCEL'
) | translate
}}
</button>
}
</div>
</div>
}
@@ -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,
@@ -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',
@@ -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';