import * as React from "react" import type { ToastActionElement, ToastProps } from "@/components/ui/toast" // Based on shadcn/ui toast hook implementation export type Toast = Omit & { id: string title?: React.ReactNode description?: React.ReactNode action?: ToastActionElement } const TOAST_LIMIT = 5 const TOAST_REMOVE_DELAY = 1000 type State = { toasts: Toast[] } type ToastInput = Omit type Listener = (state: State) => void let count = 0 function genId() { count = (count + 1) % Number.MAX_SAFE_INTEGER return count.toString() } const toastTimeouts = new Map>() const state: State = { toasts: [] } const listeners: Listener[] = [] function setState(newState: Partial) { Object.assign(state, newState) listeners.forEach((l) => l(state)) } function addToast(toast: ToastInput) { const id = genId() const newToast: Toast = { ...toast, id, open: true as any, } // Ensure limit const nextToasts = [newToast, ...state.toasts].slice(0, TOAST_LIMIT) setState({ toasts: nextToasts }) return id } function updateToast(id: string, update: Partial) { setState({ toasts: state.toasts.map((t) => (t.id === id ? { ...t, ...update } : t)), }) } function dismissToast(id?: string) { if (id) { queueRemoval(id) updateToast(id, { open: false } as any) } else { state.toasts.forEach((t) => { queueRemoval(t.id) updateToast(t.id, { open: false } as any) }) } } function removeToast(id?: string) { if (id) { setState({ toasts: state.toasts.filter((t) => t.id !== id) }) } else { setState({ toasts: [] }) } } function queueRemoval(id: string) { if (toastTimeouts.has(id)) return const timeout = setTimeout(() => { toastTimeouts.delete(id) removeToast(id) }, TOAST_REMOVE_DELAY) toastTimeouts.set(id, timeout) } export function useToast() { const [localState, setLocalState] = React.useState(state) React.useEffect(() => { listeners.push(setLocalState) return () => { const index = listeners.indexOf(setLocalState) if (index > -1) listeners.splice(index, 1) } }, []) return { ...localState, toast: ({ ...props }: ToastInput) => addToast(props), dismiss: (id?: string) => dismissToast(id), remove: (id?: string) => removeToast(id), } } export type { ToastActionElement, ToastProps }