9134e7d216
CC12: renderer views/ (5 screens + Onboarding + settings cards) + lib/ (theme/thumb/datetime/id/qualityOptions/useClipboardLink/queueStats); main core/ (buildArgs/validation/indexerCore/ytdlpPolicy). git mv preserved history; all src+test imports updated. Build emits view chunks from views/, 344 tests + typecheck green, live probe rendered all screens. CC7: wc/onProgress is the leading arg everywhere — downloadAppUpdate(wc,url), indexSource(onProgress,url,signal), indexSourceCancelable(onProgress,url). CC10: closed by-design — jsonStore backs all records; settings stay in electron-store for DPAPI secret encryption (a deliberate two-store split). Also closes the UI24/W4 context-menu cross-reference. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
46 lines
1.5 KiB
TypeScript
46 lines
1.5 KiB
TypeScript
import { create } from 'zustand'
|
|
import { newId } from '../lib/id'
|
|
|
|
/**
|
|
* A tiny transient-notification store (audit UX6/UX9). The app had no way to tell
|
|
* the user about a background failure — a swallowed `openPath` error, a file that
|
|
* moved — so those actions silently did nothing. This backs a lightweight,
|
|
* non-blocking toast stack rendered by `Toaster`; any store or component can raise
|
|
* one via {@link toast}.
|
|
*/
|
|
export type ToastTone = 'info' | 'error' | 'success'
|
|
|
|
export interface Toast {
|
|
id: string
|
|
message: string
|
|
tone: ToastTone
|
|
}
|
|
|
|
interface ToastState {
|
|
toasts: Toast[]
|
|
show: (message: string, tone?: ToastTone) => void
|
|
dismiss: (id: string) => void
|
|
}
|
|
|
|
/** How long a toast lingers before auto-dismissing. */
|
|
const AUTO_DISMISS_MS = 5000
|
|
|
|
export const useToasts = create<ToastState>((set, get) => ({
|
|
toasts: [],
|
|
show: (message, tone = 'info') => {
|
|
const id = newId('toast')
|
|
set((s) => ({ toasts: [...s.toasts, { id, message, tone }] }))
|
|
// Auto-dismiss so the stack self-clears; the user can also dismiss early.
|
|
setTimeout(() => get().dismiss(id), AUTO_DISMISS_MS)
|
|
},
|
|
dismiss: (id) => set((s) => ({ toasts: s.toasts.filter((t) => t.id !== id) }))
|
|
}))
|
|
|
|
/**
|
|
* Imperative shortcut for non-component callers (the Zustand stores), so a store
|
|
* action can surface an error without wiring the hook through a component.
|
|
*/
|
|
export function toast(message: string, tone?: ToastTone): void {
|
|
useToasts.getState().show(message, tone)
|
|
}
|