mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-18 14:22:42 +00:00
/editPage.jsx uses `unsavedChanges` state to detect when autosave should fire, or unsaved changes warning should display. /homePage.jsx uses a similar check (different variables) to detect when to show the popup "save now"! button /newPage.jsx doesn't do any of this, but probably should pop up a warning when saving hasn't happened for a long time This commit just gives all of the pages the same common `unsavedChanges` state, calculated in the same way, and updates any sections that depend on that updated state. This is precursor work to adding "unsaved changes" warnings to all three pages.
190 lines
6.5 KiB
JavaScript
190 lines
6.5 KiB
JavaScript
/* eslint-disable max-lines */
|
|
import './homePage.less';
|
|
|
|
// Common imports
|
|
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';
|
|
|
|
import SplitPane from 'client/components/splitPane/splitPane.jsx';
|
|
import Editor from '../../editor/editor.jsx';
|
|
import BrewRenderer from '../../brewRenderer/brewRenderer.jsx';
|
|
|
|
import Nav from 'naturalcrit/nav/nav.jsx';
|
|
import Navbar from '../../navbar/navbar.jsx';
|
|
import NewBrewItem from '../../navbar/newbrew.navitem.jsx';
|
|
import AccountNavItem from '../../navbar/account.navitem.jsx';
|
|
import ErrorNavItem from '../../navbar/error-navitem.jsx';
|
|
import HelpNavItem from '../../navbar/help.navitem.jsx';
|
|
import VaultNavItem from '../../navbar/vault.navitem.jsx';
|
|
import PrintNavItem from '../../navbar/print.navitem.jsx';
|
|
import { both as RecentNavItem } from '../../navbar/recent.navitem.jsx';
|
|
|
|
// Page specific imports
|
|
import { Meta } from 'vitreum/headtags';
|
|
|
|
const BREWKEY = 'homebrewery-new';
|
|
const STYLEKEY = 'homebrewery-new-style';
|
|
const SNIPKEY = 'homebrewery-new-snippets';
|
|
const METAKEY = 'homebrewery-new-meta';
|
|
|
|
const useLocalStorage = false;
|
|
|
|
const HomePage =(props)=>{
|
|
props = {
|
|
brew : DEFAULT_BREW,
|
|
ver : '0.0.0',
|
|
...props
|
|
};
|
|
|
|
const [currentBrew , setCurrentBrew] = useState(props.brew);
|
|
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 lastSavedBrew = useRef(_.cloneDeep(props.brew));
|
|
|
|
useEffect(()=>{
|
|
fetchThemeBundle(setError, setThemeBundle, currentBrew.renderer, currentBrew.theme);
|
|
|
|
const handleControlKeys = (e)=>{
|
|
if(!(e.ctrlKey || e.metaKey)) return;
|
|
if(e.keyCode === 83) trySaveRef.current(true);
|
|
if(e.keyCode === 80) printCurrentBrew();
|
|
if([83, 80].includes(e.keyCode)) {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}
|
|
};
|
|
|
|
document.addEventListener('keydown', handleControlKeys);
|
|
|
|
return () => {
|
|
document.removeEventListener('keydown', handleControlKeys);
|
|
};
|
|
}, []);
|
|
|
|
const save = ()=>{
|
|
request.post('/api')
|
|
.send(currentBrew)
|
|
.end((err, res)=>{
|
|
if(err) {
|
|
setError(err);
|
|
return;
|
|
}
|
|
const saved = res.body;
|
|
window.location = `/edit/${saved.editId}`;
|
|
});
|
|
};
|
|
|
|
useEffect(()=>{
|
|
const hasChange = !_.isEqual(currentBrew, lastSavedBrew.current);
|
|
setUnsavedChanges(hasChange);
|
|
|
|
if(autoSaveEnabled) trySave(false, hasChange);
|
|
}, [currentBrew]);
|
|
|
|
const handleSplitMove = ()=>{
|
|
editorRef.current.update();
|
|
};
|
|
|
|
const handleBrewChange = (field) => (value, subfield) => { //'text', 'style', 'snippets', 'metadata'
|
|
if (subfield == 'renderer' || subfield == 'theme')
|
|
fetchThemeBundle(setError, setThemeBundle, value.renderer, value.theme);
|
|
|
|
//If there are HTML errors, run the validator on every change to give quick feedback
|
|
if(HTMLErrors.length && (field == 'text' || field == 'snippets'))
|
|
setHTMLErrors(Markdown.validate(value));
|
|
|
|
if(field == 'metadata') setCurrentBrew(prev => ({ ...prev, ...value }));
|
|
else setCurrentBrew(prev => ({ ...prev, [field]: value }));
|
|
|
|
if(useLocalStorage) {
|
|
if(field == 'text') localStorage.setItem(BREWKEY, value);
|
|
if(field == 'style') localStorage.setItem(STYLEKEY, value);
|
|
if(field == 'snippets') localStorage.setItem(SNIPKEY, value);
|
|
if(field == 'metadata') localStorage.setItem(METAKEY, JSON.stringify({
|
|
renderer : value.renderer,
|
|
theme : value.theme,
|
|
lang : value.lang
|
|
}));
|
|
}
|
|
};
|
|
|
|
const clearError = ()=>{
|
|
setError(null);
|
|
setIsSaving(false);
|
|
};
|
|
|
|
const renderNavbar = ()=>{
|
|
return <Navbar ver={props.ver}>
|
|
<Nav.section>
|
|
{error ?
|
|
<ErrorNavItem error={error} clearError={clearError}></ErrorNavItem> :
|
|
null
|
|
}
|
|
<NewBrewItem />
|
|
<PrintNavItem />
|
|
<HelpNavItem />
|
|
<VaultNavItem />
|
|
<RecentNavItem />
|
|
<AccountNavItem />
|
|
</Nav.section>
|
|
</Navbar>;
|
|
};
|
|
|
|
return (
|
|
<div className='homePage sitePage'>
|
|
<Meta name='google-site-verification' content='NwnAQSSJZzAT7N-p5MY6ydQ7Njm67dtbu73ZSyE5Fy4' />
|
|
{renderNavbar()}
|
|
<div className='content'>
|
|
<SplitPane onDragFinish={handleSplitMove}>
|
|
<Editor
|
|
ref={editorRef}
|
|
brew={currentBrew}
|
|
onBrewChange={handleBrewChange}
|
|
renderer={currentBrew.renderer}
|
|
showEditButtons={false}
|
|
themeBundle={themeBundle}
|
|
onCursorPageChange={setCurrentEditorCursorPageNum}
|
|
onViewPageChange={setCurrentEditorViewPageNum}
|
|
currentEditorViewPageNum={currentEditorViewPageNum}
|
|
currentEditorCursorPageNum={currentEditorCursorPageNum}
|
|
currentBrewRendererPageNum={currentBrewRendererPageNum}
|
|
/>
|
|
<BrewRenderer
|
|
text={currentBrew.text}
|
|
style={currentBrew.style}
|
|
renderer={currentBrew.renderer}
|
|
onPageChange={setCurrentBrewRendererPageNum}
|
|
currentEditorViewPageNum={currentEditorViewPageNum}
|
|
currentEditorCursorPageNum={currentEditorCursorPageNum}
|
|
currentBrewRendererPageNum={currentBrewRendererPageNum}
|
|
themeBundle={themeBundle}
|
|
/>
|
|
</SplitPane>
|
|
</div>
|
|
<div className={`floatingSaveButton${unsavedChanges ? ' show' : ''}`} onClick={save}>
|
|
Save current <i className='fas fa-save' />
|
|
</div>
|
|
|
|
<a href='/new' className='floatingNewButton'>
|
|
Create your own <i className='fas fa-magic' />
|
|
</a>
|
|
</div>
|
|
)
|
|
};
|
|
|
|
module.exports = HomePage;
|