refactor(client): centralize error feedback with toast events
- emit API/network failures through a shared toast event bridge - remove per-page inline error notifications to avoid duplicate error UI - dedupe repeated toast messages to keep feedback readable
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { AlertCircle, CheckCircle2, Info, X } from 'lucide-react';
|
||||
import styles from './ToastProvider.module.css';
|
||||
import { subscribeApiErrorToasts } from '../../lib/toast-events';
|
||||
|
||||
type ToastVariant = 'success' | 'error' | 'info';
|
||||
|
||||
@@ -34,6 +35,7 @@ export interface ToastProviderProps {
|
||||
export function ToastProvider({ children, durationMs = 3500 }: ToastProviderProps) {
|
||||
const EXIT_ANIMATION_MS = 100;
|
||||
const [items, setItems] = useState<ToastItem[]>([]);
|
||||
const lastShownAtRef = useRef<Map<string, number>>(new Map());
|
||||
|
||||
const remove = useCallback((id: number) => {
|
||||
setItems((prev) => prev.filter((item) => item.id !== id));
|
||||
@@ -49,6 +51,12 @@ export function ToastProvider({ children, durationMs = 3500 }: ToastProviderProp
|
||||
}, [remove]);
|
||||
|
||||
const show = useCallback((message: string, variant: ToastVariant = 'info') => {
|
||||
const now = Date.now();
|
||||
const dedupeKey = `${variant}:${message}`;
|
||||
const previousTs = lastShownAtRef.current.get(dedupeKey) ?? 0;
|
||||
if (now - previousTs < 600) return;
|
||||
lastShownAtRef.current.set(dedupeKey, now);
|
||||
|
||||
const id = Date.now() + Math.floor(Math.random() * 1000);
|
||||
setItems((prev) => [...prev, { id, message, variant, closing: false }]);
|
||||
window.setTimeout(() => dismiss(id), durationMs);
|
||||
@@ -61,6 +69,10 @@ export function ToastProvider({ children, durationMs = 3500 }: ToastProviderProp
|
||||
info: (message: string) => show(message, 'info'),
|
||||
}), [show]);
|
||||
|
||||
useEffect(() => {
|
||||
return subscribeApiErrorToasts((message) => api.error(message));
|
||||
}, [api]);
|
||||
|
||||
return (
|
||||
<ToastContext.Provider value={api}>
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user