fix(ui): hold the provider play action and the reset row during their busy windows

The downloaded movie's provider action stays enabled while a start was
pending, so a second press could start beside a launch already inside
the player IPC. The series menu's reset row stayed enabled while a
watched batch persisted, although the hosts refuse the reset meanwhile.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5.1 committed 2026-10-03 16:31:18 +02:00
1 parent 5c9a12fa79
commit 5a5fe4adcf
6 files changed
+33 -4

No files matched your search

@@ -66,6 +66,7 @@ export class StalkerSeriesMenuService {
hasProgress: this.bindings()?.hasProgress() ?? false,
playbackActive: this.bindings()?.playbackActive() ?? false,
startPending: this.bindings()?.startPending() ?? false,
watchBatchRunning: container?.seasonWatchBatchRunning() ?? false,
sourcesCount: 0,
externalPlayerHint:
quickStart?.action &&
@@ -163,6 +163,7 @@ export class SerialDetailsMenuService {
hasProgress: this.hasProgress(),
playbackActive: this.bindings()?.playbackActive() ?? false,
startPending: this.bindings()?.startPending() ?? false,
watchBatchRunning: container?.seasonWatchBatchRunning() ?? false,
sourcesCount: 0,
externalPlayerHint:
quickStart && this.runtime.supportsManagedExternalPlayers
@@ -16,6 +16,7 @@ const BASE = {
hasProgress: true,
playbackActive: false,
startPending: false,
watchBatchRunning: false,
sourcesCount: 0,
externalPlayerHint: 'MPV' as const,
copyUrlEpisodeCode: null,
@@ -49,6 +50,12 @@ describe('buildSeriesMenuSections', () => {
expect(resetRow({ seriesActionDisabled: true })?.disabled).toBeFalsy();
});
it('disables the progress reset while a watched batch persists', () => {
// The hosts refuse the reset request meanwhile; an enabled row
// would close the menu and do nothing.
expect(resetRow({ watchBatchRunning: true })?.disabled).toBe(true);
});
it('hides the reset once every episode is watched', () => {
expect(resetRow({ seriesFullyWatched: true })).toBeUndefined();
});
@@ -195,6 +195,8 @@ export interface SeriesMenuInput {
readonly playbackActive: boolean;
/** A start has not settled: another launch would double it. */
readonly startPending: boolean;
/** A season/series watched batch still persists: a reset request is refused meanwhile. */
readonly watchBatchRunning: boolean;
readonly sourcesCount: number;
readonly externalPlayerHint: 'MPV' | 'VLC' | null;
readonly copyUrlEpisodeCode: string | null;
@@ -242,7 +244,7 @@ export function buildSeriesMenuSections(
id: SERIES_MENU_ACTION.ResetProgress,
labelKey: 'PORTALS.DETAIL.RESET_PROGRESS',
icon: 'history_toggle_off',
disabled: input.playbackActive,
disabled: input.playbackActive || input.watchBatchRunning,
testId: 'vod-menu-reset-progress',
});
}
@@ -158,6 +158,9 @@
"
icon="play_arrow"
testId="vod-play-provider"
[disabled]="
isExternalLaunchPending() || playbackStartPending()
"
(pressed)="onProviderAction()"
/>
}
@@ -160,9 +160,7 @@ describe('VodDetailsComponent offline playback', () => {
};
const closeMenu = async (): Promise<void> => {
document
.querySelector<HTMLElement>('.cdk-overlay-backdrop')
?.click();
document.querySelector<HTMLElement>('.cdk-overlay-backdrop')?.click();
fixture.detectChanges();
await fixture.whenStable();
};
@@ -420,6 +418,23 @@ describe('VodDetailsComponent offline playback', () => {
expect(playDownload).not.toHaveBeenCalled();
});
it('holds the downloaded provider action while a start is pending', async () => {
completeDownload();
await render();
expect(providerPlayButton()?.disabled).toBe(false);
// Same window as the primary action: a second start could not cancel
// a launch already inside the player IPC.
fixture.componentRef.setInput('playbackStartPending', true);
fixture.detectChanges();
expect(providerPlayButton()?.disabled).toBe(true);
providerPlayButton()?.click();
await fixture.whenStable();
expect(playClicked).not.toHaveBeenCalled();
expect(resumeClicked).not.toHaveBeenCalled();
});
it('resumes the provider source from the downloaded secondary action', async () => {
completeDownload();
await render({ playbackPosition: 83 });