From 67728afd4f4e8038b8f353a3d594550bb3e2ab0b Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 9 Sep 2026 21:08:58 +0000 Subject: [PATCH] Give "unsaved change" warning to NewPage and HomePage. Autosave is always disabled on those. --- client/homebrew/pages/editPage/editPage.jsx | 22 ++++---- client/homebrew/pages/homePage/homePage.jsx | 56 +++++++++++++------- client/homebrew/pages/newPage/newPage.jsx | 57 +++++++++++++++------ 3 files changed, 90 insertions(+), 45 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 29f215e08..f6f74c269 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -40,7 +40,7 @@ import { updateHistory, versionHistoryGarbageCollection } from '../../utils/vers import googleDriveIcon from '../../googleDrive.svg'; const SAVE_TIMEOUT = 10000; -const UNSAVED_WARNING_TIMEOUT = 900000; //Warn user afer 15 minutes of unsaved changes +const UNSAVED_WARNING_TIMEOUT = 90000; //Warn user afer 15 minutes of unsaved changes const UNSAVED_WARNING_POPUP_TIMEOUT = 4000; //Show the warning for 4 seconds const AUTOSAVE_KEY = 'HB_editor_autoSaveOn'; @@ -50,7 +50,7 @@ const SNIPKEY = 'HB_newPage_snippets'; const METAKEY = 'HB_newPage_meta'; const useLocalStorage = false; -const neverSaved = false; +const sandbox = false; const EditPage = (props)=>{ props = { @@ -101,7 +101,8 @@ const EditPage = (props)=>{ }); useEffect(()=>{ - const autoSavePref = JSON.parse(localStorage.getItem(AUTOSAVE_KEY) ?? true); + const autoSavePref = !sandbox && JSON.parse(localStorage.getItem(AUTOSAVE_KEY) ?? true); + setAutoSaveEnabled(autoSavePref); setWarnUnsavedChanges(!autoSavePref); setHTMLErrors(hbfm.validate(currentBrew.text)); @@ -122,6 +123,7 @@ const EditPage = (props)=>{ if(unsavedChangesRef.current) return 'You have unsaved changes!'; }; + return ()=>{ document.removeEventListener('keydown', handleControlKeys); window.onbeforeunload = null; @@ -313,12 +315,12 @@ const EditPage = (props)=>{ resetWarnUnsavedTimer(); const elapsedTime = Math.round((new Date() - lastSavedTime) / 1000 / 60); const text = elapsedTime === 0 - ? 'Autosave is OFF.' - : `Autosave is OFF, and you haven't saved for ${elapsedTime} minutes.`; + ? `Autosave is OFF${sandbox ?? 'for this sandbox page'}.` + : `Autosave is OFF${sandbox ?? 'for this sandbox page'}, and you haven't saved for ${elapsedTime} minutes.`; return - Reminder... -
{text}
+ Reminder... +
{text}
; } @@ -330,9 +332,9 @@ const EditPage = (props)=>{ if(autoSaveEnabled) return auto-saved; - // #5 - No unsaved changes, and has never been saved, hide the button - if(neverSaved) - return save now; + // #5 - Sandbox with no unsaved changes, and has never been saved, hide the button + if(sandbox) + return save now; // DEFAULT - No unsaved changes, show SAVED return saved; diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index f528f934f..c666703c3 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -32,13 +32,18 @@ const { both: RecentNavItem } = RecentNavItems; import Headtags from '@vitreum/headtags.js'; const Meta = Headtags.Meta; -const BREWKEY = 'homebrewery-new'; -const STYLEKEY = 'homebrewery-new-style'; -const SNIPKEY = 'homebrewery-new-snippets'; -const METAKEY = 'homebrewery-new-meta'; +const SAVE_TIMEOUT = 10000; +const UNSAVED_WARNING_TIMEOUT = 90000; //Warn user afer 15 minutes of unsaved changes +const UNSAVED_WARNING_POPUP_TIMEOUT = 4000; //Show the warning for 4 seconds + +const AUTOSAVE_KEY = 'HB_editor_autoSaveOn'; +const BREWKEY = 'HB_newPage_content'; +const STYLEKEY = 'HB_newPage_style'; +const SNIPKEY = 'HB_newPage_snippets'; +const METAKEY = 'HB_newPage_meta'; const useLocalStorage = false; -const neverSaved = true; +const sandbox = true; const HomePage =(props)=>{ props = { @@ -55,7 +60,8 @@ const HomePage =(props)=>{ const [themeBundle, setThemeBundle] = useState({}); const [unsavedChanges, setUnsavedChanges] = useState(false); const [isSaving, setIsSaving] = useState(false); - const [autoSaveEnabled, setAutoSaveEnable] = useState(false); + const [autoSaveEnabled, setAutoSaveEnabled] = useState(false); + const [warnUnsavedChanges, setWarnUnsavedChanges] = useState(true); const editorRef = useRef(null); const lastSavedBrew = useRef(_.cloneDeep(props.brew)); @@ -79,6 +85,11 @@ const HomePage =(props)=>{ }); useEffect(()=>{ + const autoSavePref = !sandbox && JSON.parse(localStorage.getItem(AUTOSAVE_KEY) ?? true); + + setAutoSaveEnabled(autoSavePref); + setWarnUnsavedChanges(!autoSavePref); + setHTMLErrors(hbfm.validate(currentBrew.text)); fetchThemeBundle(setError, setThemeBundle, currentBrew.renderer, currentBrew.theme); const handleControlKeys = (e)=>{ @@ -96,6 +107,7 @@ const HomePage =(props)=>{ if(unsavedChangesRef.current) return 'You have unsaved changes!'; }; + return ()=>{ document.removeEventListener('keydown', handleControlKeys); window.onbeforeunload = null; @@ -130,24 +142,30 @@ const HomePage =(props)=>{ editorRef.current.update(); }; + const resetWarnUnsavedTimer = ()=>{ + setTimeout(()=>setWarnUnsavedChanges(false), UNSAVED_WARNING_POPUP_TIMEOUT); // Hide the warning after 4 seconds + clearTimeout(warnUnsavedTimeout.current); + warnUnsavedTimeout.current = setTimeout(()=>setWarnUnsavedChanges(true), UNSAVED_WARNING_TIMEOUT); // 15 minutes between unsaved work warnings + }; + const renderSaveButton = ()=>{ // #1 - Currently saving, show SAVING if(isSaving) return saving...; // #2 - Unsaved changes exist, autosave is OFF and warning timer has expired, show AUTOSAVE WARNING - // if(unsavedChanges && warnUnsavedChanges) { - // resetWarnUnsavedTimer(); - // const elapsedTime = Math.round((new Date() - lastSavedTime) / 1000 / 60); - // const text = elapsedTime === 0 - // ? 'Autosave is OFF.' - // : `Autosave is OFF, and you haven't saved for ${elapsedTime} minutes.`; + if(unsavedChanges && warnUnsavedChanges) { + resetWarnUnsavedTimer(); + const elapsedTime = Math.round((new Date() - lastSavedTime) / 1000 / 60); + const text = elapsedTime === 0 + ? `Autosave is OFF${sandbox ?? 'for this sandbox page'}.` + : `Autosave is OFF${sandbox ?? 'for this sandbox page'}, and you haven't saved for ${elapsedTime} minutes.`; - // return - // Reminder... - //
{text}
- //
; - // } + return + Reminder... +
{text}
+
; + } // #3 - Unsaved changes exist, click to save, show SAVE NOW if(unsavedChanges) @@ -157,8 +175,8 @@ const HomePage =(props)=>{ if(autoSaveEnabled) return auto-saved; - // #5 - No unsaved changes, and has never been saved, hide the button - if(neverSaved) + // #5 - Sandbox with no unsaved changes, and has never been saved, hide the button + if(sandbox) return save now; // DEFAULT - No unsaved changes, show SAVED diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index e55434f5c..ab024fe7a 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -28,15 +28,20 @@ import RecentNavItems from '@navbar/recent.navitem.jsx'; const { both: RecentNavItem } = RecentNavItems; // Page specific imports +const SAVE_TIMEOUT = 10000; +const UNSAVED_WARNING_TIMEOUT = 90000; //Warn user afer 15 minutes of unsaved changes +const UNSAVED_WARNING_POPUP_TIMEOUT = 4000; //Show the warning for 4 seconds +const AUTOSAVE_KEY = 'HB_editor_autoSaveOn'; const BREWKEY = 'HB_newPage_content'; const STYLEKEY = 'HB_newPage_style'; -const METAKEY = 'HB_newPage_metadata'; const SNIPKEY = 'HB_newPage_snippets'; +const METAKEY = 'HB_newPage_meta'; + const SAVEKEYPREFIX = 'HB_editor_defaultSave_'; const useLocalStorage = true; -const neverSaved = true; +const sandbox = true; const NewPage = (props)=>{ props = { @@ -55,6 +60,7 @@ const NewPage = (props)=>{ const [themeBundle, setThemeBundle] = useState({}); const [unsavedChanges, setUnsavedChanges] = useState(false); const [autoSaveEnabled, setAutoSaveEnabled] = useState(false); + const [warnUnsavedChanges, setWarnUnsavedChanges] = useState(true); const editorRef = useRef(null); const lastSavedBrew = useRef(_.cloneDeep(props.brew)); @@ -63,6 +69,10 @@ const NewPage = (props)=>{ const trySaveRef = useRef(null); // CTRL+S listener lives outside React and needs ref to use trySave with latest copy of brew const unsavedChangesRef = useRef(unsavedChanges); // Similarly, onBeforeUnload lives outside React and needs ref to unsavedChanges + useEffect(()=>{ + loadBrew(); + }, []); + const { handleBrewChange } = useCommonEditPageFunctions({ @@ -81,7 +91,11 @@ const NewPage = (props)=>{ }); useEffect(()=>{ - loadBrew(); + const autoSavePref = !sandbox && JSON.parse(localStorage.getItem(AUTOSAVE_KEY) ?? true); + + setAutoSaveEnabled(autoSavePref); + setWarnUnsavedChanges(!autoSavePref); + setHTMLErrors(hbfm.validate(currentBrew.text)); fetchThemeBundle(setError, setThemeBundle, currentBrew.renderer, currentBrew.theme); const handleControlKeys = (e)=>{ @@ -95,9 +109,14 @@ const NewPage = (props)=>{ }; document.addEventListener('keydown', handleControlKeys); + window.onbeforeunload = ()=>{ + if(unsavedChangesRef.current) + return 'You have unsaved changes!'; + }; return ()=>{ document.removeEventListener('keydown', handleControlKeys); + window.onbeforeunload = null; }; }, []); @@ -146,6 +165,12 @@ const NewPage = (props)=>{ editorRef.current.update(); }; + const resetWarnUnsavedTimer = ()=>{ + setTimeout(()=>setWarnUnsavedChanges(false), UNSAVED_WARNING_POPUP_TIMEOUT); // Hide the warning after 4 seconds + clearTimeout(warnUnsavedTimeout.current); + warnUnsavedTimeout.current = setTimeout(()=>setWarnUnsavedChanges(true), UNSAVED_WARNING_TIMEOUT); // 15 minutes between unsaved work warnings + }; + const trySave = async ()=>{ setIsSaving(true); @@ -180,18 +205,18 @@ const NewPage = (props)=>{ return saving...; // #2 - Unsaved changes exist, autosave is OFF and warning timer has expired, show AUTOSAVE WARNING - // if(unsavedChanges && warnUnsavedChanges) { - // resetWarnUnsavedTimer(); - // const elapsedTime = Math.round((new Date() - lastSavedTime) / 1000 / 60); - // const text = elapsedTime === 0 - // ? 'Autosave is OFF.' - // : `Autosave is OFF, and you haven't saved for ${elapsedTime} minutes.`; + if(unsavedChanges && warnUnsavedChanges) { + resetWarnUnsavedTimer(); + const elapsedTime = Math.round((new Date() - lastSavedTime) / 1000 / 60); + const text = elapsedTime === 0 + ? `Autosave is OFF${sandbox ?? 'for this sandbox page'}.` + : `Autosave is OFF${sandbox ?? 'for this sandbox page'}, and you haven't saved for ${elapsedTime} minutes.`; - // return - // Reminder... - //
{text}
- //
; - // } + return + Reminder... +
{text}
+
; + } // #3 - Unsaved changes exist, click to save, show SAVE NOW if(unsavedChanges) @@ -201,8 +226,8 @@ const NewPage = (props)=>{ if(autoSaveEnabled) return auto-saved; - // #5 - No unsaved changes, and has never been saved, hide the button - if(neverSaved) + // #5 - Sandbox with no unsaved changes, and has never been saved, hide the button + if(sandbox) return save now; // DEFAULT - No unsaved changes, show SAVED