Improve toolbox persistence and goal tree coverage
All checks were successful
Deploy Sokko G / deploy (push) Successful in 15s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 15s
This commit is contained in:
parent
50029b45a2
commit
7348969ac4
10 changed files with 801 additions and 591 deletions
|
|
@ -1,5 +1,5 @@
|
|||
// Rôle : synchronise les toolboxes, données d'outils, liens de jeux et quota via IndexedDB.
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
getAllModuleData as dbGetAllModuleData,
|
||||
getLinks as dbGetLinks,
|
||||
|
|
@ -27,8 +27,42 @@ export function useIndexedToolboxes(onError) {
|
|||
const [moduleData, setModuleDataState] = useState({});
|
||||
const [toastPosition, setToastPositionState] = useState("right");
|
||||
const [storageUsage, setStorageUsage] = useState({ used: 0, limit: 0, ratio: 0 });
|
||||
const toolboxesRef = useRef(toolboxes);
|
||||
const linksRef = useRef(links);
|
||||
const moduleDataRef = useRef(moduleData);
|
||||
const toastPositionRef = useRef(toastPosition);
|
||||
const writeVersionsRef = useRef({
|
||||
toolboxes: 0,
|
||||
links: 0,
|
||||
moduleData: 0,
|
||||
toastPosition: 0
|
||||
});
|
||||
|
||||
async function refreshQuota() {
|
||||
const reportStorageError = useCallback((error) => {
|
||||
onError(error?.message || "Erreur de stockage IndexedDB.");
|
||||
}, [onError]);
|
||||
|
||||
function commitToolboxes(nextToolboxes) {
|
||||
toolboxesRef.current = nextToolboxes;
|
||||
setToolboxesState(nextToolboxes);
|
||||
}
|
||||
|
||||
function commitLinks(nextLinks) {
|
||||
linksRef.current = nextLinks;
|
||||
setLinksState(nextLinks);
|
||||
}
|
||||
|
||||
function commitModuleData(nextModuleData) {
|
||||
moduleDataRef.current = nextModuleData;
|
||||
setModuleDataState(nextModuleData);
|
||||
}
|
||||
|
||||
function commitToastPosition(nextToastPosition) {
|
||||
toastPositionRef.current = nextToastPosition;
|
||||
setToastPositionState(nextToastPosition);
|
||||
}
|
||||
|
||||
const refreshQuota = useCallback(async () => {
|
||||
try {
|
||||
const estimate = await getStorageEstimate();
|
||||
const used = estimate.usage || 0;
|
||||
|
|
@ -37,7 +71,7 @@ export function useIndexedToolboxes(onError) {
|
|||
} catch {
|
||||
setStorageUsage({ used: 0, limit: 0, ratio: 0 });
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
|
@ -50,85 +84,110 @@ export function useIndexedToolboxes(onError) {
|
|||
dbGetSetting("toastPosition", "right")
|
||||
]);
|
||||
if (cancelled) return;
|
||||
setToolboxesState((Array.isArray(storedToolboxes) ? storedToolboxes : []).map(normalizeToolbox).filter(Boolean));
|
||||
setLinksState(storedLinks && typeof storedLinks === "object" ? storedLinks : {});
|
||||
setModuleDataState(Object.fromEntries((storedModules || []).map((entry) => [entry.key, entry.data])));
|
||||
setToastPositionState(storedToastPosition === "left" ? "left" : "right");
|
||||
commitToolboxes((Array.isArray(storedToolboxes) ? storedToolboxes : []).map(normalizeToolbox).filter(Boolean));
|
||||
commitLinks(storedLinks && typeof storedLinks === "object" ? storedLinks : {});
|
||||
commitModuleData(Object.fromEntries((storedModules || []).map((entry) => [entry.key, entry.data])));
|
||||
commitToastPosition(storedToastPosition === "left" ? "left" : "right");
|
||||
setReady(true);
|
||||
refreshQuota();
|
||||
} catch (error) {
|
||||
if (!cancelled) {
|
||||
setReady(true);
|
||||
onError(error.message || "Stockage IndexedDB indisponible.");
|
||||
reportStorageError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
loadStore();
|
||||
return () => { cancelled = true; };
|
||||
}, [onError]);
|
||||
}, [refreshQuota, reportStorageError]);
|
||||
|
||||
function persistToolboxes(nextToolboxes) {
|
||||
const normalized = compactToolboxesForStorage(nextToolboxes).map(normalizeToolbox).filter(Boolean);
|
||||
setToolboxesState(normalized);
|
||||
const previousToolboxes = toolboxesRef.current;
|
||||
const version = ++writeVersionsRef.current.toolboxes;
|
||||
commitToolboxes(normalized);
|
||||
dbSetToolboxes(compactToolboxesForStorage(normalized))
|
||||
.then(refreshQuota)
|
||||
.catch((error) => onError(error.message));
|
||||
.catch((error) => {
|
||||
if (writeVersionsRef.current.toolboxes === version) commitToolboxes(previousToolboxes);
|
||||
reportStorageError(error);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
function persistLinks(nextLinks) {
|
||||
setLinksState(nextLinks);
|
||||
const previousLinks = linksRef.current;
|
||||
const version = ++writeVersionsRef.current.links;
|
||||
commitLinks(nextLinks);
|
||||
dbSetLinks(nextLinks)
|
||||
.then(refreshQuota)
|
||||
.catch((error) => onError(error.message));
|
||||
.catch((error) => {
|
||||
if (writeVersionsRef.current.links === version) commitLinks(previousLinks);
|
||||
reportStorageError(error);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
function getModuleData(toolboxId, moduleId, fallback) {
|
||||
const value = moduleData[moduleStorageKey(toolboxId, moduleId)];
|
||||
const value = moduleDataRef.current[moduleStorageKey(toolboxId, moduleId)];
|
||||
return value == null ? fallback : value;
|
||||
}
|
||||
|
||||
function updateModuleData(toolboxId, moduleId, value, moduleType = "") {
|
||||
const compact = prepareModuleDataForStorage(toolboxes, toolboxId, moduleId, value, moduleType);
|
||||
const compact = prepareModuleDataForStorage(toolboxesRef.current, toolboxId, moduleId, value, moduleType);
|
||||
const key = moduleStorageKey(toolboxId, moduleId);
|
||||
setModuleDataState((state) => {
|
||||
const nextState = { ...state };
|
||||
if (compact == null) delete nextState[key];
|
||||
else nextState[key] = compact;
|
||||
return nextState;
|
||||
});
|
||||
const previousModuleData = moduleDataRef.current;
|
||||
const version = ++writeVersionsRef.current.moduleData;
|
||||
const nextModuleData = { ...previousModuleData };
|
||||
if (compact == null) delete nextModuleData[key];
|
||||
else nextModuleData[key] = compact;
|
||||
commitModuleData(nextModuleData);
|
||||
const operation = compact == null ? dbRemoveModuleData(key) : dbSetModuleData(key, compact);
|
||||
operation.then(refreshQuota).catch((error) => onError(error.message));
|
||||
operation.then(refreshQuota).catch((error) => {
|
||||
if (writeVersionsRef.current.moduleData === version) commitModuleData(previousModuleData);
|
||||
reportStorageError(error);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
function removeModuleData(toolboxId, moduleId) {
|
||||
const key = moduleStorageKey(toolboxId, moduleId);
|
||||
setModuleDataState((state) => {
|
||||
const nextState = { ...state };
|
||||
delete nextState[key];
|
||||
return nextState;
|
||||
const previousModuleData = moduleDataRef.current;
|
||||
const version = ++writeVersionsRef.current.moduleData;
|
||||
const nextModuleData = { ...previousModuleData };
|
||||
delete nextModuleData[key];
|
||||
commitModuleData(nextModuleData);
|
||||
dbRemoveModuleData(key).then(refreshQuota).catch((error) => {
|
||||
if (writeVersionsRef.current.moduleData === version) commitModuleData(previousModuleData);
|
||||
reportStorageError(error);
|
||||
});
|
||||
dbRemoveModuleData(key).then(refreshQuota).catch((error) => onError(error.message));
|
||||
}
|
||||
|
||||
function removeToolboxModuleData(toolbox) {
|
||||
const keys = (toolbox?.modules || []).map((module) => moduleStorageKey(toolbox.id, module.id));
|
||||
setModuleDataState((state) => {
|
||||
const nextState = { ...state };
|
||||
keys.forEach((key) => delete nextState[key]);
|
||||
return nextState;
|
||||
if (!keys.length) return;
|
||||
const previousModuleData = moduleDataRef.current;
|
||||
const version = ++writeVersionsRef.current.moduleData;
|
||||
const nextModuleData = { ...previousModuleData };
|
||||
keys.forEach((key) => delete nextModuleData[key]);
|
||||
commitModuleData(nextModuleData);
|
||||
dbRemoveModuleDataKeys(keys).then(refreshQuota).catch((error) => {
|
||||
if (writeVersionsRef.current.moduleData === version) commitModuleData(previousModuleData);
|
||||
reportStorageError(error);
|
||||
});
|
||||
dbRemoveModuleDataKeys(keys).then(refreshQuota).catch((error) => onError(error.message));
|
||||
}
|
||||
|
||||
function setToastPosition(position) {
|
||||
const normalized = position === "left" ? "left" : "right";
|
||||
setToastPositionState(normalized);
|
||||
const previousToastPosition = toastPositionRef.current;
|
||||
const version = ++writeVersionsRef.current.toastPosition;
|
||||
commitToastPosition(normalized);
|
||||
dbSetSetting("toastPosition", normalized)
|
||||
.then(refreshQuota)
|
||||
.catch((error) => onError(error.message));
|
||||
.catch((error) => {
|
||||
if (writeVersionsRef.current.toastPosition === version) commitToastPosition(previousToastPosition);
|
||||
reportStorageError(error);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue