import * as React from 'react' import type { ToastActionElement, ToastProps } from '@/components/ui/toast' const TOAST_LIMIT = 10 const TOAST_REMOVE_DELAY = 1000 type ToasterToast = ToastProps & { id: string title?: React.ReactNode description?: React.ReactNode action?: ToastActionElement } const toastTimeouts = new Map>() const addToRemoveQueue = (toastId: string) => { if (toastTimeouts.has(toastId)) return const timeout = setTimeout(() => { toastTimeouts.delete(toastId) toastState.remove(toastId) }, TOAST_REMOVE_DELAY) toastTimeouts.set(toastId, timeout) } const createToastState = () => { const listeners = new Set<(toasts: ToasterToast[]) => void>() let toasts: ToasterToast[] = [] const notify = () => { listeners.forEach((listener) => listener(toasts)) } return { subscribe: (listener: (toasts: ToasterToast[]) => void) => { listeners.add(listener) return () => { listeners.delete(listener) } }, add: (toast: ToasterToast) => { toasts = [toast, ...toasts].slice(0, TOAST_LIMIT) notify() }, update: (toastId: string, toast: Partial) => { toasts = toasts.map((item) => (item.id === toastId ? { ...item, ...toast } : item)) notify() }, dismiss: (toastId: string) => { addToRemoveQueue(toastId) }, remove: (toastId: string) => { toasts = toasts.filter((item) => item.id !== toastId) notify() }, getSnapshot: () => toasts, } } const toastState = createToastState() export const useToast = () => { const [toasts, setToasts] = React.useState(toastState.getSnapshot()) React.useEffect(() => toastState.subscribe(setToasts), []) const toast = React.useCallback((props: Omit) => { const id = crypto.randomUUID() toastState.add({ id, ...props }) return id }, []) const dismiss = React.useCallback((toastId: string) => { toastState.dismiss(toastId) }, []) return { toast, dismiss, toasts, } } export type { ToastProps, ToasterToast }