0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-31 10:52:42 +00:00

Merge branch 'master' into SwappableThemes-ReorganizeFolderStructure

This commit is contained in:
Trevor Buckner
2022-06-21 16:07:00 -04:00
23 changed files with 22868 additions and 22605 deletions

View File

@@ -194,7 +194,7 @@ const BrewRenderer = createClass({
</div>
: null}
<Frame initialContent={this.state.initialContent}
<Frame id='BrewRenderer' initialContent={this.state.initialContent}
style={{ width: '100%', height: '100%', visibility: this.state.visibility }}
contentDidMount={this.frameDidMount}>
<div className={'brewRenderer'}

View File

@@ -61,8 +61,14 @@ const Editor = createClass({
window.removeEventListener('resize', this.updateEditorSize);
},
componentDidUpdate : function() {
componentDidUpdate : function(prevProps, prevState, snapshot) {
this.highlightCustomMarkdown();
if(prevProps.moveBrew !== this.props.moveBrew) {
this.brewJump();
};
if(prevProps.moveSource !== this.props.moveSource) {
this.sourceJump();
};
},
updateEditorSize : function() {
@@ -90,15 +96,20 @@ const Editor = createClass({
},
handleViewChange : function(newView){
this.props.setMoveArrows(newView === 'text');
this.setState({
view : newView
}, this.updateEditorSize); //TODO: not sure if updateeditorsize needed
},
getCurrentPage : function(){
const lines = this.props.brew.text.split('\n').slice(0, this.cursorPosition.line + 1);
const lines = this.props.brew.text.split('\n').slice(0, this.refs.codeEditor.getCursorPosition().line + 1);
return _.reduce(lines, (r, line)=>{
if(line.indexOf('\\page') !== -1) r++;
if(
(this.props.renderer == 'legacy' && line.indexOf('\\page') !== -1)
||
(this.props.renderer == 'V3' && line.match(/^\\page$/))
) r++;
return r;
}, 1);
},
@@ -120,6 +131,7 @@ const Editor = createClass({
//reset custom line styles
codeMirror.removeLineClass(lineNumber, 'background', 'pageLine');
codeMirror.removeLineClass(lineNumber, 'text');
codeMirror.removeLineClass(lineNumber, 'wrap', 'sourceMoveFlash');
// Styling for \page breaks
if((this.props.renderer == 'legacy' && line.includes('\\page')) ||
@@ -174,9 +186,76 @@ const Editor = createClass({
}
},
brewJump : function(){
const currentPage = this.getCurrentPage();
window.location.hash = `p${currentPage}`;
brewJump : function(targetPage=this.getCurrentPage()){
if(!window) return;
// console.log(`Scroll to: p${targetPage}`);
const brewRenderer = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0];
const currentPos = brewRenderer.scrollTop;
const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top;
const interimPos = targetPos >= 0 ? -30 : 30;
const bounceDelay = 100;
const scrollDelay = 500;
if(!this.throttleBrewMove) {
this.throttleBrewMove = _.throttle((currentPos, interimPos, targetPos)=>{
brewRenderer.scrollTo({ top: currentPos + interimPos, behavior: 'smooth' });
setTimeout(()=>{
brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' });
}, bounceDelay);
}, scrollDelay, { leading: true, trailing: false });
};
this.throttleBrewMove(currentPos, interimPos, targetPos);
// const hashPage = (page != 1) ? `p${page}` : '';
// window.location.hash = hashPage;
},
sourceJump : function(targetLine=null){
if(this.isText()) {
if(targetLine == null) {
targetLine = 0;
const pageCollection = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('page');
const brewRendererHeight = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer').item(0).getBoundingClientRect().height;
let currentPage = 1;
for (const page of pageCollection) {
if(page.getBoundingClientRect().bottom > (brewRendererHeight / 2)) {
currentPage = parseInt(page.id.slice(1)) || 1;
break;
}
}
const textSplit = this.props.renderer == 'V3' ? /^\\page$/gm : /\\page/;
const textString = this.props.brew.text.split(textSplit).slice(0, currentPage-1).join(textSplit);
const textPosition = textString.length;
const lineCount = textString.match('\n') ? textString.slice(0, textPosition).split('\n').length : 0;
targetLine = lineCount - 1; //Scroll to `\page`, which is one line back.
let currentY = this.refs.codeEditor.codeMirror.getScrollInfo().top;
let targetY = this.refs.codeEditor.codeMirror.heightAtLine(targetLine, 'local', true);
//Scroll 1/10 of the way every 10ms until 1px off.
const incrementalScroll = setInterval(()=>{
currentY += (targetY - currentY) / 10;
this.refs.codeEditor.codeMirror.scrollTo(null, currentY);
// Update target: target height is not accurate until within +-10 lines of the visible window
if(Math.abs(targetY - currentY > 100))
targetY = this.refs.codeEditor.codeMirror.heightAtLine(targetLine, 'local', true);
// End when close enough
if(Math.abs(targetY - currentY) < 1) {
this.refs.codeEditor.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference
this.refs.codeEditor.setCursorPosition({ line: targetLine + 1, ch: 0 });
this.refs.codeEditor.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash');
clearInterval(incrementalScroll);
}
}, 10);
}
}
},
//Called when there are changes to the editor's dimensions

View File

@@ -1,8 +1,8 @@
require('./homebrew.less');
const React = require('react');
const createClass = require('create-react-class');
const { StaticRouter:Router, Switch, Route } = require('react-router-dom');
const queryString = require('query-string');
const { StaticRouter:Router } = require('react-router-dom/server');
const { Route, Routes, useParams, useSearchParams } = require('react-router-dom');
const HomePage = require('./pages/homePage/homePage.jsx');
const EditPage = require('./pages/editPage/editPage.jsx');
@@ -12,6 +12,19 @@ const NewPage = require('./pages/newPage/newPage.jsx');
//const ErrorPage = require('./pages/errorPage/errorPage.jsx');
const PrintPage = require('./pages/printPage/printPage.jsx');
const WithRoute = (props)=>{
const params = useParams();
const searchParams = useSearchParams();
const Element = props.el;
const allProps = {
...props,
...params,
...searchParams,
el : undefined
};
return <Element {...allProps} />;
};
const Homebrew = createClass({
displayName : 'Homebrewery',
getDefaultProps : function() {
@@ -43,25 +56,24 @@ const Homebrew = createClass({
},
render : function (){
return (
<Router location={this.props.url}>
<div className='homebrew'>
<Switch>
<Route path='/edit/:id' component={(routeProps)=><EditPage id={routeProps.match.params.id} brew={this.props.brew} />}/>
<Route path='/share/:id' component={(routeProps)=><SharePage id={routeProps.match.params.id} brew={this.props.brew} />}/>
<Route path='/new/:id' component={(routeProps)=><NewPage id={routeProps.match.params.id} brew={this.props.brew} />}/>
<Route path='/new' exact component={(routeProps)=><NewPage />}/>
<Route path='/user/:username' component={(routeProps)=><UserPage username={routeProps.match.params.username} brews={this.props.brews} query={queryString.parse(routeProps.location.search)}/>}/>
<Route path='/print/:id' component={(routeProps)=><PrintPage brew={this.props.brew} query={queryString.parse(routeProps.location.search)} />}/>
<Route path='/print' exact component={(routeProps)=><PrintPage query={queryString.parse(routeProps.location.search)} />}/>
<Route path='/changelog' exact component={()=><SharePage brew={this.props.brew} />}/>
<Route path='/faq' exact component={()=><SharePage brew={this.props.brew} />}/>
<Route path='/v3_preview' exact component={()=><HomePage brew={this.props.brew} />}/>
<Route path='/' component={()=><HomePage brew={this.props.brew} />}/>
</Switch>
</div>
</Router>
);
return <Router location={this.props.url}>
<div className='homebrew'>
<Routes>
<Route path='/edit/:id' element={<WithRoute el={EditPage} brew={this.props.brew} />} />
<Route path='/share/:id' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/new/:id' element={<WithRoute el={NewPage} brew={this.props.brew} />} />
<Route path='/new' element={<WithRoute el={NewPage}/>} />
<Route path='/user/:username' element={<WithRoute el={UserPage} brews={this.props.brews} />} />
<Route path='/print/:id' element={<WithRoute el={PrintPage} brew={this.props.brew} />} />
<Route path='/print' element={<WithRoute el={PrintPage} />} />
<Route path='/changelog' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/faq' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/v3_preview' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
<Route path='/' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
<Route path='/*' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
</Routes>
</div>
</Router>;
}
});

View File

@@ -16,8 +16,8 @@ const BrewItem = createClass({
brew : {
title : '',
description : '',
authors : []
authors : [],
stubbed : true
}
};
},
@@ -50,7 +50,7 @@ const BrewItem = createClass({
if(!this.props.brew.editId) return;
let editLink = this.props.brew.editId;
if(this.props.brew.googleId) {
if(this.props.brew.googleId && !this.props.brew.stubbed) {
editLink = this.props.brew.googleId + editLink;
}
@@ -63,7 +63,7 @@ const BrewItem = createClass({
if(!this.props.brew.shareId) return;
let shareLink = this.props.brew.shareId;
if(this.props.brew.googleId) {
if(this.props.brew.googleId && !this.props.brew.stubbed) {
shareLink = this.props.brew.googleId + shareLink;
}
@@ -76,7 +76,7 @@ const BrewItem = createClass({
if(!this.props.brew.shareId) return;
let shareLink = this.props.brew.shareId;
if(this.props.brew.googleId) {
if(this.props.brew.googleId && !this.props.brew.stubbed) {
shareLink = this.props.brew.googleId + shareLink;
}
@@ -86,7 +86,7 @@ const BrewItem = createClass({
},
renderGoogleDriveIcon : function(){
if(!this.props.brew.gDrive) return;
if(!this.props.brew.googleId) return;
return <span>
<img className='googleDriveIcon' src={googleDriveIcon} alt='googleDriveIcon' />
@@ -104,8 +104,8 @@ const BrewItem = createClass({
</div>
<hr />
<div className='info'>
<span title={`Authors:\n${brew.authors.join('\n')}`}>
<i className='fas fa-user'/> {brew.authors.join(', ')}
<span title={`Authors:\n${brew.authors?.join('\n')}`}>
<i className='fas fa-user'/> {brew.authors?.join(', ')}
</span>
<br />
<span title={`Last viewed: ${moment(brew.lastViewed).local().format(dateFormatString)}`}>

View File

@@ -200,7 +200,7 @@ const EditPage = createClass({
const brew = this.state.brew;
brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1;
const params = `${transfer ? `?transfer${this.state.saveGoogle ? 'To' : 'From'}Google=true` : ''}`;
const params = `${transfer ? `?${this.state.saveGoogle ? 'saveToGoogle' : 'removeFromGoogle'}=true` : ''}`;
const res = await request
.put(`/api/update/${brew.editId}${params}`)
.send(brew)
@@ -210,9 +210,7 @@ const EditPage = createClass({
});
this.savedBrew = res.body;
if(transfer) {
history.replaceState(null, null, `/edit/${this.savedBrew.googleId ?? ''}${this.savedBrew.editId}`);
}
history.replaceState(null, null, `/edit/${this.savedBrew.editId}`);
this.setState((prevState)=>({
brew : _.merge({}, prevState.brew, {
@@ -340,7 +338,7 @@ const EditPage = createClass({
},
processShareId : function() {
return this.state.brew.googleId ?
return this.state.brew.googleId && !this.state.brew.stubbed ?
this.state.brew.googleId + this.state.brew.shareId :
this.state.brew.shareId;
},

View File

@@ -167,7 +167,7 @@ const NewPage = createClass({
brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1;
const res = await request
.post(`/api${this.state.saveGoogle ? '?transferToGoogle=true' : ''}`)
.post(`/api${this.state.saveGoogle ? '?saveToGoogle=true' : ''}`)
.send(brew)
.catch((err)=>{
console.log(err);
@@ -179,7 +179,7 @@ const NewPage = createClass({
localStorage.removeItem(BREWKEY);
localStorage.removeItem(STYLEKEY);
localStorage.removeItem(METAKEY);
window.location = `/edit/${brew.googleId ?? ''}${brew.editId}`;
window.location = `/edit/${brew.editId}`;
},
renderSaveButton : function(){

View File

@@ -49,7 +49,7 @@ const SharePage = createClass({
},
processShareId : function() {
return this.props.brew.googleId ?
return this.props.brew.googleId && !this.props.brew.stubbed ?
this.props.brew.googleId + this.props.brew.shareId :
this.props.brew.shareId;
},

View File

@@ -8,7 +8,7 @@ module.exports = async(name, title = '', props = {})=>{
<link href="//use.fontawesome.com/releases/v5.15.1/css/all.css" rel="stylesheet" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
<link href=${`/${name}/bundle.css`} rel='stylesheet' />
<link rel="icon" href="/assets/homebrew/favicon.ico" type="image/x-icon" />
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />
<meta property="og:title" content="${props.brew?.title || 'Homebrewery - Untitled Brew'}">
<meta property="og:url" content="${HOMEBREWERY_PUBLIC_URL}/${props.brew?.shareId ? `share/${props.brew.shareId}` : ''}">
<meta property="og:image" content="${props.brew?.thumbnail || `${HOMEBREWERY_PUBLIC_URL}/thumbnail.png`}">