()
| 5 | const DEFAULT_ERROR_TOAST_DURATION_MS = 6000; |
| 6 | |
| 7 | export function useErrorToasts() { |
| 8 | const [toasts, setToasts] = useState<ErrorToast[]>([]); |
| 9 | const timeoutByIdRef = useRef(new Map<string, number>()); |
| 10 | |
| 11 | const dismissToast = useCallback((id: string) => { |
| 12 | const timeoutId = timeoutByIdRef.current.get(id); |
| 13 | if (timeoutId) { |
| 14 | window.clearTimeout(timeoutId); |
| 15 | timeoutByIdRef.current.delete(id); |
| 16 | } |
| 17 | setToasts((prev) => prev.filter((toast) => toast.id !== id)); |
| 18 | }, []); |
| 19 | |
| 20 | useEffect(() => { |
| 21 | const timeouts = timeoutByIdRef.current; |
| 22 | const unsubscribe = subscribeErrorToasts((toast) => { |
| 23 | setToasts((prev) => [...prev, toast]); |
| 24 | const durationMs = toast.durationMs ?? DEFAULT_ERROR_TOAST_DURATION_MS; |
| 25 | const timeoutId = window.setTimeout(() => { |
| 26 | dismissToast(toast.id); |
| 27 | }, durationMs); |
| 28 | timeouts.set(toast.id, timeoutId); |
| 29 | }); |
| 30 | |
| 31 | return () => { |
| 32 | unsubscribe(); |
| 33 | for (const timeoutId of timeouts.values()) { |
| 34 | window.clearTimeout(timeoutId); |
| 35 | } |
| 36 | timeouts.clear(); |
| 37 | }; |
| 38 | }, [dismissToast]); |
| 39 | |
| 40 | return { |
| 41 | errorToasts: toasts, |
| 42 | dismissErrorToast: dismissToast, |
| 43 | }; |
| 44 | } |
no test coverage detected