import {create} from 'zustand' export type ToastType = 'success' | 'error' type ToastState = { message: string type: ToastType visible: boolean } type ToastStore = { toast: ToastState | null showToast: (message: string, type?: ToastType) => void clearToast: () => void } let toastTimer: ReturnType | null = null let toastRemoveTimer: ReturnType | null = null export function resolveToastMessage(source: unknown, fallback = '') { if (typeof source === 'string' && source.trim()) { return source.trim() } if (typeof source === 'object' && source !== null && 'msg' in source) { const message = (source as {msg?: unknown}).msg if (typeof message === 'string' && message.trim()) { return message.trim() } } return fallback.trim() } export const useToastStore = create((set) => ({ toast: null, showToast: (message, type = 'success') => { if (toastTimer) { clearTimeout(toastTimer) } if (toastRemoveTimer) { clearTimeout(toastRemoveTimer) } set({ toast: { message, type, visible: false, }, }) requestAnimationFrame(() => { set((state) => state.toast ? { toast: { ...state.toast, visible: true, }, } : state) }) toastTimer = setTimeout(() => { set((state) => state.toast ? { toast: { ...state.toast, visible: false, }, } : state) toastTimer = null toastRemoveTimer = setTimeout(() => { set({toast: null}) toastRemoveTimer = null }, 220) }, 2000) }, clearToast: () => { if (toastTimer) { clearTimeout(toastTimer) toastTimer = null } if (toastRemoveTimer) { clearTimeout(toastRemoveTimer) toastRemoveTimer = null } set({toast: null}) }, })) export function notifySuccess(source: unknown, fallback = '') { const message = resolveToastMessage(source, fallback) if (!message) { return } useToastStore.getState().showToast(message, 'success') } export function notifyError(source: unknown, fallback = '') { const message = resolveToastMessage(source, fallback) if (!message) { return } useToastStore.getState().showToast(message, 'error') }