84 lines
2.6 KiB
TypeScript
84 lines
2.6 KiB
TypeScript
"use client";
|
|
|
|
import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
import NotificationsToaster, { type NotificationItem, type NotificationTone } from "@/components/notifications-toaster";
|
|
|
|
type NotifyInput = {
|
|
title: string;
|
|
message?: string;
|
|
tone?: NotificationTone;
|
|
durationMs?: number;
|
|
};
|
|
|
|
type NotificationsContextValue = {
|
|
notify: (input: NotifyInput) => void;
|
|
};
|
|
|
|
const NotificationsContext = createContext<NotificationsContextValue | null>(null);
|
|
|
|
function createId() {
|
|
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
|
|
return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
}
|
|
|
|
export function NotificationsProvider({ children }: { children: React.ReactNode }) {
|
|
const [items, setItems] = useState<NotificationItem[]>([]);
|
|
const timersRef = useRef<number[]>([]);
|
|
|
|
function dismiss(id: string) {
|
|
setItems(prev => prev.map(item => item.id === id ? { ...item, closing: true } : item));
|
|
const t = window.setTimeout(() => {
|
|
setItems(prev => prev.filter(item => item.id !== id));
|
|
}, 250);
|
|
timersRef.current.push(t);
|
|
}
|
|
|
|
function notify(input: NotifyInput) {
|
|
const id = createId();
|
|
const durationMs = Math.max(1200, input.durationMs ?? 4200);
|
|
const tone = input.tone ?? "info";
|
|
|
|
setItems(prev => [
|
|
...prev,
|
|
{
|
|
id,
|
|
title: input.title,
|
|
message: input.message,
|
|
tone,
|
|
closing: false
|
|
}
|
|
]);
|
|
|
|
const fadeMs = 250;
|
|
const t1 = window.setTimeout(() => {
|
|
setItems(prev => prev.map(item => item.id === id ? { ...item, closing: true } : item));
|
|
}, Math.max(0, durationMs - fadeMs));
|
|
const t2 = window.setTimeout(() => {
|
|
setItems(prev => prev.filter(item => item.id !== id));
|
|
}, durationMs);
|
|
timersRef.current.push(t1, t2);
|
|
}
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
timersRef.current.forEach(timer => window.clearTimeout(timer));
|
|
timersRef.current = [];
|
|
};
|
|
}, []);
|
|
|
|
const value = useMemo(() => ({ notify }), []);
|
|
|
|
return (
|
|
<NotificationsContext.Provider value={value}>
|
|
{children}
|
|
<NotificationsToaster items={items} onDismiss={dismiss} />
|
|
</NotificationsContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useNotificationsContext() {
|
|
const ctx = useContext(NotificationsContext);
|
|
if (!ctx) throw new Error("NotificationsProvider is missing");
|
|
return ctx;
|
|
}
|