mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-06 14:22:52 +00:00
Merge branch 'master' into pr/2622
This commit is contained in:
40
changelog.md
40
changelog.md
@@ -52,16 +52,38 @@ pre {
|
|||||||
font-family: 'Open Sans';
|
font-family: 'Open Sans';
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
padding-bottom: 1.5cm;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## changelog
|
## changelog
|
||||||
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
|
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
|
||||||
|
|
||||||
### v3.6.0
|
### Friday 23/01/2023 - v3.6.0
|
||||||
{{taskList
|
{{taskList
|
||||||
|
##### calculuschild
|
||||||
|
|
||||||
|
* [x] Fix Google Drive brews sometimes duplicating
|
||||||
|
|
||||||
|
Fixes issues [#2603](https://github.com/naturalcrit/homebrewery/issues/2603)
|
||||||
|
|
||||||
##### Jeddai
|
##### Jeddai
|
||||||
|
|
||||||
* [x] Add unit tests with full coverage for the Homebrewery API
|
* [x] Add unit tests with full coverage for the Homebrewery API
|
||||||
|
|
||||||
|
* [x] Add message to refresh the browser if the user is missing an update to the Homebrewery
|
||||||
|
|
||||||
|
Fixes issues [#2583](https://github.com/naturalcrit/homebrewery/issues/2583)
|
||||||
|
|
||||||
|
##### G-Ambatte
|
||||||
|
|
||||||
|
* [x] Auto-compile Themes CSS on development server
|
||||||
|
|
||||||
|
##### 5e-Cleric
|
||||||
|
|
||||||
|
* [x] Fix cloned brews inheriting the parent view count
|
||||||
}}
|
}}
|
||||||
|
|
||||||
### Friday 23/12/2022 - v3.5.0
|
### Friday 23/12/2022 - v3.5.0
|
||||||
@@ -79,22 +101,6 @@ Fixes issues [#1987](https://github.com/naturalcrit/homebrewery/issues/1987)
|
|||||||
|
|
||||||
\page
|
\page
|
||||||
|
|
||||||
### Monday 05/12/2022 - v3.4.1
|
|
||||||
{{taskList
|
|
||||||
|
|
||||||
##### G-Ambatte
|
|
||||||
|
|
||||||
* [x] Fix Account page incorrect last login time
|
|
||||||
|
|
||||||
Fixes issues [#2521](https://github.com/naturalcrit/homebrewery/issues/2521)
|
|
||||||
|
|
||||||
##### Gazook
|
|
||||||
|
|
||||||
* [x] Fix crashing on iOS and Safari browsers
|
|
||||||
|
|
||||||
Fixes issues [#2531](https://github.com/naturalcrit/homebrewery/issues/2531)
|
|
||||||
}}
|
|
||||||
|
|
||||||
### Saturday 10/12/2022 - v3.4.2
|
### Saturday 10/12/2022 - v3.4.2
|
||||||
{{taskList
|
{{taskList
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ const Editor = createClass({
|
|||||||
onTextChange : ()=>{},
|
onTextChange : ()=>{},
|
||||||
onStyleChange : ()=>{},
|
onStyleChange : ()=>{},
|
||||||
onMetaChange : ()=>{},
|
onMetaChange : ()=>{},
|
||||||
|
reportError : ()=>{},
|
||||||
|
|
||||||
renderer : 'legacy'
|
renderer : 'legacy'
|
||||||
};
|
};
|
||||||
@@ -291,7 +292,8 @@ const Editor = createClass({
|
|||||||
rerenderParent={this.rerenderParent} />
|
rerenderParent={this.rerenderParent} />
|
||||||
<MetadataEditor
|
<MetadataEditor
|
||||||
metadata={this.props.brew}
|
metadata={this.props.brew}
|
||||||
onChange={this.props.onMetaChange} />
|
onChange={this.props.onMetaChange}
|
||||||
|
reportError={this.props.reportError}/>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const React = require('react');
|
|||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
const request = require('superagent');
|
const request = require('../../utils/request-middleware.js');
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx');
|
const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx');
|
||||||
|
|
||||||
@@ -37,7 +37,8 @@ const MetadataEditor = createClass({
|
|||||||
renderer : 'legacy',
|
renderer : 'legacy',
|
||||||
theme : '5ePHB'
|
theme : '5ePHB'
|
||||||
},
|
},
|
||||||
onChange : ()=>{}
|
onChange : ()=>{},
|
||||||
|
reportError : ()=>{}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -121,8 +122,12 @@ const MetadataEditor = createClass({
|
|||||||
|
|
||||||
request.delete(`/api/${this.props.metadata.googleId ?? ''}${this.props.metadata.editId}`)
|
request.delete(`/api/${this.props.metadata.googleId ?? ''}${this.props.metadata.editId}`)
|
||||||
.send()
|
.send()
|
||||||
.end(function(err, res){
|
.end((err, res)=>{
|
||||||
window.location.href = '/';
|
if(err) {
|
||||||
|
this.props.reportError(err);
|
||||||
|
} else {
|
||||||
|
window.location.href = '/';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
85
client/homebrew/navbar/error-navitem.jsx
Normal file
85
client/homebrew/navbar/error-navitem.jsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
require('./error-navitem.less');
|
||||||
|
const React = require('react');
|
||||||
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
|
const createClass = require('create-react-class');
|
||||||
|
|
||||||
|
const ErrorNavItem = createClass({
|
||||||
|
getDefaultProps : function() {
|
||||||
|
return {
|
||||||
|
error : '',
|
||||||
|
parent : null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
render : function() {
|
||||||
|
const clearError = ()=>{
|
||||||
|
const state = {
|
||||||
|
error : null
|
||||||
|
};
|
||||||
|
if(this.props.parent.state.isSaving) {
|
||||||
|
state.isSaving = false;
|
||||||
|
}
|
||||||
|
this.props.parent.setState(state);
|
||||||
|
};
|
||||||
|
|
||||||
|
const error = this.props.error;
|
||||||
|
const response = error.response;
|
||||||
|
const status = response.status;
|
||||||
|
const message = response.body?.message;
|
||||||
|
let errMsg = '';
|
||||||
|
try {
|
||||||
|
errMsg += `${error.toString()}\n\n`;
|
||||||
|
errMsg += `\`\`\`\n${error.stack}\n`;
|
||||||
|
errMsg += `${JSON.stringify(response.error, null, ' ')}\n\`\`\``;
|
||||||
|
console.log(errMsg);
|
||||||
|
} catch (e){}
|
||||||
|
|
||||||
|
if(status === 409) {
|
||||||
|
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
|
Oops!
|
||||||
|
<div className='errorContainer' onClick={clearError}>
|
||||||
|
{message ?? 'Conflict: please refresh to get latest changes'}
|
||||||
|
</div>
|
||||||
|
</Nav.item>;
|
||||||
|
} else if(status === 412) {
|
||||||
|
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
|
Oops!
|
||||||
|
<div className='errorContainer' onClick={clearError}>
|
||||||
|
{message ?? 'Your client is out of date. Please save your changes elsewhere and refresh.'}
|
||||||
|
</div>
|
||||||
|
</Nav.item>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(response.req.url.match(/^\/api.*Google.*$/m)){
|
||||||
|
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
|
Oops!
|
||||||
|
<div className='errorContainer' onClick={clearError}>
|
||||||
|
Looks like your Google credentials have
|
||||||
|
expired! Visit our log in page to sign out
|
||||||
|
and sign back in with Google,
|
||||||
|
then try saving again!
|
||||||
|
<a target='_blank' rel='noopener noreferrer'
|
||||||
|
href={`https://www.naturalcrit.com/login?redirect=${window.location.href}`}>
|
||||||
|
<div className='confirm'>
|
||||||
|
Sign In
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div className='deny'>
|
||||||
|
Not Now
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Nav.item>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
||||||
|
Oops!
|
||||||
|
<div className='errorContainer'>
|
||||||
|
Looks like there was a problem saving. <br />
|
||||||
|
Report the issue <a target='_blank' rel='noopener noreferrer' href={`https://github.com/naturalcrit/homebrewery/issues/new?template=save_issue.yml&error-code=${encodeURIComponent(errMsg)}`}>
|
||||||
|
here
|
||||||
|
</a>.
|
||||||
|
</div>
|
||||||
|
</Nav.item>;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = ErrorNavItem;
|
||||||
77
client/homebrew/navbar/error-navitem.less
Normal file
77
client/homebrew/navbar/error-navitem.less
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
.navItem {
|
||||||
|
&.error {
|
||||||
|
position : relative;
|
||||||
|
background-color : @red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorContainer{
|
||||||
|
animation-name: glideDown;
|
||||||
|
animation-duration: 0.4s;
|
||||||
|
position : absolute;
|
||||||
|
top : 100%;
|
||||||
|
left : 50%;
|
||||||
|
z-index : 1000;
|
||||||
|
width : 140px;
|
||||||
|
padding : 3px;
|
||||||
|
color : white;
|
||||||
|
background-color : #333;
|
||||||
|
border : 3px solid #444;
|
||||||
|
border-radius : 5px;
|
||||||
|
transform : translate(-50% + 3px, 10px);
|
||||||
|
text-align : center;
|
||||||
|
font-size : 10px;
|
||||||
|
font-weight : 800;
|
||||||
|
text-transform : uppercase;
|
||||||
|
a{
|
||||||
|
color : @teal;
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
position: absolute;
|
||||||
|
border-left: 10px solid transparent;
|
||||||
|
border-right: 10px solid transparent;
|
||||||
|
border-top: 10px solid transparent;
|
||||||
|
border-bottom: 10px solid #444;
|
||||||
|
left: 53px;
|
||||||
|
top: -23px;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
position: absolute;
|
||||||
|
border-left: 10px solid transparent;
|
||||||
|
border-right: 10px solid transparent;
|
||||||
|
border-top: 10px solid transparent;
|
||||||
|
border-bottom: 10px solid #333;
|
||||||
|
left: 53px;
|
||||||
|
top: -19px;
|
||||||
|
}
|
||||||
|
.deny {
|
||||||
|
width : 48%;
|
||||||
|
margin : 1px;
|
||||||
|
padding : 5px;
|
||||||
|
background-color : #333;
|
||||||
|
display : inline-block;
|
||||||
|
border-left : 1px solid #666;
|
||||||
|
.animate(background-color);
|
||||||
|
&:hover{
|
||||||
|
background-color : red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.confirm {
|
||||||
|
width : 48%;
|
||||||
|
margin : 1px;
|
||||||
|
padding : 5px;
|
||||||
|
background-color : #333;
|
||||||
|
display : inline-block;
|
||||||
|
color : white;
|
||||||
|
.animate(background-color);
|
||||||
|
&:hover{
|
||||||
|
background-color : teal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ const createClass = require('create-react-class');
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const request = require('superagent');
|
const request = require('../../../../utils/request-middleware.js');
|
||||||
|
|
||||||
const googleDriveIcon = require('../../../../googleDrive.png');
|
const googleDriveIcon = require('../../../../googleDrive.png');
|
||||||
const dedent = require('dedent-tabs').default;
|
const dedent = require('dedent-tabs').default;
|
||||||
@@ -18,7 +18,8 @@ const BrewItem = createClass({
|
|||||||
description : '',
|
description : '',
|
||||||
authors : [],
|
authors : [],
|
||||||
stubbed : true
|
stubbed : true
|
||||||
}
|
},
|
||||||
|
reportError : ()=>{}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -33,8 +34,12 @@ const BrewItem = createClass({
|
|||||||
|
|
||||||
request.delete(`/api/${this.props.brew.googleId ?? ''}${this.props.brew.editId}`)
|
request.delete(`/api/${this.props.brew.googleId ?? ''}${this.props.brew.editId}`)
|
||||||
.send()
|
.send()
|
||||||
.end(function(err, res){
|
.end((err, res)=>{
|
||||||
location.reload();
|
if(err) {
|
||||||
|
this.props.reportError(err);
|
||||||
|
} else {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ const ListPage = createClass({
|
|||||||
brews : []
|
brews : []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
navItems : <></>
|
navItems : <></>,
|
||||||
|
reportError : null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
@@ -81,7 +82,7 @@ const ListPage = createClass({
|
|||||||
if(!brews || !brews.length) return <div className='noBrews'>No Brews.</div>;
|
if(!brews || !brews.length) return <div className='noBrews'>No Brews.</div>;
|
||||||
|
|
||||||
return _.map(brews, (brew, idx)=>{
|
return _.map(brews, (brew, idx)=>{
|
||||||
return <BrewItem brew={brew} key={idx}/>;
|
return <BrewItem brew={brew} key={idx} reportError={this.props.reportError}/>;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ require('./editPage.less');
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const request = require('superagent');
|
const request = require('../../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');
|
||||||
@@ -12,6 +12,7 @@ const Navbar = require('../../navbar/navbar.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 PrintLink = require('../../navbar/print.navitem.jsx');
|
const PrintLink = require('../../navbar/print.navitem.jsx');
|
||||||
|
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
||||||
const Account = require('../../navbar/account.navitem.jsx');
|
const Account = require('../../navbar/account.navitem.jsx');
|
||||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ const EditPage = createClass({
|
|||||||
alertLoginToTransfer : false,
|
alertLoginToTransfer : false,
|
||||||
saveGoogle : this.props.brew.googleId ? true : false,
|
saveGoogle : this.props.brew.googleId ? true : false,
|
||||||
confirmGoogleTransfer : false,
|
confirmGoogleTransfer : false,
|
||||||
errors : null,
|
error : null,
|
||||||
htmlErrors : Markdown.validate(this.props.brew.text),
|
htmlErrors : Markdown.validate(this.props.brew.text),
|
||||||
url : '',
|
url : '',
|
||||||
autoSave : true,
|
autoSave : true,
|
||||||
@@ -60,7 +61,6 @@ const EditPage = createClass({
|
|||||||
url : window.location.href
|
url : window.location.href
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy
|
this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy
|
||||||
|
|
||||||
this.setState({ autoSave: JSON.parse(localStorage.getItem('AUTOSAVE_ON')) ?? true }, ()=>{
|
this.setState({ autoSave: JSON.parse(localStorage.getItem('AUTOSAVE_ON')) ?? true }, ()=>{
|
||||||
@@ -157,7 +157,10 @@ const EditPage = createClass({
|
|||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
confirmGoogleTransfer : !prevState.confirmGoogleTransfer
|
confirmGoogleTransfer : !prevState.confirmGoogleTransfer
|
||||||
}));
|
}));
|
||||||
this.clearErrors();
|
this.setState({
|
||||||
|
error : null,
|
||||||
|
isSaving : false
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
closeAlerts : function(event){
|
closeAlerts : function(event){
|
||||||
@@ -173,24 +176,16 @@ const EditPage = createClass({
|
|||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
saveGoogle : !prevState.saveGoogle,
|
saveGoogle : !prevState.saveGoogle,
|
||||||
isSaving : false,
|
isSaving : false,
|
||||||
errors : null
|
error : null
|
||||||
}), ()=>this.save());
|
}), ()=>this.save());
|
||||||
},
|
},
|
||||||
|
|
||||||
clearErrors : function(){
|
|
||||||
this.setState({
|
|
||||||
errors : null,
|
|
||||||
isSaving : false
|
|
||||||
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
save : async function(){
|
save : async function(){
|
||||||
if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel();
|
if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel();
|
||||||
|
|
||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
isSaving : true,
|
isSaving : true,
|
||||||
errors : null,
|
error : null,
|
||||||
htmlErrors : Markdown.validate(prevState.brew.text)
|
htmlErrors : Markdown.validate(prevState.brew.text)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -205,8 +200,9 @@ const EditPage = createClass({
|
|||||||
.send(brew)
|
.send(brew)
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.log('Error Updating Local Brew');
|
console.log('Error Updating Local Brew');
|
||||||
this.setState({ errors: err });
|
this.setState({ error: err });
|
||||||
});
|
});
|
||||||
|
if(!res) return;
|
||||||
|
|
||||||
this.savedBrew = res.body;
|
this.savedBrew = res.body;
|
||||||
history.replaceState(null, null, `/edit/${this.savedBrew.editId}`);
|
history.replaceState(null, null, `/edit/${this.savedBrew.editId}`);
|
||||||
@@ -266,77 +262,6 @@ const EditPage = createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
renderSaveButton : function(){
|
renderSaveButton : function(){
|
||||||
if(this.state.errors){
|
|
||||||
let errMsg = '';
|
|
||||||
try {
|
|
||||||
errMsg += `${this.state.errors.toString()}\n\n`;
|
|
||||||
errMsg += `\`\`\`\n${this.state.errors.stack}\n`;
|
|
||||||
errMsg += `${JSON.stringify(this.state.errors.response.error, null, ' ')}\n\`\`\``;
|
|
||||||
console.log(errMsg);
|
|
||||||
} catch (e){}
|
|
||||||
|
|
||||||
// if(this.state.errors.status == '401'){
|
|
||||||
// return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
// Oops!
|
|
||||||
// <div className='errorContainer' onClick={this.clearErrors}>
|
|
||||||
// You must be signed in to a Google account
|
|
||||||
// to save this to<br />Google Drive!<br />
|
|
||||||
// <a target='_blank' rel='noopener noreferrer'
|
|
||||||
// href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
|
|
||||||
// <div className='confirm'>
|
|
||||||
// Sign In
|
|
||||||
// </div>
|
|
||||||
// </a>
|
|
||||||
// <div className='deny'>
|
|
||||||
// Not Now
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// </Nav.item>;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if(this.state.errors.response.req.url.match(/^\/api.*Google.*$/m)){
|
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
Oops!
|
|
||||||
<div className='errorContainer' onClick={this.clearErrors}>
|
|
||||||
Looks like your Google credentials have
|
|
||||||
expired! Visit our log in page to sign out
|
|
||||||
and sign back in with Google,
|
|
||||||
then try saving again!
|
|
||||||
<a target='_blank' rel='noopener noreferrer'
|
|
||||||
href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
|
|
||||||
<div className='confirm'>
|
|
||||||
Sign In
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<div className='deny'>
|
|
||||||
Not Now
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Nav.item>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.state.errors.response.error.status === 409) {
|
|
||||||
const message = this.state.errors.response.body?.message;
|
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
Oops!
|
|
||||||
<div className='errorContainer'>
|
|
||||||
{message ? message : 'Conflict: please refresh to get latest changes'}
|
|
||||||
</div>
|
|
||||||
</Nav.item>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
Oops!
|
|
||||||
<div className='errorContainer'>
|
|
||||||
Looks like there was a problem saving. <br />
|
|
||||||
Report the issue <a target='_blank' rel='noopener noreferrer'
|
|
||||||
href={`https://github.com/naturalcrit/homebrewery/issues/new?template=save_issue.yml&error-code=${encodeURIComponent(errMsg)}`}>
|
|
||||||
here
|
|
||||||
</a>.
|
|
||||||
</div>
|
|
||||||
</Nav.item>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.state.autoSaveWarning && this.hasChanges()){
|
if(this.state.autoSaveWarning && this.hasChanges()){
|
||||||
this.setAutosaveWarning();
|
this.setAutosaveWarning();
|
||||||
const elapsedTime = Math.round((new Date() - this.state.unsavedTime) / 1000 / 60);
|
const elapsedTime = Math.round((new Date() - this.state.unsavedTime) / 1000 / 60);
|
||||||
@@ -380,6 +305,12 @@ const EditPage = createClass({
|
|||||||
this.warningTimer;
|
this.warningTimer;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
errorReported : function(error) {
|
||||||
|
this.setState({
|
||||||
|
error
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
renderAutoSaveButton : function(){
|
renderAutoSaveButton : function(){
|
||||||
return <Nav.item onClick={this.handleAutoSave}>
|
return <Nav.item onClick={this.handleAutoSave}>
|
||||||
Autosave <i className={this.state.autoSave ? 'fas fa-power-off active' : 'fas fa-power-off'}></i>
|
Autosave <i className={this.state.autoSave ? 'fas fa-power-off active' : 'fas fa-power-off'}></i>
|
||||||
@@ -424,10 +355,13 @@ const EditPage = createClass({
|
|||||||
|
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
{this.renderGoogleDriveIcon()}
|
{this.renderGoogleDriveIcon()}
|
||||||
<Nav.dropdown className='save-menu'>
|
{this.state.error ?
|
||||||
{this.renderSaveButton()}
|
<ErrorNavItem error={this.state.error} parent={this}></ErrorNavItem> :
|
||||||
{this.renderAutoSaveButton()}
|
<Nav.dropdown className='save-menu'>
|
||||||
</Nav.dropdown>
|
{this.renderSaveButton()}
|
||||||
|
{this.renderAutoSaveButton()}
|
||||||
|
</Nav.dropdown>
|
||||||
|
}
|
||||||
<NewBrew />
|
<NewBrew />
|
||||||
<HelpNavItem/>
|
<HelpNavItem/>
|
||||||
<Nav.dropdown>
|
<Nav.dropdown>
|
||||||
@@ -465,6 +399,7 @@ const EditPage = createClass({
|
|||||||
onTextChange={this.handleTextChange}
|
onTextChange={this.handleTextChange}
|
||||||
onStyleChange={this.handleStyleChange}
|
onStyleChange={this.handleStyleChange}
|
||||||
onMetaChange={this.handleMetaChange}
|
onMetaChange={this.handleMetaChange}
|
||||||
|
reportError={this.errorReported}
|
||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} errors={this.state.htmlErrors} />
|
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} errors={this.state.htmlErrors} />
|
||||||
|
|||||||
@@ -13,10 +13,6 @@
|
|||||||
cursor : initial;
|
cursor : initial;
|
||||||
color : #666;
|
color : #666;
|
||||||
}
|
}
|
||||||
&.error{
|
|
||||||
position : relative;
|
|
||||||
background-color : @red;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.googleDriveStorage {
|
.googleDriveStorage {
|
||||||
position : relative;
|
position : relative;
|
||||||
@@ -26,74 +22,4 @@
|
|||||||
padding : 0px;
|
padding : 0px;
|
||||||
margin : -5px;
|
margin : -5px;
|
||||||
}
|
}
|
||||||
.errorContainer{
|
|
||||||
animation-name: glideDown;
|
|
||||||
animation-duration: 0.4s;
|
|
||||||
position : absolute;
|
|
||||||
top : 100%;
|
|
||||||
left : 50%;
|
|
||||||
z-index : 500;
|
|
||||||
width : 140px;
|
|
||||||
padding : 3px;
|
|
||||||
color : white;
|
|
||||||
background-color : #333;
|
|
||||||
border : 3px solid #444;
|
|
||||||
border-radius : 5px;
|
|
||||||
transform : translate(-50% + 3px, 10px);
|
|
||||||
text-align : center;
|
|
||||||
font-size : 10px;
|
|
||||||
font-weight : 800;
|
|
||||||
text-transform : uppercase;
|
|
||||||
a{
|
|
||||||
color : @teal;
|
|
||||||
}
|
|
||||||
&:before {
|
|
||||||
content: "";
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
position: absolute;
|
|
||||||
border-left: 10px solid transparent;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
border-top: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid #444;
|
|
||||||
left: 53px;
|
|
||||||
top: -23px;
|
|
||||||
}
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
position: absolute;
|
|
||||||
border-left: 10px solid transparent;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
border-top: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid #333;
|
|
||||||
left: 53px;
|
|
||||||
top: -19px;
|
|
||||||
}
|
|
||||||
.deny {
|
|
||||||
width : 48%;
|
|
||||||
margin : 1px;
|
|
||||||
padding : 5px;
|
|
||||||
background-color : #333;
|
|
||||||
display : inline-block;
|
|
||||||
border-left : 1px solid #666;
|
|
||||||
.animate(background-color);
|
|
||||||
&:hover{
|
|
||||||
background-color : red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.confirm {
|
|
||||||
width : 48%;
|
|
||||||
margin : 1px;
|
|
||||||
padding : 5px;
|
|
||||||
background-color : #333;
|
|
||||||
display : inline-block;
|
|
||||||
color : white;
|
|
||||||
.animate(background-color);
|
|
||||||
&:hover{
|
|
||||||
background-color : teal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const React = require('react');
|
|||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
const request = require('superagent');
|
const request = require('../../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');
|
||||||
@@ -12,6 +12,7 @@ const NewBrewItem = require('../../navbar/newbrew.navitem.jsx');
|
|||||||
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
||||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
||||||
const AccountNavItem = require('../../navbar/account.navitem.jsx');
|
const AccountNavItem = require('../../navbar/account.navitem.jsx');
|
||||||
|
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
||||||
|
|
||||||
|
|
||||||
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
|
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
|
||||||
@@ -31,14 +32,18 @@ const HomePage = createClass({
|
|||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
return {
|
return {
|
||||||
brew : this.props.brew,
|
brew : this.props.brew,
|
||||||
welcomeText : this.props.brew.text
|
welcomeText : this.props.brew.text,
|
||||||
|
error : undefined
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleSave : function(){
|
handleSave : function(){
|
||||||
request.post('/api')
|
request.post('/api')
|
||||||
.send(this.state.brew)
|
.send(this.state.brew)
|
||||||
.end((err, res)=>{
|
.end((err, res)=>{
|
||||||
if(err) return;
|
if(err) {
|
||||||
|
this.setState({ error: err });
|
||||||
|
return;
|
||||||
|
}
|
||||||
const brew = res.body;
|
const brew = res.body;
|
||||||
window.location = `/edit/${brew.editId}`;
|
window.location = `/edit/${brew.editId}`;
|
||||||
});
|
});
|
||||||
@@ -54,6 +59,10 @@ const HomePage = createClass({
|
|||||||
renderNavbar : function(){
|
renderNavbar : function(){
|
||||||
return <Navbar ver={this.props.ver}>
|
return <Navbar ver={this.props.ver}>
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
|
{this.state.error ?
|
||||||
|
<ErrorNavItem error={this.state.error} parent={this}></ErrorNavItem> :
|
||||||
|
null
|
||||||
|
}
|
||||||
<NewBrewItem />
|
<NewBrewItem />
|
||||||
<HelpNavItem />
|
<HelpNavItem />
|
||||||
<RecentNavItem />
|
<RecentNavItem />
|
||||||
|
|||||||
@@ -40,4 +40,11 @@
|
|||||||
right : 350px;
|
right : 350px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navItem.save{
|
||||||
|
background-color: @orange;
|
||||||
|
&:hover{
|
||||||
|
background-color: @green;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ require('./newPage.less');
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const request = require('superagent');
|
const request = require('../../utils/request-middleware.js');
|
||||||
|
|
||||||
const Markdown = require('naturalcrit/markdown.js');
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
const Navbar = require('../../navbar/navbar.jsx');
|
const Navbar = require('../../navbar/navbar.jsx');
|
||||||
const AccountNavItem = require('../../navbar/account.navitem.jsx');
|
const AccountNavItem = require('../../navbar/account.navitem.jsx');
|
||||||
|
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
||||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
||||||
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ const NewPage = createClass({
|
|||||||
brew : brew,
|
brew : brew,
|
||||||
isSaving : false,
|
isSaving : false,
|
||||||
saveGoogle : (global.account && global.account.googleId ? true : false),
|
saveGoogle : (global.account && global.account.googleId ? true : false),
|
||||||
errors : null,
|
error : null,
|
||||||
htmlErrors : Markdown.validate(brew.text)
|
htmlErrors : Markdown.validate(brew.text)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -122,14 +123,6 @@ const NewPage = createClass({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
clearErrors : function(){
|
|
||||||
this.setState({
|
|
||||||
errors : null,
|
|
||||||
isSaving : false
|
|
||||||
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
save : async function(){
|
save : async function(){
|
||||||
this.setState({
|
this.setState({
|
||||||
isSaving : true
|
isSaving : true
|
||||||
@@ -152,7 +145,7 @@ const NewPage = createClass({
|
|||||||
.send(brew)
|
.send(brew)
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.log(err);
|
console.log(err);
|
||||||
this.setState({ isSaving: false, errors: err });
|
this.setState({ isSaving: false, error: err });
|
||||||
});
|
});
|
||||||
if(!res) return;
|
if(!res) return;
|
||||||
|
|
||||||
@@ -164,67 +157,6 @@ const NewPage = createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
renderSaveButton : function(){
|
renderSaveButton : function(){
|
||||||
if(this.state.errors){
|
|
||||||
let errMsg = '';
|
|
||||||
try {
|
|
||||||
errMsg += `${this.state.errors.toString()}\n\n`;
|
|
||||||
errMsg += `\`\`\`\n${this.state.errors.stack}\n`;
|
|
||||||
errMsg += `${JSON.stringify(this.state.errors.response.error, null, ' ')}\n\`\`\``;
|
|
||||||
console.log(errMsg);
|
|
||||||
} catch (e){}
|
|
||||||
|
|
||||||
// if(this.state.errors.status == '401'){
|
|
||||||
// return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
// Oops!
|
|
||||||
// <div className='errorContainer' onClick={this.clearErrors}>
|
|
||||||
// You must be signed in to a Google account
|
|
||||||
// to save this to<br />Google Drive!<br />
|
|
||||||
// <a target='_blank' rel='noopener noreferrer'
|
|
||||||
// href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
|
|
||||||
// <div className='confirm'>
|
|
||||||
// Sign In
|
|
||||||
// </div>
|
|
||||||
// </a>
|
|
||||||
// <div className='deny'>
|
|
||||||
// Not Now
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// </Nav.item>;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if(this.state.errors.response.req.url.match(/^\/api.*Google.*$/m)){
|
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
Oops!
|
|
||||||
<div className='errorContainer' onClick={this.clearErrors}>
|
|
||||||
Looks like your Google credentials have
|
|
||||||
expired! Visit our log in page to sign out
|
|
||||||
and sign back in with Google,
|
|
||||||
then try saving again!
|
|
||||||
<a target='_blank' rel='noopener noreferrer'
|
|
||||||
href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
|
|
||||||
<div className='confirm'>
|
|
||||||
Sign In
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<div className='deny'>
|
|
||||||
Not Now
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Nav.item>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
|
|
||||||
Oops!
|
|
||||||
<div className='errorContainer'>
|
|
||||||
Looks like there was a problem saving. <br />
|
|
||||||
Report the issue <a target='_blank' rel='noopener noreferrer'
|
|
||||||
href={`https://github.com/naturalcrit/homebrewery/issues/new?body=${encodeURIComponent(errMsg)}`}>
|
|
||||||
here
|
|
||||||
</a>.
|
|
||||||
</div>
|
|
||||||
</Nav.item>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.state.isSaving){
|
if(this.state.isSaving){
|
||||||
return <Nav.item icon='fas fa-spinner fa-spin' className='save'>
|
return <Nav.item icon='fas fa-spinner fa-spin' className='save'>
|
||||||
save...
|
save...
|
||||||
@@ -254,7 +186,10 @@ const NewPage = createClass({
|
|||||||
</Nav.section>
|
</Nav.section>
|
||||||
|
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
{this.renderSaveButton()}
|
{this.state.error ?
|
||||||
|
<ErrorNavItem error={this.state.error} parent={this}></ErrorNavItem> :
|
||||||
|
this.renderSaveButton()
|
||||||
|
}
|
||||||
{this.renderLocalPrintButton()}
|
{this.renderLocalPrintButton()}
|
||||||
<HelpNavItem />
|
<HelpNavItem />
|
||||||
<RecentNavItem />
|
<RecentNavItem />
|
||||||
|
|||||||
@@ -4,79 +4,5 @@
|
|||||||
&:hover{
|
&:hover{
|
||||||
background-color: @green;
|
background-color: @green;
|
||||||
}
|
}
|
||||||
&.error{
|
|
||||||
position : relative;
|
|
||||||
background-color : @red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.errorContainer{
|
|
||||||
animation-name: glideDown;
|
|
||||||
animation-duration: 0.4s;
|
|
||||||
position : absolute;
|
|
||||||
top : 100%;
|
|
||||||
left : 50%;
|
|
||||||
z-index : 100000;
|
|
||||||
width : 140px;
|
|
||||||
padding : 3px;
|
|
||||||
color : white;
|
|
||||||
background-color : #333;
|
|
||||||
border : 3px solid #444;
|
|
||||||
border-radius : 5px;
|
|
||||||
transform : translate(-50% + 3px, 10px);
|
|
||||||
text-align : center;
|
|
||||||
font-size : 10px;
|
|
||||||
font-weight : 800;
|
|
||||||
text-transform : uppercase;
|
|
||||||
a{
|
|
||||||
color : @teal;
|
|
||||||
}
|
|
||||||
&:before {
|
|
||||||
content: "";
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
position: absolute;
|
|
||||||
border-left: 10px solid transparent;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
border-top: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid #444;
|
|
||||||
left: 53px;
|
|
||||||
top: -23px;
|
|
||||||
}
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
position: absolute;
|
|
||||||
border-left: 10px solid transparent;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
border-top: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid #333;
|
|
||||||
left: 53px;
|
|
||||||
top: -19px;
|
|
||||||
}
|
|
||||||
.deny {
|
|
||||||
width : 48%;
|
|
||||||
margin : 1px;
|
|
||||||
padding : 5px;
|
|
||||||
background-color : #333;
|
|
||||||
display : inline-block;
|
|
||||||
border-left : 1px solid #666;
|
|
||||||
.animate(background-color);
|
|
||||||
&:hover{
|
|
||||||
background-color : red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.confirm {
|
|
||||||
width : 48%;
|
|
||||||
margin : 1px;
|
|
||||||
padding : 5px;
|
|
||||||
background-color : #333;
|
|
||||||
display : inline-block;
|
|
||||||
color : white;
|
|
||||||
.animate(background-color);
|
|
||||||
&:hover{
|
|
||||||
background-color : teal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
|||||||
const Account = require('../../navbar/account.navitem.jsx');
|
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 ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
||||||
|
|
||||||
const UserPage = createClass({
|
const UserPage = createClass({
|
||||||
displayName : 'UserPage',
|
displayName : 'UserPage',
|
||||||
@@ -19,7 +20,8 @@ const UserPage = createClass({
|
|||||||
return {
|
return {
|
||||||
username : '',
|
username : '',
|
||||||
brews : [],
|
brews : [],
|
||||||
query : ''
|
query : '',
|
||||||
|
error : null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
@@ -50,10 +52,19 @@ const UserPage = createClass({
|
|||||||
brewCollection : brewCollection
|
brewCollection : brewCollection
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
errorReported : function(error) {
|
||||||
|
this.setState({
|
||||||
|
error
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
navItems : function() {
|
navItems : function() {
|
||||||
return <Navbar>
|
return <Navbar>
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
|
{this.state.error ?
|
||||||
|
<ErrorNavItem error={this.state.error} parent={this}></ErrorNavItem> :
|
||||||
|
null
|
||||||
|
}
|
||||||
<NewBrew />
|
<NewBrew />
|
||||||
<HelpNavItem />
|
<HelpNavItem />
|
||||||
<RecentNavItem />
|
<RecentNavItem />
|
||||||
@@ -63,7 +74,7 @@ const UserPage = createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
return <ListPage brewCollection={this.state.brewCollection} navItems={this.navItems()} query={this.props.query}></ListPage>;
|
return <ListPage brewCollection={this.state.brewCollection} navItems={this.navItems()} query={this.props.query} reportError={this.errorReported}></ListPage>;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
12
client/homebrew/utils/request-middleware.js
Normal file
12
client/homebrew/utils/request-middleware.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const request = require('superagent');
|
||||||
|
|
||||||
|
const addHeader = (request)=>request.set('Homebrewery-Version', global.version);
|
||||||
|
|
||||||
|
const requestMiddleware = {
|
||||||
|
get : (path)=>addHeader(request.get(path)),
|
||||||
|
put : (path)=>addHeader(request.put(path)),
|
||||||
|
post : (path)=>addHeader(request.post(path)),
|
||||||
|
delete : (path)=>addHeader(request.delete(path)),
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = requestMiddleware;
|
||||||
3218
package-lock.json
generated
3218
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "homebrewery",
|
"name": "homebrewery",
|
||||||
"description": "Create authentic looking D&D homebrews using only markdown",
|
"description": "Create authentic looking D&D homebrews using only markdown",
|
||||||
"version": "3.5.0",
|
"version": "3.6.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "16.11.x"
|
"node": "16.11.x"
|
||||||
},
|
},
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"quick": "node scripts/quick.js",
|
"quick": "node scripts/quick.js",
|
||||||
"build": "node scripts/buildHomebrew.js",
|
"build": "node scripts/buildHomebrew.js",
|
||||||
"buildall": "node scripts/buildHomebrew.js && node scripts/buildAdmin.js",
|
"buildall": "node scripts/buildHomebrew.js && node scripts/buildAdmin.js",
|
||||||
|
"builddev": "node scripts/buildHomebrew.js --dev",
|
||||||
"lint": "eslint --fix **/*.{js,jsx}",
|
"lint": "eslint --fix **/*.{js,jsx}",
|
||||||
"lint:dry": "eslint **/*.{js,jsx}",
|
"lint:dry": "eslint **/*.{js,jsx}",
|
||||||
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
|
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
|
||||||
@@ -50,10 +51,10 @@
|
|||||||
"lines" : 25
|
"lines" : 25
|
||||||
},
|
},
|
||||||
"server/homebrew.api.js" : {
|
"server/homebrew.api.js" : {
|
||||||
"statements" : 71,
|
"statements" : 65,
|
||||||
"branches" : 54,
|
"branches" : 50,
|
||||||
"functions" : 66,
|
"functions" : 60,
|
||||||
"lines" : 73
|
"lines" : 70
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -86,26 +87,26 @@
|
|||||||
"jwt-simple": "^0.5.6",
|
"jwt-simple": "^0.5.6",
|
||||||
"less": "^3.13.1",
|
"less": "^3.13.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"marked": "4.2.5",
|
"marked": "4.2.12",
|
||||||
"marked-extended-tables": "^1.0.5",
|
"marked-extended-tables": "^1.0.5",
|
||||||
"markedLegacy": "npm:marked@^0.3.19",
|
"markedLegacy": "npm:marked@^0.3.19",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"mongoose": "^6.8.3",
|
"mongoose": "^6.9.0",
|
||||||
"nanoid": "3.3.4",
|
"nanoid": "3.3.4",
|
||||||
"nconf": "^0.12.0",
|
"nconf": "^0.12.0",
|
||||||
"npm": "^8.10.0",
|
"npm": "^8.10.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-frame-component": "4.1.3",
|
"react-frame-component": "4.1.3",
|
||||||
"react-router-dom": "6.6.1",
|
"react-router-dom": "6.8.0",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"superagent": "^6.1.0",
|
"superagent": "^6.1.0",
|
||||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.31.0",
|
"eslint": "^8.32.0",
|
||||||
"eslint-plugin-react": "^7.31.11",
|
"eslint-plugin-react": "^7.32.1",
|
||||||
"jest": "^29.2.2",
|
"jest": "^29.4.1",
|
||||||
"supertest": "^6.3.3"
|
"supertest": "^6.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,6 +137,6 @@ fs.emptyDirSync('./build');
|
|||||||
if(isDev){
|
if(isDev){
|
||||||
livereload('./build');
|
livereload('./build');
|
||||||
watchFile('./server.js', {
|
watchFile('./server.js', {
|
||||||
watch : ['./client', './server'] // Watch additional folders if you want
|
watch : ['./client', './server', './themes'] // Watch additional folders if you want
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ app.get('/edit/:id', asyncHandler(getBrew('edit')), (req, res, next)=>{
|
|||||||
app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
|
app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
|
||||||
sanitizeBrew(req.brew, 'share');
|
sanitizeBrew(req.brew, 'share');
|
||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
|
req.brew.views = 0;
|
||||||
req.brew.title = `CLONE - ${req.brew.title}`;
|
req.brew.title = `CLONE - ${req.brew.title}`;
|
||||||
|
|
||||||
req.ogMeta = { ...defaultMetaTags,
|
req.ogMeta = { ...defaultMetaTags,
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ If you believe you should have access to this brew, ask the file owner to invite
|
|||||||
excludePropsFromUpdate : (brew)=>{
|
excludePropsFromUpdate : (brew)=>{
|
||||||
// Remove undesired properties
|
// Remove undesired properties
|
||||||
const modified = _.clone(brew);
|
const modified = _.clone(brew);
|
||||||
const propsToExclude = ['_id', 'views', 'lastViewed', 'editId', 'shareId', 'googleId'];
|
const propsToExclude = ['_id', 'views', 'lastViewed'];
|
||||||
for (const prop of propsToExclude) {
|
for (const prop of propsToExclude) {
|
||||||
delete modified[prop];
|
delete modified[prop];
|
||||||
}
|
}
|
||||||
@@ -189,17 +189,18 @@ If you believe you should have access to this brew, ask the file owner to invite
|
|||||||
res.status(200).send(saved);
|
res.status(200).send(saved);
|
||||||
},
|
},
|
||||||
updateBrew : async (req, res)=>{
|
updateBrew : async (req, res)=>{
|
||||||
// Initialize brew from request and body, destructure query params, set a constant for the google id, and set the initial value for the after-save method
|
// Initialize brew from request and body, destructure query params, and set the initial value for the after-save method
|
||||||
const brewFromClient = api.excludePropsFromUpdate(req.body);
|
const brewFromClient = api.excludePropsFromUpdate(req.body);
|
||||||
if(req.brew.version && brewFromClient.version && req.brew.version > brewFromClient.version) {
|
const brewFromServer = req.brew;
|
||||||
console.log(`Version mismatch on brew ${req.body.editId}`);
|
if(brewFromServer.version && brewFromClient.version && brewFromServer.version > brewFromClient.version) {
|
||||||
|
console.log(`Version mismatch on brew ${brewFromClient.editId}`);
|
||||||
res.setHeader('Content-Type', 'application/json');
|
res.setHeader('Content-Type', 'application/json');
|
||||||
return res.status(409).send(JSON.stringify({ message: `The brew has been changed on a different device. Please save your changes elsewhere, refresh, and try again.` }));
|
return res.status(409).send(JSON.stringify({ message: `The brew has been changed on a different device. Please save your changes elsewhere, refresh, and try again.` }));
|
||||||
}
|
}
|
||||||
|
|
||||||
let brew = _.assign(req.brew, brewFromClient);
|
let brew = _.assign(brewFromServer, brewFromClient);
|
||||||
const { saveToGoogle, removeFromGoogle } = req.query;
|
|
||||||
const googleId = brew.googleId;
|
const googleId = brew.googleId;
|
||||||
|
const { saveToGoogle, removeFromGoogle } = req.query;
|
||||||
let afterSave = async ()=>true;
|
let afterSave = async ()=>true;
|
||||||
|
|
||||||
brew.text = api.mergeBrewText(brew);
|
brew.text = api.mergeBrewText(brew);
|
||||||
@@ -337,6 +338,7 @@ If you believe you should have access to this brew, ask the file owner to invite
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
router.use('/api', require('./middleware/check-client-version.js'));
|
||||||
router.post('/api', asyncHandler(api.newBrew));
|
router.post('/api', asyncHandler(api.newBrew));
|
||||||
router.put('/api/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew));
|
router.put('/api/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew));
|
||||||
router.put('/api/update/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew));
|
router.put('/api/update/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew));
|
||||||
|
|||||||
@@ -342,9 +342,6 @@ brew`);
|
|||||||
expect(result._id).toBeUndefined();
|
expect(result._id).toBeUndefined();
|
||||||
expect(result.views).toBeUndefined();
|
expect(result.views).toBeUndefined();
|
||||||
expect(result.lastViewed).toBeUndefined();
|
expect(result.lastViewed).toBeUndefined();
|
||||||
expect(result.editId).toBeUndefined();
|
|
||||||
expect(result.shareId).toBeUndefined();
|
|
||||||
expect(result.googleId).toBeUndefined();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('excludeGoogleProps removes the correct keys', ()=>{
|
it('excludeGoogleProps removes the correct keys', ()=>{
|
||||||
|
|||||||
12
server/middleware/check-client-version.js
Normal file
12
server/middleware/check-client-version.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
module.exports = (req, res, next)=>{
|
||||||
|
const userVersion = req.get('Homebrewery-Version');
|
||||||
|
const version = require('../../package.json').version;
|
||||||
|
|
||||||
|
if(userVersion != version) {
|
||||||
|
return res.status(412).send({
|
||||||
|
message : `Client version ${userVersion} is out of date. Please save your changes elsewhere and refresh to pick up client version ${version}.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
@@ -81,28 +81,28 @@
|
|||||||
/* Cover Page */
|
/* Cover Page */
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: NodestoCapsCondensed;
|
font-family: NodestoCapsCondensed;
|
||||||
src: url('../fonts/5e/Nodesto Caps Condensed.woff2');
|
src: url('../../../fonts/5e/Nodesto Caps Condensed.woff2');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: NodestoCapsCondensed;
|
font-family: NodestoCapsCondensed;
|
||||||
src: url('../fonts/5e/Nodesto Caps Condensed Bold.woff2');
|
src: url('../../../fonts/5e/Nodesto Caps Condensed Bold.woff2');
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: NodestoCapsCondensed;
|
font-family: NodestoCapsCondensed;
|
||||||
src: url('../fonts/5e/Nodesto Caps Condensed Italic.woff2');
|
src: url('../../../fonts/5e/Nodesto Caps Condensed Italic.woff2');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: NodestoCapsCondensed;
|
font-family: NodestoCapsCondensed;
|
||||||
src: url('../fonts/5e/Nodesto Caps Condensed Bold Italic.woff2');
|
src: url('../../../fonts/5e/Nodesto Caps Condensed Bold Italic.woff2');
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user