0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-24 18:32:41 +00:00

Make autosaving work

debouncing does not play nice with functional component. Any debounced function gets locked in as the original state, meaning we keep saving the original document and overwriting the current document when a save fires.

Must pass in the parameters instead of pulling directly from state to work properly.
This commit is contained in:
Trevor Buckner
2025-09-09 01:57:13 -04:00
parent 90a81237ec
commit 90f6e7ec37

View File

@@ -1,7 +1,7 @@
/* eslint-disable max-lines */
import './editPage.less';
import React, { useState, useEffect, useRef, useMemo } from 'react';
import React, { useState, useEffect, useRef, useCallback } from 'react';
import request from '../../utils/request-middleware.js';
import Markdown from 'naturalcrit/markdown.js';
@@ -35,16 +35,13 @@ import { updateHistory, versionHistoryGarbageCollection } from '../../utils/vers
import googleDriveIcon from '../../googleDrive.svg';
const SAVE_TIMEOUT = 10000;
const SAVE_TIMEOUT = 5000;
const EditPage = (props) => {
props = {
brew: DEFAULT_BREW_LOAD,
...props
};
const editorRef = useRef(null);
const savedBrew = useRef(_.cloneDeep(props.brew));
const warningTimer = useRef(null);
const [currentBrew , setCurrentBrew ] = useState(props.brew);
const [isSaving , setIsSaving ] = useState(false);
@@ -64,7 +61,10 @@ const EditPage = (props) => {
const [autoSaveWarning , setAutoSaveWarning ] = useState(false);
const [unsavedTime , setUnsavedTime ] = useState(new Date());
const debounceSave = useMemo(() => _.debounce(() => trySave(), SAVE_TIMEOUT), []);
const editorRef = useRef(null);
const savedBrew = useRef(_.cloneDeep(props.brew));
const warningTimer = useRef(null);
const debounceSave = useCallback(_.debounce((brew, saveToGoogle)=>save(brew, saveToGoogle), SAVE_TIMEOUT), []);
useEffect(() => {
setUrl(window.location.href);
@@ -92,7 +92,7 @@ const EditPage = (props) => {
const hasChange = !_.isEqual(currentBrew, savedBrew.current);
setUnsavedChanges(hasChange);
if(autoSaveEnabled) save();
if(hasChange && autoSaveEnabled) trySave();
}, [currentBrew]);
const handleControlKeys = (e) => {
@@ -130,12 +130,10 @@ const EditPage = (props) => {
setHTMLErrors(HTMLErrors);
setCurrentBrew((prevBrew) => ({ ...prevBrew, text }));
if (autoSaveEnabled) debounceSave();
};
const handleStyleChange = (style) => {
setCurrentBrew(prevBrew => ({ ...prevBrew, style }));
if (autoSaveEnabled) debounceSave();
};
const handleSnipChange = (snippet)=>{
@@ -145,7 +143,6 @@ const EditPage = (props) => {
setHTMLErrors(HTMLErrors);
setCurrentBrew((prevBrew) => ({ ...prevBrew, snippets: snippet }));
if (autoSaveEnabled) debounceSave();
};
const handleMetaChange = (metadata, field = undefined) => {
@@ -153,7 +150,6 @@ const EditPage = (props) => {
fetchThemeBundle(setError, setThemeBundle, metadata.renderer, metadata.theme);
setCurrentBrew(prev => ({ ...prev, ...metadata }));
if (autoSaveEnabled) debounceSave();
};
const updateBrew = (newData) =>
@@ -165,22 +161,21 @@ const EditPage = (props) => {
}));
const trySave = (immediate = false) => {
if (!debounceSave.current) return;
//debounceSave = _.debounce(save, SAVE_TIMEOUT);
if (isSaving) return;
const hasChange = !_.isEqual(currentBrew, savedBrew.current);
if (immediate) {
debounceSave.current();
debounceSave.current.flush?.();
debounceSave(currentBrew, saveGoogle);
debounceSave.flush?.();
return;
}
if (hasChange) {
debounceSave.current();
} else {
debounceSave.current.cancel?.();
}
if (hasChange)
debounceSave(currentBrew, saveGoogle);
else
debounceSave.cancel?.();
};
const handleGoogleClick = () => {
@@ -206,29 +201,29 @@ const EditPage = (props) => {
trySave(true);
};
const save = async () => {
debounceSave.current?.cancel?.();
const save = async (brew, saveToGoogle) => {
debounceSave?.cancel?.();
setIsSaving(true);
setError(null);
setHTMLErrors(Markdown.validate(currentBrew.text));
setHTMLErrors(Markdown.validate(brew.text));
await updateHistory(currentBrew).catch(console.error);
await updateHistory(brew).catch(console.error);
await versionHistoryGarbageCollection().catch(console.error);
//Prepare content to send to server
const brewToSave = {
...currentBrew,
text : currentBrew.text.normalize('NFC'),
pageCount: ((currentBrew.renderer === 'legacy' ? currentBrew.text.match(/\\page/g) : currentBrew.text.match(/^\\page$/gm)) || []).length + 1,
patches : stringifyPatches(makePatches(encodeURI(savedBrew.current.text.normalize('NFC')), encodeURI(currentBrew.text.normalize('NFC')))),
...brew,
text : brew.text.normalize('NFC'),
pageCount: ((brew.renderer === 'legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1,
patches : stringifyPatches(makePatches(encodeURI(savedBrew.current.text.normalize('NFC')), encodeURI(brew.text.normalize('NFC')))),
hash : await md5(savedBrew.current.text),
textBin : undefined
};
const compressedBrew = gzipSync(strToU8(JSON.stringify(brewToSave)));
const transfer = saveGoogle === _.isNil(currentBrew.googleId);
const params = transfer ? `?${saveGoogle ? 'saveToGoogle' : 'removeFromGoogle'}=true` : '';
const transfer = saveToGoogle === _.isNil(brew.googleId);
const params = transfer ? `?${saveToGoogle ? 'saveToGoogle' : 'removeFromGoogle'}=true` : '';
const res = await request
.put(`/api/update/${brewToSave.editId}${params}`)
@@ -244,24 +239,17 @@ const EditPage = (props) => {
const { googleId, editId, shareId, version } = res.body;
savedBrew.current = {
...currentBrew,
...brew,
googleId: googleId ?? null,
editId,
shareId,
version
};
setCurrentBrew(prev => ({
...prev,
googleId: googleId ?? null,
editId,
shareId,
version
}));
setCurrentBrew(savedBrew.current);
setIsSaving(false);
setUnsavedTime(new Date());
setUnsavedChanges(!_.isEqual(currentBrew, savedBrew.current));
history.replaceState(null, null, `/edit/${editId}`);
};