Improve local backup permissions and change tracking
All checks were successful
Deploy Sokko G / deploy (push) Successful in 19s

This commit is contained in:
Shinuwa 2026-09-25 10:45:12 +02:00
parent 34e542be28
commit ee823d2c37
13 changed files with 372 additions and 37 deletions

View file

@ -1,6 +1,6 @@
// Rôle : point d'entrée React, assemble données, routes, shell et overlays.
/* eslint-disable react-refresh/only-export-components -- point d'entrée Vite sans export de composant. */
import { lazy, StrictMode, Suspense, useCallback, useEffect, useState } from "react";
import { lazy, StrictMode, Suspense, useCallback, useEffect, useRef, useState } from "react";
import { createRoot } from "react-dom/client";
import "./styles/main.scss";
import { AppOverlays } from "./components/AppOverlays.jsx";
@ -24,6 +24,7 @@ const DEFAULT_NOTIFICATION_DURATION_MS = 120000;
const SHORT_NOTIFICATION_DURATION_MS = 5000;
function getNotificationDuration(message, options = {}) {
if (options.persistent) return 0;
if (Number.isFinite(options.durationMs)) return Math.max(1000, Number(options.durationMs));
const normalized = String(message || "").toLowerCase();
return /\bcopi[ée]|\bcoll[ée]|presse-papiers/.test(normalized) ? SHORT_NOTIFICATION_DURATION_MS : DEFAULT_NOTIFICATION_DURATION_MS;
@ -42,16 +43,26 @@ function App() {
const [pendingTutorialToolboxId, setPendingTutorialToolboxId] = useState("");
const [notifications, setNotifications] = useState([]);
const [storageError, setStorageError] = useState("");
const backupReminderIdRef = useRef("");
const handleStorageError = useCallback((message) => setStorageError(message), []);
const store = useIndexedToolboxes(handleStorageError);
const autoBackup = useAutoBackup(store);
const { backupNeeded, requestPermission: requestBackupPermission } = autoBackup;
const guideChecklistSession = useGuideChecklistSession(store);
const notify = useCallback((message, options = {}) => {
const id = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
setNotifications((current) => [
{ id: `${Date.now()}-${Math.random().toString(36).slice(2)}`, message, durationMs: getNotificationDuration(message, options) },
{
id,
message,
durationMs: getNotificationDuration(message, options),
actionLabel: options.actionLabel,
onAction: options.onAction
},
...current
].slice(0, 5));
return id;
}, []);
const dismissNotification = useCallback((notificationId) => {
@ -67,6 +78,21 @@ function App() {
return () => window.removeEventListener("sokkog:notify", handleNotification);
}, [notify]);
useEffect(() => {
if (!backupNeeded) {
if (backupReminderIdRef.current) dismissNotification(backupReminderIdRef.current);
backupReminderIdRef.current = "";
return;
}
if (backupReminderIdRef.current || !siteContent?.toolboxes?.autoBackup) return;
const content = siteContent.toolboxes.autoBackup;
backupReminderIdRef.current = notify(content.reminderMessage, {
persistent: true,
actionLabel: content.reminderAction,
onAction: () => void requestBackupPermission()
});
}, [backupNeeded, dismissNotification, notify, requestBackupPermission, siteContent]);
const t = (key, { capitalize = false } = {}) => {
const value = mhwilds.translations[key] || String(key || "").replace(/_/g, " ");
return capitalize ? value.charAt(0).toUpperCase() + value.slice(1) : value;