mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-06-09 15:13:32 -07:00
refactor(notifications): extract routing predicates and fix pre-overlay
- Extract shouldShowOsd/Overlay/Desktop into notification-routing.ts (was duplicated in 3 files) - Add resolveOverlayReadinessNotificationType: preserves system channel when overlay not ready (both→osd-system, system→system, overlay→osd) - Route overlay loading status through showConfiguredStatusNotification instead of raw OSD
This commit is contained in:
+9
-5
@@ -609,6 +609,7 @@ import {
|
|||||||
notifyConfiguredStatus,
|
notifyConfiguredStatus,
|
||||||
type ConfiguredStatusNotificationOptions,
|
type ConfiguredStatusNotificationOptions,
|
||||||
} from './main/runtime/configured-status-notification';
|
} from './main/runtime/configured-status-notification';
|
||||||
|
import { resolveOverlayReadinessNotificationType } from './main/runtime/notification-routing';
|
||||||
import { createUpdateDialogPresenter } from './main/runtime/update/update-dialogs';
|
import { createUpdateDialogPresenter } from './main/runtime/update/update-dialogs';
|
||||||
import {
|
import {
|
||||||
runUpdateCliCommand,
|
runUpdateCliCommand,
|
||||||
@@ -3337,10 +3338,7 @@ function isVisibleOverlayContentReady(): boolean {
|
|||||||
|
|
||||||
function getConfiguredStatusNotificationType(): NotificationType {
|
function getConfiguredStatusNotificationType(): NotificationType {
|
||||||
const configuredType = getResolvedConfig().ankiConnect.behavior.notificationType;
|
const configuredType = getResolvedConfig().ankiConnect.behavior.notificationType;
|
||||||
if (configuredType === 'none' || isVisibleOverlayContentReady()) {
|
return resolveOverlayReadinessNotificationType(configuredType, isVisibleOverlayContentReady());
|
||||||
return configuredType;
|
|
||||||
}
|
|
||||||
return 'osd';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendOverlayNotificationEvent(payload: OverlayNotificationEventPayload): void {
|
function sendOverlayNotificationEvent(payload: OverlayNotificationEventPayload): void {
|
||||||
@@ -3414,7 +3412,13 @@ function showYoutubeFlowStatusNotification(message: string): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showOverlayLoadingStatusNotification(message: string): void {
|
function showOverlayLoadingStatusNotification(message: string): void {
|
||||||
showMpvOsd(message);
|
showConfiguredStatusNotification(message, {
|
||||||
|
id: 'overlay-loading-status',
|
||||||
|
title: 'SubMiner',
|
||||||
|
variant: 'progress',
|
||||||
|
persistent: true,
|
||||||
|
desktop: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildBroadcastRuntimeOptionsChangedMainDepsHandler =
|
const buildBroadcastRuntimeOptionsChangedMainDepsHandler =
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { CharacterDictionaryAutoSyncStatusEvent } from './character-dictionary-auto-sync';
|
import type { CharacterDictionaryAutoSyncStatusEvent } from './character-dictionary-auto-sync';
|
||||||
import type { StartupOsdSequencerCharacterDictionaryEvent } from './startup-osd-sequencer';
|
import type { StartupOsdSequencerCharacterDictionaryEvent } from './startup-osd-sequencer';
|
||||||
import type { NotificationType, OverlayNotificationPayload } from '../../types/notification';
|
import type { NotificationType, OverlayNotificationPayload } from '../../types/notification';
|
||||||
|
import { shouldShowDesktop, shouldShowOverlay, shouldShowOsd } from './notification-routing';
|
||||||
|
|
||||||
export type CharacterDictionaryAutoSyncNotificationEvent = CharacterDictionaryAutoSyncStatusEvent;
|
export type CharacterDictionaryAutoSyncNotificationEvent = CharacterDictionaryAutoSyncStatusEvent;
|
||||||
|
|
||||||
@@ -16,18 +17,6 @@ export interface CharacterDictionaryAutoSyncNotificationDeps {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldShowOsd(type: NotificationType): boolean {
|
|
||||||
return type === 'osd' || type === 'osd-system';
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldShowOverlay(type: NotificationType): boolean {
|
|
||||||
return type === 'overlay' || type === 'both';
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldShowDesktop(type: NotificationType): boolean {
|
|
||||||
return type === 'system' || type === 'both' || type === 'osd-system';
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTerminalPhase(phase: CharacterDictionaryAutoSyncNotificationEvent['phase']): boolean {
|
function isTerminalPhase(phase: CharacterDictionaryAutoSyncNotificationEvent['phase']): boolean {
|
||||||
return phase === 'ready' || phase === 'failed';
|
return phase === 'ready' || phase === 'failed';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ test('notifyConfiguredStatus routes both to overlay and system without osd', ()
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('notifyConfiguredStatus routes pre-overlay status to osd only', () => {
|
test('notifyConfiguredStatus routes pre-overlay both status to osd and desktop', () => {
|
||||||
const calls: string[] = [];
|
const calls: string[] = [];
|
||||||
|
|
||||||
notifyConfiguredStatus('Overlay loading...', {
|
notifyConfiguredStatus('Overlay loading...', {
|
||||||
@@ -42,7 +42,25 @@ test('notifyConfiguredStatus routes pre-overlay status to osd only', () => {
|
|||||||
calls.push(`desktop:${title}:${options.body ?? ''}`),
|
calls.push(`desktop:${title}:${options.body ?? ''}`),
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.deepEqual(calls, ['osd:Overlay loading...']);
|
assert.deepEqual(calls, ['osd:Overlay loading...', 'desktop:SubMiner:Overlay loading...']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notifyConfiguredStatus routes pre-overlay system status to desktop only', () => {
|
||||||
|
const calls: string[] = [];
|
||||||
|
|
||||||
|
notifyConfiguredStatus('Overlay loading...', {
|
||||||
|
getNotificationType: () => 'system',
|
||||||
|
isOverlayReady: () => false,
|
||||||
|
showOsd: (message) => {
|
||||||
|
calls.push(`osd:${message}`);
|
||||||
|
},
|
||||||
|
showOverlayNotification: (payload) =>
|
||||||
|
calls.push(`overlay:${payload.id ?? ''}:${payload.body ?? ''}`),
|
||||||
|
showDesktopNotification: (title, options) =>
|
||||||
|
calls.push(`desktop:${title}:${options.body ?? ''}`),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(calls, ['desktop:SubMiner:Overlay loading...']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('notifyConfiguredStatus keeps osd-system on legacy surfaces', () => {
|
test('notifyConfiguredStatus keeps osd-system on legacy surfaces', () => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { NotificationType, OverlayNotificationPayload } from '../../types/notification';
|
import type { NotificationType, OverlayNotificationPayload } from '../../types/notification';
|
||||||
|
import { shouldShowDesktop, shouldShowOverlay, shouldShowOsd } from './notification-routing';
|
||||||
|
|
||||||
export interface ConfiguredStatusNotificationDeps {
|
export interface ConfiguredStatusNotificationDeps {
|
||||||
getNotificationType: () => NotificationType | undefined;
|
getNotificationType: () => NotificationType | undefined;
|
||||||
@@ -17,18 +18,6 @@ export interface ConfiguredStatusNotificationOptions {
|
|||||||
delivery?: 'notification' | 'feedback';
|
delivery?: 'notification' | 'feedback';
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldShowOverlay(type: NotificationType): boolean {
|
|
||||||
return type === 'overlay' || type === 'both';
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldShowOsd(type: NotificationType): boolean {
|
|
||||||
return type === 'osd' || type === 'osd-system';
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldShowDesktop(type: NotificationType): boolean {
|
|
||||||
return type === 'system' || type === 'both' || type === 'osd-system';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPlaybackFeedbackNotificationOptions(
|
export function getPlaybackFeedbackNotificationOptions(
|
||||||
message: string,
|
message: string,
|
||||||
): ConfiguredStatusNotificationOptions {
|
): ConfiguredStatusNotificationOptions {
|
||||||
@@ -60,12 +49,9 @@ export function notifyConfiguredStatus(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deps.isOverlayReady?.() === false) {
|
const overlayReady = deps.isOverlayReady?.() !== false;
|
||||||
deps.showOsd(message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showOverlay) {
|
if (showOverlay && overlayReady) {
|
||||||
if (deps.showOverlayNotification) {
|
if (deps.showOverlayNotification) {
|
||||||
deps.showOverlayNotification({
|
deps.showOverlayNotification({
|
||||||
id: options.id,
|
id: options.id,
|
||||||
@@ -77,6 +63,8 @@ export function notifyConfiguredStatus(
|
|||||||
} else if (desktopEnabled && !shouldShowDesktop(type)) {
|
} else if (desktopEnabled && !shouldShowDesktop(type)) {
|
||||||
deps.showDesktopNotification(options.title ?? 'SubMiner', { body: message });
|
deps.showDesktopNotification(options.title ?? 'SubMiner', { body: message });
|
||||||
}
|
}
|
||||||
|
} else if (showOverlay && !showOsd) {
|
||||||
|
deps.showOsd(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showOsd) {
|
if (showOsd) {
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import {
|
||||||
|
resolveOverlayReadinessNotificationType,
|
||||||
|
shouldShowDesktop,
|
||||||
|
shouldShowOverlay,
|
||||||
|
shouldShowOsd,
|
||||||
|
} from './notification-routing';
|
||||||
|
|
||||||
|
test('notification routing preserves system notification while overlay is not ready', () => {
|
||||||
|
assert.equal(resolveOverlayReadinessNotificationType('system', false), 'system');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notification routing preserves both as osd plus system while overlay is not ready', () => {
|
||||||
|
assert.equal(resolveOverlayReadinessNotificationType('both', false), 'osd-system');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notification routing falls back overlay-only notification to osd while overlay is not ready', () => {
|
||||||
|
assert.equal(resolveOverlayReadinessNotificationType('overlay', false), 'osd');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notification routing predicates classify delivery channels', () => {
|
||||||
|
assert.equal(shouldShowOverlay('both'), true);
|
||||||
|
assert.equal(shouldShowOverlay('system'), false);
|
||||||
|
assert.equal(shouldShowOsd('osd-system'), true);
|
||||||
|
assert.equal(shouldShowOsd('both'), false);
|
||||||
|
assert.equal(shouldShowDesktop('osd-system'), true);
|
||||||
|
assert.equal(shouldShowDesktop('overlay'), false);
|
||||||
|
});
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import type { NotificationType } from '../../types/notification';
|
||||||
|
|
||||||
|
export function shouldShowOsd(type: NotificationType): boolean {
|
||||||
|
return type === 'osd' || type === 'osd-system';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldShowOverlay(type: NotificationType): boolean {
|
||||||
|
return type === 'overlay' || type === 'both';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldShowDesktop(type: NotificationType): boolean {
|
||||||
|
return type === 'system' || type === 'both' || type === 'osd-system';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveOverlayReadinessNotificationType(
|
||||||
|
type: NotificationType,
|
||||||
|
overlayReady: boolean,
|
||||||
|
): NotificationType {
|
||||||
|
if (overlayReady) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
if (type === 'overlay') {
|
||||||
|
return 'osd';
|
||||||
|
}
|
||||||
|
if (type === 'both') {
|
||||||
|
return 'osd-system';
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { NotificationType, OverlayNotificationPayload } from '../../types/notification';
|
import type { NotificationType, OverlayNotificationPayload } from '../../types/notification';
|
||||||
|
import { shouldShowDesktop, shouldShowOverlay, shouldShowOsd } from './notification-routing';
|
||||||
|
|
||||||
export interface StartupOsdSequencerCharacterDictionaryEvent {
|
export interface StartupOsdSequencerCharacterDictionaryEvent {
|
||||||
phase: 'checking' | 'generating' | 'syncing' | 'building' | 'importing' | 'ready' | 'failed';
|
phase: 'checking' | 'generating' | 'syncing' | 'building' | 'importing' | 'ready' | 'failed';
|
||||||
@@ -21,18 +22,6 @@ interface StartupStatusNotificationOptions {
|
|||||||
desktop?: boolean;
|
desktop?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldShowOsd(type: NotificationType): boolean {
|
|
||||||
return type === 'osd' || type === 'osd-system';
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldShowOverlay(type: NotificationType): boolean {
|
|
||||||
return type === 'overlay' || type === 'both';
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldShowDesktop(type: NotificationType): boolean {
|
|
||||||
return type === 'system' || type === 'both' || type === 'osd-system';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createStartupOsdSequencer(deps: StartupOsdSequencerDeps): {
|
export function createStartupOsdSequencer(deps: StartupOsdSequencerDeps): {
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
showTokenizationLoading: (message: string) => void;
|
showTokenizationLoading: (message: string) => void;
|
||||||
|
|||||||
Reference in New Issue
Block a user