diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index bda7143fc..a19d33375 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -295,12 +295,6 @@ const BrewRenderer = (props)=>{ rowGap : `${displayOptions.rowGap}px` }; - const styleObject = {}; - - if(global.config.deployment) { - styleObject.backgroundImage = `url("data:image/svg+xml;utf8,${global.config.deployment}")`; - } - const renderedStyle = useMemo(()=>renderStyle(), [props.style, props.themeBundle]); renderedPages = useMemo(()=>renderPages(), [props.text, displayOptions]); @@ -329,10 +323,9 @@ const BrewRenderer = (props)=>{ contentDidMount={frameDidMount} onClick={()=>{emitClick();}} > -
{/* Apply CSS from Style tab and render pages from Markdown tab */} diff --git a/client/homebrew/brewRenderer/brewRenderer.less b/client/homebrew/brewRenderer/brewRenderer.less index b0a3e9779..bb4fe69c5 100644 --- a/client/homebrew/brewRenderer/brewRenderer.less +++ b/client/homebrew/brewRenderer/brewRenderer.less @@ -6,7 +6,6 @@ overflow-y : scroll; will-change : transform; &:has(.facing, .flow) { padding : 60px 30px; } - &.deployment { background-color : darkred; } :where(.pages) { &.facing { display : grid; diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 1aff5067d..e7e006808 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -19,7 +19,6 @@ const WithRoute = ({ el: Element, ...rest })=>{ const params = useParams(); const [searchParams] = useSearchParams(); const queryParams = Object.fromEntries(searchParams?.entries() || []); - return ; }; @@ -50,11 +49,20 @@ const Homebrew = (props)=>{ global.enable_themes = enable_themes; global.config = config; + const backgroundObject = ()=>{ + if(global.config.deployment || (config.local && config.development)){ + const bgText = global.config.deployment || 'Local'; + return { + backgroundImage : `url("data:image/svg+xml;utf8,${bgText}")` + }; + } + return null; + }; updateLocalStorage(); return ( -
+
} /> } /> diff --git a/client/homebrew/homebrew.less b/client/homebrew/homebrew.less index e265c2941..2cbc35857 100644 --- a/client/homebrew/homebrew.less +++ b/client/homebrew/homebrew.less @@ -1,12 +1,14 @@ @import 'naturalcrit/styles/core.less'; .homebrew { height : 100%; + background-color:@steel; + &.deployment { background-color : darkred; } + .sitePage { display : flex; flex-direction : column; height : 100%; overflow-y : hidden; - background-color : @steel; .content { position : relative; flex : auto; diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index e3d449a55..d4e0061b1 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -5,6 +5,7 @@ import './editPage.less'; import React, { useState, useEffect, useRef } from 'react'; import request from '../../utils/request-middleware.js'; import Markdown from 'naturalcrit/markdown.js'; +import _ from 'lodash'; import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js'; import { printCurrentBrew, fetchThemeBundle, splitTextStyleAndMetadata } from '../../../../shared/helpers.js'; @@ -25,7 +26,6 @@ import { both as RecentNavItem } from '../../navbar/recent.navitem.jsx'; // Page specific imports import { Meta } from 'vitreum/headtags'; -import _ from 'lodash'; import { md5 } from 'hash-wasm'; import { gzipSync, strToU8 } from 'fflate'; import { makePatches, stringifyPatches } from '@sanity/diff-match-patch'; @@ -46,8 +46,8 @@ const STYLEKEY = 'HB_newPage_style'; const SNIPKEY = 'HB_newPage_snippets'; const METAKEY = 'HB_newPage_meta'; - const useLocalStorage = false; +const neverSaved = false; const EditPage = (props)=>{ props = { @@ -309,14 +309,18 @@ const EditPage = (props)=>{ // #3 - Unsaved changes exist, click to save, show SAVE NOW if(unsavedChanges) - return trySave(true)} color='blue' icon='fas fa-save'>Save Now; + return trySave(true)} color='blue' icon='fas fa-save'>save now; // #4 - No unsaved changes, autosave is ON, show AUTO-SAVED if(autoSaveEnabled) - return auto-saved.; + return auto-saved; + + // #5 - No unsaved changes, and has never been saved, hide the button + if(neverSaved) + return save now; // DEFAULT - No unsaved changes, show SAVED - return saved.; + return saved; }; const toggleAutoSave = ()=>{ diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index fdc439ab5..fe57a9913 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -5,6 +5,7 @@ import './homePage.less'; import React, { useState, useEffect, useRef } from 'react'; import request from '../../utils/request-middleware.js'; import Markdown from 'naturalcrit/markdown.js'; +import _ from 'lodash'; import { DEFAULT_BREW } from '../../../../server/brewDefaults.js'; import { printCurrentBrew, fetchThemeBundle, splitTextStyleAndMetadata } from '../../../../shared/helpers.js'; @@ -32,6 +33,7 @@ const SNIPKEY = 'homebrewery-new-snippets'; const METAKEY = 'homebrewery-new-meta'; const useLocalStorage = false; +const neverSaved = true; const HomePage =(props)=>{ props = { @@ -41,16 +43,18 @@ const HomePage =(props)=>{ }; const [currentBrew , setCurrentBrew] = useState(props.brew); - const [welcomeText , setWelcomeText] = useState(props.brew.text); const [error , setError] = useState(undefined); const [HTMLErrors , setHTMLErrors] = useState(Markdown.validate(props.brew.text)); const [currentEditorViewPageNum , setCurrentEditorViewPageNum] = useState(1); const [currentEditorCursorPageNum, setCurrentEditorCursorPageNum] = useState(1); const [currentBrewRendererPageNum, setCurrentBrewRendererPageNum] = useState(1); const [themeBundle , setThemeBundle] = useState({}); + const [unsavedChanges , setUnsavedChanges] = useState(false); const [isSaving , setIsSaving] = useState(false); + const [autoSaveEnabled , setAutoSaveEnable] = useState(false); - const editorRef = useRef(null); + const editorRef = useRef(null); + const lastSavedBrew = useRef(_.cloneDeep(props.brew)); useEffect(()=>{ fetchThemeBundle(setError, setThemeBundle, currentBrew.renderer, currentBrew.theme); @@ -85,6 +89,13 @@ const HomePage =(props)=>{ }); }; + useEffect(()=>{ + const hasChange = !_.isEqual(currentBrew, lastSavedBrew.current); + setUnsavedChanges(hasChange); + + if(autoSaveEnabled) trySave(false, hasChange); + }, [currentBrew]); + const handleSplitMove = ()=>{ editorRef.current.update(); }; @@ -112,6 +123,41 @@ const HomePage =(props)=>{ } }; + 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.`; + + // return + // Reminder... + //
{text}
+ //
; + // } + + // #3 - Unsaved changes exist, click to save, show SAVE NOW + if(unsavedChanges) + return save now; + + // #4 - No unsaved changes, autosave is ON, show AUTO-SAVED + if(autoSaveEnabled) + return auto-saved; + + // #5 - No unsaved changes, and has never been saved, hide the button + if(neverSaved) + return save now; + + // DEFAULT - No unsaved changes, show SAVED + return saved; + }; + const clearError = ()=>{ setError(null); setIsSaving(false); @@ -120,10 +166,9 @@ const HomePage =(props)=>{ const renderNavbar = ()=>{ return - {error ? - : - null - } + {error + ? + : renderSaveButton()} @@ -165,7 +210,7 @@ const HomePage =(props)=>{ />
-
+
Save current
diff --git a/client/homebrew/pages/homePage/homePage.less b/client/homebrew/pages/homePage/homePage.less index 4cf9ff4fe..c3ec1815c 100644 --- a/client/homebrew/pages/homePage/homePage.less +++ b/client/homebrew/pages/homePage/homePage.less @@ -34,7 +34,13 @@ } .navItem.save { + .fadeInRight(); + .transition(opacity); background-color : @orange; &:hover { background-color : @green; } + &.neverSaved { + .fadeOutRight(); + opacity: 0; + } } } diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index d1d1fa75c..bc2b39c44 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -5,6 +5,7 @@ import './newPage.less'; import React, { useState, useEffect, useRef } from 'react'; import request from '../../utils/request-middleware.js'; import Markdown from 'naturalcrit/markdown.js'; +import _ from 'lodash'; import { DEFAULT_BREW } from '../../../../server/brewDefaults.js'; import { printCurrentBrew, fetchThemeBundle, splitTextStyleAndMetadata } from '../../../../shared/helpers.js'; @@ -26,15 +27,14 @@ import { both as RecentNavItem } from '../../navbar/recent.navitem.jsx'; // Page specific imports import { Meta } from 'vitreum/headtags'; - const BREWKEY = 'HB_newPage_content'; const STYLEKEY = 'HB_newPage_style'; const METAKEY = 'HB_newPage_metadata'; const SNIPKEY = 'HB_newPage_snippets'; const SAVEKEYPREFIX = 'HB_editor_defaultSave_'; - const useLocalStorage = true; +const neverSaved = true; const NewPage = (props) => { props = { @@ -51,8 +51,11 @@ const NewPage = (props) => { const [currentEditorCursorPageNum, setCurrentEditorCursorPageNum] = useState(1); const [currentBrewRendererPageNum, setCurrentBrewRendererPageNum] = useState(1); const [themeBundle , setThemeBundle ] = useState({}); + const [unsavedChanges , setUnsavedChanges ] = useState(false); + const [autoSaveEnabled , setAutoSaveEnabled ] = useState(false); - const editorRef = useRef(null); + const editorRef = useRef(null); + const lastSavedBrew = useRef(_.cloneDeep(props.brew)); useEffect(() => { loadBrew(); @@ -93,6 +96,7 @@ const NewPage = (props) => { const saveStorage = localStorage.getItem(SAVEKEY) || 'HOMEBREWERY'; setCurrentBrew(brew); + lastSavedBrew.current = brew; setSaveGoogle(saveStorage == 'GOOGLE-DRIVE' && saveGoogle); localStorage.setItem(BREWKEY, brew.text); @@ -103,6 +107,13 @@ const NewPage = (props) => { window.history.replaceState({}, window.location.title, '/new/'); }; + useEffect(()=>{ + const hasChange = !_.isEqual(currentBrew, lastSavedBrew.current); + setUnsavedChanges(hasChange); + + if(autoSaveEnabled) trySave(false, hasChange); + }, [currentBrew]); + const handleSplitMove = ()=>{ editorRef.current.update(); }; @@ -159,15 +170,38 @@ const NewPage = (props) => { }; const renderSaveButton = ()=>{ - if(isSaving){ - return - save... - ; - } else { - return - save - ; - } + // #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.`; + + // return + // Reminder... + //
{text}
+ //
; + // } + + // #3 - Unsaved changes exist, click to save, show SAVE NOW + if(unsavedChanges) + return save now; + + // #4 - No unsaved changes, autosave is ON, show AUTO-SAVED + if(autoSaveEnabled) + return auto-saved; + + // #5 - No unsaved changes, and has never been saved, hide the button + if(neverSaved) + return save now; + + // DEFAULT - No unsaved changes, show SAVED + return saved; }; const clearError = ()=>{ diff --git a/client/homebrew/pages/newPage/newPage.less b/client/homebrew/pages/newPage/newPage.less index ebc44d543..083e1ee09 100644 --- a/client/homebrew/pages/newPage/newPage.less +++ b/client/homebrew/pages/newPage/newPage.less @@ -1,6 +1,12 @@ .newPage { .navItem.save { + .fadeInRight(); + .transition(opacity); background-color : @orange; &:hover { background-color : @green; } + &.neverSaved { + .fadeOutRight(); + opacity: 0; + } } } diff --git a/client/homebrew/pages/vaultPage/vaultPage.less b/client/homebrew/pages/vaultPage/vaultPage.less index 8a5f3a714..304fefc72 100644 --- a/client/homebrew/pages/vaultPage/vaultPage.less +++ b/client/homebrew/pages/vaultPage/vaultPage.less @@ -1,14 +1,16 @@ .vaultPage { height : 100%; overflow-y : hidden; - background-color : #2C3E50; *:not(input) { user-select : none; } + .form { + background:white; + } + :where(.content .dataGroup) { width : 100%; height : 100%; - background : white; &.form .brewLookup { position : relative; @@ -171,7 +173,6 @@ max-height : 100%; padding : 70px 50px; overflow-y : scroll; - background-color : #2C3E50; container-type : inline-size; h3 { font-size : 25px; } diff --git a/client/homebrew/utils/updateLocalStorage/localStorageKeyMap.js b/client/homebrew/utils/updateLocalStorage/localStorageKeyMap.js index b4a05974f..f6e01c54f 100644 --- a/client/homebrew/utils/updateLocalStorage/localStorageKeyMap.js +++ b/client/homebrew/utils/updateLocalStorage/localStorageKeyMap.js @@ -26,7 +26,7 @@ const getLocalStorageMap = function(){ if(global?.account?.username){ const username = global.account.username; - localStorageMap[`HOMEBREWERY-DEFAULT-SAVE-LOCATION-${username}`] = `HB_editor_defaultSave_${username}`; + localStorageMap[`HOMEBREWERY-DEFAULT-SAVE-LOCATION-${username}`] = `HB_editor_defaultSave_${username}`; } return localStorageMap; diff --git a/client/homebrew/utils/updateLocalStorage/localStorageKeyMap.spec.js b/client/homebrew/utils/updateLocalStorage/localStorageKeyMap.spec.js deleted file mode 100644 index ac61d4add..000000000 --- a/client/homebrew/utils/updateLocalStorage/localStorageKeyMap.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -import getLocalStorageMap from './localStorageKeyMap.js'; - -describe('getLocalStorageMap', ()=>{ - it('no username', ()=>{ - const account = global.account; - - delete global.account; - - const map = getLocalStorageMap(); - - global.account = account; - - expect(map).toBeInstanceOf(Object); - expect(Object.entries(map)).toHaveLength(16); - }); - - it('no username', ()=>{ - const account = global.account; - - global.account = { username: 'test' }; - - const map = getLocalStorageMap(); - - global.account = account; - - expect(map).toBeInstanceOf(Object); - expect(Object.entries(map)).toHaveLength(17); - expect(map).toHaveProperty('HOMEBREWERY-DEFAULT-SAVE-LOCATION-test', 'HB_editor_defaultSave_test'); - }); -}); \ No newline at end of file diff --git a/client/homebrew/utils/updateLocalStorage/updateLocalStorageKeys.js b/client/homebrew/utils/updateLocalStorage/updateLocalStorageKeys.js index 1a8231f73..912c4297b 100644 --- a/client/homebrew/utils/updateLocalStorage/updateLocalStorageKeys.js +++ b/client/homebrew/utils/updateLocalStorage/updateLocalStorageKeys.js @@ -4,10 +4,7 @@ const updateLocalStorage = function(){ // Return if no window and thus no local storage if(typeof window === 'undefined') return; - // Return if the local storage key map has no content const localStorageKeyMap = getLocalStorageMap(); - if(Object.keys(localStorageKeyMap).length == 0) return; - const storage = window.localStorage; Object.keys(localStorageKeyMap).forEach((key)=>{ diff --git a/config/default.json b/config/default.json index bea3b2663..0a2d7281e 100644 --- a/config/default.json +++ b/config/default.json @@ -1,4 +1,5 @@ { + "development": true, "host" : "homebrewery.local.naturalcrit.com:8000", "naturalcrit_url" : "local.naturalcrit.com:8010", "secret" : "secret", diff --git a/themes/V3/Blank/style.less b/themes/V3/Blank/style.less index 42a392454..9d41181e9 100644 --- a/themes/V3/Blank/style.less +++ b/themes/V3/Blank/style.less @@ -611,3 +611,17 @@ h6, } .toc.wide li { break-inside : auto; } } + + +/********************************** +Firefox endruns +**********************************/ + +@supports (-moz-user-select: none) { // This section will only apply to Firefox; it's the only browser that supports `-mos-xyz...` + .page { + blockquote, table { + page-break-inside: auto; + break-inside: auto; + } + } +} \ No newline at end of file