Merge branch 'master' into cleanup

This commit is contained in:
Trevor Buckner
2026-10-05 15:50:17 -04:00
committed by GitHub
4 changed files with 103 additions and 111 deletions
+12 -26
View File
@@ -3,7 +3,7 @@ import './editPage.less';
// Common imports // Common imports
import React, { useState, useEffect, useRef, useEffectEvent } from 'react'; import React, { useState, useEffect, useRef, useEffectEvent } from 'react';
import request from '../../utils/request-middleware.js'; import request from '../../utils/request-middleware.js';
import { hbfm } from 'marked-hbfm';
import _ from 'lodash'; import _ from 'lodash';
import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js'; import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js';
@@ -20,16 +20,11 @@ import { makePatches, stringifyPatches } from '@sanity/diff-match-patch';
import { updateHistory, versionHistoryGarbageCollection } from '../../utils/versionHistory.js'; import { updateHistory, versionHistoryGarbageCollection } from '../../utils/versionHistory.js';
import googleDriveIcon from '../../googleDrive.svg'; import googleDriveIcon from '../../googleDrive.svg';
const BREWKEY = 'HB_newPage_content';
const STYLEKEY = 'HB_newPage_style';
const SNIPKEY = 'HB_newPage_snippets';
const METAKEY = 'HB_newPage_meta';
const useLocalStorage = false; const useLocalStorage = false;
const sandbox = false; const sandbox = false;
const showFloatingButtons = false; const showFloatingButtons = false;
const showEditorButtons = true; const showEditorButtons = true;
const pageName = "editPage"; const pageName = 'editPage';
const EditPage = (props)=>{ const EditPage = (props)=>{
props = { props = {
@@ -108,23 +103,17 @@ const EditPage = (props)=>{
if(!res) return; if(!res) return;
const updatedFields = { const updatedFields = {
googleId : res.body.googleId ?? null, googleId : res.body.googleId ?? null, //TODO: investigate if we can set googleId:null in server/update instead of undefined
editId : res.body.editId, editId : res.body.editId, //then, editPage can just apply res.body instead of breaking out these fields.
shareId : res.body.shareId, shareId : res.body.shareId,
version : res.body.version version : res.body.version
}; };
lastSavedBrew.current = { return updatedFields;
...brew, };
...updatedFields
};
setCurrentBrew((prevBrew)=>({ const onSaveSuccess = (savedBrew)=>{
...prevBrew, history.replaceState(null, null, `/edit/${savedBrew.editId}`);;
...updatedFields
}));
history.replaceState(null, null, `/edit/${res.body.editId}`);
}; };
const renderGoogleDriveIcon = ()=>( const renderGoogleDriveIcon = ()=>(
@@ -176,24 +165,21 @@ const EditPage = (props)=>{
trySave, trySave,
} = useCommonEditPageFunctions({ } = useCommonEditPageFunctions({
saveGoogle, saveGoogle,
setSaveGoogle,
error, error,
setError, setError,
currentBrew, currentBrew,
setCurrentBrew, setCurrentBrew,
useLocalStorage, useLocalStorage,
BREWKEY,
STYLEKEY,
SNIPKEY,
METAKEY,
hbfm,
sandbox, sandbox,
showFloatingButtons, showFloatingButtons,
lastSavedBrew, lastSavedBrew,
save, save,
onSaveSuccess,
pageName, pageName,
showEditorButtons, showEditorButtons,
renderGoogleDriveIcon, renderGoogleDriveIcon,
userThemes: props.userThemes userThemes : props.userThemes
}); });
return renderPanels(); return renderPanels();
+12 -18
View File
@@ -3,7 +3,6 @@ import './homePage.less';
// Common imports // Common imports
import React, { useState, useEffect, useRef, useEffectEvent } from 'react'; import React, { useState, useEffect, useRef, useEffectEvent } from 'react';
import request from '../../utils/request-middleware.js'; import request from '../../utils/request-middleware.js';
import { hbfm } from 'marked-hbfm';
import _ from 'lodash'; import _ from 'lodash';
import { DEFAULT_BREW } from '../../../../server/brewDefaults.js'; import { DEFAULT_BREW } from '../../../../server/brewDefaults.js';
@@ -11,16 +10,11 @@ import { DEFAULT_BREW } from '../../../../server/brewDefau
import useCommonEditPageFunctions from '../../utils/commonEditPageFunctions.jsx'; import useCommonEditPageFunctions from '../../utils/commonEditPageFunctions.jsx';
// Page specific imports // Page specific imports
const BREWKEY = 'HB_newPage_content';
const STYLEKEY = 'HB_newPage_style';
const SNIPKEY = 'HB_newPage_snippets';
const METAKEY = 'HB_newPage_meta';
const useLocalStorage = false; const useLocalStorage = false;
const sandbox = true; const sandbox = true;
const showFloatingButtons = true; const showFloatingButtons = true;
const showEditorButtons = false; const showEditorButtons = false;
const pageName = "homePage"; const pageName = 'homePage';
const HomePage =(props)=>{ const HomePage =(props)=>{
props = { props = {
@@ -28,9 +22,9 @@ const HomePage =(props)=>{
...props ...props
}; };
const [currentBrew, setCurrentBrew] = useState(props.brew); const [currentBrew, setCurrentBrew] = useState(props.brew);
const [saveGoogle, setSaveGoogle] = useState(global.account?.googleId ? true : false); const [saveGoogle, setSaveGoogle] = useState(global.account?.googleId ? true : false);
const [error, setError] = useState(undefined); const [error, setError] = useState(undefined);
const lastSavedBrew = useRef(_.cloneDeep(props.brew)); const lastSavedBrew = useRef(_.cloneDeep(props.brew));
@@ -44,32 +38,32 @@ const HomePage =(props)=>{
}); });
if(!res) return; if(!res) return;
const saved = res.body; return res.body;
};
const onSaveSuccess = (savedBrew)=>{
window.onbeforeunload = null; window.onbeforeunload = null;
window.location = `/edit/${saved.editId}`; window.location = `/edit/${savedBrew.editId}`;
}; };
const { const {
renderPanels renderPanels
} = useCommonEditPageFunctions({ } = useCommonEditPageFunctions({
saveGoogle, saveGoogle,
setSaveGoogle,
error, error,
setError, setError,
currentBrew, currentBrew,
setCurrentBrew, setCurrentBrew,
useLocalStorage, useLocalStorage,
BREWKEY,
STYLEKEY,
SNIPKEY,
METAKEY,
hbfm,
sandbox, sandbox,
showFloatingButtons, showFloatingButtons,
lastSavedBrew, lastSavedBrew,
save, save,
onSaveSuccess,
pageName, pageName,
showEditorButtons, showEditorButtons,
userThemes: {} userThemes : {}
}); });
return renderPanels(); return renderPanels();
+9 -54
View File
@@ -3,7 +3,6 @@ import './newPage.less';
// Common imports // Common imports
import React, { useState, useEffect, useRef, useEffectEvent } from 'react'; import React, { useState, useEffect, useRef, useEffectEvent } from 'react';
import request from '../../utils/request-middleware.js'; import request from '../../utils/request-middleware.js';
import { hbfm } from 'marked-hbfm';
import _ from 'lodash'; import _ from 'lodash';
import { DEFAULT_BREW } from '../../../../server/brewDefaults.js'; import { DEFAULT_BREW } from '../../../../server/brewDefaults.js';
@@ -11,18 +10,11 @@ import { DEFAULT_BREW } from '../../../../server/brewDefau
import useCommonEditPageFunctions from '../../utils/commonEditPageFunctions.jsx'; import useCommonEditPageFunctions from '../../utils/commonEditPageFunctions.jsx';
// Page specific imports // Page specific imports
const BREWKEY = 'HB_newPage_content';
const STYLEKEY = 'HB_newPage_style';
const SNIPKEY = 'HB_newPage_snippets';
const METAKEY = 'HB_newPage_meta';
const SAVEKEYPREFIX = 'HB_editor_defaultSave_';
const useLocalStorage = true; const useLocalStorage = true;
const sandbox = true; const sandbox = true;
const showFloatingButtons = false; const showFloatingButtons = false;
const showEditorButtons = true; const showEditorButtons = true;
const pageName = "newPage"; const pageName = 'newPage';
const NewPage = (props)=>{ const NewPage = (props)=>{
props = { props = {
@@ -31,44 +23,11 @@ const NewPage = (props)=>{
}; };
const [currentBrew, setCurrentBrew] = useState(props.brew); const [currentBrew, setCurrentBrew] = useState(props.brew);
const [saveGoogle, setSaveGoogle] = useState(global.account?.googleId ? true : false); const [saveGoogle, setSaveGoogle] = useState(global.account?.googleId ? true : false);
const [error, setError] = useState(null); const [error, setError] = useState(null);
const lastSavedBrew = useRef(_.cloneDeep(props.brew)); const lastSavedBrew = useRef(_.cloneDeep(props.brew));
useEffect(()=>{
loadBrew();
}, []);
const loadBrew = ()=>{
const brew = { ...currentBrew };
if(!brew.shareId && typeof window !== 'undefined') { //Load from localStorage if in client browser
const brewStorage = localStorage.getItem(BREWKEY);
const styleStorage = localStorage.getItem(STYLEKEY);
const metaStorage = JSON.parse(localStorage.getItem(METAKEY));
brew.text = brewStorage ?? brew.text;
brew.style = styleStorage ?? brew.style;
brew.renderer = metaStorage?.renderer ?? brew.renderer;
brew.theme = metaStorage?.theme ?? brew.theme;
brew.lang = metaStorage?.lang ?? brew.lang;
}
const SAVEKEY = `${SAVEKEYPREFIX}${global.account?.username}`;
const saveStorage = localStorage.getItem(SAVEKEY) || 'HOMEBREWERY';
setCurrentBrew(brew);
lastSavedBrew.current = brew;
setSaveGoogle(saveStorage == 'GOOGLE-DRIVE' && saveGoogle);
localStorage.setItem(BREWKEY, brew.text);
if(brew.style)
localStorage.setItem(STYLEKEY, brew.style);
localStorage.setItem(METAKEY, JSON.stringify({ renderer: brew.renderer, theme: brew.theme, lang: brew.lang }));
if(window.location.pathname !== '/new')
window.history.replaceState({}, window.location.title, '/new/');
};
const save = async (brew, saveToGoogle)=>{ const save = async (brew, saveToGoogle)=>{
//Prepare content to send to server //Prepare content to send to server
const brewToSave = { const brewToSave = {
@@ -87,11 +46,10 @@ const NewPage = (props)=>{
}); });
if(!res) return; if(!res) return;
const savedBrew = res.body; return res.body;
};
localStorage.removeItem(BREWKEY); const onSaveSuccess = (savedBrew)=>{
localStorage.removeItem(STYLEKEY);
localStorage.removeItem(METAKEY);
window.onbeforeunload = null; window.onbeforeunload = null;
window.location = `/edit/${savedBrew.editId}`; window.location = `/edit/${savedBrew.editId}`;
}; };
@@ -100,23 +58,20 @@ const NewPage = (props)=>{
renderPanels renderPanels
} = useCommonEditPageFunctions({ } = useCommonEditPageFunctions({
saveGoogle, saveGoogle,
setSaveGoogle,
error, error,
setError, setError,
currentBrew, currentBrew,
setCurrentBrew, setCurrentBrew,
useLocalStorage, useLocalStorage,
BREWKEY,
STYLEKEY,
SNIPKEY,
METAKEY,
hbfm,
sandbox, sandbox,
showFloatingButtons, showFloatingButtons,
lastSavedBrew, lastSavedBrew,
save, save,
onSaveSuccess,
pageName, pageName,
showEditorButtons, showEditorButtons,
userThemes: props.userThemes userThemes : props.userThemes
}); });
return renderPanels(); return renderPanels();
@@ -1,6 +1,7 @@
import React, { useState, useEffect, useEffectEvent, useRef } from 'react'; import React, { useState, useEffect, useEffectEvent, useRef } from 'react';
import { printCurrentBrew, fetchThemeBundle } from '@shared/helpers.js'; import { printCurrentBrew, fetchThemeBundle } from '@shared/helpers.js';
import _ from 'lodash'; import _ from 'lodash';
import { hbfm } from 'marked-hbfm';
import Headtags from '@vitreum/headtags.js'; import Headtags from '@vitreum/headtags.js';
import SplitPane from '@components/splitPane/splitPane.jsx'; import SplitPane from '@components/splitPane/splitPane.jsx';
@@ -22,7 +23,12 @@ import ShareNavItem from '@navbar/share.navitem.jsx';
import RecentNavItems from '@navbar/recent.navitem.jsx'; import RecentNavItems from '@navbar/recent.navitem.jsx';
const { both: RecentNavItem } = RecentNavItems; const { both: RecentNavItem } = RecentNavItems;
const AUTOSAVE_KEY = 'HB_editor_autoSaveOn'; 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 SAVEKEY_PREFIX = 'HB_editor_defaultSave_';
const SAVE_TIMEOUT = 10000; //Autosave 10 seconds after last change const SAVE_TIMEOUT = 10000; //Autosave 10 seconds after last change
const UNSAVED_WARNING_TIMEOUT = 900000; //Warn user afer 15 minutes of unsaved changes const UNSAVED_WARNING_TIMEOUT = 900000; //Warn user afer 15 minutes of unsaved changes
@@ -31,20 +37,17 @@ const UNSAVED_WARNING_POPUP_TIMEOUT = 4000; //Show the warning for 4 seconds
export default function useCommonEditPageFunctions(dependencies) { export default function useCommonEditPageFunctions(dependencies) {
const { const {
saveGoogle, saveGoogle,
setSaveGoogle,
error, error,
setError, setError,
currentBrew, currentBrew,
setCurrentBrew, setCurrentBrew,
useLocalStorage, useLocalStorage,
BREWKEY,
STYLEKEY,
SNIPKEY,
METAKEY,
hbfm,
sandbox, sandbox,
showFloatingButtons, showFloatingButtons,
lastSavedBrew, lastSavedBrew,
save, save,
onSaveSuccess,
pageName, pageName,
renderGoogleDriveIcon = ()=>{}, renderGoogleDriveIcon = ()=>{},
showEditorButtons, showEditorButtons,
@@ -70,6 +73,7 @@ export default function useCommonEditPageFunctions(dependencies) {
//==--------- Page setup ----------==// //==--------- Page setup ----------==//
useEffect(()=>{ useEffect(()=>{
if(useLocalStorage) loadBrewFromLocalStorage();
const autoSavePref = !sandbox && JSON.parse(localStorage.getItem(AUTOSAVE_KEY) ?? true); const autoSavePref = !sandbox && JSON.parse(localStorage.getItem(AUTOSAVE_KEY) ?? true);
setAutoSaveEnabled(autoSavePref); setAutoSaveEnabled(autoSavePref);
setWarnUnsavedChanges(!autoSavePref); setWarnUnsavedChanges(!autoSavePref);
@@ -95,6 +99,44 @@ export default function useCommonEditPageFunctions(dependencies) {
}; };
}, []); }, []);
const loadBrewFromLocalStorage = ()=>{
const brew = { ...currentBrew };
if(!brew.shareId && typeof window !== 'undefined') { //Load from localStorage if in client browser
const brewStorage = localStorage.getItem(BREWKEY);
const styleStorage = localStorage.getItem(STYLEKEY);
const snipStorage = localStorage.getItem(SNIPKEY);
const metaStorage = JSON.parse(localStorage.getItem(METAKEY));
brew.text = brewStorage ?? brew.text;
brew.style = styleStorage ?? brew.style;
brew.snippets = snipStorage ?? brew.snippets;
brew.renderer = metaStorage?.renderer ?? brew.renderer;
brew.theme = metaStorage?.theme ?? brew.theme;
brew.lang = metaStorage?.lang ?? brew.lang;
}
const SAVEKEY = `${SAVEKEY_PREFIX}${global.account?.username}`;
const saveStorage = localStorage.getItem(SAVEKEY) || 'HOMEBREWERY';
setCurrentBrew(brew);
lastSavedBrew.current = brew;
setSaveGoogle(saveStorage == 'GOOGLE-DRIVE' && saveGoogle);
localStorage.setItem(BREWKEY, brew.text);
if(brew.style) localStorage.setItem(STYLEKEY, brew.style);
if(brew.snippets) localStorage.setItem(SNIPKEY, brew.snippets);
localStorage.setItem(METAKEY, JSON.stringify({ renderer: brew.renderer, theme: brew.theme, lang: brew.lang }));
if(window.location.pathname !== '/new')
window.history.replaceState({}, window.location.title, '/new/');
};
const clearLocalStorage = ()=>{
localStorage.removeItem(BREWKEY);
localStorage.removeItem(STYLEKEY);
localStorage.removeItem(SNIPKEY);
localStorage.removeItem(METAKEY);
};
//======----- Check for unsaved changes and autosave if enabled -----====== //======----- Check for unsaved changes and autosave if enabled -----======
useEffect(()=>{ useEffect(()=>{
const hasChange = !_.isEqual(currentBrew, lastSavedBrew.current); const hasChange = !_.isEqual(currentBrew, lastSavedBrew.current);
@@ -127,12 +169,12 @@ export default function useCommonEditPageFunctions(dependencies) {
if(useLocalStorage) { if(useLocalStorage) {
if(field == 'text') localStorage.setItem(BREWKEY, value); if(field == 'text') localStorage.setItem(BREWKEY, value);
if(field == 'style') localStorage.setItem(STYLEKEY, value); if(field == 'style') localStorage.setItem(STYLEKEY, value);
if(field == 'snippets') localStorage.setItem(SNIPKEY, value); if(field == 'snippets') localStorage.setItem(SNIPKEY, value);
if(field == 'metadata') localStorage.setItem(METAKEY, JSON.stringify({ if(field == 'metadata') localStorage.setItem(METAKEY, JSON.stringify({
renderer : value.renderer, renderer : value.renderer,
theme : value.theme, theme : value.theme,
lang : value.lang lang : value.lang
})); }));
} }
}; };
@@ -160,8 +202,23 @@ export default function useCommonEditPageFunctions(dependencies) {
setIsSaving(true); setIsSaving(true);
setError(null); setError(null);
setHTMLErrors(hbfm.validate(currentBrew.text)); setHTMLErrors(hbfm.validate(currentBrew.text));
await save(currentBrew, saveToGoogle) const brewToSave = currentBrew;
let savedBrew = await save(brewToSave, saveToGoogle)
.catch((err)=>{setError(err);}); .catch((err)=>{setError(err);});
if(savedBrew) {
lastSavedBrew.current = {
...brewToSave,
...savedBrew
};
setCurrentBrew((prevBrew)=>({
...prevBrew,
...savedBrew
}));
if(useLocalStorage) clearLocalStorage();
onSaveSuccess(savedBrew);
}
setIsSaving(false); setIsSaving(false);
setLastSavedTime(new Date()); setLastSavedTime(new Date());
if(!autoSaveEnabled) resetWarnUnsavedTimer(); if(!autoSaveEnabled) resetWarnUnsavedTimer();
@@ -216,14 +273,14 @@ export default function useCommonEditPageFunctions(dependencies) {
? <ErrorNavItem error={error} clearError={clearError} /> ? <ErrorNavItem error={error} clearError={clearError} />
: <Nav.dropdown className='save-menu'> : <Nav.dropdown className='save-menu'>
{renderSaveButton()} {renderSaveButton()}
{pageName == "editPage" && renderAutoSaveButton()} {pageName == 'editPage' && renderAutoSaveButton()}
</Nav.dropdown>} </Nav.dropdown>}
<NewBrewItem /> <NewBrewItem />
<PrintNavItem /> <PrintNavItem />
<HelpNavItem /> <HelpNavItem />
<VaultNavItem /> <VaultNavItem />
{(pageName == "editPage") && <ShareNavItem brew={currentBrew} currentPage={currentBrewRendererPageNum} />} {(pageName == 'editPage') && <ShareNavItem brew={currentBrew} currentPage={currentBrewRendererPageNum} />}
<RecentNavItem brew={currentBrew} storageKey={(pageName == "editPage") ? 'edit' : undefined} /> <RecentNavItem brew={currentBrew} storageKey={(pageName == 'editPage') ? 'edit' : undefined} />
<AccountNavItem /> <AccountNavItem />
</Nav.section> </Nav.section>
</Navbar> </Navbar>