fix(portals): the Stalker launch guard is per movie

A launch still resolving for the movie the user left no longer blocks
the explicit launch of the movie now on screen; only a repeat for the
same movie is ignored.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5.1 committed 2026-10-03 12:47:04 +02:00
1 parent b47aeda543
commit 95a3130883
2 files changed
+38 -6

No files matched your search

@@ -131,6 +131,31 @@ describe('createStalkerVodDetailActions openExternal', () => {
expect(t.openExternalPlayback).toHaveBeenCalledTimes(1);
});
it('still launches another movie while the first link resolves', async () => {
let selected = 42;
const t = setup(() => selected);
void t.actions.openExternal({
item: MOVIE,
player: 'mpv',
positionSeconds: null,
});
selected = 7;
const other = {
...MOVIE,
data: { id: '7' },
} as unknown as VodDetailsItem;
const second = t.actions.openExternal({
item: other,
player: 'mpv',
positionSeconds: null,
});
t.resolveLink();
await second;
expect(t.beginPendingStart).toHaveBeenCalledTimes(2);
expect(t.openExternalPlayback).toHaveBeenCalledTimes(1);
});
it('drops the stream once another movie was selected meanwhile', async () => {
let selected = 42;
const t = setup(() => selected);
@@ -71,9 +71,10 @@ export function createStalkerVodDetailActions(
deps.snackBar.open(deps.translate.instant(key), undefined, {
duration: 3000,
});
// A second "Open in external player" during a slow `create_link` would
// resolve beside the first and start a second player.
let externalLaunchInFlight = false;
// A second "Open in external player" for the SAME movie during a slow
// `create_link` would resolve beside the first and start a second
// player; another movie's launch is not held back by it.
let externalLaunchInFlight: string | null = null;
return {
/** The inline player copied the stream URL. */
@@ -96,15 +97,19 @@ export function createStalkerVodDetailActions(
},
async openExternal(event: StalkerVodExternalPlayEvent): Promise<void> {
if (event.item.type !== 'stalker' || externalLaunchInFlight) {
if (event.item.type !== 'stalker') {
return;
}
externalLaunchInFlight = true;
// The `create_link` round trip may outlive the selection: a
// stream resolved for a movie the user left is dropped, and its
// failure is not reported over the new one.
const playlistId = deps.playlistId();
const vodId = Number(event.item.data.id);
const launchKey = `${playlistId}:${vodId}`;
if (externalLaunchInFlight === launchKey) {
return;
}
externalLaunchInFlight = launchKey;
const stillSelected = () =>
deps.selectedVodId() === vodId &&
deps.playlistId() === playlistId;
@@ -141,7 +146,9 @@ export function createStalkerVodDetailActions(
notify('PORTALS.PLAYBACK_ERROR');
}
} finally {
externalLaunchInFlight = false;
if (externalLaunchInFlight === launchKey) {
externalLaunchInFlight = null;
}
settlePendingStart?.();
}
},