mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fix(workspace): defer embedded mpv palette support check
This commit is contained in:
1 parent
f7ad17298f
commit
d7e7a8abda
6 files changed
+108
-22
No files matched your search
+22
-7
@@ -50,6 +50,14 @@ describe('WorkspacePlayerCommandsContributor', () => {
|
||||
supportsManagedExternalPlayers: boolean;
|
||||
supportsEmbeddedMpv: boolean;
|
||||
};
|
||||
let electronStub:
|
||||
| {
|
||||
getEmbeddedMpvSupport: jest.Mock<
|
||||
Promise<{ supported: boolean }>,
|
||||
[]
|
||||
>;
|
||||
}
|
||||
| undefined;
|
||||
let translate: { instant: jest.Mock; onLangChange: ReturnType<typeof of> };
|
||||
|
||||
function bootstrap(options: {
|
||||
@@ -72,7 +80,7 @@ describe('WorkspacePlayerCommandsContributor', () => {
|
||||
supportsEmbeddedMpv: options.supportsEmbeddedMpv ?? false,
|
||||
};
|
||||
|
||||
const electronStub = runtime.supportsEmbeddedMpv
|
||||
electronStub = runtime.supportsEmbeddedMpv
|
||||
? {
|
||||
getEmbeddedMpvSupport: jest.fn().mockResolvedValue(
|
||||
options.embeddedMpvSupportResult ?? {
|
||||
@@ -153,15 +161,23 @@ describe('WorkspacePlayerCommandsContributor', () => {
|
||||
expect(resolveBoolean(embedded?.visible)).toBe(false);
|
||||
});
|
||||
|
||||
it('shows embedded MPV once support resolves to supported', async () => {
|
||||
it('does not verify embedded MPV support during contributor bootstrap', () => {
|
||||
bootstrap({
|
||||
supportsManagedExternalPlayers: true,
|
||||
supportsEmbeddedMpv: true,
|
||||
});
|
||||
|
||||
expect(electronStub?.getEmbeddedMpvSupport).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shows embedded MPV once support resolves to supported', async () => {
|
||||
const contributor = bootstrap({
|
||||
supportsManagedExternalPlayers: true,
|
||||
supportsEmbeddedMpv: true,
|
||||
embeddedMpvSupportResult: { supported: true },
|
||||
});
|
||||
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
await contributor.ensureEmbeddedMpvSupportLoaded();
|
||||
|
||||
const embedded = getRegistered(viewCommands).find(
|
||||
(c) => c.id === 'switch-player-embedded-mpv'
|
||||
@@ -170,14 +186,13 @@ describe('WorkspacePlayerCommandsContributor', () => {
|
||||
});
|
||||
|
||||
it('keeps embedded MPV hidden when support resolves to unsupported', async () => {
|
||||
bootstrap({
|
||||
const contributor = bootstrap({
|
||||
supportsManagedExternalPlayers: true,
|
||||
supportsEmbeddedMpv: true,
|
||||
embeddedMpvSupportResult: { supported: false },
|
||||
});
|
||||
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
await contributor.ensureEmbeddedMpvSupportLoaded();
|
||||
|
||||
const embedded = getRegistered(viewCommands).find(
|
||||
(c) => c.id === 'switch-player-embedded-mpv'
|
||||
|
||||
+18
-5
@@ -86,14 +86,14 @@ export class WorkspacePlayerCommandsContributor {
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly runtime = inject(RuntimeCapabilitiesService);
|
||||
private readonly embeddedMpvSupported = signal(false);
|
||||
private embeddedMpvSupportChecked = false;
|
||||
private embeddedMpvSupportLoad: Promise<void> | null = null;
|
||||
|
||||
constructor() {
|
||||
const unregisters = PLAYER_COMMAND_DEFS.map((def) =>
|
||||
this.viewCommands.registerCommand(this.toContribution(def))
|
||||
);
|
||||
|
||||
void this.loadEmbeddedMpvSupport();
|
||||
|
||||
this.destroyRef.onDestroy(() => {
|
||||
for (const unregister of unregisters) {
|
||||
unregister();
|
||||
@@ -101,23 +101,36 @@ export class WorkspacePlayerCommandsContributor {
|
||||
});
|
||||
}
|
||||
|
||||
private async loadEmbeddedMpvSupport(): Promise<void> {
|
||||
ensureEmbeddedMpvSupportLoaded(): Promise<void> | undefined {
|
||||
if (this.embeddedMpvSupportChecked) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.runtime.supportsEmbeddedMpv ||
|
||||
typeof window === 'undefined' ||
|
||||
!window.electron?.getEmbeddedMpvSupport
|
||||
) {
|
||||
return;
|
||||
this.embeddedMpvSupportChecked = true;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this.embeddedMpvSupportLoad ??= this.loadEmbeddedMpvSupport();
|
||||
return this.embeddedMpvSupportLoad;
|
||||
}
|
||||
|
||||
private async loadEmbeddedMpvSupport(): Promise<void> {
|
||||
try {
|
||||
const support = await window.electron.getEmbeddedMpvSupport();
|
||||
const support = await window.electron?.getEmbeddedMpvSupport?.();
|
||||
this.embeddedMpvSupported.set(!!support?.supported);
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
'Failed to verify embedded MPV support for the command palette.',
|
||||
error
|
||||
);
|
||||
} finally {
|
||||
this.embeddedMpvSupportChecked = true;
|
||||
this.embeddedMpvSupportLoad = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-3
@@ -20,8 +20,7 @@ export class WorkspaceShellCommandPaletteService {
|
||||
private readonly viewCommands = inject(WorkspaceViewCommandService);
|
||||
private readonly recentCommands = inject(RecentCommandsService);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
// Eager construction registers the player-switch commands via WorkspaceViewCommandService.
|
||||
private readonly _playerCommandsBootstrap = inject(
|
||||
private readonly playerCommands = inject(
|
||||
WorkspacePlayerCommandsContributor
|
||||
);
|
||||
|
||||
@@ -29,6 +28,7 @@ export class WorkspaceShellCommandPaletteService {
|
||||
WorkspaceCommandPaletteComponent,
|
||||
WorkspaceCommandSelection | undefined
|
||||
> | null = null;
|
||||
private commandPaletteOpening = false;
|
||||
|
||||
buildPaletteCommands(
|
||||
ctx: CommandBuilderContext
|
||||
@@ -42,8 +42,36 @@ export class WorkspaceShellCommandPaletteService {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.commandPaletteOpening) {
|
||||
return;
|
||||
}
|
||||
|
||||
const embeddedMpvSupportLoad =
|
||||
this.playerCommands.ensureEmbeddedMpvSupportLoaded();
|
||||
if (embeddedMpvSupportLoad) {
|
||||
this.commandPaletteOpening = true;
|
||||
void embeddedMpvSupportLoad.finally(() => {
|
||||
this.commandPaletteOpening = false;
|
||||
this.openResolvedCommandPalette(ctx, initialQuery);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.openResolvedCommandPalette(ctx, initialQuery);
|
||||
}
|
||||
|
||||
private openResolvedCommandPalette(
|
||||
ctx: CommandBuilderContext,
|
||||
initialQuery: string
|
||||
): void {
|
||||
if (this.commandPaletteRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
const commands = this.buildPaletteCommands(ctx);
|
||||
const recentIds = this.recentCommands.entries().map((entry) => entry.id);
|
||||
const recentIds = this.recentCommands
|
||||
.entries()
|
||||
.map((entry) => entry.id);
|
||||
const dialogRef = this.dialog.open<
|
||||
WorkspaceCommandPaletteComponent,
|
||||
{
|
||||
|
||||
+28
-1
@@ -111,6 +111,9 @@ describe('WorkspaceShellFacade', () => {
|
||||
record: jest.Mock;
|
||||
prune: jest.Mock;
|
||||
};
|
||||
let playerCommands: {
|
||||
ensureEmbeddedMpvSupportLoaded: jest.Mock;
|
||||
};
|
||||
let router: {
|
||||
url: string;
|
||||
events: ReturnType<typeof of>;
|
||||
@@ -218,6 +221,9 @@ describe('WorkspaceShellFacade', () => {
|
||||
record: jest.fn(),
|
||||
prune: jest.fn(),
|
||||
};
|
||||
playerCommands = {
|
||||
ensureEmbeddedMpvSupportLoaded: jest.fn(),
|
||||
};
|
||||
|
||||
const selectSignal = jest.fn().mockReturnValue(playlistsSignal);
|
||||
|
||||
@@ -349,7 +355,7 @@ describe('WorkspaceShellFacade', () => {
|
||||
},
|
||||
{
|
||||
provide: WorkspacePlayerCommandsContributor,
|
||||
useValue: {},
|
||||
useValue: playerCommands,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -880,6 +886,27 @@ describe('WorkspaceShellFacade', () => {
|
||||
expect(recentCommands.record).toHaveBeenCalledWith('open-settings');
|
||||
});
|
||||
|
||||
it('waits for embedded MPV support preload before opening the palette', async () => {
|
||||
const dialog = TestBed.inject(MatDialog) as unknown as {
|
||||
open: jest.Mock;
|
||||
};
|
||||
let resolveSupport!: () => void;
|
||||
playerCommands.ensureEmbeddedMpvSupportLoaded.mockReturnValueOnce(
|
||||
new Promise<void>((resolve) => {
|
||||
resolveSupport = resolve;
|
||||
})
|
||||
);
|
||||
|
||||
facade.openCommandPalette();
|
||||
|
||||
expect(dialog.open).not.toHaveBeenCalled();
|
||||
|
||||
resolveSupport();
|
||||
await Promise.resolve();
|
||||
|
||||
expect(dialog.open).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not record when the palette closes without a selection', () => {
|
||||
const dialog = TestBed.inject(MatDialog) as unknown as {
|
||||
open: jest.Mock;
|
||||
|
||||
Reference in new issue
Block a user