diff --git a/.eslintrc.js b/.eslintrc.js index e8618b8c7..bc8b5c8cd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,7 +32,7 @@ module.exports = { skipBlankLines : true, }], 'max-depth' : ['warn', { max: 4 }], - 'max-params' : ['warn', { max: 4 }], + 'max-params' : ['warn', { max: 5 }], 'no-restricted-syntax' : ['warn', 'ClassDeclaration', 'SwitchStatement'], 'no-unused-vars' : ['warn', { vars : 'all', diff --git a/client/homebrew/editor/snippetbar/snippets/snippets.js b/client/homebrew/editor/snippetbar/snippets/snippets.js index 7aa2c2208..e1341be24 100644 --- a/client/homebrew/editor/snippetbar/snippets/snippets.js +++ b/client/homebrew/editor/snippetbar/snippets/snippets.js @@ -57,7 +57,6 @@ module.exports = [ `https://homebrewery.naturalcrit.com/share/${brew.shareId}` + `&size=100x100) {width:100px;mix-blend-mode:multiply}`; } - }, { name : 'Page Number', @@ -121,13 +120,23 @@ module.exports = [ name : 'Image', icon : 'fas fa-image', gen : dedent` - ![cat warrior](https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg) {width:325px} - Credit: Kyounghwan Kim` + ![cat warrior](https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg) {width:325px,mix-blend-mode:multiply} + + {{artist,position:relative,top:-230px,left:-100px,margin-bottom:-30px + ##### Cat Warrior + [Kyoung Hwan Kim](https://www.artstation.com/tahra) + }}` }, { name : 'Background Image', icon : 'fas fa-tree', - gen : `![homebrew mug](http://i.imgur.com/hMna6G0.png) {position:absolute,top:50px,right:30px,width:280px}` + gen : dedent` + ![homebrew mug](http://i.imgur.com/hMna6G0.png) {position:absolute,top:50px,right:30px,width:280px} + + {{artist,top:90px,right:30px + ##### Homebrew Mug + [naturalcrit](https://homebrew.naturalcrit.com) + }}` }, { name : 'Class Table Decoration', @@ -217,6 +226,18 @@ module.exports = [ icon : 'fas fa-hat-wizard', gen : MagicGen.item, }, + { + name : 'Artist Credit', + icon : 'fas fa-signature', + gen : function(){ + return dedent` + {{artist,top:90px,right:30px + ##### Starry Night + [Van Gogh](https://www.vangoghmuseum.nl/en) + }} + \n`; + }, + }, ] }, diff --git a/client/homebrew/editor/snippetbar/snippetsLegacy/snippets.js b/client/homebrew/editor/snippetbar/snippetsLegacy/snippets.js index 3ce871d91..69f8e1597 100644 --- a/client/homebrew/editor/snippetbar/snippetsLegacy/snippets.js +++ b/client/homebrew/editor/snippetbar/snippetsLegacy/snippets.js @@ -171,6 +171,14 @@ module.exports = [ icon : 'far fa-file-word', gen : CoverPageGen, }, + { + name : 'Artist Credit', + icon : 'fas fa-signature', + gen : '
\n' + + '##### Starry Night\n' + + '[Van Gogh](https://www.vangoghmuseum.nl/en)\n' + + '
\n' + }, ] }, diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index a643296d4..0967e05e2 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -322,7 +322,9 @@ const EditPage = createClass({ let errMsg = ''; try { errMsg += `${this.state.errors.toString()}\n\n`; - errMsg += `\`\`\`\n${JSON.stringify(this.state.errors.response.error, null, ' ')}\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'){ @@ -344,6 +346,27 @@ const EditPage = createClass({ ; } + if(this.state.errors.status == '403' && this.state.errors.response.body.errors[0].reason == 'insufficientPermissions'){ + return + Oops! +
+ Looks like your Google credentials have + expired! Visit the log in page to sign out + and sign back in with Google + to save this to Google Drive! + +
+ Sign In +
+
+
+ Not Now +
+
+
; + } + return Oops!
diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 1925d2dfa..e77fbc987 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -61,7 +61,7 @@ const NewPage = createClass({ isSaving : false, saveGoogle : (global.account && global.account.googleId ? true : false), - errors : [], + errors : null, htmlErrors : Markdown.validate(this.props.brew.text) }; }, @@ -138,6 +138,14 @@ const NewPage = createClass({ })); }, + clearErrors : function(){ + this.setState({ + errors : null, + isSaving : false + + }); + }, + save : async function(){ this.setState({ isSaving : true @@ -161,7 +169,7 @@ const NewPage = createClass({ console.log(err.status === 401 ? 'Not signed in!' : 'Error Creating New Google Brew!'); - this.setState({ isSaving: false }); + this.setState({ isSaving: false, errors: err }); return; }); @@ -191,12 +199,73 @@ const NewPage = createClass({ }, 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 + Oops! +
+ You must be signed in to a Google account + to save this to
Google Drive!
+ +
+ Sign In +
+
+
+ Not Now +
+
+
; + } + + if(this.state.errors.status == '403' && this.state.errors.response.body.errors[0].reason == 'insufficientPermissions'){ + return + Oops! +
+ Looks like your Google credentials have + expired! Visit the log in page to sign out + and sign back in with Google + to save this to Google Drive! + +
+ Sign In +
+
+
+ Not Now +
+
+
; + } + + return + Oops! +
+ Looks like there was a problem saving.
+ Report the issue + here + . +
+
; + } + if(this.state.isSaving){ - return + return save... ; } else { - return + return save ; } diff --git a/client/homebrew/pages/newPage/newPage.less b/client/homebrew/pages/newPage/newPage.less index 8cd21b2ec..4f39e626d 100644 --- a/client/homebrew/pages/newPage/newPage.less +++ b/client/homebrew/pages/newPage/newPage.less @@ -1,10 +1,82 @@ .newPage{ - - .saveButton{ + .navItem.save{ background-color: @orange; &:hover{ background-color: @green; } + &.error{ + position : relative; + background-color : @red; + } } - -} \ No newline at end of file + .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; + } + } + } +} diff --git a/package-lock.json b/package-lock.json index 19aa2bb61..b96f158e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,8 +30,8 @@ "marked": "2.1.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.29.1", - "mongoose": "^5.13.6", - "nanoid": "3.1.23", + "mongoose": "^5.13.7", + "nanoid": "3.1.25", "nconf": "^0.11.3", "prop-types": "15.7.2", "query-string": "7.0.1", @@ -6321,9 +6321,9 @@ } }, "node_modules/mongoose": { - "version": "5.13.6", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.6.tgz", - "integrity": "sha512-IyswXkgxnnl+rpiU+lzXl5/BOEle2llDfuPBrN6K+Eb5vS6a/HN/A9zrdtOcSTb0tVoCZ0QN5PfDSwa/EEGBuQ==", + "version": "5.13.7", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.7.tgz", + "integrity": "sha512-ADIvftZ+KfoTALMZ0n8HvBlezFhcUd73hQaHQDwQ+3X+JZlqE47fUy9yhFZ2SjT+qzmuaCcIXCfhewIc38t2fQ==", "dependencies": { "@types/mongodb": "^3.5.27", "bson": "^1.1.4", @@ -6405,9 +6405,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -14345,9 +14345,9 @@ } }, "mongoose": { - "version": "5.13.6", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.6.tgz", - "integrity": "sha512-IyswXkgxnnl+rpiU+lzXl5/BOEle2llDfuPBrN6K+Eb5vS6a/HN/A9zrdtOcSTb0tVoCZ0QN5PfDSwa/EEGBuQ==", + "version": "5.13.7", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.7.tgz", + "integrity": "sha512-ADIvftZ+KfoTALMZ0n8HvBlezFhcUd73hQaHQDwQ+3X+JZlqE47fUy9yhFZ2SjT+qzmuaCcIXCfhewIc38t2fQ==", "requires": { "@types/mongodb": "^3.5.27", "bson": "^1.1.4", @@ -14420,9 +14420,9 @@ "optional": true }, "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==" }, "nanomatch": { "version": "1.2.13", diff --git a/package.json b/package.json index 3e185be9d..988c36f55 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,8 @@ "marked": "2.1.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.29.1", - "mongoose": "^5.13.6", - "nanoid": "3.1.23", + "mongoose": "^5.13.7", + "nanoid": "3.1.25", "nconf": "^0.11.3", "prop-types": "15.7.2", "query-string": "7.0.1", diff --git a/server/googleActions.js b/server/googleActions.js index bfe1b406f..eab6babf3 100644 --- a/server/googleActions.js +++ b/server/googleActions.js @@ -168,6 +168,7 @@ GoogleActions = { .catch((err)=>{ console.log('Error saving to google'); console.error(err); + throw (err); //return res.status(500).send('Error while saving'); }); } @@ -203,8 +204,9 @@ GoogleActions = { media : media }) .catch((err)=>{ + console.log('Error while creating new Google brew'); console.error(err); - return res.status(500).send('Error while creating google brew'); + throw (err); }); if(!obj) return; diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 1f988761d..36d0aa576 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -141,9 +141,12 @@ const newGoogleBrew = async (req, res, next)=>{ req.body = brew; - const newBrew = await GoogleActions.newGoogleBrew(oAuth2Client, brew); - - return res.status(200).send(newBrew); + try { + const newBrew = await GoogleActions.newGoogleBrew(oAuth2Client, brew); + return res.status(200).send(newBrew); + } catch (err) { + return res.status(err.response.status).send(err); + } }; const updateGoogleBrew = async (req, res, next)=>{ @@ -154,9 +157,12 @@ const updateGoogleBrew = async (req, res, next)=>{ const brew = req.body; brew.text = mergeBrewText(brew.text, brew.style); - const updatedBrew = await GoogleActions.updateGoogleBrew(oAuth2Client, brew); - - return res.status(200).send(updatedBrew); + try { + const updatedBrew = await GoogleActions.updateGoogleBrew(oAuth2Client, brew); + return res.status(200).send(updatedBrew); + } catch (err) { + return res.status(err.response.status).send(err); + } }; router.post('/api', newBrew); diff --git a/shared/naturalcrit/codeEditor/codeEditor.jsx b/shared/naturalcrit/codeEditor/codeEditor.jsx index bbfdbfa75..9707bde56 100644 --- a/shared/naturalcrit/codeEditor/codeEditor.jsx +++ b/shared/naturalcrit/codeEditor/codeEditor.jsx @@ -47,12 +47,6 @@ const CodeEditor = createClass({ indentWithTabs : true, tabSize : 2, extraKeys : { - 'Ctrl-B' : this.makeBold, - 'Cmd-B' : this.makeBold, - 'Ctrl-I' : this.makeItalic, - 'Cmd-I' : this.makeItalic, - 'Ctrl-M' : this.makeSpan, - 'Cmd-M' : this.makeSpan, 'Ctrl-B' : this.makeBold, 'Cmd-B' : this.makeBold, 'Ctrl-I' : this.makeItalic, @@ -60,7 +54,7 @@ const CodeEditor = createClass({ 'Ctrl-M' : this.makeSpan, 'Cmd-M' : this.makeSpan, 'Ctrl-/' : this.makeComment, - 'Cmd-/' : this.makeComment, + 'Cmd-/' : this.makeComment } }); diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 01abc33a7..0eef037db 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -369,45 +369,28 @@ const getTableCell = (text, cell, type, align)=>{ }; const splitCells = (tableRow, count, prevRow = [])=>{ - // trim any excessive pipes at start of row - tableRow = tableRow.replace(/^\|+(?=\|)/, '') - .replace(/(\|+)/g, (match, p1, offset, str)=>{ - let escaped = false, - curr = offset; - while (--curr >= 0 && str[curr] === '\\') escaped = !escaped; - if(escaped) { - // odd number of slashes means | is escaped - // so we leave it and the slashes alone - return p1; - } else { - // add space before unescaped | to distinguish it from an escaped pipe - return ` ${p1}`; - } - }); + const cells = [...tableRow.matchAll(/(?:[^|\\]|\\.?)+(?:\|+|$)/g)].map((x)=>x[0]); - const cells = tableRow.split(/(?: \||(?<=\|)\|)(?=[^\|]|$)/g); - let i = 0; - - // First/last cell in a row cannot be empty if it has no leading/trailing pipe - if(!cells[0].trim()) { cells.shift(); } - if(!cells[cells.length - 1].trim()) { cells.pop(); } + // Remove first/last cell in a row if whitespace only and no leading/trailing pipe + if(!cells[0]?.trim()) { cells.shift(); } + if(!cells[cells.length - 1]?.trim()) { cells.pop(); } let numCols = 0; + let i, j, trimmedCell, prevCell, prevCols; - for (; i < cells.length; i++) { - const trimmedCell = cells[i].split(/ \|+$/)[0]; + for (i = 0; i < cells.length; i++) { + trimmedCell = cells[i].split(/\|+$/)[0]; cells[i] = { rowspan : 1, colspan : Math.max(cells[i].length - trimmedCell.length, 1), text : trimmedCell.trim().replace(/\\\|/g, '|') - // trim whitespace and display escaped pipes as normal character + // display escaped pipes as normal character }; // Handle Rowspan if(trimmedCell.slice(-1) == '^' && prevRow.length) { // Find matching cell in previous row - let prevCols = 0; - let j, prevCell; + prevCols = 0; for (j = 0; j < prevRow.length; j++) { prevCell = prevRow[j]; if((prevCols == numCols) && (prevCell.colspan == cells[i].colspan)) { diff --git a/themes/5ePhb.style.less b/themes/5ePhb.style.less index 9e56a7009..095d7c568 100644 --- a/themes/5ePhb.style.less +++ b/themes/5ePhb.style.less @@ -2,12 +2,13 @@ @import (less) './themes/assets/assets.less'; //Colors -@background : #EEE5CE; -@noteGreen : #e0e5c1; -@headerUnderline : #c9ad6a; -@horizontalRule : #9c2b1b; -@headerText : #58180D; -@monsterStatBackground : #EEDBAB; +@background : #EEE5CE; // Light parchment +@noteGreen : #e0e5c1; // Pastel green +@headerUnderline : #c9ad6a; // Gold +@horizontalRule : #9c2b1b; // Maroon +@headerText : #58180D; // Dark maroon +@monsterStatBackground : #EEDBAB; // Light orange parchment +@captionText : #766649; // Brown @page { margin: 0; } body { counter-reset : phb-page-numbers; @@ -263,6 +264,33 @@ body { margin-bottom : 0em; } } + //***************************** + // * ARTIST CREDIT BLOCK + // *****************************/ + .artist { + position : absolute; + text-align : center; + font-family : WalterTurncoat; + font-size : 0.27cm; + color : @captionText; + p, p + p { + margin : unset; + text-indent : unset; + line-height : 1em; + } + h5 { + font-size : 1.3em; + font-family : WalterTurncoat; + } + a{ + color : inherit; + text-decoration : unset; + &:hover { + text-decoration : underline; + } + } + } + //***************************** // * MONSTER STAT BLOCK // *****************************/ diff --git a/themes/5ePhbLegacy.style.less b/themes/5ePhbLegacy.style.less index 0f1cae7ec..d814d4036 100644 --- a/themes/5ePhbLegacy.style.less +++ b/themes/5ePhbLegacy.style.less @@ -2,12 +2,13 @@ @import (less) './themes/assets/assets.less'; @import (less) './themes/phb.depricated.less'; //Colors -@background : #EEE5CE; -@noteGreen : #e0e5c1; -@headerUnderline : #c9ad6a; -@horizontalRule : #9c2b1b; -@headerText : #58180D; -@monsterStatBackground : #FDF1DC; +@background : #EEE5CE; // Light parchment +@noteGreen : #e0e5c1; // Pastel green +@headerUnderline : #c9ad6a; // Gold +@horizontalRule : #9c2b1b; // Maroon +@headerText : #58180D; // Dark maroon +@monsterStatBackground : #FDF1DC; // Lighter parchment +@captionText : #766649; // Brown @page { margin: 0; } body { counter-reset : phb-page-numbers; @@ -445,6 +446,35 @@ body { .phb pre+.descriptive{ margin-top : 8px; } + +//***************************** +// * ARTIST CREDIT BLOCK +// *****************************/ +.phb { + .artist { + position : absolute; + text-align : center; + font-family : WalterTurncoat; + font-size : 0.27cm; + color : @captionText; + p, p + p { + margin : unset; + text-indent : unset; + line-height : 1em; + } + h5 { + font-size : 1.3em; + font-family : WalterTurncoat; + } + a{ + color : inherit; + text-decoration : unset; + &:hover { + text-decoration : underline; + } + } + } +} //***************************** // * TABLE OF CONTENTS // *****************************/ diff --git a/themes/fonts/5e legacy/WalterTurncoat-Regular.woff2 b/themes/fonts/5e legacy/WalterTurncoat-Regular.woff2 new file mode 100644 index 000000000..758e2400d Binary files /dev/null and b/themes/fonts/5e legacy/WalterTurncoat-Regular.woff2 differ diff --git a/themes/fonts/5e legacy/fonts.less b/themes/fonts/5e legacy/fonts.less index 0c47c8e7e..a3527130b 100644 --- a/themes/fonts/5e legacy/fonts.less +++ b/themes/fonts/5e legacy/fonts.less @@ -37,6 +37,12 @@ font-weight: normal; font-style: normal; } +@font-face { + font-family: WalterTurncoat; + src: url('../fonts/5e legacy/WalterTurncoat-Regular.woff2'); + font-weight: normal; + font-style: normal; +} /* Headers */ @font-face { diff --git a/themes/fonts/5e/WalterTurncoat-Regular.woff2 b/themes/fonts/5e/WalterTurncoat-Regular.woff2 new file mode 100644 index 000000000..758e2400d Binary files /dev/null and b/themes/fonts/5e/WalterTurncoat-Regular.woff2 differ diff --git a/themes/fonts/5e/fonts.less b/themes/fonts/5e/fonts.less index 7cd1c9652..df4f169d2 100644 --- a/themes/fonts/5e/fonts.less +++ b/themes/fonts/5e/fonts.less @@ -55,6 +55,12 @@ font-weight: normal; font-style: normal; } +@font-face { + font-family: WalterTurncoat; + src: url('../fonts/5e/WalterTurncoat-Regular.woff2'); + font-weight: normal; + font-style: normal; +} /* Headers */ @font-face {