Coalesce quota refreshes and consolidate shared helpers
Some checks failed
Deploy Sokko G / deploy (push) Failing after 2s
Some checks failed
Deploy Sokko G / deploy (push) Failing after 2s
This commit is contained in:
parent
a4cf633ab4
commit
748415082b
19 changed files with 216 additions and 64 deletions
|
|
@ -3,12 +3,10 @@ import { useEffect, useRef, useState } from "react";
|
|||
import { DrawingControls } from "../features/toolboxes/modules/DrawingControls.jsx";
|
||||
import { DrawingOverlay } from "../features/toolboxes/modules/DrawingOverlay.jsx";
|
||||
import { lockBodyScroll } from "../utils/bodyScrollLock.js";
|
||||
import { DRAWING_COLORS, DRAWING_WIDTHS } from "../utils/drawingOptions.js";
|
||||
import { Icon } from "./Icon.jsx";
|
||||
import { Tooltip } from "./Tooltip.jsx";
|
||||
|
||||
const DRAWING_COLORS = ["#f5f7ff", "#b4bdd3", "#f6c453", "#22d3ee", "#8b5cf6", "#d946ef"];
|
||||
const DRAWING_WIDTHS = [2, 4, 8, 12];
|
||||
|
||||
function clampPercent(value) {
|
||||
const parsed = Number(value);
|
||||
if (!Number.isFinite(parsed)) return 0;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// Rôle : fournit l'outil Combos avec palettes d'inputs, rendu visuel et échange texte.
|
||||
import { toggleListValue } from "../../../utils/collectionUtils.js";
|
||||
import { getGroupedEntries } from "../../../hooks/useGroupedReorder.js";
|
||||
import { TextExchangeActions } from "./TextExchangeActions.jsx";
|
||||
import { CombosEditor } from "./CombosEditor.jsx";
|
||||
|
|
@ -40,9 +41,7 @@ export function CombosModule({ toolboxId, moduleId, context, editing, textExchan
|
|||
}
|
||||
|
||||
function toggleCategoryCollapsed(category) {
|
||||
const collapsedCategories = data.collapsedCategories.includes(category)
|
||||
? data.collapsedCategories.filter((item) => item !== category)
|
||||
: [...data.collapsedCategories, category];
|
||||
const collapsedCategories = toggleListValue(data.collapsedCategories, category);
|
||||
save({ ...data, collapsedCategories });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// Rôle : fournit l'outil liens avec ajout manuel et échange texte.
|
||||
import { toggleListValue } from "../../../utils/collectionUtils.js";
|
||||
import { useState } from "react";
|
||||
import { CategoryAutocompleteInput } from "../../../components/CategoryAutocompleteInput.jsx";
|
||||
import { Icon } from "../../../components/Icon.jsx";
|
||||
|
|
@ -143,9 +144,7 @@ export function LinksModule({ toolboxId, moduleId, context, editing, textExchang
|
|||
}
|
||||
|
||||
function toggleCategoryCollapsed(category) {
|
||||
const collapsedCategories = data.collapsedCategories.includes(category)
|
||||
? data.collapsedCategories.filter((item) => item !== category)
|
||||
: [...data.collapsedCategories, category];
|
||||
const collapsedCategories = toggleListValue(data.collapsedCategories, category);
|
||||
save({ ...data, collapsedCategories });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// Rôle : fournit l'outil task planner avec tâches récurrentes, parents, pré requis et échange texte.
|
||||
import { toggleListValue } from "../../../utils/collectionUtils.js";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useDraftForm } from "../../../hooks/useDraftForm.js";
|
||||
import { TaskPlannerBranch } from "./TaskPlannerBranch.jsx";
|
||||
|
|
@ -131,9 +132,7 @@ export function TaskPlannerModule({ toolboxId, moduleId, context, editing, textE
|
|||
}
|
||||
|
||||
function toggleCategoryCollapsed(category) {
|
||||
const collapsedCategories = data.collapsedCategories.includes(category)
|
||||
? data.collapsedCategories.filter((item) => item !== category)
|
||||
: [...data.collapsedCategories, category];
|
||||
const collapsedCategories = toggleListValue(data.collapsedCategories, category);
|
||||
save({ ...data, collapsedCategories });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Rôle : regroupe les constantes et helpers DOM du Bloc Note riche.
|
||||
export const TEXT_COLORS = ["#f5f7ff", "#b4bdd3", "#f6c453", "#22d3ee", "#8b5cf6", "#d946ef"];
|
||||
export { DRAWING_COLORS as TEXT_COLORS, DRAWING_WIDTHS } from "../../../utils/drawingOptions.js";
|
||||
export const HIGHLIGHT_COLORS = ["#f6c453", "#22d3ee", "#8b5cf6", "#d946ef", "#202745"];
|
||||
export const DRAWING_WIDTHS = [2, 4, 8, 12];
|
||||
|
||||
export function getPlainText(element) {
|
||||
return (element?.innerText || "").replace(/\n{3,}/g, "\n\n").trim();
|
||||
|
|
|
|||
38
website/src/features/toolboxes/storage/quotaRefresh.js
Normal file
38
website/src/features/toolboxes/storage/quotaRefresh.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// Rôle : regroupe les estimations de quota et ignore les résultats devenus obsolètes.
|
||||
export function createQuotaRefresh({ estimate, onResult, delay = 250, schedule = setTimeout, cancel = clearTimeout }) {
|
||||
let timer = null;
|
||||
let running = false;
|
||||
let dirty = false;
|
||||
let disposed = false;
|
||||
|
||||
function request() {
|
||||
if (disposed) return;
|
||||
dirty = true;
|
||||
if (!running && timer === null) timer = schedule(run, delay);
|
||||
}
|
||||
|
||||
async function run() {
|
||||
timer = null;
|
||||
if (disposed) return;
|
||||
dirty = false;
|
||||
running = true;
|
||||
try {
|
||||
const result = await estimate();
|
||||
if (!disposed && !dirty) onResult(result);
|
||||
} catch {
|
||||
// Une estimation indisponible ne remet pas à zéro le dernier quota connu.
|
||||
} finally {
|
||||
running = false;
|
||||
if (!disposed && dirty) request();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
request,
|
||||
dispose() {
|
||||
disposed = true;
|
||||
if (timer !== null) cancel(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// Rôle : synchronise l'état optimiste des toolboxes avec une file d'écritures IndexedDB.
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
getAllModuleData as dbGetAllModuleData,
|
||||
getLinks as dbGetLinks,
|
||||
|
|
@ -22,6 +22,7 @@ import {
|
|||
} from "./toolboxStorage.js";
|
||||
import { applyModuleDataValue, removeModuleDataKeys } from "./optimisticModuleData.js";
|
||||
import { createPersistenceQueue } from "./persistenceQueue.js";
|
||||
import { createQuotaRefresh } from "./quotaRefresh.js";
|
||||
|
||||
function normalizeToolboxes(toolboxes) {
|
||||
return compactToolboxesForStorage(toolboxes).map(normalizeToolbox).filter(Boolean);
|
||||
|
|
@ -32,20 +33,29 @@ export function useIndexedToolboxes(onError) {
|
|||
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 });
|
||||
const quotaRefreshRef = useRef(null);
|
||||
const reportStorageError = useCallback((error) => {
|
||||
onError(error?.message || "Erreur de stockage IndexedDB.");
|
||||
}, [onError]);
|
||||
|
||||
const refreshQuota = useCallback(async () => {
|
||||
try {
|
||||
const estimate = await getStorageEstimate();
|
||||
const used = estimate.usage || 0;
|
||||
const limit = estimate.quota || 0;
|
||||
setStorageUsage({ used, limit, ratio: limit ? Math.min(1, used / limit) : 0 });
|
||||
} catch {
|
||||
setStorageUsage({ used: 0, limit: 0, ratio: 0 });
|
||||
}
|
||||
useEffect(() => {
|
||||
const refresh = createQuotaRefresh({
|
||||
estimate: getStorageEstimate,
|
||||
onResult: (estimate) => {
|
||||
const used = estimate.usage || 0;
|
||||
const limit = estimate.quota || 0;
|
||||
setStorageUsage((current) => current.used === used && current.limit === limit
|
||||
? current
|
||||
: { used, limit, ratio: limit ? Math.min(1, used / limit) : 0 });
|
||||
}
|
||||
});
|
||||
quotaRefreshRef.current = refresh;
|
||||
return () => {
|
||||
refresh.dispose();
|
||||
quotaRefreshRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
const refreshQuota = useCallback(() => quotaRefreshRef.current?.request(), []);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
|
|
|||
4
website/src/utils/collectionUtils.js
Normal file
4
website/src/utils/collectionUtils.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// Rôle : fournit les modifications immuables communes aux listes de valeurs.
|
||||
export function toggleListValue(values, value) {
|
||||
return values.includes(value) ? values.filter((item) => item !== value) : [...values, value];
|
||||
}
|
||||
3
website/src/utils/drawingOptions.js
Normal file
3
website/src/utils/drawingOptions.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// Rôle : partage les couleurs et épaisseurs des outils de dessin.
|
||||
export const DRAWING_COLORS = ["#f5f7ff", "#b4bdd3", "#f6c453", "#22d3ee", "#8b5cf6", "#d946ef"];
|
||||
export const DRAWING_WIDTHS = [2, 4, 8, 12];
|
||||
Loading…
Add table
Add a link
Reference in a new issue