| 89 | * The hook handles auto-dismissal of toasts after their duration expires. |
| 90 | */ |
| 91 | export function useToast() { |
| 92 | const { toasts, addToast, removeToast, clearToasts } = useToastStore() |
| 93 | |
| 94 | // Track active timers for cleanup |
| 95 | const timersRef = useRef<Map<string, NodeJS.Timeout>>(new Map()) |
| 96 | |
| 97 | // Get the current toast to display (first in queue) |
| 98 | const currentToast = toasts.length > 0 ? toasts[0] : null |
| 99 | |
| 100 | // Set up auto-dismissal timer for current toast |
| 101 | useEffect(() => { |
| 102 | if (!currentToast) { |
| 103 | return |
| 104 | } |
| 105 | |
| 106 | // Check if timer already exists for this toast |
| 107 | if (timersRef.current.has(currentToast.id)) { |
| 108 | return |
| 109 | } |
| 110 | |
| 111 | // Calculate remaining time (accounts for time already elapsed) |
| 112 | const elapsed = Date.now() - currentToast.createdAt |
| 113 | const remainingTime = Math.max(0, currentToast.duration - elapsed) |
| 114 | |
| 115 | const timer = setTimeout(() => { |
| 116 | removeToast(currentToast.id) |
| 117 | timersRef.current.delete(currentToast.id) |
| 118 | }, remainingTime) |
| 119 | |
| 120 | timersRef.current.set(currentToast.id, timer) |
| 121 | |
| 122 | return () => { |
| 123 | // Clean up timer if toast is removed before expiry |
| 124 | const existingTimer = timersRef.current.get(currentToast.id) |
| 125 | if (existingTimer) { |
| 126 | clearTimeout(existingTimer) |
| 127 | timersRef.current.delete(currentToast.id) |
| 128 | } |
| 129 | } |
| 130 | }, [currentToast?.id, currentToast?.createdAt, currentToast?.duration, removeToast]) |
| 131 | |
| 132 | // Cleanup all timers on unmount |
| 133 | useEffect(() => { |
| 134 | return () => { |
| 135 | timersRef.current.forEach((timer) => clearTimeout(timer)) |
| 136 | timersRef.current.clear() |
| 137 | } |
| 138 | }, []) |
| 139 | |
| 140 | // Convenience methods for different toast types |
| 141 | const showToast = useCallback( |
| 142 | (message: string, type?: ToastType, duration?: number) => { |
| 143 | return addToast(message, type, duration) |
| 144 | }, |
| 145 | [addToast], |
| 146 | ) |
| 147 | |
| 148 | const showInfo = useCallback( |