45 lines
1.1 KiB
TypeScript
45 lines
1.1 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
|
|
export type ToastType = "success" | "error" | "info";
|
|
|
|
export interface ToastItem {
|
|
id: number;
|
|
type: ToastType;
|
|
message: string;
|
|
onRetry?: () => void;
|
|
}
|
|
|
|
let toastId = 0;
|
|
const listeners = new Set<(items: ToastItem[]) => void>();
|
|
let queue: ToastItem[] = [];
|
|
|
|
function notify() {
|
|
listeners.forEach((fn) => fn([...queue]));
|
|
}
|
|
|
|
function dismiss(id: number) {
|
|
queue = queue.filter((t) => t.id !== id);
|
|
notify();
|
|
}
|
|
|
|
export function toast(message: string, type: ToastType = "info", onRetry?: () => void) {
|
|
const id = ++toastId;
|
|
queue = [...queue.slice(-4), { id, type, message, onRetry }];
|
|
notify();
|
|
setTimeout(() => dismiss(id), type === "error" ? 5000 : 3000);
|
|
return id;
|
|
}
|
|
|
|
toast.success = (msg: string) => toast(msg, "success");
|
|
toast.error = (msg: string, onRetry?: () => void) => toast(msg, "error", onRetry);
|
|
toast.info = (msg: string) => toast(msg, "info");
|
|
|
|
export function useToastState() {
|
|
const [items, setItems] = useState<ToastItem[]>([]);
|
|
useEffect(() => {
|
|
listeners.add(setItems);
|
|
return () => { listeners.delete(setItems); };
|
|
}, []);
|
|
return { items, dismiss };
|
|
}
|