99 lines
4.6 KiB
JavaScript
99 lines
4.6 KiB
JavaScript
// Rôle : surveille globalement les alertes internes des outils timer et calendrier.
|
|
import { useEffect, useRef } from "react";
|
|
import { getCalendarAlertOffsets, getCurrentCalendarOccurrence, getNextCalendarAlertTime } from "../features/toolboxes/modules/calendarUtils.js";
|
|
import { getCountdownTargetMs } from "../features/toolboxes/modules/timerUtils.js";
|
|
import { moduleStorageKey } from "../features/toolboxes/storage/toolboxStorage.js";
|
|
|
|
function getVisibleToolboxIds(route, drawerGameId, links) {
|
|
const visibleIds = new Set();
|
|
const toolboxMatch = route.match(/^\/toolbox\/([^/]+)/);
|
|
if (toolboxMatch?.[1]) visibleIds.add(toolboxMatch[1]);
|
|
const drawerToolboxId = drawerGameId ? links[drawerGameId] : "";
|
|
if (drawerToolboxId) visibleIds.add(drawerToolboxId);
|
|
return visibleIds;
|
|
}
|
|
|
|
function isAlertAllowed(alertMode, toolboxId, visibleToolboxIds) {
|
|
if (alertMode === "off") return false;
|
|
if (alertMode === "visible") return visibleToolboxIds.has(toolboxId);
|
|
return alertMode === "site";
|
|
}
|
|
|
|
function formatAlertMessage(template, toolbox, label) {
|
|
return template
|
|
.replaceAll("{label}", label)
|
|
.replaceAll("{toolbox}", toolbox.name);
|
|
}
|
|
|
|
export function useToolboxAlerts({ toolboxes, moduleData, links, route, drawerGameId, normalizeTimerData, normalizeCalendarData, textContent, notify }) {
|
|
const previousTargetsRef = useRef(new Map());
|
|
const resumedCalendarEventsRef = useRef(new Set());
|
|
const lastTickRef = useRef(Date.now());
|
|
|
|
useEffect(() => {
|
|
const visibleToolboxIds = getVisibleToolboxIds(route, drawerGameId, links);
|
|
const timerAlertTemplate = textContent?.timer?.alertMessage || "Compte à rebours terminé : {label}";
|
|
const calendarAlertTemplate = textContent?.calendar?.alertMessage || "Événement à venir : {label}";
|
|
const calendarResumeTemplate = textContent?.calendar?.resumeAlertMessage || "Événement en cours : {label}";
|
|
|
|
function tick() {
|
|
const nowMs = Date.now();
|
|
const lastTickMs = lastTickRef.current;
|
|
const nextTargets = new Map();
|
|
|
|
toolboxes.forEach((toolbox) => {
|
|
toolbox.modules.forEach((module) => {
|
|
if (module.type === "timer") {
|
|
const data = normalizeTimerData(moduleData[moduleStorageKey(toolbox.id, module.id)]);
|
|
|
|
data.countdowns.forEach((countdown) => {
|
|
if (!isAlertAllowed(countdown.alertMode, toolbox.id, visibleToolboxIds)) return;
|
|
|
|
const key = `${toolbox.id}:${module.id}:${countdown.id}`;
|
|
const targetMs = getCountdownTargetMs(countdown, nowMs);
|
|
const previousTargetMs = previousTargetsRef.current.get(key) || targetMs;
|
|
nextTargets.set(key, targetMs);
|
|
|
|
if (!targetMs || previousTargetMs <= lastTickMs || previousTargetMs > nowMs) return;
|
|
notify(formatAlertMessage(timerAlertTemplate, toolbox, countdown.label));
|
|
});
|
|
}
|
|
|
|
if (module.type === "calendar") {
|
|
const data = normalizeCalendarData(moduleData[moduleStorageKey(toolbox.id, module.id)]);
|
|
|
|
data.events.forEach((event) => {
|
|
if (!isAlertAllowed(event.alertMode, toolbox.id, visibleToolboxIds)) return;
|
|
|
|
const key = `${toolbox.id}:${module.id}:${event.id}`;
|
|
getCalendarAlertOffsets(event).forEach((offset) => {
|
|
const alertKey = `${key}:alert:${offset}`;
|
|
const alertMs = getNextCalendarAlertTime({ ...event, alertOffsetMinutes: offset }, nowMs);
|
|
const previousAlertMs = previousTargetsRef.current.get(alertKey) || alertMs;
|
|
if (alertMs) nextTargets.set(alertKey, alertMs);
|
|
|
|
if (alertMs && previousAlertMs > lastTickMs && previousAlertMs <= nowMs) {
|
|
notify(formatAlertMessage(calendarAlertTemplate, toolbox, event.title));
|
|
}
|
|
});
|
|
|
|
const currentOccurrence = getCurrentCalendarOccurrence(event, nowMs);
|
|
if (!currentOccurrence) return;
|
|
const resumeKey = `${key}:${currentOccurrence.startMs}`;
|
|
if (resumedCalendarEventsRef.current.has(resumeKey)) return;
|
|
resumedCalendarEventsRef.current.add(resumeKey);
|
|
notify(formatAlertMessage(calendarResumeTemplate, toolbox, event.title));
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
previousTargetsRef.current = nextTargets;
|
|
lastTickRef.current = nowMs;
|
|
}
|
|
|
|
tick();
|
|
const intervalId = window.setInterval(tick, 500);
|
|
return () => window.clearInterval(intervalId);
|
|
}, [drawerGameId, links, moduleData, normalizeCalendarData, normalizeTimerData, notify, route, textContent, toolboxes]);
|
|
}
|