67 lines
2.5 KiB
JavaScript
67 lines
2.5 KiB
JavaScript
import { useMemo, useRef, useState } from "react";
|
|
|
|
export default function useNotifications() {
|
|
const [toasts, setToasts] = useState([]);
|
|
const toastTimers = useRef(new Map());
|
|
const [notifications, setNotifications] = useState([]);
|
|
const [notificationFilter, setNotificationFilter] = useState("all");
|
|
|
|
const showNotification = (text, tone = "info") => {
|
|
if (!text) return;
|
|
const key = `${tone}|${text}`;
|
|
const now = Date.now();
|
|
setNotifications((prev) => {
|
|
const existingIndex = prev.findIndex((item) => item.key === key);
|
|
if (existingIndex >= 0) {
|
|
const updated = { ...prev[existingIndex], count: (prev[existingIndex].count || 1) + 1, lastAt: now };
|
|
const next = [updated, ...prev.filter((_, index) => index !== existingIndex)];
|
|
return next.slice(0, 20);
|
|
}
|
|
const entry = { text, tone, id: `${now}-${Math.random().toString(36).slice(2)}`, key, count: 1, lastAt: now };
|
|
return [entry, ...prev].slice(0, 20);
|
|
});
|
|
setToasts((prev) => {
|
|
const existingIndex = prev.findIndex((item) => item.key === key);
|
|
if (existingIndex >= 0) {
|
|
const updated = { ...prev[existingIndex], count: (prev[existingIndex].count || 1) + 1, lastAt: now };
|
|
const next = [updated, ...prev.filter((_, index) => index !== existingIndex)];
|
|
return next.slice(0, 4);
|
|
}
|
|
const entry = { text, tone, id: `${now}-${Math.random().toString(36).slice(2)}`, key, count: 1, lastAt: now };
|
|
return [entry, ...prev].slice(0, 4);
|
|
});
|
|
if (toastTimers.current.has(key)) {
|
|
clearTimeout(toastTimers.current.get(key));
|
|
}
|
|
const timeoutId = setTimeout(() => {
|
|
setToasts((prev) => prev.filter((item) => item.key !== key));
|
|
toastTimers.current.delete(key);
|
|
}, 6000);
|
|
toastTimers.current.set(key, timeoutId);
|
|
};
|
|
|
|
const dismissToast = (toast) => {
|
|
setToasts((prev) => prev.filter((item) => item.id !== toast.id));
|
|
if (toast.key && toastTimers.current.has(toast.key)) {
|
|
clearTimeout(toastTimers.current.get(toast.key));
|
|
toastTimers.current.delete(toast.key);
|
|
}
|
|
};
|
|
|
|
const filteredNotifications = useMemo(() => {
|
|
if (notificationFilter === "all") return notifications;
|
|
return notifications.filter((item) => item.tone === notificationFilter);
|
|
}, [notifications, notificationFilter]);
|
|
|
|
return {
|
|
toasts,
|
|
notifications,
|
|
setNotifications,
|
|
notificationFilter,
|
|
setNotificationFilter,
|
|
filteredNotifications,
|
|
showNotification,
|
|
dismissToast
|
|
};
|
|
}
|