Protect toolbox data after IndexedDB startup failures
All checks were successful
Deploy Sokko G / deploy (push) Successful in 18s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 18s
This commit is contained in:
parent
b53c7c17c8
commit
09a2d4b71a
13 changed files with 242 additions and 5 deletions
|
|
@ -30,6 +30,9 @@ function normalizeToolboxes(toolboxes) {
|
|||
|
||||
export function useIndexedToolboxes(onError) {
|
||||
const [ready, setReady] = useState(false);
|
||||
const [loadError, setLoadError] = useState("");
|
||||
const [loadAttempt, setLoadAttempt] = useState(0);
|
||||
const writableRef = useRef(false);
|
||||
const [state, setState] = useState({ toolboxes: [], links: {}, moduleData: {}, toastPosition: "right" });
|
||||
const [queue] = useState(() => createPersistenceQueue(state, setState));
|
||||
const [storageUsage, setStorageUsage] = useState({ used: 0, limit: 0, ratio: 0 });
|
||||
|
|
@ -71,26 +74,34 @@ export function useIndexedToolboxes(onError) {
|
|||
moduleData: Object.fromEntries((modules || []).map((entry) => [entry.key, entry.data])),
|
||||
toastPosition: toastPosition === "left" ? "left" : "right"
|
||||
});
|
||||
writableRef.current = true;
|
||||
setReady(true);
|
||||
refreshQuota();
|
||||
} catch (error) {
|
||||
if (!cancelled) {
|
||||
setReady(true);
|
||||
reportStorageError(error);
|
||||
setLoadError(error?.message || "Erreur de stockage IndexedDB.");
|
||||
}
|
||||
}
|
||||
}
|
||||
loadStore();
|
||||
return () => { cancelled = true; };
|
||||
}, [queue, refreshQuota, reportStorageError]);
|
||||
return () => { cancelled = true; writableRef.current = false; };
|
||||
}, [queue, refreshQuota, loadAttempt]);
|
||||
|
||||
function retryLoad() {
|
||||
if (ready || !loadError) return;
|
||||
setLoadError("");
|
||||
setLoadAttempt((attempt) => attempt + 1);
|
||||
}
|
||||
|
||||
function enqueue(apply, persist) {
|
||||
if (!writableRef.current) return Promise.resolve(false);
|
||||
const result = queue.enqueue(apply, persist, reportStorageError);
|
||||
void result.then((success) => { if (success) refreshQuota(); });
|
||||
return result;
|
||||
}
|
||||
|
||||
function persistToolboxes(value) {
|
||||
if (!writableRef.current) return false;
|
||||
const apply = typeof value === "function" ? value : () => value;
|
||||
enqueue(
|
||||
(current) => ({ ...current, toolboxes: normalizeToolboxes(apply(current.toolboxes)) }),
|
||||
|
|
@ -100,6 +111,7 @@ export function useIndexedToolboxes(onError) {
|
|||
}
|
||||
|
||||
function persistLinks(value) {
|
||||
if (!writableRef.current) return false;
|
||||
enqueue(
|
||||
(current) => ({ ...current, links: typeof value === "function" ? value(current.links) : value }),
|
||||
(next) => dbSetLinks(next.links)
|
||||
|
|
@ -112,6 +124,7 @@ export function useIndexedToolboxes(onError) {
|
|||
}
|
||||
|
||||
function updateModuleData(toolboxId, moduleId, value, moduleType = "") {
|
||||
if (!writableRef.current) return false;
|
||||
const compact = prepareModuleDataForStorage(queue.getCurrent().toolboxes, toolboxId, moduleId, value, moduleType);
|
||||
const key = moduleStorageKey(toolboxId, moduleId);
|
||||
const hasModule = (current) => current.toolboxes.some((toolbox) => toolbox.id === toolboxId && toolbox.modules.some((module) => module.id === moduleId));
|
||||
|
|
@ -134,6 +147,7 @@ export function useIndexedToolboxes(onError) {
|
|||
}
|
||||
|
||||
function removeToolboxModuleData(toolbox) {
|
||||
if (!writableRef.current) return Promise.resolve(false);
|
||||
const keys = (toolbox?.modules || []).map((module) => moduleStorageKey(toolbox.id, module.id));
|
||||
if (!keys.length) return Promise.resolve(true);
|
||||
return enqueue(
|
||||
|
|
@ -150,6 +164,7 @@ export function useIndexedToolboxes(onError) {
|
|||
}
|
||||
|
||||
function setToastPosition(position) {
|
||||
if (!writableRef.current) return false;
|
||||
const toastPosition = position === "left" ? "left" : "right";
|
||||
enqueue(
|
||||
(current) => ({ ...current, toastPosition }),
|
||||
|
|
@ -160,6 +175,8 @@ export function useIndexedToolboxes(onError) {
|
|||
return {
|
||||
...state,
|
||||
ready,
|
||||
loadError,
|
||||
retryLoad,
|
||||
storageUsage,
|
||||
setToolboxes: persistToolboxes,
|
||||
setLinks: persistLinks,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ function getLibraryUrl() {
|
|||
|
||||
export function useToolboxActions({ store, notify, creationTemplate, setConfirmModal, setCreateModal, setLinkModalGameId, setDrawerGameId, setImage }) {
|
||||
function createToolbox(name, gameId = "", options = {}) {
|
||||
if (!store.ready) return null;
|
||||
const now = new Date().toISOString();
|
||||
const memosModuleId = uid("mod");
|
||||
const linksModuleId = uid("mod");
|
||||
|
|
@ -241,6 +242,7 @@ export function useToolboxActions({ store, notify, creationTemplate, setConfirmM
|
|||
store.setToolboxes((toolboxes) => [...toolboxes].sort((a, b) => (order.get(a.id) ?? 9999) - (order.get(b.id) ?? 9999)));
|
||||
},
|
||||
importToolbox: async (file, gameId = "") => {
|
||||
if (!store.ready) return null;
|
||||
try {
|
||||
const toolbox = await importToolboxPayload(file, gameId);
|
||||
if (toolbox) notify("Toolbox importée.");
|
||||
|
|
@ -257,6 +259,7 @@ export function useToolboxActions({ store, notify, creationTemplate, setConfirmM
|
|||
}
|
||||
},
|
||||
importAllToolboxes: async (file) => {
|
||||
if (!store.ready) return false;
|
||||
try {
|
||||
await importAllToolboxesPayload(file);
|
||||
notify("Import global terminé.");
|
||||
|
|
@ -273,12 +276,14 @@ export function useToolboxActions({ store, notify, creationTemplate, setConfirmM
|
|||
}
|
||||
},
|
||||
exportToolbox: (id) => {
|
||||
if (!store.ready) return false;
|
||||
const toolbox = normalizeToolbox(store.toolboxes.find((item) => item.id === id));
|
||||
if (!toolbox) return;
|
||||
downloadJson(createToolboxExportPayload(toolbox, store.moduleData), `${toolbox.name.toLowerCase().replace(/[^a-z0-9]+/gi, "-")}.sokkog.json`);
|
||||
notify("Export de la toolbox lancé.");
|
||||
},
|
||||
exportAllToolboxes: () => {
|
||||
if (!store.ready) return false;
|
||||
downloadJson(createGlobalExportPayload(store.toolboxes, store.links, store.moduleData), `sokkog-global-${new Date().toISOString().slice(0, 10)}.json`);
|
||||
notify("Export global lancé.");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue