Fix temporary annotation storage toast spam
All checks were successful
Deploy Sokko G / deploy (push) Successful in 18s

This commit is contained in:
Shinuwa 2026-09-03 20:17:17 +02:00
parent 9aa98c892d
commit e3d823d0b1
2 changed files with 43 additions and 9 deletions

View file

@ -12,7 +12,10 @@ function markerLabel(index, marker, textContent) {
export function ImageAnnotationModule({ toolboxId, moduleId, context, editing, textExchangeOpen }) { export function ImageAnnotationModule({ toolboxId, moduleId, context, editing, textExchangeOpen }) {
const data = context.normalizeImageAnnotationData(context.getModuleData(toolboxId, moduleId, { image: "", markers: [] })); const data = context.normalizeImageAnnotationData(context.getModuleData(toolboxId, moduleId, { image: "", markers: [] }));
const textContent = context.moduleText?.imageAnnotation || {}; const textContent = context.moduleText?.imageAnnotation || {};
const notify = context.notify;
const previewMediaRef = useRef(null); const previewMediaRef = useRef(null);
const temporaryDrawingsDirtyRef = useRef(false);
const sessionStorageFailureRef = useRef("");
const sessionKey = `sokkog:image-annotation-drawings:${toolboxId}:${moduleId}`; const sessionKey = `sokkog:image-annotation-drawings:${toolboxId}:${moduleId}`;
const [dragOver, setDragOver] = useState(false); const [dragOver, setDragOver] = useState(false);
const [drawingMode, setDrawingMode] = useState("permanent"); const [drawingMode, setDrawingMode] = useState("permanent");
@ -26,6 +29,8 @@ export function ImageAnnotationModule({ toolboxId, moduleId, context, editing, t
}), []); }), []);
useEffect(() => { useEffect(() => {
temporaryDrawingsDirtyRef.current = false;
sessionStorageFailureRef.current = "";
try { try {
const stored = JSON.parse(sessionStorage.getItem(sessionKey) || "{\"strokes\":[]}"); const stored = JSON.parse(sessionStorage.getItem(sessionKey) || "{\"strokes\":[]}");
setTemporaryDrawings(stored?.strokes ? stored : { strokes: [] }); setTemporaryDrawings(stored?.strokes ? stored : { strokes: [] });
@ -36,12 +41,19 @@ export function ImageAnnotationModule({ toolboxId, moduleId, context, editing, t
useEffect(() => { useEffect(() => {
if (drawingMode !== "temporary") return; if (drawingMode !== "temporary") return;
if (!temporaryDrawingsDirtyRef.current) return;
const serializedDrawings = JSON.stringify(temporaryDrawings);
try { try {
sessionStorage.setItem(sessionKey, JSON.stringify(temporaryDrawings)); sessionStorage.setItem(sessionKey, serializedDrawings);
temporaryDrawingsDirtyRef.current = false;
sessionStorageFailureRef.current = "";
} catch { } catch {
context.notify?.(textContent.sessionStorageError || "Les annotations temporaires n'ont pas pu être sauvegardées."); const failureKey = `${sessionKey}:${serializedDrawings}`;
if (sessionStorageFailureRef.current === failureKey) return;
sessionStorageFailureRef.current = failureKey;
notify?.(textContent.sessionStorageError || "Les annotations temporaires n'ont pas pu être sauvegardées.");
} }
}, [context, drawingMode, sessionKey, temporaryDrawings, textContent.sessionStorageError]); }, [drawingMode, notify, sessionKey, temporaryDrawings, textContent.sessionStorageError]);
function save(nextData) { function save(nextData) {
context.setModuleData(toolboxId, moduleId, nextData); context.setModuleData(toolboxId, moduleId, nextData);
@ -151,12 +163,18 @@ export function ImageAnnotationModule({ toolboxId, moduleId, context, editing, t
onChangeMarkers: (nextMarkers) => save({ ...data, markers: nextMarkers }), onChangeMarkers: (nextMarkers) => save({ ...data, markers: nextMarkers }),
onChangeDrawings: (nextDrawings, nextMode = drawingMode) => { onChangeDrawings: (nextDrawings, nextMode = drawingMode) => {
if (nextMode === "permanent") save({ ...data, drawings: nextDrawings }); if (nextMode === "permanent") save({ ...data, drawings: nextDrawings });
else setTemporaryDrawings(nextDrawings); else {
temporaryDrawingsDirtyRef.current = true;
setTemporaryDrawings(nextDrawings);
}
}, },
onChangeDrawingMode: (nextMode, currentDrawings) => { onChangeDrawingMode: (nextMode, currentDrawings) => {
setDrawingMode(nextMode); setDrawingMode(nextMode);
if (nextMode === "permanent") save({ ...data, drawings: currentDrawings }); if (nextMode === "permanent") save({ ...data, drawings: currentDrawings });
else setTemporaryDrawings(currentDrawings); else {
temporaryDrawingsDirtyRef.current = true;
setTemporaryDrawings(currentDrawings);
}
} }
})} })}
aria-label={textContent.previewAriaLabel || "Agrandir l'image annotée"} aria-label={textContent.previewAriaLabel || "Agrandir l'image annotée"}

View file

@ -23,12 +23,15 @@ export function NotepadModule({ toolboxId, moduleId, context, textExchangeOpen }
const storedData = context.getModuleData(toolboxId, moduleId, { text: "" }); const storedData = context.getModuleData(toolboxId, moduleId, { text: "" });
const normalizedData = useMemo(() => context.normalizeNotepadData(storedData), [context, storedData]); const normalizedData = useMemo(() => context.normalizeNotepadData(storedData), [context, storedData]);
const textContent = context.moduleText?.notepad || {}; const textContent = context.moduleText?.notepad || {};
const notify = context.notify;
const activePage = normalizedData.pages.find((page) => page.id === normalizedData.activePageId) || normalizedData.pages[0]; const activePage = normalizedData.pages.find((page) => page.id === normalizedData.activePageId) || normalizedData.pages[0];
const editorRef = useRef(null); const editorRef = useRef(null);
const surfaceRef = useRef(null); const surfaceRef = useRef(null);
const selectionOffsetRef = useRef(null); const selectionOffsetRef = useRef(null);
const localSaveUpdatedAtRef = useRef(""); const localSaveUpdatedAtRef = useRef("");
const pageKeyRef = useRef(`${toolboxId}:${moduleId}:${activePage.id}`); const pageKeyRef = useRef(`${toolboxId}:${moduleId}:${activePage.id}`);
const temporaryStrokesDirtyRef = useRef(false);
const sessionStorageFailureRef = useRef("");
const [html, setHtml] = useState(activePage.html); const [html, setHtml] = useState(activePage.html);
const [text, setText] = useState(activePage.text); const [text, setText] = useState(activePage.text);
const [updatedAt, setUpdatedAt] = useState(activePage.updatedAt); const [updatedAt, setUpdatedAt] = useState(activePage.updatedAt);
@ -113,6 +116,8 @@ export function NotepadModule({ toolboxId, moduleId, context, textExchangeOpen }
}, []); }, []);
useEffect(() => { useEffect(() => {
temporaryStrokesDirtyRef.current = false;
sessionStorageFailureRef.current = "";
try { try {
const stored = JSON.parse(sessionStorage.getItem(sessionKey) || "[]"); const stored = JSON.parse(sessionStorage.getItem(sessionKey) || "[]");
setTemporaryStrokes(Array.isArray(stored) ? stored : []); setTemporaryStrokes(Array.isArray(stored) ? stored : []);
@ -123,12 +128,19 @@ export function NotepadModule({ toolboxId, moduleId, context, textExchangeOpen }
useEffect(() => { useEffect(() => {
if (drawingMode !== "temporary") return; if (drawingMode !== "temporary") return;
if (!temporaryStrokesDirtyRef.current) return;
const serializedStrokes = JSON.stringify(temporaryStrokes);
try { try {
sessionStorage.setItem(sessionKey, JSON.stringify(temporaryStrokes)); sessionStorage.setItem(sessionKey, serializedStrokes);
temporaryStrokesDirtyRef.current = false;
sessionStorageFailureRef.current = "";
} catch { } catch {
context.notify?.(textContent.sessionStorageError || "Les annotations temporaires n'ont pas pu être sauvegardées."); const failureKey = `${sessionKey}:${serializedStrokes}`;
if (sessionStorageFailureRef.current === failureKey) return;
sessionStorageFailureRef.current = failureKey;
notify?.(textContent.sessionStorageError || "Les annotations temporaires n'ont pas pu être sauvegardées.");
} }
}, [context, drawingMode, sessionKey, temporaryStrokes, textContent.sessionStorageError]); }, [drawingMode, notify, sessionKey, temporaryStrokes, textContent.sessionStorageError]);
const activeStrokes = drawingMode === "permanent" ? activePage.drawings.strokes : temporaryStrokes; const activeStrokes = drawingMode === "permanent" ? activePage.drawings.strokes : temporaryStrokes;
const formattedUpdatedAt = formatUpdatedAt(updatedAt); const formattedUpdatedAt = formatUpdatedAt(updatedAt);
@ -160,7 +172,10 @@ export function NotepadModule({ toolboxId, moduleId, context, textExchangeOpen }
function saveDrawingMode(nextMode, nextStrokes) { function saveDrawingMode(nextMode, nextStrokes) {
setDrawingMode(nextMode); setDrawingMode(nextMode);
if (nextMode === "temporary") setTemporaryStrokes(nextStrokes); if (nextMode === "temporary") {
temporaryStrokesDirtyRef.current = true;
setTemporaryStrokes(nextStrokes);
}
updateActivePage((page) => ({ updateActivePage((page) => ({
...page, ...page,
html, html,
@ -182,6 +197,7 @@ export function NotepadModule({ toolboxId, moduleId, context, textExchangeOpen }
drawings: { strokes: nextStrokes } drawings: { strokes: nextStrokes }
})); }));
} else { } else {
temporaryStrokesDirtyRef.current = true;
setTemporaryStrokes(nextStrokes); setTemporaryStrokes(nextStrokes);
} }
} }