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