import type { NotificationOptions } from '../types/anki'; import type { NotificationType, OverlayNotificationPayload } from '../types/notification'; export interface UiFeedbackState { progressDepth: number; progressTimer: ReturnType | null; progressMessage: string; progressFrame: number; } export interface UiFeedbackResult { success: boolean; message: string; } export interface UiFeedbackNotificationContext { getNotificationType: () => NotificationType | undefined; showOsd: (text: string) => void; showOverlayNotification?: (payload: OverlayNotificationPayload) => void; showSystemNotification: (title: string, options: NotificationOptions) => void; } export interface UiFeedbackOptions { setUpdateInProgress: (value: boolean) => void; showOsdNotification: (text: string) => void; } export function createUiFeedbackState(): UiFeedbackState { return { progressDepth: 0, progressTimer: null, progressMessage: '', progressFrame: 0, }; } export function showStatusNotification( message: string, context: UiFeedbackNotificationContext, ): void { const type = context.getNotificationType() ?? 'osd'; if (type === 'none') { return; } if (type === 'overlay' || type === 'both') { if (context.showOverlayNotification) { context.showOverlayNotification({ title: 'SubMiner', body: message, variant: 'info', }); } else if (type === 'overlay') { context.showSystemNotification('SubMiner', { body: message }); } } if (type === 'osd' || type === 'osd-system') { context.showOsd(message); } if (type === 'system' || type === 'both' || type === 'osd-system') { context.showSystemNotification('SubMiner', { body: message }); } } export function beginUpdateProgress( state: UiFeedbackState, initialMessage: string, showProgressTick: (text: string) => void, ): void { state.progressDepth += 1; if (state.progressDepth > 1) return; state.progressMessage = initialMessage; state.progressFrame = 0; showProgressTick(`${state.progressMessage}`); state.progressTimer = setInterval(() => { showProgressTick(`${state.progressMessage} ${['|', '/', '-', '\\'][state.progressFrame % 4]}`); state.progressFrame += 1; }, 180); } export function endUpdateProgress( state: UiFeedbackState, clearProgressTimer: (timer: ReturnType) => void, ): void { state.progressDepth = Math.max(0, state.progressDepth - 1); if (state.progressDepth > 0) return; clearUpdateProgress(state, clearProgressTimer); } export function clearUpdateProgress( state: UiFeedbackState, clearProgressTimer: (timer: ReturnType) => void, ): void { state.progressDepth = 0; if (state.progressTimer) { clearProgressTimer(state.progressTimer); state.progressTimer = null; } state.progressMessage = ''; state.progressFrame = 0; } export function showProgressTick( state: UiFeedbackState, showOsdNotification: (text: string) => void, ): void { if (!state.progressMessage) return; const frames = ['|', '/', '-', '\\']; const frame = frames[state.progressFrame % frames.length]; state.progressFrame += 1; showOsdNotification(`${state.progressMessage} ${frame}`); } export function showUpdateResult( state: UiFeedbackState, options: { clearProgressTimer: (timer: ReturnType) => void; showOsdNotification: (text: string) => void; }, result: UiFeedbackResult, ): void { clearUpdateProgress(state, options.clearProgressTimer); const prefix = result.success ? '✓' : 'x'; options.showOsdNotification(`${prefix} ${result.message}`); } export async function withUpdateProgress( state: UiFeedbackState, options: UiFeedbackOptions, initialMessage: string, action: () => Promise, ): Promise { beginUpdateProgress(state, initialMessage, () => showProgressTick(state, options.showOsdNotification), ); options.setUpdateInProgress(true); try { return await action(); } finally { options.setUpdateInProgress(false); endUpdateProgress(state, clearInterval); } }