mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-27 16:03:07 +00:00
Compare commits
33 Commits
dependabot
...
editBasePa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90ceb52ffc | ||
|
|
6f9caf0590 | ||
|
|
253dbb358b | ||
|
|
719edd82c5 | ||
|
|
63d957fdc6 | ||
|
|
7751c0e37b | ||
|
|
990bf80b59 | ||
|
|
f16598f238 | ||
|
|
579e9e0ec5 | ||
|
|
f6629f2f9e | ||
|
|
b87c78474d | ||
|
|
958d282a58 | ||
|
|
7e56ae2019 | ||
|
|
ebca50ed4b | ||
|
|
bfd14757c2 | ||
|
|
3626ed5a31 | ||
|
|
d385bacdd6 | ||
|
|
cbbb2c0a7d | ||
|
|
fbe637ff82 | ||
|
|
82bd16c623 | ||
|
|
d1f13af67b | ||
|
|
b6c03e88b8 | ||
|
|
b587d17397 | ||
|
|
0a02f910f8 | ||
|
|
ddfa06e76b | ||
|
|
0c2b1fec04 | ||
|
|
6de7a64acd | ||
|
|
b9fe4c3901 | ||
|
|
702ece6671 | ||
|
|
b547486c48 | ||
|
|
6d0d6f08b5 | ||
|
|
77dcc9b433 | ||
|
|
5648e55774 |
@@ -20,6 +20,8 @@ import HeaderNav from './headerNav/headerNav.jsx';
|
|||||||
import { safeHTML } from './safeHTML.js';
|
import { safeHTML } from './safeHTML.js';
|
||||||
|
|
||||||
const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m;
|
const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m;
|
||||||
|
const PAGEBREAK_REGEX_LEGACY = /\\page(?:break)?/m;
|
||||||
|
const COLUMNBREAK_REGEX_LEGACY = /\\column(:?break)?/m;
|
||||||
const PAGE_HEIGHT = 1056;
|
const PAGE_HEIGHT = 1056;
|
||||||
|
|
||||||
const INITIAL_CONTENT = dedent`
|
const INITIAL_CONTENT = dedent`
|
||||||
@@ -130,7 +132,7 @@ const BrewRenderer = (props)=>{
|
|||||||
const pagesRef = useRef(null);
|
const pagesRef = useRef(null);
|
||||||
|
|
||||||
if(props.renderer == 'legacy') {
|
if(props.renderer == 'legacy') {
|
||||||
rawPages = props.text.split('\\page');
|
rawPages = props.text.split(PAGEBREAK_REGEX_LEGACY);
|
||||||
} else {
|
} else {
|
||||||
rawPages = props.text.split(PAGEBREAK_REGEX_V3);
|
rawPages = props.text.split(PAGEBREAK_REGEX_V3);
|
||||||
}
|
}
|
||||||
@@ -187,6 +189,7 @@ const BrewRenderer = (props)=>{
|
|||||||
let attributes = {};
|
let attributes = {};
|
||||||
|
|
||||||
if(props.renderer == 'legacy') {
|
if(props.renderer == 'legacy') {
|
||||||
|
pageText.replace(COLUMNBREAK_REGEX_LEGACY, '```\n````\n'); // Allow Legacy brews to use `\column(break)`
|
||||||
const html = MarkdownLegacy.render(pageText);
|
const html = MarkdownLegacy.render(pageText);
|
||||||
|
|
||||||
return <BrewPage className='page phb' index={index} key={index} contents={html} style={styles} onVisibilityChange={handlePageVisibilityChange} />;
|
return <BrewPage className='page phb' index={index} key={index} contents={html} style={styles} onVisibilityChange={handlePageVisibilityChange} />;
|
||||||
|
|||||||
@@ -1,50 +1,35 @@
|
|||||||
//╔===--------------- Polyfills --------------===╗//
|
/* eslint-disable camelcase */
|
||||||
import 'core-js/es/string/to-well-formed.js';
|
import 'core-js/es/string/to-well-formed.js'; //Polyfill for older browsers
|
||||||
//╚===--------------- ---------------===╝//
|
import './homebrew.less';
|
||||||
|
import React from 'react';
|
||||||
|
import { StaticRouter as Router, Route, Routes, useParams, useSearchParams } from 'react-router';
|
||||||
|
|
||||||
require('./homebrew.less');
|
import HomePage from './pages/homePage/homePage.jsx';
|
||||||
const React = require('react');
|
import EditPage from './pages/editPage/editPage.jsx';
|
||||||
const createClass = require('create-react-class');
|
import UserPage from './pages/userPage/userPage.jsx';
|
||||||
const { StaticRouter:Router } = require('react-router');
|
import SharePage from './pages/sharePage/sharePage.jsx';
|
||||||
const { Route, Routes, useParams, useSearchParams } = require('react-router');
|
import NewPage from './pages/newPage/newPage.jsx';
|
||||||
|
import ErrorPage from './pages/errorPage/errorPage.jsx';
|
||||||
|
import VaultPage from './pages/vaultPage/vaultPage.jsx';
|
||||||
|
import AccountPage from './pages/accountPage/accountPage.jsx';
|
||||||
|
|
||||||
const HomePage = require('./pages/homePage/homePage.jsx');
|
const WithRoute = ({ el: Element, ...rest })=>{
|
||||||
const EditPage = require('./pages/editPage/editPage.jsx');
|
|
||||||
const UserPage = require('./pages/userPage/userPage.jsx');
|
|
||||||
const SharePage = require('./pages/sharePage/sharePage.jsx');
|
|
||||||
const NewPage = require('./pages/newPage/newPage.jsx');
|
|
||||||
const ErrorPage = require('./pages/errorPage/errorPage.jsx');
|
|
||||||
const VaultPage = require('./pages/vaultPage/vaultPage.jsx');
|
|
||||||
const AccountPage = require('./pages/accountPage/accountPage.jsx');
|
|
||||||
|
|
||||||
const WithRoute = (props)=>{
|
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const queryParams = {};
|
const queryParams = Object.fromEntries(searchParams?.entries() || []);
|
||||||
for (const [key, value] of searchParams?.entries() || []) {
|
|
||||||
queryParams[key] = value;
|
return <Element {...rest} {...params} query={queryParams} />;
|
||||||
}
|
|
||||||
const Element = props.el;
|
|
||||||
const allProps = {
|
|
||||||
...props,
|
|
||||||
...params,
|
|
||||||
query : queryParams,
|
|
||||||
el : undefined
|
|
||||||
};
|
|
||||||
return <Element {...allProps} />;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Homebrew = createClass({
|
const Homebrew = (props)=>{
|
||||||
displayName : 'Homebrewery',
|
const {
|
||||||
getDefaultProps : function() {
|
url = '',
|
||||||
return {
|
version = '0.0.0',
|
||||||
url : '',
|
account = null,
|
||||||
welcomeText : '',
|
enable_v3 = false,
|
||||||
changelog : '',
|
enable_themes,
|
||||||
version : '0.0.0',
|
config,
|
||||||
account : null,
|
brew = {
|
||||||
enable_v3 : false,
|
|
||||||
brew : {
|
|
||||||
title : '',
|
title : '',
|
||||||
text : '',
|
text : '',
|
||||||
shareId : null,
|
shareId : null,
|
||||||
@@ -52,44 +37,39 @@ const Homebrew = createClass({
|
|||||||
createdAt : null,
|
createdAt : null,
|
||||||
updatedAt : null,
|
updatedAt : null,
|
||||||
lang : ''
|
lang : ''
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
userThemes,
|
||||||
|
brews
|
||||||
|
} = props;
|
||||||
|
|
||||||
getInitialState : function() {
|
global.account = account;
|
||||||
global.account = this.props.account;
|
global.version = version;
|
||||||
global.version = this.props.version;
|
global.enable_v3 = enable_v3;
|
||||||
global.enable_v3 = this.props.enable_v3;
|
global.enable_themes = enable_themes;
|
||||||
global.enable_themes = this.props.enable_themes;
|
global.config = config;
|
||||||
global.config = this.props.config;
|
|
||||||
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
|
|
||||||
render : function (){
|
|
||||||
return (
|
return (
|
||||||
<Router location={this.props.url}>
|
<Router location={url}>
|
||||||
<div className='homebrew'>
|
<div className='homebrew'>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='/edit/:id' element={<WithRoute el={EditPage} brew={this.props.brew} userThemes={this.props.userThemes}/>} />
|
<Route path='/edit/:id' element={<WithRoute el={EditPage} brew={brew} userThemes={userThemes}/>} />
|
||||||
<Route path='/share/:id' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
|
<Route path='/share/:id' element={<WithRoute el={SharePage} brew={brew} />} />
|
||||||
<Route path='/new/:id' element={<WithRoute el={NewPage} brew={this.props.brew} userThemes={this.props.userThemes}/>} />
|
<Route path='/new/:id' element={<WithRoute el={NewPage} brew={brew} userThemes={userThemes}/>} />
|
||||||
<Route path='/new' element={<WithRoute el={NewPage} userThemes={this.props.userThemes}/> } />
|
<Route path='/new' element={<WithRoute el={NewPage} userThemes={userThemes}/> } />
|
||||||
<Route path='/user/:username' element={<WithRoute el={UserPage} brews={this.props.brews} />} />
|
<Route path='/user/:username' element={<WithRoute el={UserPage} brews={brews} />} />
|
||||||
<Route path='/vault' element={<WithRoute el={VaultPage}/>}/>
|
<Route path='/vault' element={<WithRoute el={VaultPage}/>}/>
|
||||||
<Route path='/changelog' element={<WithRoute el={SharePage} brew={this.props.brew} disableMeta={true} />} />
|
<Route path='/changelog' element={<WithRoute el={SharePage} brew={brew} disableMeta={true} />} />
|
||||||
<Route path='/faq' element={<WithRoute el={SharePage} brew={this.props.brew} disableMeta={true} />} />
|
<Route path='/faq' element={<WithRoute el={SharePage} brew={brew} disableMeta={true} />} />
|
||||||
<Route path='/migrate' element={<WithRoute el={SharePage} brew={this.props.brew} disableMeta={true} />} />
|
<Route path='/migrate' element={<WithRoute el={SharePage} brew={brew} disableMeta={true} />} />
|
||||||
<Route path='/account' element={<WithRoute el={AccountPage} brew={this.props.brew} accountDetails={this.props.brew.accountDetails} />} />
|
<Route path='/account' element={<WithRoute el={AccountPage} brew={brew} accountDetails={brew.accountDetails} />} />
|
||||||
<Route path='/legacy' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
|
<Route path='/legacy' element={<WithRoute el={HomePage} brew={brew} />} />
|
||||||
<Route path='/error' element={<WithRoute el={ErrorPage} brew={this.props.brew} />} />
|
<Route path='/error' element={<WithRoute el={ErrorPage} brew={brew} />} />
|
||||||
<Route path='/' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
|
<Route path='/' element={<WithRoute el={HomePage} brew={brew} />} />
|
||||||
<Route path='/*' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
|
<Route path='/*' element={<WithRoute el={HomePage} brew={brew} />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Homebrew;
|
module.exports = Homebrew;
|
||||||
37
client/homebrew/pages/basePages/editPage/editPage.jsx
Normal file
37
client/homebrew/pages/basePages/editPage/editPage.jsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
require('./editPage.less');
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
|
const Navbar = require('../../../navbar/navbar.jsx');
|
||||||
|
const NewBrewItem = require('../../../navbar/newbrew.navitem.jsx');
|
||||||
|
const HelpNavItem = require('../../../navbar/help.navitem.jsx');
|
||||||
|
const PrintNavItem = require('../../../navbar/print.navitem.jsx');
|
||||||
|
const ErrorNavItem = require('../../../navbar/error-navitem.jsx');
|
||||||
|
const AccountNavItem = require('../../../navbar/account.navitem.jsx');
|
||||||
|
const RecentNavItem = require('../../../navbar/recent.navitem.jsx').both;
|
||||||
|
const VaultNavItem = require('../../../navbar/vault.navitem.jsx');
|
||||||
|
|
||||||
|
const BaseEditPage = (props)=>{
|
||||||
|
return (
|
||||||
|
<div className={`sitePage ${props.className || ''}`}>
|
||||||
|
<Navbar>
|
||||||
|
<Nav.section>
|
||||||
|
<Nav.item className='brewTitle'>{props.brew.title}</Nav.item>
|
||||||
|
</Nav.section>
|
||||||
|
<Nav.section>
|
||||||
|
{props.navButtons}
|
||||||
|
<PrintNavItem />
|
||||||
|
<NewBrewItem />
|
||||||
|
<HelpNavItem />
|
||||||
|
<VaultNavItem />
|
||||||
|
<RecentNavItem brew={props.brew} storageKey={props.recentStorageKey} />
|
||||||
|
<AccountNavItem />
|
||||||
|
</Nav.section>
|
||||||
|
</Navbar>
|
||||||
|
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = BaseEditPage;
|
||||||
@@ -11,17 +11,11 @@ import request from '../../utils/request-middleware.js';
|
|||||||
const { Meta } = require('vitreum/headtags');
|
const { Meta } = require('vitreum/headtags');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
const Navbar = require('../../navbar/navbar.jsx');
|
|
||||||
|
|
||||||
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
|
|
||||||
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
|
||||||
const PrintNavItem = require('../../navbar/print.navitem.jsx');
|
|
||||||
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
||||||
const Account = require('../../navbar/account.navitem.jsx');
|
|
||||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
|
||||||
const VaultNavItem = require('../../navbar/vault.navitem.jsx');
|
|
||||||
|
|
||||||
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
|
const BaseEditPage = require('../basePages/editPage/editPage.jsx');
|
||||||
|
const SplitPane = require('client/components/splitPane/splitPane.jsx');
|
||||||
const Editor = require('../../editor/editor.jsx');
|
const Editor = require('../../editor/editor.jsx');
|
||||||
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
||||||
|
|
||||||
@@ -266,7 +260,7 @@ const EditPage = createClass({
|
|||||||
brew.text = brew.text.normalize('NFC');
|
brew.text = brew.text.normalize('NFC');
|
||||||
this.savedBrew.text = this.savedBrew.text.normalize('NFC');
|
this.savedBrew.text = this.savedBrew.text.normalize('NFC');
|
||||||
brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1;
|
brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1;
|
||||||
brew.patches = stringifyPatches(makePatches(this.savedBrew.text, brew.text));
|
brew.patches = stringifyPatches(makePatches(encodeURI(this.savedBrew.text), encodeURI(brew.text)));
|
||||||
brew.hash = await md5(this.savedBrew.text);
|
brew.hash = await md5(this.savedBrew.text);
|
||||||
//brew.text = undefined; - Temporary parallel path
|
//brew.text = undefined; - Temporary parallel path
|
||||||
brew.textBin = undefined;
|
brew.textBin = undefined;
|
||||||
@@ -441,11 +435,7 @@ const EditPage = createClass({
|
|||||||
renderNavbar : function(){
|
renderNavbar : function(){
|
||||||
const shareLink = this.processShareId();
|
const shareLink = this.processShareId();
|
||||||
|
|
||||||
return <Navbar>
|
return <>
|
||||||
<Nav.section>
|
|
||||||
<Nav.item className='brewTitle'>{this.state.brew.title}</Nav.item>
|
|
||||||
</Nav.section>
|
|
||||||
|
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
{this.renderGoogleDriveIcon()}
|
{this.renderGoogleDriveIcon()}
|
||||||
{this.state.error ?
|
{this.state.error ?
|
||||||
@@ -455,8 +445,6 @@ const EditPage = createClass({
|
|||||||
{this.renderAutoSaveButton()}
|
{this.renderAutoSaveButton()}
|
||||||
</Nav.dropdown>
|
</Nav.dropdown>
|
||||||
}
|
}
|
||||||
<NewBrew />
|
|
||||||
<HelpNavItem/>
|
|
||||||
<Nav.dropdown>
|
<Nav.dropdown>
|
||||||
<Nav.item color='teal' icon='fas fa-share-alt'>
|
<Nav.item color='teal' icon='fas fa-share-alt'>
|
||||||
share
|
share
|
||||||
@@ -471,20 +459,19 @@ const EditPage = createClass({
|
|||||||
post to reddit
|
post to reddit
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
</Nav.dropdown>
|
</Nav.dropdown>
|
||||||
<PrintNavItem />
|
|
||||||
<VaultNavItem />
|
|
||||||
<RecentNavItem brew={this.state.brew} storageKey='edit' />
|
|
||||||
<Account />
|
|
||||||
</Nav.section>
|
</Nav.section>
|
||||||
|
</>;
|
||||||
</Navbar>;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
return <div className='editPage sitePage'>
|
return <BaseEditPage
|
||||||
|
className="editPage"
|
||||||
|
errorState={this.state.error}
|
||||||
|
parent={this}
|
||||||
|
brew={this.state.brew}
|
||||||
|
navButtons={this.renderNavbar()}
|
||||||
|
recentStorageKey='edit'>
|
||||||
<Meta name='robots' content='noindex, nofollow' />
|
<Meta name='robots' content='noindex, nofollow' />
|
||||||
{this.renderNavbar()}
|
|
||||||
|
|
||||||
{this.props.brew.lock && <LockNotification shareId={this.props.brew.shareId} message={this.props.brew.lock.editMessage} reviewRequested={this.props.brew.lock.reviewRequested} />}
|
{this.props.brew.lock && <LockNotification shareId={this.props.brew.shareId} message={this.props.brew.lock.editMessage} reviewRequested={this.props.brew.lock.reviewRequested} />}
|
||||||
<div className='content'>
|
<div className='content'>
|
||||||
<SplitPane onDragFinish={this.handleSplitMove}>
|
<SplitPane onDragFinish={this.handleSplitMove}>
|
||||||
@@ -522,7 +509,7 @@ const EditPage = createClass({
|
|||||||
/>
|
/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</BaseEditPage>;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,16 +6,11 @@ import request from '../../utils/request-middleware.js';
|
|||||||
const { Meta } = require('vitreum/headtags');
|
const { Meta } = require('vitreum/headtags');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
const Navbar = require('../../navbar/navbar.jsx');
|
|
||||||
const NewBrewItem = require('../../navbar/newbrew.navitem.jsx');
|
|
||||||
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
|
||||||
const VaultNavItem = require('../../navbar/vault.navitem.jsx');
|
|
||||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
|
||||||
const AccountNavItem = require('../../navbar/account.navitem.jsx');
|
|
||||||
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
||||||
const { fetchThemeBundle } = require('../../../../shared/helpers.js');
|
const { fetchThemeBundle } = require('../../../../shared/helpers.js');
|
||||||
|
|
||||||
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
|
const BaseEditPage = require('../basePages/editPage/editPage.jsx');
|
||||||
|
const SplitPane = require('client/components/splitPane/splitPane.jsx');
|
||||||
const Editor = require('../../editor/editor.jsx');
|
const Editor = require('../../editor/editor.jsx');
|
||||||
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
||||||
|
|
||||||
@@ -25,13 +20,13 @@ const HomePage = createClass({
|
|||||||
displayName : 'HomePage',
|
displayName : 'HomePage',
|
||||||
getDefaultProps : function() {
|
getDefaultProps : function() {
|
||||||
return {
|
return {
|
||||||
brew : DEFAULT_BREW,
|
brew : DEFAULT_BREW
|
||||||
ver : '0.0.0'
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
return {
|
return {
|
||||||
brew : this.props.brew,
|
brew : this.props.brew,
|
||||||
|
isSaving : false,
|
||||||
welcomeText : this.props.brew.text,
|
welcomeText : this.props.brew.text,
|
||||||
error : undefined,
|
error : undefined,
|
||||||
currentEditorViewPageNum : 1,
|
currentEditorViewPageNum : 1,
|
||||||
@@ -47,7 +42,11 @@ const HomePage = createClass({
|
|||||||
fetchThemeBundle(this, this.props.brew.renderer, this.props.brew.theme);
|
fetchThemeBundle(this, this.props.brew.renderer, this.props.brew.theme);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSave : function(){
|
save : function(){
|
||||||
|
this.setState({
|
||||||
|
isSaving : true
|
||||||
|
});
|
||||||
|
|
||||||
request.post('/api')
|
request.post('/api')
|
||||||
.send(this.state.brew)
|
.send(this.state.brew)
|
||||||
.end((err, res)=>{
|
.end((err, res)=>{
|
||||||
@@ -57,6 +56,9 @@ const HomePage = createClass({
|
|||||||
}
|
}
|
||||||
const brew = res.body;
|
const brew = res.body;
|
||||||
window.location = `/edit/${brew.editId}`;
|
window.location = `/edit/${brew.editId}`;
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
this.setState({ isSaving: false, error: err });
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSplitMove : function(){
|
handleSplitMove : function(){
|
||||||
@@ -80,26 +82,38 @@ const HomePage = createClass({
|
|||||||
brew : { ...prevState.brew, text: text },
|
brew : { ...prevState.brew, text: text },
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderSaveButton : function(){
|
||||||
|
if(this.state.isSaving){
|
||||||
|
return <Nav.item icon='fas fa-spinner fa-spin' className='save'>
|
||||||
|
save...
|
||||||
|
</Nav.item>;
|
||||||
|
} else {
|
||||||
|
return <Nav.item icon='fas fa-save' className='save' onClick={this.save}>
|
||||||
|
save
|
||||||
|
</Nav.item>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
renderNavbar : function(){
|
renderNavbar : function(){
|
||||||
return <Navbar ver={this.props.ver}>
|
return <>
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
{this.state.error ?
|
{this.state.error ?
|
||||||
<ErrorNavItem error={this.state.error} parent={this}></ErrorNavItem> :
|
<ErrorNavItem error={this.state.error} parent={this}></ErrorNavItem> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
<NewBrewItem />
|
|
||||||
<HelpNavItem />
|
|
||||||
<VaultNavItem />
|
|
||||||
<RecentNavItem />
|
|
||||||
<AccountNavItem />
|
|
||||||
</Nav.section>
|
</Nav.section>
|
||||||
</Navbar>;
|
</>;
|
||||||
},
|
},
|
||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
return <div className='homePage sitePage'>
|
return <BaseEditPage
|
||||||
|
className="homePage"
|
||||||
|
errorState={this.state.error}
|
||||||
|
parent={this}
|
||||||
|
brew={this.state.brew}
|
||||||
|
navButtons={this.renderNavbar()}>
|
||||||
<Meta name='google-site-verification' content='NwnAQSSJZzAT7N-p5MY6ydQ7Njm67dtbu73ZSyE5Fy4' />
|
<Meta name='google-site-verification' content='NwnAQSSJZzAT7N-p5MY6ydQ7Njm67dtbu73ZSyE5Fy4' />
|
||||||
{this.renderNavbar()}
|
|
||||||
<div className='content'>
|
<div className='content'>
|
||||||
<SplitPane onDragFinish={this.handleSplitMove}>
|
<SplitPane onDragFinish={this.handleSplitMove}>
|
||||||
<Editor
|
<Editor
|
||||||
@@ -127,14 +141,14 @@ const HomePage = createClass({
|
|||||||
/>
|
/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
<div className={cx('floatingSaveButton', { show: this.state.welcomeText != this.state.brew.text })} onClick={this.handleSave}>
|
<div className={cx('floatingSaveButton', { show: this.state.welcomeText != this.state.brew.text })} onClick={this.save}>
|
||||||
Save current <i className='fas fa-save' />
|
Save current <i className='fas fa-save' />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href='/new' className='floatingNewButton'>
|
<a href='/new' className='floatingNewButton'>
|
||||||
Create your own <i className='fas fa-magic' />
|
Create your own <i className='fas fa-magic' />
|
||||||
</a>
|
</a>
|
||||||
</div>;
|
</BaseEditPage>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,10 @@ import request from '../../utils/request-middleware.js';
|
|||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'naturalcrit/markdown.js';
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
const PrintNavItem = require('../../navbar/print.navitem.jsx');
|
|
||||||
const Navbar = require('../../navbar/navbar.jsx');
|
|
||||||
const AccountNavItem = require('../../navbar/account.navitem.jsx');
|
|
||||||
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
||||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
|
||||||
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
|
||||||
|
|
||||||
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
|
const BaseEditPage = require('../basePages/editPage/editPage.jsx');
|
||||||
|
const SplitPane = require('client/components/splitPane/splitPane.jsx');
|
||||||
const Editor = require('../../editor/editor.jsx');
|
const Editor = require('../../editor/editor.jsx');
|
||||||
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
||||||
|
|
||||||
@@ -212,28 +208,23 @@ const NewPage = createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
renderNavbar : function(){
|
renderNavbar : function(){
|
||||||
return <Navbar>
|
return <>
|
||||||
|
|
||||||
<Nav.section>
|
|
||||||
<Nav.item className='brewTitle'>{this.state.brew.title}</Nav.item>
|
|
||||||
</Nav.section>
|
|
||||||
|
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
{this.state.error ?
|
{this.state.error ?
|
||||||
<ErrorNavItem error={this.state.error} parent={this}></ErrorNavItem> :
|
<ErrorNavItem error={this.state.error} parent={this}></ErrorNavItem> :
|
||||||
this.renderSaveButton()
|
this.renderSaveButton()
|
||||||
}
|
}
|
||||||
<PrintNavItem />
|
|
||||||
<HelpNavItem />
|
|
||||||
<RecentNavItem />
|
|
||||||
<AccountNavItem />
|
|
||||||
</Nav.section>
|
</Nav.section>
|
||||||
</Navbar>;
|
</>;
|
||||||
},
|
},
|
||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
return <div className='newPage sitePage'>
|
return <BaseEditPage
|
||||||
{this.renderNavbar()}
|
className="newPage"
|
||||||
|
errorState={this.state.error}
|
||||||
|
parent={this}
|
||||||
|
brew={this.state.brew}
|
||||||
|
navButtons={this.renderNavbar()}>
|
||||||
<div className='content'>
|
<div className='content'>
|
||||||
<SplitPane onDragFinish={this.handleSplitMove}>
|
<SplitPane onDragFinish={this.handleSplitMove}>
|
||||||
<Editor
|
<Editor
|
||||||
@@ -268,7 +259,7 @@ const NewPage = createClass({
|
|||||||
/>
|
/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</BaseEditPage>;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const Account = require('../../navbar/account.navitem.jsx');
|
|||||||
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
|
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
|
||||||
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
||||||
const BrewItem = require('../basePages/listPage/brewItem/brewItem.jsx');
|
const BrewItem = require('../basePages/listPage/brewItem/brewItem.jsx');
|
||||||
const SplitPane = require('../../../../shared/naturalcrit/splitPane/splitPane.jsx');
|
const SplitPane = require('client/components/splitPane/splitPane.jsx');
|
||||||
const ErrorIndex = require('../errorPage/errors/errorIndex.js');
|
const ErrorIndex = require('../errorPage/errors/errorIndex.js');
|
||||||
|
|
||||||
import request from '../../utils/request-middleware.js';
|
import request from '../../utils/request-middleware.js';
|
||||||
|
|||||||
1249
package-lock.json
generated
1249
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -93,7 +93,7 @@
|
|||||||
"@sanity/diff-match-patch": "^3.2.0",
|
"@sanity/diff-match-patch": "^3.2.0",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"codemirror": "^6.0.2",
|
"codemirror": "^5.65.6",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"core-js": "^3.44.0",
|
"core-js": "^3.44.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
@@ -142,13 +142,13 @@
|
|||||||
"eslint-plugin-jest": "^29.0.1",
|
"eslint-plugin-jest": "^29.0.1",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"globals": "^16.3.0",
|
"globals": "^16.3.0",
|
||||||
"jest": "^30.0.4",
|
"jest": "^30.0.5",
|
||||||
"jest-expect-message": "^1.1.3",
|
"jest-expect-message": "^1.1.3",
|
||||||
"jsdom-global": "^3.0.2",
|
"jsdom-global": "^3.0.2",
|
||||||
"postcss-less": "^6.0.0",
|
"postcss-less": "^6.0.0",
|
||||||
"stylelint": "^16.21.1",
|
"stylelint": "^16.22.0",
|
||||||
"stylelint-config-recess-order": "^7.1.0",
|
"stylelint-config-recess-order": "^7.1.0",
|
||||||
"stylelint-config-recommended": "^16.0.0",
|
"stylelint-config-recommended": "^16.0.0",
|
||||||
"supertest": "^7.1.3"
|
"supertest": "^7.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -383,6 +383,7 @@ app.get('/edit/:id', asyncHandler(getBrew('edit')), asyncHandler(async(req, res,
|
|||||||
title : req.brew.title || 'Untitled Brew',
|
title : req.brew.title || 'Untitled Brew',
|
||||||
description : req.brew.description || 'No description.',
|
description : req.brew.description || 'No description.',
|
||||||
image : req.brew.thumbnail || defaultMetaTags.image,
|
image : req.brew.thumbnail || defaultMetaTags.image,
|
||||||
|
locale : req.brew.lang,
|
||||||
type : 'article'
|
type : 'article'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -52,13 +52,13 @@ const api = {
|
|||||||
// ID Validation Checks
|
// ID Validation Checks
|
||||||
// Homebrewery ID
|
// Homebrewery ID
|
||||||
// Typically 12 characters, but the DB shows a range of 7 to 14 characters
|
// Typically 12 characters, but the DB shows a range of 7 to 14 characters
|
||||||
if(!id.match(/^[A-Za-z0-9_-]{7,14}$/)){
|
if(!id.match(/^[a-zA-Z0-9-_]{7,14}$/)){
|
||||||
throw { name: 'ID Error', message: 'Invalid ID', status: 404, HBErrorCode: '11', brewId: id };
|
throw { name: 'ID Error', message: 'Invalid ID', status: 404, HBErrorCode: '11', brewId: id };
|
||||||
}
|
}
|
||||||
// Google ID
|
// Google ID
|
||||||
// Typically 33 characters, old format is 44 - always starts with a 1
|
// Typically 33 characters, old format is 44 - always starts with a 1
|
||||||
// Managed by Google, may change outside of our control, so any length between 33 and 44 is acceptable
|
// Managed by Google, may change outside of our control, so any length between 33 and 44 is acceptable
|
||||||
if(googleId && !googleId.match(/^1(?:[A-Za-z0-9+\/]{32,43})$/)){
|
if(googleId && !googleId.match(/^1(?:[a-zA-Z0-9-_]{32,43})$/)){
|
||||||
throw { name: 'Google ID Error', message: 'Invalid ID', status: 404, HBErrorCode: '12', brewId: id };
|
throw { name: 'Google ID Error', message: 'Invalid ID', status: 404, HBErrorCode: '12', brewId: id };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,14 +375,14 @@ const api = {
|
|||||||
try {
|
try {
|
||||||
const patches = parsePatch(brewFromClient.patches);
|
const patches = parsePatch(brewFromClient.patches);
|
||||||
// Patch to a throwaway variable while parallelizing - we're more concerned with error/no error.
|
// Patch to a throwaway variable while parallelizing - we're more concerned with error/no error.
|
||||||
const patchedResult = applyPatches(patches, brewFromServer.text, { allowExceedingIndices: true })[0];
|
const patchedResult = decodeURI(applyPatches(patches, encodeURI(brewFromServer.text))[0]);
|
||||||
if(patchedResult != brewFromClient.text)
|
if(patchedResult != brewFromClient.text)
|
||||||
throw("Patches did not apply cleanly, text mismatch detected");
|
throw("Patches did not apply cleanly, text mismatch detected");
|
||||||
// brew.text = applyPatches(patches, brewFromServer.text)[0];
|
// brew.text = applyPatches(patches, brewFromServer.text)[0];
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
//debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`);
|
//debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`);
|
||||||
console.error('Failed to apply patches:', {
|
console.error('Failed to apply patches:', {
|
||||||
patches : brewFromClient.patches,
|
//patches : brewFromClient.patches,
|
||||||
brewId : brewFromClient.editId || 'unknown',
|
brewId : brewFromClient.editId || 'unknown',
|
||||||
error : err
|
error : err
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ const mustacheSpans = {
|
|||||||
start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const completeSpan = /^{{[^\n]*}}/; // Regex for the complete token
|
const completeSpan = /^{{[^\n]*}}/; // Regex for the complete token
|
||||||
const inlineRegex = /{{(?=((?:[:=](?:"['\w,\-()#%=?. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1 *|}}/g;
|
const inlineRegex = /{{(?=((?:[:=](?:"['\w,\-+*/()#%=?. ]*"|[\w\-+*/()#%.]*)|[^"=':{}\s]*)*))\1 *|}}/g;
|
||||||
const match = completeSpan.exec(src);
|
const match = completeSpan.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
//Find closing delimiter
|
//Find closing delimiter
|
||||||
@@ -242,7 +242,7 @@ const mustacheDivs = {
|
|||||||
start(src) { return src.match(/\n *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/\n *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const completeBlock = /^ *{{[^\n}]* *\n.*\n *}}/s; // Regex for the complete token
|
const completeBlock = /^ *{{[^\n}]* *\n.*\n *}}/s; // Regex for the complete token
|
||||||
const blockRegex = /^ *{{(?=((?:[:=](?:"['\w,\-()#%=?. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1 *$|^ *}}$/gm;
|
const blockRegex = /^ *{{(?=((?:[:=](?:"['\w,\-+*/()#%=?. ]*"|[\w\-+*/()#%.]*)|[^"=':{}\s]*)*))\1 *$|^ *}}$/gm;
|
||||||
const match = completeBlock.exec(src);
|
const match = completeBlock.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
//Find closing delimiter
|
//Find closing delimiter
|
||||||
@@ -297,7 +297,7 @@ const mustacheInjectInline = {
|
|||||||
level : 'inline',
|
level : 'inline',
|
||||||
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-()#%=?. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1}/g;
|
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-+*/()#%=?. ]*"|[\w\-+*/()#%.]*)|[^"=':{}\s]*)*))\1}/g;
|
||||||
const match = inlineRegex.exec(src);
|
const match = inlineRegex.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
const lastToken = tokens[tokens.length - 1];
|
const lastToken = tokens[tokens.length - 1];
|
||||||
@@ -343,7 +343,7 @@ const mustacheInjectBlock = {
|
|||||||
level : 'block',
|
level : 'block',
|
||||||
start(src) { return src.match(/\n *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/\n *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-()#%=?. ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1}/ym;
|
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-+*/()#%=?. ]*"|[\w\-+*/()#%.]*)|[^"=':{}\s]*)*))\1}/ym;
|
||||||
const match = inlineRegex.exec(src);
|
const match = inlineRegex.exec(src);
|
||||||
if(match) {
|
if(match) {
|
||||||
const lastToken = tokens[tokens.length - 1];
|
const lastToken = tokens[tokens.length - 1];
|
||||||
|
|||||||
Reference in New Issue
Block a user