mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 06:06:18 -08:00
fix(search): address global search review feedback
This commit is contained in:
1 parent
12d931f4cc
commit
357d1de050
11 files changed
+216
-44
No files matched your search
+22
@@ -0,0 +1,22 @@
|
||||
import { PlaylistMeta } from '@iptvnator/shared/interfaces';
|
||||
|
||||
type SearchablePlaylistMeta = Pick<
|
||||
PlaylistMeta,
|
||||
'count' | 'filePath' | 'macAddress' | 'serverUrl' | 'url'
|
||||
>;
|
||||
|
||||
export function isWorkspaceGlobalSearchablePlaylist(
|
||||
playlist: SearchablePlaylistMeta
|
||||
): boolean {
|
||||
if (playlist.serverUrl) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (playlist.macAddress) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Boolean(
|
||||
playlist.filePath || playlist.url || Number(playlist.count ?? 0) > 0
|
||||
);
|
||||
}
|
||||
+17
-7
@@ -1,4 +1,10 @@
|
||||
import { computed, DestroyRef, inject, Injectable, signal } from '@angular/core';
|
||||
import {
|
||||
computed,
|
||||
DestroyRef,
|
||||
inject,
|
||||
Injectable,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
@@ -84,12 +90,16 @@ export class WorkspaceShellRouteStateService {
|
||||
path: ['/workspace/sources'],
|
||||
});
|
||||
|
||||
links.push({
|
||||
icon: 'search',
|
||||
tooltip: this.translateText('WORKSPACE.SHELL.RAIL_GLOBAL_SEARCH'),
|
||||
path: ['/workspace/search'],
|
||||
exact: true,
|
||||
});
|
||||
if (this.runtime.isElectron) {
|
||||
links.push({
|
||||
icon: 'search',
|
||||
tooltip: this.translateText(
|
||||
'WORKSPACE.SHELL.RAIL_GLOBAL_SEARCH'
|
||||
),
|
||||
path: ['/workspace/search'],
|
||||
exact: true,
|
||||
});
|
||||
}
|
||||
|
||||
links.push({
|
||||
icon: 'favorite',
|
||||
|
||||
+10
-14
@@ -5,6 +5,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { startWith } from 'rxjs';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { RuntimeCapabilitiesService } from '@iptvnator/services';
|
||||
import { WorkspaceSearchCapability } from '@iptvnator/workspace/shell/util';
|
||||
import {
|
||||
SEARCH_LOADED_ONLY_STATUS,
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
resolveSearchPlaceholderKey,
|
||||
resolveSearchScopeLabel,
|
||||
} from './helpers/workspace-shell-search-labels';
|
||||
import { isWorkspaceGlobalSearchablePlaylist } from './helpers/workspace-shell-searchable-playlists';
|
||||
import { WorkspaceShellRouteStateService } from './workspace-shell-route-state.service';
|
||||
import { WorkspaceShellSearchSyncService } from './workspace-shell-search-sync.service';
|
||||
|
||||
@@ -23,6 +25,7 @@ export class WorkspaceShellSearchService {
|
||||
private readonly xtreamStore = inject(XtreamStore);
|
||||
private readonly stalkerStore = inject(StalkerStore);
|
||||
private readonly translate = inject(TranslateService);
|
||||
private readonly runtime = inject(RuntimeCapabilitiesService);
|
||||
private readonly routeState = inject(WorkspaceShellRouteStateService);
|
||||
private readonly searchSync = inject(WorkspaceShellSearchSyncService);
|
||||
|
||||
@@ -83,25 +86,18 @@ export class WorkspaceShellSearchService {
|
||||
}
|
||||
|
||||
if (route.kind === 'global-search') {
|
||||
const hasSearchablePlaylists = this.routeState.playlists().some(
|
||||
(playlist) =>
|
||||
!!playlist.serverUrl ||
|
||||
(!playlist.macAddress &&
|
||||
(!!playlist.filePath ||
|
||||
!!playlist.url ||
|
||||
!playlist.serverUrl))
|
||||
);
|
||||
const hasSearchablePlaylists =
|
||||
this.runtime.isElectron &&
|
||||
this.routeState
|
||||
.playlists()
|
||||
.some(isWorkspaceGlobalSearchablePlaylist);
|
||||
|
||||
return {
|
||||
enabled: hasSearchablePlaylists,
|
||||
behavior: hasSearchablePlaylists
|
||||
? 'remote-search'
|
||||
: 'disabled',
|
||||
behavior: hasSearchablePlaylists ? 'remote-search' : 'disabled',
|
||||
context: null,
|
||||
section: null,
|
||||
searchMode: hasSearchablePlaylists
|
||||
? 'remote-search'
|
||||
: 'none',
|
||||
searchMode: hasSearchablePlaylists ? 'remote-search' : 'none',
|
||||
placeholderKey: 'WORKSPACE.SHELL.SEARCH_GLOBAL_PLACEHOLDER',
|
||||
scopeLabel: this.translateText(
|
||||
'WORKSPACE.SHELL.RAIL_GLOBAL_SEARCH'
|
||||
|
||||
+47
-3
@@ -675,6 +675,17 @@ describe('WorkspaceShellFacade', () => {
|
||||
expect(facade.brandLink()).toBe('/workspace/sources');
|
||||
});
|
||||
|
||||
it('hides the Electron-only global search rail link in the web runtime', () => {
|
||||
runtime.isElectron = false;
|
||||
showDashboardSignal.set(false);
|
||||
|
||||
expect(facade.workspaceLinks().map((link) => link.path)).toEqual([
|
||||
['/workspace/sources'],
|
||||
['/workspace/global-favorites'],
|
||||
['/workspace/global-recent'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('persists the last restorable route from navigation events', () => {
|
||||
expect(
|
||||
startupPreferences.persistLastRestorablePath
|
||||
@@ -731,9 +742,42 @@ describe('WorkspaceShellFacade', () => {
|
||||
]);
|
||||
facade.currentUrl.set('/workspace/dashboard');
|
||||
|
||||
expect(facade.commandPaletteCommands().map((command) => command.id)).toContain(
|
||||
'global-search'
|
||||
);
|
||||
expect(
|
||||
facade.commandPaletteCommands().map((command) => command.id)
|
||||
).toContain('global-search');
|
||||
});
|
||||
|
||||
it('does not enable global search for empty invalid playlist metadata', () => {
|
||||
activePlaylistSignal.set(null);
|
||||
playlistsSignal.set([
|
||||
{
|
||||
_id: 'empty-playlist',
|
||||
title: 'Empty playlist',
|
||||
} as PlaylistSignalMeta,
|
||||
]);
|
||||
facade.currentUrl.set('/workspace/search');
|
||||
|
||||
expect(facade.canUseSearch()).toBe(false);
|
||||
expect(
|
||||
facade.commandPaletteCommands().map((command) => command.id)
|
||||
).not.toContain('global-search');
|
||||
});
|
||||
|
||||
it('hides the global search command and header capability in the web runtime', () => {
|
||||
runtime.isElectron = false;
|
||||
playlistsSignal.set([
|
||||
{
|
||||
_id: 'pl-m3u',
|
||||
title: 'Playlist M3U',
|
||||
count: 10,
|
||||
} as PlaylistSignalMeta,
|
||||
]);
|
||||
facade.currentUrl.set('/workspace/search');
|
||||
|
||||
expect(facade.canUseSearch()).toBe(false);
|
||||
expect(
|
||||
facade.commandPaletteCommands().map((command) => command.id)
|
||||
).not.toContain('global-search');
|
||||
});
|
||||
|
||||
it('hides the downloads command when downloads are unsupported', () => {
|
||||
|
||||
+4
-8
@@ -17,6 +17,7 @@ import {
|
||||
CommandBuilderActions,
|
||||
CommandBuilderContext,
|
||||
} from './helpers/workspace-shell-command-builders';
|
||||
import { isWorkspaceGlobalSearchablePlaylist } from './helpers/workspace-shell-searchable-playlists';
|
||||
import { WorkspaceShellCommandPaletteService } from './workspace-shell-command-palette.service';
|
||||
import { WorkspaceShellHeaderService } from './workspace-shell-header.service';
|
||||
import { WorkspaceShellRouteStateService } from './workspace-shell-route-state.service';
|
||||
@@ -219,14 +220,9 @@ export class WorkspaceShellFacade {
|
||||
context: this.currentContext(),
|
||||
section: this.currentSection(),
|
||||
hasActivePlaylist: !!this.activePlaylist(),
|
||||
hasSearchablePlaylists: this.playlists().some(
|
||||
(playlist) =>
|
||||
!!playlist.serverUrl ||
|
||||
(!playlist.macAddress &&
|
||||
(!!playlist.filePath ||
|
||||
!!playlist.url ||
|
||||
!playlist.serverUrl))
|
||||
),
|
||||
hasSearchablePlaylists:
|
||||
this.isElectron &&
|
||||
this.playlists().some(isWorkspaceGlobalSearchablePlaylist),
|
||||
canRefreshPlaylist: this.canRefreshPlaylist(),
|
||||
supportsDownloads: this.supportsDownloads,
|
||||
showDashboard: this.showDashboard(),
|
||||
|
||||
Reference in new issue
Block a user