Files
SubMiner/src/main/runtime/character-dictionary-auto-sync-notifications.ts
T
sudacode 501304e451 fix(startup): release autoplay gate before first subtitle line
- Send synthetic `__warm__` payload when no current subtitle exists so the gate can release without waiting for a subtitle event that can't fire while paused
- Visible-overlay readiness accepts `__warm__` once the overlay is content-ready, rejects it otherwise
- Autoplay gate self-retries via scheduled polling when signal target isn't ready, removing reliance on an external flush event
- Skip duplicate desktop notification when overlay or startup sequencer already delivered it
2026-06-08 02:22:54 -07:00

71 lines
2.5 KiB
TypeScript

import type { CharacterDictionaryAutoSyncStatusEvent } from './character-dictionary-auto-sync';
import type { StartupOsdSequencerCharacterDictionaryEvent } from './startup-osd-sequencer';
import type { NotificationType, OverlayNotificationPayload } from '../../types/notification';
import { shouldShowDesktop, shouldShowOverlay, shouldShowOsd } from './notification-routing';
export type CharacterDictionaryAutoSyncNotificationEvent = CharacterDictionaryAutoSyncStatusEvent;
export interface CharacterDictionaryAutoSyncNotificationDeps {
getNotificationType: () => NotificationType | undefined;
showOsd: (message: string) => boolean | void;
showOverlayNotification?: (payload: OverlayNotificationPayload) => void;
showDesktopNotification: (title: string, options: { body?: string }) => void;
startupOsdSequencer?: {
notifyCharacterDictionaryStatus: (
event: StartupOsdSequencerCharacterDictionaryEvent,
) => boolean;
};
}
function isTerminalPhase(phase: CharacterDictionaryAutoSyncNotificationEvent['phase']): boolean {
return phase === 'ready' || phase === 'failed';
}
function overlayVariantForPhase(
phase: CharacterDictionaryAutoSyncNotificationEvent['phase'],
): OverlayNotificationPayload['variant'] {
if (phase === 'ready') return 'success';
if (phase === 'failed') return 'error';
return 'progress';
}
export function notifyCharacterDictionaryAutoSyncStatus(
event: CharacterDictionaryAutoSyncNotificationEvent,
deps: CharacterDictionaryAutoSyncNotificationDeps,
): void {
const type = deps.getNotificationType() ?? 'overlay';
if (type === 'none') return;
let overlayShown = false;
let startupSequencerShown = false;
if (shouldShowOverlay(type)) {
if (deps.showOverlayNotification) {
deps.showOverlayNotification({
id: 'character-dictionary-auto-sync',
title: 'Character dictionary',
body: event.message,
variant: overlayVariantForPhase(event.phase),
persistent: !isTerminalPhase(event.phase),
});
overlayShown = true;
} else if (!shouldShowDesktop(type)) {
deps.showDesktopNotification('SubMiner', { body: event.message });
}
}
if (shouldShowOsd(type)) {
if (deps.startupOsdSequencer) {
startupSequencerShown = deps.startupOsdSequencer.notifyCharacterDictionaryStatus({
phase: event.phase,
message: event.message,
});
} else {
deps.showOsd(event.message);
}
}
if (shouldShowDesktop(type) && !overlayShown && !startupSequencerShown) {
deps.showDesktopNotification('SubMiner', { body: event.message });
}
}