From fd0eb4ca7da707eff86d124ee270c72ed5f1a365 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 4 Nov 2021 10:42:27 +1300 Subject: [PATCH 01/21] Add & implement ListPage --- .../basePages/listPage/brewItem/brewItem.jsx | 144 +++++++++++++ .../basePages/listPage/brewItem/brewItem.less | 75 +++++++ .../pages/basePages/listPage/listPage.jsx | 189 ++++++++++++++++++ .../pages/basePages/listPage/listPage.less | 77 +++++++ client/homebrew/pages/userPage/userPage.jsx | 162 ++------------- 5 files changed, 504 insertions(+), 143 deletions(-) create mode 100644 client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx create mode 100644 client/homebrew/pages/basePages/listPage/brewItem/brewItem.less create mode 100644 client/homebrew/pages/basePages/listPage/listPage.jsx create mode 100644 client/homebrew/pages/basePages/listPage/listPage.less diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx new file mode 100644 index 000000000..c7c4d6f94 --- /dev/null +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx @@ -0,0 +1,144 @@ +require('./brewItem.less'); +const React = require('react'); +const createClass = require('create-react-class'); +const _ = require('lodash'); +const cx = require('classnames'); +const moment = require('moment'); +const request = require('superagent'); + +const googleDriveIcon = require('../../../../googleDrive.png'); +const dedent = require('dedent-tabs').default; + +const BrewItem = createClass({ + getDefaultProps : function() { + return { + brew : { + title : '', + description : '', + + authors : [] + } + }; + }, + + deleteBrew : function(){ + if(this.props.brew.authors.length <= 1){ + if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return; + if(!confirm('Are you REALLY sure? You will not be able to recover the document.')) return; + } else { + if(!confirm('Are you sure you want to remove this brew from your collection? This will remove you as an editor, but other owners will still be able to access the document.')) return; + if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return; + } + + if(this.props.brew.googleId) { + request.get(`/api/removeGoogle/${this.props.brew.googleId}${this.props.brew.editId}`) + .send() + .end(function(err, res){ + location.reload(); + }); + } else { + request.delete(`/api/${this.props.brew.editId}`) + .send() + .end(function(err, res){ + location.reload(); + }); + } + }, + + renderDeleteBrewLink : function(){ + if(!this.props.brew.editId) return; + + return + + ; + }, + + renderEditLink : function(){ + if(!this.props.brew.editId) return; + + let editLink = this.props.brew.editId; + if(this.props.brew.googleId) { + editLink = this.props.brew.googleId + editLink; + } + + return + + ; + }, + + renderShareLink : function(){ + if(!this.props.brew.shareId) return; + + let shareLink = this.props.brew.shareId; + if(this.props.brew.googleId) { + shareLink = this.props.brew.googleId + shareLink; + } + + return + + ; + }, + + renderDownloadLink : function(){ + if(!this.props.brew.shareId) return; + + let shareLink = this.props.brew.shareId; + if(this.props.brew.googleId) { + shareLink = this.props.brew.googleId + shareLink; + } + + return + + ; + }, + + renderGoogleDriveIcon : function(){ + if(!this.props.brew.gDrive) return; + + return + googleDriveIcon + ; + }, + + render : function(){ + const brew = this.props.brew; + const dateFormatString = 'YYYY-MM-DD HH:mm:ss'; + + return
+
+

{brew.title}

+

{brew.description}

+
+
+
+ + {brew.authors.join(', ')} + +
+ + {brew.views} + + {brew.pageCount && + + {brew.pageCount} + + } + + {moment(brew.updatedAt).fromNow()} + + {this.renderGoogleDriveIcon()} +
+ +
+ {this.renderShareLink()} + {this.renderEditLink()} + {this.renderDownloadLink()} + {this.renderDeleteBrewLink()} +
+
; + } +}); + +module.exports = BrewItem; diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.less b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.less new file mode 100644 index 000000000..d323874f5 --- /dev/null +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.less @@ -0,0 +1,75 @@ + +.brewItem{ + position : relative; + display : inline-block; + vertical-align : top; + box-sizing : border-box; + box-sizing : border-box; + overflow : hidden; + width : 48%; + min-height : 105px; + margin-right : 15px; + margin-bottom : 15px; + padding : 5px 15px 2px 8px; + padding-right : 15px; + border : 1px solid #c9ad6a; + border-radius : 5px; + -webkit-column-break-inside : avoid; + page-break-inside : avoid; + break-inside : avoid; + .text { + min-height : 54px; + h4{ + margin-bottom : 5px; + font-size : 2.2em; + } + } + .info{ + position: initial; + bottom: 2px; + font-family : ScalySans; + font-size : 1.2em; + &>span{ + margin-right : 12px; + line-height : 1.5em; + } + } + &:hover{ + .links{ + opacity : 1; + } + } + &:nth-child(2n + 1){ + margin-right : 0px; + } + .links{ + .animate(opacity); + position : absolute; + top : 0px; + right : 0px; + height : 100%; + width : 2em; + opacity : 0; + background-color : fade(black, 60%); + text-align : center; + a{ + .animate(opacity); + display : block; + margin : 8px 0px; + opacity : 0.6; + font-size : 1.3em; + color : white; + &:hover{ + opacity : 1; + } + i{ + cursor : pointer; + } + } + } + .googleDriveIcon { + height : 20px; + padding : 0px; + margin : -5px; + } +} diff --git a/client/homebrew/pages/basePages/listPage/listPage.jsx b/client/homebrew/pages/basePages/listPage/listPage.jsx new file mode 100644 index 000000000..e4beb4a94 --- /dev/null +++ b/client/homebrew/pages/basePages/listPage/listPage.jsx @@ -0,0 +1,189 @@ +require('./listPage.less'); +const React = require('react'); +const createClass = require('create-react-class'); +const _ = require('lodash'); +const cx = require('classnames'); + +const moment = require('moment'); + +const Nav = require('naturalcrit/nav/nav.jsx'); +const Navbar = require('../../../navbar/navbar.jsx'); + +const RecentNavItem = require('../../../navbar/recent.navitem.jsx').both; +const Account = require('../../../navbar/account.navitem.jsx'); +const NewBrew = require('../../../navbar/newbrew.navitem.jsx'); +const BrewItem = require('./brewItem/brewItem.jsx'); +const ReportIssue = require('../../../navbar/issue.navitem.jsx'); + +// const brew = { +// title : 'SUPER Long title woah now', +// authors : [] +// }; + +//const BREWS = _.times(25, ()=>{ return brew;}); + + +const ListPage = createClass({ + getDefaultProps : function() { + return { + brewCollection : [ + { + title : '', + class : '', + brews : [] + } + ] + }; + }, + getInitialState : function() { + return { + sortType : 'alpha', + sortDir : 'asc', + filterString : '' + }; + }, + + renderBrews : function(brews){ + if(!brews || !brews.length) return
No Brews.
; + + const sortedBrews = this.sortBrews(brews); + + return _.map(sortedBrews, (brew, idx)=>{ + return ; + }); + }, + + sortBrewOrder : function(brew){ + if(!brew.title){brew.title = 'No Title';} + const mapping = { + 'alpha' : _.deburr(brew.title.toLowerCase()), + 'created' : moment(brew.createdAt).format(), + 'updated' : moment(brew.updatedAt).format(), + 'views' : brew.views, + 'latest' : moment(brew.lastViewed).format() + }; + return mapping[this.state.sortType]; + }, + + sortBrews : function(brews){ + return _.orderBy(brews, (brew)=>{ return this.sortBrewOrder(brew); }, this.state.sortDir); + }, + + handleSortOptionChange : function(event){ + this.setState({ + sortType : event.target.value + }); + }, + + handleSortDirChange : function(event){ + this.setState({ + sortDir : `${(this.state.sortDir == 'asc' ? 'desc' : 'asc')}` + }); + }, + + renderSortOption : function(sortTitle, sortValue){ + return + + ; + }, + + handleFilterTextChange : function(e){ + this.setState({ + filterString : e.target.value + }); + return; + }, + + renderFilterOption : function(){ + return + + ; + }, + + renderSortOptions : function(){ + return
+ + + + + {this.renderSortOption('Title', 'alpha')} + {this.renderSortOption('Created Date', 'created')} + {this.renderSortOption('Updated Date', 'updated')} + {this.renderSortOption('Views', 'views')} + {/* {this.renderSortOption('Latest', 'latest')} */} + + + {this.renderFilterOption()} + + +
+
Sort by :
+
+
Direction :
+
+ +
+
; + }, + + getSortedBrews : function(brewCollection){ + const testString = _.deburr(this.state.filterString).toLowerCase(); + const brews = this.state.filterString ? _.filter(brewCollection.brews, (brew)=>{ + return (_.deburr(brew.title).toLowerCase().includes(testString)) || + (_.deburr(brew.description).toLowerCase().includes(testString)); + }) : this.props.brewCollection.brews; + return _.groupBy(brews, (brew)=>{ + return (brew.published ? 'published' : 'private'); + }); + }, + + renderBrewCollection : function(brewCollection){ + return _.map(brewCollection, (brewItem, idx)=>{ + return
+

{brewItem.title || 'No Title'}

+ {this.renderBrews(brewItem.brews)} +
; + }); + }, + + render : function(){ + return
+ + + + + + + + + + +
+
+ {this.renderSortOptions()} + {this.renderBrewCollection(this.props.brewCollection)} +
+
+
; + } +}); + +module.exports = ListPage; diff --git a/client/homebrew/pages/basePages/listPage/listPage.less b/client/homebrew/pages/basePages/listPage/listPage.less new file mode 100644 index 000000000..6be946404 --- /dev/null +++ b/client/homebrew/pages/basePages/listPage/listPage.less @@ -0,0 +1,77 @@ + +.noColumns(){ + column-count : auto; + column-fill : auto; + column-gap : auto; + column-width : auto; + -webkit-column-count : auto; + -moz-column-count : auto; + -webkit-column-width : auto; + -moz-column-width : auto; + -webkit-column-gap : auto; + -moz-column-gap : auto; +} +.listPage{ + .content{ + overflow-y : scroll; + .phb{ + .noColumns(); + height : auto; + min-height : 279.4mm; + margin : 20px auto; + &::after{ + display : none; + } + .noBrews{ + margin : 10px 0px; + font-size : 1.3em; + font-style : italic; + } + + } + } + .sort-container{ + font-family : 'Open Sans', sans-serif; + position : fixed; + top : 35px; + left : calc(50vw - 408px); + border : 2px solid #58180D; + width : 800px; + background-color : #EEE5CE; + padding : 2px; + text-align : center; + z-index : 15; + h6{ + text-transform : uppercase; + font-family : 'Open Sans', sans-serif; + font-size : 11px; + font-weight : bold; + color : #58180D; + } + table{ + margin : 0px; + vertical-align : middle; + tbody tr{ + background-color: transparent !important; + i{ + padding-right : 5px + } + button{ + background-color : transparent; + color : #58180D; + font-family : 'Open Sans', sans-serif; + font-size : 11px; + text-transform : uppercase; + font-weight : normal; + &.active{ + font-weight : bold; + border : 2px solid #58180D; + } + &.sortDir{ + width : 75px; + } + } + } + } + } +} diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index bd84ce6b5..db069a7ac 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -4,16 +4,7 @@ const createClass = require('create-react-class'); const _ = require('lodash'); const cx = require('classnames'); -const moment = require('moment'); - -const Nav = require('naturalcrit/nav/nav.jsx'); -const Navbar = require('../../navbar/navbar.jsx'); - -const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; -const Account = require('../../navbar/account.navitem.jsx'); -const NewBrew = require('../../navbar/newbrew.navitem.jsx'); -const BrewItem = require('./brewItem/brewItem.jsx'); -const ReportIssue = require('../../navbar/issue.navitem.jsx'); +const ListPage = require('../basePages/listPage/listPage.jsx'); // const brew = { // title : 'SUPER Long title woah now', @@ -43,114 +34,8 @@ const UserPage = createClass({ return `${this.props.username}'s`; }, - renderBrews : function(brews){ - if(!brews || !brews.length) return
No Brews.
; - - const sortedBrews = this.sortBrews(brews); - - return _.map(sortedBrews, (brew, idx)=>{ - return ; - }); - }, - - sortBrewOrder : function(brew){ - if(!brew.title){brew.title = 'No Title';} - const mapping = { - 'alpha' : _.deburr(brew.title.toLowerCase()), - 'created' : moment(brew.createdAt).format(), - 'updated' : moment(brew.updatedAt).format(), - 'views' : brew.views, - 'latest' : moment(brew.lastViewed).format() - }; - return mapping[this.state.sortType]; - }, - - sortBrews : function(brews){ - return _.orderBy(brews, (brew)=>{ return this.sortBrewOrder(brew); }, this.state.sortDir); - }, - - handleSortOptionChange : function(event){ - this.setState({ - sortType : event.target.value - }); - }, - - handleSortDirChange : function(event){ - this.setState({ - sortDir : `${(this.state.sortDir == 'asc' ? 'desc' : 'asc')}` - }); - }, - - renderSortOption : function(sortTitle, sortValue){ - return - - ; - }, - - handleFilterTextChange : function(e){ - this.setState({ - filterString : e.target.value - }); - return; - }, - - renderFilterOption : function(){ - return - - ; - }, - - renderSortOptions : function(){ - return
- - - - - {this.renderSortOption('Title', 'alpha')} - {this.renderSortOption('Created Date', 'created')} - {this.renderSortOption('Updated Date', 'updated')} - {this.renderSortOption('Views', 'views')} - {/* {this.renderSortOption('Latest', 'latest')} */} - - - {this.renderFilterOption()} - - -
-
Sort by :
-
-
Direction :
-
- -
-
; - }, - getSortedBrews : function(){ - const testString = _.deburr(this.state.filterString).toLowerCase(); - const brewCollection = this.state.filterString ? _.filter(this.props.brews, (brew)=>{ - return (_.deburr(brew.title).toLowerCase().includes(testString)) || - (_.deburr(brew.description).toLowerCase().includes(testString)); - }) : this.props.brews; - return _.groupBy(brewCollection, (brew)=>{ + return _.groupBy(this.props.brews, (brew)=>{ return (brew.published ? 'published' : 'private'); }); }, @@ -158,33 +43,24 @@ const UserPage = createClass({ render : function(){ const brews = this.getSortedBrews(); - return
- - - - - - - - - + const brewCollections = [ + { + title : `${this.getUsernameWithS()} published brews`, + class : 'published', + brews : brews.published + } + ]; + if(this.props.username == global.account?.username){ + brewCollections.push( + { + title : `${this.getUsernameWithS()} unpublished brews`, + class : 'unpublished', + brews : brews.private + } + ); + } -
-
- {this.renderSortOptions()} -
-

{this.getUsernameWithS()} published brews

- {this.renderBrews(brews.published)} -
- {this.props.username == global.account?.username && -
-

{this.getUsernameWithS()} unpublished brews

- {this.renderBrews(brews.private)} -
- } -
-
-
; + return ; } }); From 4fc0bbc9d7f079f8255aea4ed8a36b11ff49ec01 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 4 Nov 2021 11:44:05 +1300 Subject: [PATCH 02/21] Remove unnecessary `userPage.less` --- client/homebrew/pages/userPage/userPage.jsx | 2 +- client/homebrew/pages/userPage/userPage.less | 77 -------------------- 2 files changed, 1 insertion(+), 78 deletions(-) delete mode 100644 client/homebrew/pages/userPage/userPage.less diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index db069a7ac..708f45a61 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -1,4 +1,4 @@ -require('./userPage.less'); +//require('./userPage.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); diff --git a/client/homebrew/pages/userPage/userPage.less b/client/homebrew/pages/userPage/userPage.less deleted file mode 100644 index d968aab9a..000000000 --- a/client/homebrew/pages/userPage/userPage.less +++ /dev/null @@ -1,77 +0,0 @@ - -.noColumns(){ - column-count : auto; - column-fill : auto; - column-gap : auto; - column-width : auto; - -webkit-column-count : auto; - -moz-column-count : auto; - -webkit-column-width : auto; - -moz-column-width : auto; - -webkit-column-gap : auto; - -moz-column-gap : auto; -} -.userPage{ - .content{ - overflow-y : scroll; - .phb{ - .noColumns(); - height : auto; - min-height : 279.4mm; - margin : 20px auto; - &::after{ - display : none; - } - .noBrews{ - margin : 10px 0px; - font-size : 1.3em; - font-style : italic; - } - - } - } - .sort-container{ - font-family : 'Open Sans', sans-serif; - position : fixed; - top : 35px; - left : calc(50vw - 408px); - border : 2px solid #58180D; - width : 800px; - background-color : #EEE5CE; - padding : 2px; - text-align : center; - z-index : 15; - h6{ - text-transform : uppercase; - font-family : 'Open Sans', sans-serif; - font-size : 11px; - font-weight : bold; - color : #58180D; - } - table{ - margin : 0px; - vertical-align : middle; - tbody tr{ - background-color: transparent !important; - i{ - padding-right : 5px - } - button{ - background-color : transparent; - color : #58180D; - font-family : 'Open Sans', sans-serif; - font-size : 11px; - text-transform : uppercase; - font-weight : normal; - &.active{ - font-weight : bold; - border : 2px solid #58180D; - } - &.sortDir{ - width : 75px; - } - } - } - } - } -} From fe708e0a0bb09a7badb88dcdc50c9ca3569bb07d Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 4 Nov 2021 11:44:41 +1300 Subject: [PATCH 03/21] Remove line rather than comment it out --- client/homebrew/pages/userPage/userPage.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index 708f45a61..1168e0982 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -1,4 +1,3 @@ -//require('./userPage.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); From 6cd56dfd6213011969937e962e0b0ca5d8109d97 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 4 Nov 2021 14:03:57 +1300 Subject: [PATCH 04/21] Initial pass at `editorPage.jsx`. --- .../pages/basePages/editorPage/editorPage.jsx | 578 ++++++++++++++++++ .../basePages/editorPage/editorPage.less | 99 +++ client/homebrew/pages/editPage/editPage.jsx | 454 +------------- client/homebrew/pages/newPage/newPage.jsx | 250 +------- 4 files changed, 695 insertions(+), 686 deletions(-) create mode 100644 client/homebrew/pages/basePages/editorPage/editorPage.jsx create mode 100644 client/homebrew/pages/basePages/editorPage/editorPage.less diff --git a/client/homebrew/pages/basePages/editorPage/editorPage.jsx b/client/homebrew/pages/basePages/editorPage/editorPage.jsx new file mode 100644 index 000000000..99a5c2b52 --- /dev/null +++ b/client/homebrew/pages/basePages/editorPage/editorPage.jsx @@ -0,0 +1,578 @@ +/* eslint-disable max-lines */ +require('./editorPage.less'); +const React = require('react'); +const createClass = require('create-react-class'); +const _ = require('lodash'); +const request = require('superagent'); +const { Meta } = require('vitreum/headtags'); + +const Nav = require('naturalcrit/nav/nav.jsx'); +const Navbar = require('../../../navbar/navbar.jsx'); + +const NewBrew = require('../../../navbar/newbrew.navitem.jsx'); +const ReportIssue = require('../../../navbar/issue.navitem.jsx'); +const PrintLink = require('../../../navbar/print.navitem.jsx'); +const Account = require('../../../navbar/account.navitem.jsx'); +const RecentNavItem = require('../../../navbar/recent.navitem.jsx').both; + +const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); +const Editor = require('../../../editor/editor.jsx'); +const BrewRenderer = require('../../../brewRenderer/brewRenderer.jsx'); + +const Markdown = require('naturalcrit/markdown.js'); + +const googleDriveActive = require('../../../googleDrive.png'); +const googleDriveInactive = require('../../../googleDriveMono.png'); + +const SAVE_TIMEOUT = 3000; + +const BREWKEY = 'homebrewery-new'; +const STYLEKEY = 'homebrewery-new-style'; +const METAKEY = 'homebrewery-new-meta'; + +const EditorPage = createClass({ + getDefaultProps : function() { + return { + brew : { + text : '', + style : '', + shareId : null, + editId : null, + createdAt : null, + updatedAt : null, + gDrive : false, + trashed : false, + + title : '', + description : '', + tags : '', + published : false, + authors : [], + systems : [], + renderer : 'legacy' + }, + pageType : 'edit', + googleDriveOptions : [ + 'DRIVE > HB', + 'HB > DRIVE' + ] + }; + }, + + getInitialState : function() { + return { + brew : this.props.brew, + isSaving : false, + isPending : false, + alertTrashedGoogleBrew : this.props.brew.trashed, + alertLoginToTransfer : false, + saveGoogle : this.props.brew.googleId ? true : false, + confirmGoogleTransfer : false, + errors : null, + htmlErrors : Markdown.validate(this.props.brew.text), + url : '' + }; + // return { + // brew : { + // text : brew.text || '', + // style : brew.style || undefined, + // gDrive : false, + // title : brew.title || '', + // description : brew.description || '', + // tags : brew.tags || '', + // published : false, + // authors : [], + // systems : brew.systems || [], + // renderer : brew.renderer || 'legacy' + // }, + + // isSaving : false, + // isPending : false, + // alertTrashedGoogleBrew : this.props.brew.trashed, + // alertLoginToTransfer : false, + // saveGoogle : (global.account && global.account.googleId ? true : false), + // confirmGoogleTransfer : false, + // errors : null, + // htmlErrors : Markdown.validate(brew.text), + // url : '' + // }; + }, + savedBrew : null, + + componentDidMount : function(){ + this.setState({ + url : window.location.href + }); + + this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy + + this.trySave(); + window.onbeforeunload = ()=>{ + if(this.state.isSaving || this.state.isPending){ + return 'You have unsaved changes!'; + } + }; + + this.setState((prevState)=>({ + htmlErrors : Markdown.validate(prevState.brew.text) + })); + + document.addEventListener('keydown', this.handleControlKeys); + }, + componentWillUnmount : function() { + window.onbeforeunload = function(){}; + document.removeEventListener('keydown', this.handleControlKeys); + }, + + handleControlKeys : function(e){ + if(!(e.ctrlKey || e.metaKey)) return; + const S_KEY = 83; + const P_KEY = 80; + if(e.keyCode == S_KEY) this.save(); + if(e.keyCode == P_KEY) window.open(`/print/${this.processShareId()}?dialog=true`, '_blank').focus(); + if(e.keyCode == P_KEY || e.keyCode == S_KEY){ + e.stopPropagation(); + e.preventDefault(); + } + }, + + isEdit : function(){ + return this.props.pageType == 'edit'; + }, + + isNew : function(){ + return this.props.pageType == 'new'; + }, + + handleSplitMove : function(){ + this.refs.editor.update(); + }, + + handleTextChange : function(text){ + //If there are errors, run the validator on every change to give quick feedback + let htmlErrors = this.state.htmlErrors; + if(htmlErrors.length) htmlErrors = Markdown.validate(text); + + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, { text: text }), + isPending : true, + htmlErrors : htmlErrors + }), ()=>this.trySave()); + }, + + handleStyleChange : function(style){ + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, { style: style }), + isPending : true + }), ()=>this.trySave()); + }, + + handleMetaChange : function(metadata){ + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, metadata), + isPending : true, + }), ()=>this.trySave()); + + }, + + hasChanges : function(){ + return !_.isEqual(this.state.brew, this.savedBrew); + }, + + trySave : function(){ + if(!this.isEdit()) return; + if(!this.debounceSave) this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT); + if(this.hasChanges()){ + this.debounceSave(); + } else { + this.debounceSave.cancel(); + } + }, + + handleGoogleClick : function(){ + if(!global.account?.googleId) { + this.setState({ + alertLoginToTransfer : true + }); + return; + } + this.setState((prevState)=>({ + confirmGoogleTransfer : !prevState.confirmGoogleTransfer + })); + this.clearErrors(); + }, + + closeAlerts : function(event){ + event.stopPropagation(); //Only handle click once so alert doesn't reopen + this.setState({ + alertTrashedGoogleBrew : false, + alertLoginToTransfer : false, + confirmGoogleTransfer : false + }); + }, + + toggleGoogleStorage : function(){ + this.setState((prevState)=>({ + saveGoogle : !prevState.saveGoogle, + isSaving : false, + errors : null + }), ()=>this.isEdit() && this.save()); + }, + + clearErrors : function(){ + this.setState({ + errors : null, + isSaving : false + + }); + }, + + save : async function(){ + this.setState((prevState)=>({ + isSaving : true, + errors : null, + htmlErrors : Markdown.validate(prevState.brew.text) + })); + + if(this.isEdit()){ + if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel(); + + const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId); + + const brew = this.state.brew; + brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; + + if(this.state.saveGoogle) { + if(transfer) { + const res = await request + .post('/api/newGoogle/') + .send(brew) + .catch((err)=>{ + console.log(err.status === 401 + ? 'Not signed in!' + : 'Error Transferring to Google!'); + this.setState({ errors: err, saveGoogle: false }); + }); + + if(!res) { return; } + + console.log('Deleting Local Copy'); + await request.delete(`/api/${brew.editId}`) + .send() + .catch((err)=>{ + console.log('Error deleting Local Copy'); + }); + + this.savedBrew = res.body; + history.replaceState(null, null, `/edit/${this.savedBrew.googleId}${this.savedBrew.editId}`); //update URL to match doc ID + } else { + const res = await request + .put(`/api/updateGoogle/${brew.editId}`) + .send(brew) + .catch((err)=>{ + console.log(err.status === 401 + ? 'Not signed in!' + : 'Error Saving to Google!'); + this.setState({ errors: err }); + return; + }); + + this.savedBrew = res.body; + } + } else { + if(transfer) { + const res = await request.post('/api') + .send(brew) + .catch((err)=>{ + console.log('Error creating Local Copy'); + this.setState({ errors: err }); + return; + }); + + await request.get(`/api/removeGoogle/${brew.googleId}${brew.editId}`) + .send() + .catch((err)=>{ + console.log('Error Deleting Google Brew'); + }); + + this.savedBrew = res.body; + history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); //update URL to match doc ID + } else { + const res = await request + .put(`/api/update/${brew.editId}`) + .send(brew) + .catch((err)=>{ + console.log('Error Updating Local Brew'); + this.setState({ errors: err }); + return; + }); + + this.savedBrew = res.body; + } + } + + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, { + googleId : this.savedBrew.googleId ? this.savedBrew.googleId : null, + editId : this.savedBrew.editId, + shareId : this.savedBrew.shareId + }), + isPending : false, + isSaving : false, + })); + } + + if(this.isNew()){ + console.log('saving new brew'); + + let brew = this.state.brew; + // Split out CSS to Style if CSS codefence exists + if(brew.text.startsWith('```css') && brew.text.indexOf('```\n\n') > 0) { + const index = brew.text.indexOf('```\n\n'); + brew.style = `${brew.style ? `${brew.style}\n` : ''}${brew.text.slice(7, index - 1)}`; + brew.text = brew.text.slice(index + 5); + }; + + brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; + + if(this.state.saveGoogle) { + const res = await request + .post('/api/newGoogle/') + .send(brew) + .catch((err)=>{ + console.log(err.status === 401 + ? 'Not signed in!' + : 'Error Creating New Google Brew!'); + this.setState({ isSaving: false, errors: err }); + return; + }); + + brew = res.body; + localStorage.removeItem(BREWKEY); + localStorage.removeItem(STYLEKEY); + localStorage.removeItem(METAKEY); + window.location = `/edit/${brew.googleId}${brew.editId}`; + } else { + request.post('/api') + .send(brew) + .end((err, res)=>{ + if(err){ + this.setState({ + isSaving : false + }); + return; + } + window.onbeforeunload = function(){}; + brew = res.body; + localStorage.removeItem(BREWKEY); + localStorage.removeItem(STYLEKEY); + localStorage.removeItem(METAKEY); + window.location = `/edit/${brew.editId}`; + }); + } + } + }, + + renderGoogleDriveIcon : function(){ + return + {this.state.saveGoogle + ? googleDriveActive + : googleDriveInactive + } + + {this.state.confirmGoogleTransfer && +
+ { this.state.saveGoogle + ? this.props.googleDriveOptions[0] + : this.props.googleDriveOptions[1] + } +
+
+ Yes +
+
+ No +
+
+ } + + {this.state.alertLoginToTransfer && +
+ You must be signed in to a Google account to transfer + between the homebrewery and Google Drive! + +
+ Sign In +
+
+
+ Not Now +
+
+ } +
; + }, + + 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 saving...; + } + if(this.state.isPending && this.hasChanges()){ + return Save Now; + } + if(!this.state.isPending && !this.state.isSaving){ + return saved.; + } + }, + + processShareId : function() { + return this.state.brew.googleId ? + this.state.brew.googleId + this.state.brew.shareId : + this.state.brew.shareId; + }, + + getRedditLink : function(){ + + const shareLink = this.processShareId(); + const systems = this.props.brew.systems.length > 0 ? ` [${this.props.brew.systems.join(' - ')}]` : ''; + const title = `${this.props.brew.title} ${systems}`; + const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. + +**[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`; + + return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`; + }, + + renderNavbar : function(){ + const shareLink = this.processShareId(); + + return + + {this.state.alertTrashedGoogleBrew && +
+ This brew is currently in your Trash folder on Google Drive!
If you want to keep it, make sure to move it before it is deleted permanently!
+
+ OK +
+
+ } + + + {this.state.brew.title} + + + + {this.renderGoogleDriveIcon()} + {this.renderSaveButton()} + + + {this.isEdit() && <> + + + share + + + view + + {navigator.clipboard.writeText(`https://homebrewery.naturalcrit.com/share/${shareLink}`);}}> + copy url + + + post to reddit + + + + + } + + + + +
; + }, + + render : function(){ + return
+ + {this.renderNavbar()} + +
+ + + + +
+
; + } +}); + +module.exports = EditorPage; diff --git a/client/homebrew/pages/basePages/editorPage/editorPage.less b/client/homebrew/pages/basePages/editorPage/editorPage.less new file mode 100644 index 000000000..0cbfadcbd --- /dev/null +++ b/client/homebrew/pages/basePages/editorPage/editorPage.less @@ -0,0 +1,99 @@ +@keyframes glideDown { + 0% {transform : translate(-50% + 3px, 0px); + opacity : 0;} + 100% {transform : translate(-50% + 3px, 10px); + opacity : 1;} +} +.editPage{ + .navItem.save{ + width : 106px; + text-align : center; + position : relative; + &.saved{ + cursor : initial; + color : #666; + } + &.error{ + position : relative; + background-color : @red; + } + } + .googleDriveStorage { + position : relative; + } + .googleDriveStorage img{ + height : 20px; + padding : 0px; + margin : -5px; + } + .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/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index ee4f41f5b..8e70cee32 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -3,28 +3,8 @@ require('./editPage.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); -const request = require('superagent'); -const { Meta } = require('vitreum/headtags'); -const Nav = require('naturalcrit/nav/nav.jsx'); -const Navbar = require('../../navbar/navbar.jsx'); - -const NewBrew = require('../../navbar/newbrew.navitem.jsx'); -const ReportIssue = require('../../navbar/issue.navitem.jsx'); -const PrintLink = require('../../navbar/print.navitem.jsx'); -const Account = require('../../navbar/account.navitem.jsx'); -const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; - -const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); -const Editor = require('../../editor/editor.jsx'); -const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx'); - -const Markdown = require('naturalcrit/markdown.js'); - -const googleDriveActive = require('../../googleDrive.png'); -const googleDriveInactive = require('../../googleDriveMono.png'); - -const SAVE_TIMEOUT = 3000; +const EditorPage = require('../basePages/editorPage/editorPage.jsx'); const EditPage = createClass({ getDefaultProps : function() { @@ -50,431 +30,17 @@ const EditPage = createClass({ }; }, - getInitialState : function() { - return { - brew : this.props.brew, - isSaving : false, - isPending : false, - alertTrashedGoogleBrew : this.props.brew.trashed, - alertLoginToTransfer : false, - saveGoogle : this.props.brew.googleId ? true : false, - confirmGoogleTransfer : false, - errors : null, - htmlErrors : Markdown.validate(this.props.brew.text), - url : '' - }; - }, - savedBrew : null, - - componentDidMount : function(){ - this.setState({ - url : window.location.href - }); - - this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy - - this.trySave(); - window.onbeforeunload = ()=>{ - if(this.state.isSaving || this.state.isPending){ - return 'You have unsaved changes!'; - } - }; - - this.setState((prevState)=>({ - htmlErrors : Markdown.validate(prevState.brew.text) - })); - - document.addEventListener('keydown', this.handleControlKeys); - }, - componentWillUnmount : function() { - window.onbeforeunload = function(){}; - document.removeEventListener('keydown', this.handleControlKeys); - }, - - handleControlKeys : function(e){ - if(!(e.ctrlKey || e.metaKey)) return; - const S_KEY = 83; - const P_KEY = 80; - if(e.keyCode == S_KEY) this.save(); - if(e.keyCode == P_KEY) window.open(`/print/${this.processShareId()}?dialog=true`, '_blank').focus(); - if(e.keyCode == P_KEY || e.keyCode == S_KEY){ - e.stopPropagation(); - e.preventDefault(); - } - }, - - handleSplitMove : function(){ - this.refs.editor.update(); - }, - - handleTextChange : function(text){ - //If there are errors, run the validator on every change to give quick feedback - let htmlErrors = this.state.htmlErrors; - if(htmlErrors.length) htmlErrors = Markdown.validate(text); - - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, { text: text }), - isPending : true, - htmlErrors : htmlErrors - }), ()=>this.trySave()); - }, - - handleStyleChange : function(style){ - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, { style: style }), - isPending : true - }), ()=>this.trySave()); - }, - - handleMetaChange : function(metadata){ - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, metadata), - isPending : true, - }), ()=>this.trySave()); - - }, - - hasChanges : function(){ - return !_.isEqual(this.state.brew, this.savedBrew); - }, - - trySave : function(){ - if(!this.debounceSave) this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT); - if(this.hasChanges()){ - this.debounceSave(); - } else { - this.debounceSave.cancel(); - } - }, - - handleGoogleClick : function(){ - if(!global.account?.googleId) { - this.setState({ - alertLoginToTransfer : true - }); - return; - } - this.setState((prevState)=>({ - confirmGoogleTransfer : !prevState.confirmGoogleTransfer - })); - this.clearErrors(); - }, - - closeAlerts : function(event){ - event.stopPropagation(); //Only handle click once so alert doesn't reopen - this.setState({ - alertTrashedGoogleBrew : false, - alertLoginToTransfer : false, - confirmGoogleTransfer : false - }); - }, - - toggleGoogleStorage : function(){ - this.setState((prevState)=>({ - saveGoogle : !prevState.saveGoogle, - isSaving : false, - errors : null - }), ()=>this.save()); - }, - - clearErrors : function(){ - this.setState({ - errors : null, - isSaving : false - - }); - }, - - save : async function(){ - if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel(); - - this.setState((prevState)=>({ - isSaving : true, - errors : null, - htmlErrors : Markdown.validate(prevState.brew.text) - })); - - const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId); - - const brew = this.state.brew; - brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; - - if(this.state.saveGoogle) { - if(transfer) { - const res = await request - .post('/api/newGoogle/') - .send(brew) - .catch((err)=>{ - console.log(err.status === 401 - ? 'Not signed in!' - : 'Error Transferring to Google!'); - this.setState({ errors: err, saveGoogle: false }); - }); - - if(!res) { return; } - - console.log('Deleting Local Copy'); - await request.delete(`/api/${brew.editId}`) - .send() - .catch((err)=>{ - console.log('Error deleting Local Copy'); - }); - - this.savedBrew = res.body; - history.replaceState(null, null, `/edit/${this.savedBrew.googleId}${this.savedBrew.editId}`); //update URL to match doc ID - } else { - const res = await request - .put(`/api/updateGoogle/${brew.editId}`) - .send(brew) - .catch((err)=>{ - console.log(err.status === 401 - ? 'Not signed in!' - : 'Error Saving to Google!'); - this.setState({ errors: err }); - return; - }); - - this.savedBrew = res.body; - } - } else { - if(transfer) { - const res = await request.post('/api') - .send(brew) - .catch((err)=>{ - console.log('Error creating Local Copy'); - this.setState({ errors: err }); - return; - }); - - await request.get(`/api/removeGoogle/${brew.googleId}${brew.editId}`) - .send() - .catch((err)=>{ - console.log('Error Deleting Google Brew'); - }); - - this.savedBrew = res.body; - history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); //update URL to match doc ID - } else { - const res = await request - .put(`/api/update/${brew.editId}`) - .send(brew) - .catch((err)=>{ - console.log('Error Updating Local Brew'); - this.setState({ errors: err }); - return; - }); - - this.savedBrew = res.body; - } - } - - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, { - googleId : this.savedBrew.googleId ? this.savedBrew.googleId : null, - editId : this.savedBrew.editId, - shareId : this.savedBrew.shareId - }), - isPending : false, - isSaving : false, - })); - }, - - renderGoogleDriveIcon : function(){ - return - {this.state.saveGoogle - ? googleDriveActive - : googleDriveInactive - } - - {this.state.confirmGoogleTransfer && -
- { this.state.saveGoogle - ? `Would you like to transfer this brew from your Google Drive storage back to the Homebrewery?` - : `Would you like to transfer this brew from the Homebrewery to your personal Google Drive storage?` - } -
-
- Yes -
-
- No -
-
- } - - {this.state.alertLoginToTransfer && -
- You must be signed in to a Google account to transfer - between the homebrewery and Google Drive! - -
- Sign In -
-
-
- Not Now -
-
- } -
; - }, - - 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 saving...; - } - if(this.state.isPending && this.hasChanges()){ - return Save Now; - } - if(!this.state.isPending && !this.state.isSaving){ - return saved.; - } - }, - - processShareId : function() { - return this.state.brew.googleId ? - this.state.brew.googleId + this.state.brew.shareId : - this.state.brew.shareId; - }, - - getRedditLink : function(){ - - const shareLink = this.processShareId(); - const systems = this.props.brew.systems.length > 0 ? ` [${this.props.brew.systems.join(' - ')}]` : ''; - const title = `${this.props.brew.title} ${systems}`; - const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. - -**[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`; - - return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`; - }, - - renderNavbar : function(){ - const shareLink = this.processShareId(); - - return - - {this.state.alertTrashedGoogleBrew && -
- This brew is currently in your Trash folder on Google Drive!
If you want to keep it, make sure to move it before it is deleted permanently!
-
- OK -
-
- } - - - {this.state.brew.title} - - - - {this.renderGoogleDriveIcon()} - {this.renderSaveButton()} - - - - - share - - - view - - {navigator.clipboard.writeText(`https://homebrewery.naturalcrit.com/share/${shareLink}`);}}> - copy url - - - post to reddit - - - - - - - -
; - }, - render : function(){ - return
- - {this.renderNavbar()} + const googleDriveOptions = [ + 'Would you like to transfer this brew from your Google Drive storage back to the Homebrewery?', + 'Would you like to transfer this brew from the Homebrewery to your personal Google Drive storage?' + ]; -
- - - - -
-
; + return ; } }); diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 3f09cac4e..0e424a998 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -3,20 +3,11 @@ require('./newPage.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); -const request = require('superagent'); + +const EditorPage = require('../basePages/editorPage/editorPage.jsx'); const Markdown = require('naturalcrit/markdown.js'); -const Nav = require('naturalcrit/nav/nav.jsx'); -const Navbar = require('../../navbar/navbar.jsx'); -const AccountNavItem = require('../../navbar/account.navitem.jsx'); -const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; -const IssueNavItem = require('../../navbar/issue.navitem.jsx'); - -const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); -const Editor = require('../../editor/editor.jsx'); -const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx'); - const BREWKEY = 'homebrewery-new'; const STYLEKEY = 'homebrewery-new-style'; const METAKEY = 'homebrewery-new-meta'; @@ -82,238 +73,13 @@ const NewPage = createClass({ }; }, - componentDidMount : function() { - document.addEventListener('keydown', this.handleControlKeys); - }, - componentWillUnmount : function() { - document.removeEventListener('keydown', this.handleControlKeys); - }, - - handleControlKeys : function(e){ - if(!(e.ctrlKey || e.metaKey)) return; - const S_KEY = 83; - const P_KEY = 80; - if(e.keyCode == S_KEY) this.save(); - if(e.keyCode == P_KEY) this.print(); - if(e.keyCode == P_KEY || e.keyCode == S_KEY){ - e.stopPropagation(); - e.preventDefault(); - } - }, - - handleSplitMove : function(){ - this.refs.editor.update(); - }, - - handleTextChange : function(text){ - //If there are errors, run the validator on every change to give quick feedback - let htmlErrors = this.state.htmlErrors; - if(htmlErrors.length) htmlErrors = Markdown.validate(text); - - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, { text: text }), - htmlErrors : htmlErrors - })); - localStorage.setItem(BREWKEY, text); - }, - - handleStyleChange : function(style){ - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, { style: style }), - })); - localStorage.setItem(STYLEKEY, style); - }, - - handleMetaChange : function(metadata){ - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, metadata), - })); - localStorage.setItem(METAKEY, JSON.stringify({ - // 'title' : this.state.brew.title, - // 'description' : this.state.brew.description, - 'renderer' : this.state.brew.renderer - })); - }, - - clearErrors : function(){ - this.setState({ - errors : null, - isSaving : false - - }); - }, - - save : async function(){ - this.setState({ - isSaving : true - }); - - console.log('saving new brew'); - - let brew = this.state.brew; - // Split out CSS to Style if CSS codefence exists - if(brew.text.startsWith('```css') && brew.text.indexOf('```\n\n') > 0) { - const index = brew.text.indexOf('```\n\n'); - brew.style = `${brew.style ? `${brew.style}\n` : ''}${brew.text.slice(7, index - 1)}`; - brew.text = brew.text.slice(index + 5); - }; - - brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; - - if(this.state.saveGoogle) { - const res = await request - .post('/api/newGoogle/') - .send(brew) - .catch((err)=>{ - console.log(err.status === 401 - ? 'Not signed in!' - : 'Error Creating New Google Brew!'); - this.setState({ isSaving: false, errors: err }); - return; - }); - - brew = res.body; - localStorage.removeItem(BREWKEY); - localStorage.removeItem(STYLEKEY); - localStorage.removeItem(METAKEY); - window.location = `/edit/${brew.googleId}${brew.editId}`; - } else { - request.post('/api') - .send(brew) - .end((err, res)=>{ - if(err){ - this.setState({ - isSaving : false - }); - return; - } - window.onbeforeunload = function(){}; - brew = res.body; - localStorage.removeItem(BREWKEY); - localStorage.removeItem(STYLEKEY); - localStorage.removeItem(METAKEY); - window.location = `/edit/${brew.editId}`; - }); - } - }, - - 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 - save... - ; - } else { - return - save - ; - } - }, - - print : function(){ - window.open('/print?dialog=true&local=print', '_blank'); - }, - - renderLocalPrintButton : function(){ - return - get PDF - ; - }, - - renderNavbar : function(){ - return - - - {this.state.brew.title} - - - - {this.renderSaveButton()} - {this.renderLocalPrintButton()} - - - - - ; - }, - render : function(){ - return
- {this.renderNavbar()} -
- - - - -
-
; + const googleDriveOptions = [ + 'Set save location to the Homebrewery?', + 'Set save location to your personal Google Drive storage?' + ]; + + return ; } }); From f422b22af14898352054f915e2bd0b4895fec56e Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 4 Nov 2021 14:07:11 +1300 Subject: [PATCH 05/21] Revert "Initial pass at `editorPage.jsx`." This reverts commit 6cd56dfd6213011969937e962e0b0ca5d8109d97. --- .../pages/basePages/editorPage/editorPage.jsx | 578 ------------------ .../basePages/editorPage/editorPage.less | 99 --- client/homebrew/pages/editPage/editPage.jsx | 456 +++++++++++++- client/homebrew/pages/newPage/newPage.jsx | 250 +++++++- 4 files changed, 687 insertions(+), 696 deletions(-) delete mode 100644 client/homebrew/pages/basePages/editorPage/editorPage.jsx delete mode 100644 client/homebrew/pages/basePages/editorPage/editorPage.less diff --git a/client/homebrew/pages/basePages/editorPage/editorPage.jsx b/client/homebrew/pages/basePages/editorPage/editorPage.jsx deleted file mode 100644 index 99a5c2b52..000000000 --- a/client/homebrew/pages/basePages/editorPage/editorPage.jsx +++ /dev/null @@ -1,578 +0,0 @@ -/* eslint-disable max-lines */ -require('./editorPage.less'); -const React = require('react'); -const createClass = require('create-react-class'); -const _ = require('lodash'); -const request = require('superagent'); -const { Meta } = require('vitreum/headtags'); - -const Nav = require('naturalcrit/nav/nav.jsx'); -const Navbar = require('../../../navbar/navbar.jsx'); - -const NewBrew = require('../../../navbar/newbrew.navitem.jsx'); -const ReportIssue = require('../../../navbar/issue.navitem.jsx'); -const PrintLink = require('../../../navbar/print.navitem.jsx'); -const Account = require('../../../navbar/account.navitem.jsx'); -const RecentNavItem = require('../../../navbar/recent.navitem.jsx').both; - -const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); -const Editor = require('../../../editor/editor.jsx'); -const BrewRenderer = require('../../../brewRenderer/brewRenderer.jsx'); - -const Markdown = require('naturalcrit/markdown.js'); - -const googleDriveActive = require('../../../googleDrive.png'); -const googleDriveInactive = require('../../../googleDriveMono.png'); - -const SAVE_TIMEOUT = 3000; - -const BREWKEY = 'homebrewery-new'; -const STYLEKEY = 'homebrewery-new-style'; -const METAKEY = 'homebrewery-new-meta'; - -const EditorPage = createClass({ - getDefaultProps : function() { - return { - brew : { - text : '', - style : '', - shareId : null, - editId : null, - createdAt : null, - updatedAt : null, - gDrive : false, - trashed : false, - - title : '', - description : '', - tags : '', - published : false, - authors : [], - systems : [], - renderer : 'legacy' - }, - pageType : 'edit', - googleDriveOptions : [ - 'DRIVE > HB', - 'HB > DRIVE' - ] - }; - }, - - getInitialState : function() { - return { - brew : this.props.brew, - isSaving : false, - isPending : false, - alertTrashedGoogleBrew : this.props.brew.trashed, - alertLoginToTransfer : false, - saveGoogle : this.props.brew.googleId ? true : false, - confirmGoogleTransfer : false, - errors : null, - htmlErrors : Markdown.validate(this.props.brew.text), - url : '' - }; - // return { - // brew : { - // text : brew.text || '', - // style : brew.style || undefined, - // gDrive : false, - // title : brew.title || '', - // description : brew.description || '', - // tags : brew.tags || '', - // published : false, - // authors : [], - // systems : brew.systems || [], - // renderer : brew.renderer || 'legacy' - // }, - - // isSaving : false, - // isPending : false, - // alertTrashedGoogleBrew : this.props.brew.trashed, - // alertLoginToTransfer : false, - // saveGoogle : (global.account && global.account.googleId ? true : false), - // confirmGoogleTransfer : false, - // errors : null, - // htmlErrors : Markdown.validate(brew.text), - // url : '' - // }; - }, - savedBrew : null, - - componentDidMount : function(){ - this.setState({ - url : window.location.href - }); - - this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy - - this.trySave(); - window.onbeforeunload = ()=>{ - if(this.state.isSaving || this.state.isPending){ - return 'You have unsaved changes!'; - } - }; - - this.setState((prevState)=>({ - htmlErrors : Markdown.validate(prevState.brew.text) - })); - - document.addEventListener('keydown', this.handleControlKeys); - }, - componentWillUnmount : function() { - window.onbeforeunload = function(){}; - document.removeEventListener('keydown', this.handleControlKeys); - }, - - handleControlKeys : function(e){ - if(!(e.ctrlKey || e.metaKey)) return; - const S_KEY = 83; - const P_KEY = 80; - if(e.keyCode == S_KEY) this.save(); - if(e.keyCode == P_KEY) window.open(`/print/${this.processShareId()}?dialog=true`, '_blank').focus(); - if(e.keyCode == P_KEY || e.keyCode == S_KEY){ - e.stopPropagation(); - e.preventDefault(); - } - }, - - isEdit : function(){ - return this.props.pageType == 'edit'; - }, - - isNew : function(){ - return this.props.pageType == 'new'; - }, - - handleSplitMove : function(){ - this.refs.editor.update(); - }, - - handleTextChange : function(text){ - //If there are errors, run the validator on every change to give quick feedback - let htmlErrors = this.state.htmlErrors; - if(htmlErrors.length) htmlErrors = Markdown.validate(text); - - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, { text: text }), - isPending : true, - htmlErrors : htmlErrors - }), ()=>this.trySave()); - }, - - handleStyleChange : function(style){ - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, { style: style }), - isPending : true - }), ()=>this.trySave()); - }, - - handleMetaChange : function(metadata){ - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, metadata), - isPending : true, - }), ()=>this.trySave()); - - }, - - hasChanges : function(){ - return !_.isEqual(this.state.brew, this.savedBrew); - }, - - trySave : function(){ - if(!this.isEdit()) return; - if(!this.debounceSave) this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT); - if(this.hasChanges()){ - this.debounceSave(); - } else { - this.debounceSave.cancel(); - } - }, - - handleGoogleClick : function(){ - if(!global.account?.googleId) { - this.setState({ - alertLoginToTransfer : true - }); - return; - } - this.setState((prevState)=>({ - confirmGoogleTransfer : !prevState.confirmGoogleTransfer - })); - this.clearErrors(); - }, - - closeAlerts : function(event){ - event.stopPropagation(); //Only handle click once so alert doesn't reopen - this.setState({ - alertTrashedGoogleBrew : false, - alertLoginToTransfer : false, - confirmGoogleTransfer : false - }); - }, - - toggleGoogleStorage : function(){ - this.setState((prevState)=>({ - saveGoogle : !prevState.saveGoogle, - isSaving : false, - errors : null - }), ()=>this.isEdit() && this.save()); - }, - - clearErrors : function(){ - this.setState({ - errors : null, - isSaving : false - - }); - }, - - save : async function(){ - this.setState((prevState)=>({ - isSaving : true, - errors : null, - htmlErrors : Markdown.validate(prevState.brew.text) - })); - - if(this.isEdit()){ - if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel(); - - const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId); - - const brew = this.state.brew; - brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; - - if(this.state.saveGoogle) { - if(transfer) { - const res = await request - .post('/api/newGoogle/') - .send(brew) - .catch((err)=>{ - console.log(err.status === 401 - ? 'Not signed in!' - : 'Error Transferring to Google!'); - this.setState({ errors: err, saveGoogle: false }); - }); - - if(!res) { return; } - - console.log('Deleting Local Copy'); - await request.delete(`/api/${brew.editId}`) - .send() - .catch((err)=>{ - console.log('Error deleting Local Copy'); - }); - - this.savedBrew = res.body; - history.replaceState(null, null, `/edit/${this.savedBrew.googleId}${this.savedBrew.editId}`); //update URL to match doc ID - } else { - const res = await request - .put(`/api/updateGoogle/${brew.editId}`) - .send(brew) - .catch((err)=>{ - console.log(err.status === 401 - ? 'Not signed in!' - : 'Error Saving to Google!'); - this.setState({ errors: err }); - return; - }); - - this.savedBrew = res.body; - } - } else { - if(transfer) { - const res = await request.post('/api') - .send(brew) - .catch((err)=>{ - console.log('Error creating Local Copy'); - this.setState({ errors: err }); - return; - }); - - await request.get(`/api/removeGoogle/${brew.googleId}${brew.editId}`) - .send() - .catch((err)=>{ - console.log('Error Deleting Google Brew'); - }); - - this.savedBrew = res.body; - history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); //update URL to match doc ID - } else { - const res = await request - .put(`/api/update/${brew.editId}`) - .send(brew) - .catch((err)=>{ - console.log('Error Updating Local Brew'); - this.setState({ errors: err }); - return; - }); - - this.savedBrew = res.body; - } - } - - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, { - googleId : this.savedBrew.googleId ? this.savedBrew.googleId : null, - editId : this.savedBrew.editId, - shareId : this.savedBrew.shareId - }), - isPending : false, - isSaving : false, - })); - } - - if(this.isNew()){ - console.log('saving new brew'); - - let brew = this.state.brew; - // Split out CSS to Style if CSS codefence exists - if(brew.text.startsWith('```css') && brew.text.indexOf('```\n\n') > 0) { - const index = brew.text.indexOf('```\n\n'); - brew.style = `${brew.style ? `${brew.style}\n` : ''}${brew.text.slice(7, index - 1)}`; - brew.text = brew.text.slice(index + 5); - }; - - brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; - - if(this.state.saveGoogle) { - const res = await request - .post('/api/newGoogle/') - .send(brew) - .catch((err)=>{ - console.log(err.status === 401 - ? 'Not signed in!' - : 'Error Creating New Google Brew!'); - this.setState({ isSaving: false, errors: err }); - return; - }); - - brew = res.body; - localStorage.removeItem(BREWKEY); - localStorage.removeItem(STYLEKEY); - localStorage.removeItem(METAKEY); - window.location = `/edit/${brew.googleId}${brew.editId}`; - } else { - request.post('/api') - .send(brew) - .end((err, res)=>{ - if(err){ - this.setState({ - isSaving : false - }); - return; - } - window.onbeforeunload = function(){}; - brew = res.body; - localStorage.removeItem(BREWKEY); - localStorage.removeItem(STYLEKEY); - localStorage.removeItem(METAKEY); - window.location = `/edit/${brew.editId}`; - }); - } - } - }, - - renderGoogleDriveIcon : function(){ - return - {this.state.saveGoogle - ? googleDriveActive - : googleDriveInactive - } - - {this.state.confirmGoogleTransfer && -
- { this.state.saveGoogle - ? this.props.googleDriveOptions[0] - : this.props.googleDriveOptions[1] - } -
-
- Yes -
-
- No -
-
- } - - {this.state.alertLoginToTransfer && -
- You must be signed in to a Google account to transfer - between the homebrewery and Google Drive! - -
- Sign In -
-
-
- Not Now -
-
- } -
; - }, - - 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 saving...; - } - if(this.state.isPending && this.hasChanges()){ - return Save Now; - } - if(!this.state.isPending && !this.state.isSaving){ - return saved.; - } - }, - - processShareId : function() { - return this.state.brew.googleId ? - this.state.brew.googleId + this.state.brew.shareId : - this.state.brew.shareId; - }, - - getRedditLink : function(){ - - const shareLink = this.processShareId(); - const systems = this.props.brew.systems.length > 0 ? ` [${this.props.brew.systems.join(' - ')}]` : ''; - const title = `${this.props.brew.title} ${systems}`; - const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. - -**[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`; - - return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`; - }, - - renderNavbar : function(){ - const shareLink = this.processShareId(); - - return - - {this.state.alertTrashedGoogleBrew && -
- This brew is currently in your Trash folder on Google Drive!
If you want to keep it, make sure to move it before it is deleted permanently!
-
- OK -
-
- } - - - {this.state.brew.title} - - - - {this.renderGoogleDriveIcon()} - {this.renderSaveButton()} - - - {this.isEdit() && <> - - - share - - - view - - {navigator.clipboard.writeText(`https://homebrewery.naturalcrit.com/share/${shareLink}`);}}> - copy url - - - post to reddit - - - - - } - - - - -
; - }, - - render : function(){ - return
- - {this.renderNavbar()} - -
- - - - -
-
; - } -}); - -module.exports = EditorPage; diff --git a/client/homebrew/pages/basePages/editorPage/editorPage.less b/client/homebrew/pages/basePages/editorPage/editorPage.less deleted file mode 100644 index 0cbfadcbd..000000000 --- a/client/homebrew/pages/basePages/editorPage/editorPage.less +++ /dev/null @@ -1,99 +0,0 @@ -@keyframes glideDown { - 0% {transform : translate(-50% + 3px, 0px); - opacity : 0;} - 100% {transform : translate(-50% + 3px, 10px); - opacity : 1;} -} -.editPage{ - .navItem.save{ - width : 106px; - text-align : center; - position : relative; - &.saved{ - cursor : initial; - color : #666; - } - &.error{ - position : relative; - background-color : @red; - } - } - .googleDriveStorage { - position : relative; - } - .googleDriveStorage img{ - height : 20px; - padding : 0px; - margin : -5px; - } - .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/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 8e70cee32..ee4f41f5b 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -3,8 +3,28 @@ require('./editPage.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); +const request = require('superagent'); +const { Meta } = require('vitreum/headtags'); -const EditorPage = require('../basePages/editorPage/editorPage.jsx'); +const Nav = require('naturalcrit/nav/nav.jsx'); +const Navbar = require('../../navbar/navbar.jsx'); + +const NewBrew = require('../../navbar/newbrew.navitem.jsx'); +const ReportIssue = require('../../navbar/issue.navitem.jsx'); +const PrintLink = require('../../navbar/print.navitem.jsx'); +const Account = require('../../navbar/account.navitem.jsx'); +const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; + +const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); +const Editor = require('../../editor/editor.jsx'); +const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx'); + +const Markdown = require('naturalcrit/markdown.js'); + +const googleDriveActive = require('../../googleDrive.png'); +const googleDriveInactive = require('../../googleDriveMono.png'); + +const SAVE_TIMEOUT = 3000; const EditPage = createClass({ getDefaultProps : function() { @@ -30,17 +50,431 @@ const EditPage = createClass({ }; }, - render : function(){ - const googleDriveOptions = [ - 'Would you like to transfer this brew from your Google Drive storage back to the Homebrewery?', - 'Would you like to transfer this brew from the Homebrewery to your personal Google Drive storage?' - ]; + getInitialState : function() { + return { + brew : this.props.brew, + isSaving : false, + isPending : false, + alertTrashedGoogleBrew : this.props.brew.trashed, + alertLoginToTransfer : false, + saveGoogle : this.props.brew.googleId ? true : false, + confirmGoogleTransfer : false, + errors : null, + htmlErrors : Markdown.validate(this.props.brew.text), + url : '' + }; + }, + savedBrew : null, - return ; + componentDidMount : function(){ + this.setState({ + url : window.location.href + }); + + this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy + + this.trySave(); + window.onbeforeunload = ()=>{ + if(this.state.isSaving || this.state.isPending){ + return 'You have unsaved changes!'; + } + }; + + this.setState((prevState)=>({ + htmlErrors : Markdown.validate(prevState.brew.text) + })); + + document.addEventListener('keydown', this.handleControlKeys); + }, + componentWillUnmount : function() { + window.onbeforeunload = function(){}; + document.removeEventListener('keydown', this.handleControlKeys); + }, + + handleControlKeys : function(e){ + if(!(e.ctrlKey || e.metaKey)) return; + const S_KEY = 83; + const P_KEY = 80; + if(e.keyCode == S_KEY) this.save(); + if(e.keyCode == P_KEY) window.open(`/print/${this.processShareId()}?dialog=true`, '_blank').focus(); + if(e.keyCode == P_KEY || e.keyCode == S_KEY){ + e.stopPropagation(); + e.preventDefault(); + } + }, + + handleSplitMove : function(){ + this.refs.editor.update(); + }, + + handleTextChange : function(text){ + //If there are errors, run the validator on every change to give quick feedback + let htmlErrors = this.state.htmlErrors; + if(htmlErrors.length) htmlErrors = Markdown.validate(text); + + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, { text: text }), + isPending : true, + htmlErrors : htmlErrors + }), ()=>this.trySave()); + }, + + handleStyleChange : function(style){ + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, { style: style }), + isPending : true + }), ()=>this.trySave()); + }, + + handleMetaChange : function(metadata){ + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, metadata), + isPending : true, + }), ()=>this.trySave()); + + }, + + hasChanges : function(){ + return !_.isEqual(this.state.brew, this.savedBrew); + }, + + trySave : function(){ + if(!this.debounceSave) this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT); + if(this.hasChanges()){ + this.debounceSave(); + } else { + this.debounceSave.cancel(); + } + }, + + handleGoogleClick : function(){ + if(!global.account?.googleId) { + this.setState({ + alertLoginToTransfer : true + }); + return; + } + this.setState((prevState)=>({ + confirmGoogleTransfer : !prevState.confirmGoogleTransfer + })); + this.clearErrors(); + }, + + closeAlerts : function(event){ + event.stopPropagation(); //Only handle click once so alert doesn't reopen + this.setState({ + alertTrashedGoogleBrew : false, + alertLoginToTransfer : false, + confirmGoogleTransfer : false + }); + }, + + toggleGoogleStorage : function(){ + this.setState((prevState)=>({ + saveGoogle : !prevState.saveGoogle, + isSaving : false, + errors : null + }), ()=>this.save()); + }, + + clearErrors : function(){ + this.setState({ + errors : null, + isSaving : false + + }); + }, + + save : async function(){ + if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel(); + + this.setState((prevState)=>({ + isSaving : true, + errors : null, + htmlErrors : Markdown.validate(prevState.brew.text) + })); + + const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId); + + const brew = this.state.brew; + brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; + + if(this.state.saveGoogle) { + if(transfer) { + const res = await request + .post('/api/newGoogle/') + .send(brew) + .catch((err)=>{ + console.log(err.status === 401 + ? 'Not signed in!' + : 'Error Transferring to Google!'); + this.setState({ errors: err, saveGoogle: false }); + }); + + if(!res) { return; } + + console.log('Deleting Local Copy'); + await request.delete(`/api/${brew.editId}`) + .send() + .catch((err)=>{ + console.log('Error deleting Local Copy'); + }); + + this.savedBrew = res.body; + history.replaceState(null, null, `/edit/${this.savedBrew.googleId}${this.savedBrew.editId}`); //update URL to match doc ID + } else { + const res = await request + .put(`/api/updateGoogle/${brew.editId}`) + .send(brew) + .catch((err)=>{ + console.log(err.status === 401 + ? 'Not signed in!' + : 'Error Saving to Google!'); + this.setState({ errors: err }); + return; + }); + + this.savedBrew = res.body; + } + } else { + if(transfer) { + const res = await request.post('/api') + .send(brew) + .catch((err)=>{ + console.log('Error creating Local Copy'); + this.setState({ errors: err }); + return; + }); + + await request.get(`/api/removeGoogle/${brew.googleId}${brew.editId}`) + .send() + .catch((err)=>{ + console.log('Error Deleting Google Brew'); + }); + + this.savedBrew = res.body; + history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); //update URL to match doc ID + } else { + const res = await request + .put(`/api/update/${brew.editId}`) + .send(brew) + .catch((err)=>{ + console.log('Error Updating Local Brew'); + this.setState({ errors: err }); + return; + }); + + this.savedBrew = res.body; + } + } + + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, { + googleId : this.savedBrew.googleId ? this.savedBrew.googleId : null, + editId : this.savedBrew.editId, + shareId : this.savedBrew.shareId + }), + isPending : false, + isSaving : false, + })); + }, + + renderGoogleDriveIcon : function(){ + return + {this.state.saveGoogle + ? googleDriveActive + : googleDriveInactive + } + + {this.state.confirmGoogleTransfer && +
+ { this.state.saveGoogle + ? `Would you like to transfer this brew from your Google Drive storage back to the Homebrewery?` + : `Would you like to transfer this brew from the Homebrewery to your personal Google Drive storage?` + } +
+
+ Yes +
+
+ No +
+
+ } + + {this.state.alertLoginToTransfer && +
+ You must be signed in to a Google account to transfer + between the homebrewery and Google Drive! + +
+ Sign In +
+
+
+ Not Now +
+
+ } +
; + }, + + 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 saving...; + } + if(this.state.isPending && this.hasChanges()){ + return Save Now; + } + if(!this.state.isPending && !this.state.isSaving){ + return saved.; + } + }, + + processShareId : function() { + return this.state.brew.googleId ? + this.state.brew.googleId + this.state.brew.shareId : + this.state.brew.shareId; + }, + + getRedditLink : function(){ + + const shareLink = this.processShareId(); + const systems = this.props.brew.systems.length > 0 ? ` [${this.props.brew.systems.join(' - ')}]` : ''; + const title = `${this.props.brew.title} ${systems}`; + const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. + +**[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`; + + return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`; + }, + + renderNavbar : function(){ + const shareLink = this.processShareId(); + + return + + {this.state.alertTrashedGoogleBrew && +
+ This brew is currently in your Trash folder on Google Drive!
If you want to keep it, make sure to move it before it is deleted permanently!
+
+ OK +
+
+ } + + + {this.state.brew.title} + + + + {this.renderGoogleDriveIcon()} + {this.renderSaveButton()} + + + + + share + + + view + + {navigator.clipboard.writeText(`https://homebrewery.naturalcrit.com/share/${shareLink}`);}}> + copy url + + + post to reddit + + + + + + + +
; + }, + + render : function(){ + return
+ + {this.renderNavbar()} + +
+ + + + +
+
; } }); diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 0e424a998..3f09cac4e 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -3,11 +3,20 @@ require('./newPage.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); - -const EditorPage = require('../basePages/editorPage/editorPage.jsx'); +const request = require('superagent'); const Markdown = require('naturalcrit/markdown.js'); +const Nav = require('naturalcrit/nav/nav.jsx'); +const Navbar = require('../../navbar/navbar.jsx'); +const AccountNavItem = require('../../navbar/account.navitem.jsx'); +const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; +const IssueNavItem = require('../../navbar/issue.navitem.jsx'); + +const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); +const Editor = require('../../editor/editor.jsx'); +const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx'); + const BREWKEY = 'homebrewery-new'; const STYLEKEY = 'homebrewery-new-style'; const METAKEY = 'homebrewery-new-meta'; @@ -73,13 +82,238 @@ const NewPage = createClass({ }; }, - render : function(){ - const googleDriveOptions = [ - 'Set save location to the Homebrewery?', - 'Set save location to your personal Google Drive storage?' - ]; + componentDidMount : function() { + document.addEventListener('keydown', this.handleControlKeys); + }, + componentWillUnmount : function() { + document.removeEventListener('keydown', this.handleControlKeys); + }, - return ; + handleControlKeys : function(e){ + if(!(e.ctrlKey || e.metaKey)) return; + const S_KEY = 83; + const P_KEY = 80; + if(e.keyCode == S_KEY) this.save(); + if(e.keyCode == P_KEY) this.print(); + if(e.keyCode == P_KEY || e.keyCode == S_KEY){ + e.stopPropagation(); + e.preventDefault(); + } + }, + + handleSplitMove : function(){ + this.refs.editor.update(); + }, + + handleTextChange : function(text){ + //If there are errors, run the validator on every change to give quick feedback + let htmlErrors = this.state.htmlErrors; + if(htmlErrors.length) htmlErrors = Markdown.validate(text); + + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, { text: text }), + htmlErrors : htmlErrors + })); + localStorage.setItem(BREWKEY, text); + }, + + handleStyleChange : function(style){ + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, { style: style }), + })); + localStorage.setItem(STYLEKEY, style); + }, + + handleMetaChange : function(metadata){ + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, metadata), + })); + localStorage.setItem(METAKEY, JSON.stringify({ + // 'title' : this.state.brew.title, + // 'description' : this.state.brew.description, + 'renderer' : this.state.brew.renderer + })); + }, + + clearErrors : function(){ + this.setState({ + errors : null, + isSaving : false + + }); + }, + + save : async function(){ + this.setState({ + isSaving : true + }); + + console.log('saving new brew'); + + let brew = this.state.brew; + // Split out CSS to Style if CSS codefence exists + if(brew.text.startsWith('```css') && brew.text.indexOf('```\n\n') > 0) { + const index = brew.text.indexOf('```\n\n'); + brew.style = `${brew.style ? `${brew.style}\n` : ''}${brew.text.slice(7, index - 1)}`; + brew.text = brew.text.slice(index + 5); + }; + + brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; + + if(this.state.saveGoogle) { + const res = await request + .post('/api/newGoogle/') + .send(brew) + .catch((err)=>{ + console.log(err.status === 401 + ? 'Not signed in!' + : 'Error Creating New Google Brew!'); + this.setState({ isSaving: false, errors: err }); + return; + }); + + brew = res.body; + localStorage.removeItem(BREWKEY); + localStorage.removeItem(STYLEKEY); + localStorage.removeItem(METAKEY); + window.location = `/edit/${brew.googleId}${brew.editId}`; + } else { + request.post('/api') + .send(brew) + .end((err, res)=>{ + if(err){ + this.setState({ + isSaving : false + }); + return; + } + window.onbeforeunload = function(){}; + brew = res.body; + localStorage.removeItem(BREWKEY); + localStorage.removeItem(STYLEKEY); + localStorage.removeItem(METAKEY); + window.location = `/edit/${brew.editId}`; + }); + } + }, + + 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 + save... + ; + } else { + return + save + ; + } + }, + + print : function(){ + window.open('/print?dialog=true&local=print', '_blank'); + }, + + renderLocalPrintButton : function(){ + return + get PDF + ; + }, + + renderNavbar : function(){ + return + + + {this.state.brew.title} + + + + {this.renderSaveButton()} + {this.renderLocalPrintButton()} + + + + + ; + }, + + render : function(){ + return
+ {this.renderNavbar()} +
+ + + + +
+
; } }); From 48a5c12ab71d92b1be3d2b54ea79d2bdde16bef6 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Fri, 31 Dec 2021 12:59:48 +1300 Subject: [PATCH 06/21] Rebase on new `master` --- .../basePages/listPage/brewItem/brewItem.jsx | 144 +++++++++++++ .../basePages/listPage/brewItem/brewItem.less | 75 +++++++ .../pages/basePages/listPage/listPage.jsx | 189 ++++++++++++++++++ .../pages/basePages/listPage/listPage.less | 77 +++++++ 4 files changed, 485 insertions(+) create mode 100644 client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx create mode 100644 client/homebrew/pages/basePages/listPage/brewItem/brewItem.less create mode 100644 client/homebrew/pages/basePages/listPage/listPage.jsx create mode 100644 client/homebrew/pages/basePages/listPage/listPage.less diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx new file mode 100644 index 000000000..c7c4d6f94 --- /dev/null +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx @@ -0,0 +1,144 @@ +require('./brewItem.less'); +const React = require('react'); +const createClass = require('create-react-class'); +const _ = require('lodash'); +const cx = require('classnames'); +const moment = require('moment'); +const request = require('superagent'); + +const googleDriveIcon = require('../../../../googleDrive.png'); +const dedent = require('dedent-tabs').default; + +const BrewItem = createClass({ + getDefaultProps : function() { + return { + brew : { + title : '', + description : '', + + authors : [] + } + }; + }, + + deleteBrew : function(){ + if(this.props.brew.authors.length <= 1){ + if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return; + if(!confirm('Are you REALLY sure? You will not be able to recover the document.')) return; + } else { + if(!confirm('Are you sure you want to remove this brew from your collection? This will remove you as an editor, but other owners will still be able to access the document.')) return; + if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return; + } + + if(this.props.brew.googleId) { + request.get(`/api/removeGoogle/${this.props.brew.googleId}${this.props.brew.editId}`) + .send() + .end(function(err, res){ + location.reload(); + }); + } else { + request.delete(`/api/${this.props.brew.editId}`) + .send() + .end(function(err, res){ + location.reload(); + }); + } + }, + + renderDeleteBrewLink : function(){ + if(!this.props.brew.editId) return; + + return + + ; + }, + + renderEditLink : function(){ + if(!this.props.brew.editId) return; + + let editLink = this.props.brew.editId; + if(this.props.brew.googleId) { + editLink = this.props.brew.googleId + editLink; + } + + return + + ; + }, + + renderShareLink : function(){ + if(!this.props.brew.shareId) return; + + let shareLink = this.props.brew.shareId; + if(this.props.brew.googleId) { + shareLink = this.props.brew.googleId + shareLink; + } + + return + + ; + }, + + renderDownloadLink : function(){ + if(!this.props.brew.shareId) return; + + let shareLink = this.props.brew.shareId; + if(this.props.brew.googleId) { + shareLink = this.props.brew.googleId + shareLink; + } + + return + + ; + }, + + renderGoogleDriveIcon : function(){ + if(!this.props.brew.gDrive) return; + + return + googleDriveIcon + ; + }, + + render : function(){ + const brew = this.props.brew; + const dateFormatString = 'YYYY-MM-DD HH:mm:ss'; + + return
+
+

{brew.title}

+

{brew.description}

+
+
+
+ + {brew.authors.join(', ')} + +
+ + {brew.views} + + {brew.pageCount && + + {brew.pageCount} + + } + + {moment(brew.updatedAt).fromNow()} + + {this.renderGoogleDriveIcon()} +
+ +
+ {this.renderShareLink()} + {this.renderEditLink()} + {this.renderDownloadLink()} + {this.renderDeleteBrewLink()} +
+
; + } +}); + +module.exports = BrewItem; diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.less b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.less new file mode 100644 index 000000000..d323874f5 --- /dev/null +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.less @@ -0,0 +1,75 @@ + +.brewItem{ + position : relative; + display : inline-block; + vertical-align : top; + box-sizing : border-box; + box-sizing : border-box; + overflow : hidden; + width : 48%; + min-height : 105px; + margin-right : 15px; + margin-bottom : 15px; + padding : 5px 15px 2px 8px; + padding-right : 15px; + border : 1px solid #c9ad6a; + border-radius : 5px; + -webkit-column-break-inside : avoid; + page-break-inside : avoid; + break-inside : avoid; + .text { + min-height : 54px; + h4{ + margin-bottom : 5px; + font-size : 2.2em; + } + } + .info{ + position: initial; + bottom: 2px; + font-family : ScalySans; + font-size : 1.2em; + &>span{ + margin-right : 12px; + line-height : 1.5em; + } + } + &:hover{ + .links{ + opacity : 1; + } + } + &:nth-child(2n + 1){ + margin-right : 0px; + } + .links{ + .animate(opacity); + position : absolute; + top : 0px; + right : 0px; + height : 100%; + width : 2em; + opacity : 0; + background-color : fade(black, 60%); + text-align : center; + a{ + .animate(opacity); + display : block; + margin : 8px 0px; + opacity : 0.6; + font-size : 1.3em; + color : white; + &:hover{ + opacity : 1; + } + i{ + cursor : pointer; + } + } + } + .googleDriveIcon { + height : 20px; + padding : 0px; + margin : -5px; + } +} diff --git a/client/homebrew/pages/basePages/listPage/listPage.jsx b/client/homebrew/pages/basePages/listPage/listPage.jsx new file mode 100644 index 000000000..e4beb4a94 --- /dev/null +++ b/client/homebrew/pages/basePages/listPage/listPage.jsx @@ -0,0 +1,189 @@ +require('./listPage.less'); +const React = require('react'); +const createClass = require('create-react-class'); +const _ = require('lodash'); +const cx = require('classnames'); + +const moment = require('moment'); + +const Nav = require('naturalcrit/nav/nav.jsx'); +const Navbar = require('../../../navbar/navbar.jsx'); + +const RecentNavItem = require('../../../navbar/recent.navitem.jsx').both; +const Account = require('../../../navbar/account.navitem.jsx'); +const NewBrew = require('../../../navbar/newbrew.navitem.jsx'); +const BrewItem = require('./brewItem/brewItem.jsx'); +const ReportIssue = require('../../../navbar/issue.navitem.jsx'); + +// const brew = { +// title : 'SUPER Long title woah now', +// authors : [] +// }; + +//const BREWS = _.times(25, ()=>{ return brew;}); + + +const ListPage = createClass({ + getDefaultProps : function() { + return { + brewCollection : [ + { + title : '', + class : '', + brews : [] + } + ] + }; + }, + getInitialState : function() { + return { + sortType : 'alpha', + sortDir : 'asc', + filterString : '' + }; + }, + + renderBrews : function(brews){ + if(!brews || !brews.length) return
No Brews.
; + + const sortedBrews = this.sortBrews(brews); + + return _.map(sortedBrews, (brew, idx)=>{ + return ; + }); + }, + + sortBrewOrder : function(brew){ + if(!brew.title){brew.title = 'No Title';} + const mapping = { + 'alpha' : _.deburr(brew.title.toLowerCase()), + 'created' : moment(brew.createdAt).format(), + 'updated' : moment(brew.updatedAt).format(), + 'views' : brew.views, + 'latest' : moment(brew.lastViewed).format() + }; + return mapping[this.state.sortType]; + }, + + sortBrews : function(brews){ + return _.orderBy(brews, (brew)=>{ return this.sortBrewOrder(brew); }, this.state.sortDir); + }, + + handleSortOptionChange : function(event){ + this.setState({ + sortType : event.target.value + }); + }, + + handleSortDirChange : function(event){ + this.setState({ + sortDir : `${(this.state.sortDir == 'asc' ? 'desc' : 'asc')}` + }); + }, + + renderSortOption : function(sortTitle, sortValue){ + return + + ; + }, + + handleFilterTextChange : function(e){ + this.setState({ + filterString : e.target.value + }); + return; + }, + + renderFilterOption : function(){ + return + + ; + }, + + renderSortOptions : function(){ + return
+ + + + + {this.renderSortOption('Title', 'alpha')} + {this.renderSortOption('Created Date', 'created')} + {this.renderSortOption('Updated Date', 'updated')} + {this.renderSortOption('Views', 'views')} + {/* {this.renderSortOption('Latest', 'latest')} */} + + + {this.renderFilterOption()} + + +
+
Sort by :
+
+
Direction :
+
+ +
+
; + }, + + getSortedBrews : function(brewCollection){ + const testString = _.deburr(this.state.filterString).toLowerCase(); + const brews = this.state.filterString ? _.filter(brewCollection.brews, (brew)=>{ + return (_.deburr(brew.title).toLowerCase().includes(testString)) || + (_.deburr(brew.description).toLowerCase().includes(testString)); + }) : this.props.brewCollection.brews; + return _.groupBy(brews, (brew)=>{ + return (brew.published ? 'published' : 'private'); + }); + }, + + renderBrewCollection : function(brewCollection){ + return _.map(brewCollection, (brewItem, idx)=>{ + return
+

{brewItem.title || 'No Title'}

+ {this.renderBrews(brewItem.brews)} +
; + }); + }, + + render : function(){ + return
+ + + + + + + + + + +
+
+ {this.renderSortOptions()} + {this.renderBrewCollection(this.props.brewCollection)} +
+
+
; + } +}); + +module.exports = ListPage; diff --git a/client/homebrew/pages/basePages/listPage/listPage.less b/client/homebrew/pages/basePages/listPage/listPage.less new file mode 100644 index 000000000..6be946404 --- /dev/null +++ b/client/homebrew/pages/basePages/listPage/listPage.less @@ -0,0 +1,77 @@ + +.noColumns(){ + column-count : auto; + column-fill : auto; + column-gap : auto; + column-width : auto; + -webkit-column-count : auto; + -moz-column-count : auto; + -webkit-column-width : auto; + -moz-column-width : auto; + -webkit-column-gap : auto; + -moz-column-gap : auto; +} +.listPage{ + .content{ + overflow-y : scroll; + .phb{ + .noColumns(); + height : auto; + min-height : 279.4mm; + margin : 20px auto; + &::after{ + display : none; + } + .noBrews{ + margin : 10px 0px; + font-size : 1.3em; + font-style : italic; + } + + } + } + .sort-container{ + font-family : 'Open Sans', sans-serif; + position : fixed; + top : 35px; + left : calc(50vw - 408px); + border : 2px solid #58180D; + width : 800px; + background-color : #EEE5CE; + padding : 2px; + text-align : center; + z-index : 15; + h6{ + text-transform : uppercase; + font-family : 'Open Sans', sans-serif; + font-size : 11px; + font-weight : bold; + color : #58180D; + } + table{ + margin : 0px; + vertical-align : middle; + tbody tr{ + background-color: transparent !important; + i{ + padding-right : 5px + } + button{ + background-color : transparent; + color : #58180D; + font-family : 'Open Sans', sans-serif; + font-size : 11px; + text-transform : uppercase; + font-weight : normal; + &.active{ + font-weight : bold; + border : 2px solid #58180D; + } + &.sortDir{ + width : 75px; + } + } + } + } + } +} From 77f5e3e835749ad4ed347ae34f8e1699af5f3625 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 4 Nov 2021 11:44:05 +1300 Subject: [PATCH 07/21] Remove unnecessary `userPage.less` --- client/homebrew/pages/userPage/userPage.jsx | 2 +- client/homebrew/pages/userPage/userPage.less | 77 -------------------- 2 files changed, 1 insertion(+), 78 deletions(-) delete mode 100644 client/homebrew/pages/userPage/userPage.less diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index 700ca4131..498ff9df3 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -1,4 +1,4 @@ -require('./userPage.less'); +//require('./userPage.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); diff --git a/client/homebrew/pages/userPage/userPage.less b/client/homebrew/pages/userPage/userPage.less deleted file mode 100644 index d968aab9a..000000000 --- a/client/homebrew/pages/userPage/userPage.less +++ /dev/null @@ -1,77 +0,0 @@ - -.noColumns(){ - column-count : auto; - column-fill : auto; - column-gap : auto; - column-width : auto; - -webkit-column-count : auto; - -moz-column-count : auto; - -webkit-column-width : auto; - -moz-column-width : auto; - -webkit-column-gap : auto; - -moz-column-gap : auto; -} -.userPage{ - .content{ - overflow-y : scroll; - .phb{ - .noColumns(); - height : auto; - min-height : 279.4mm; - margin : 20px auto; - &::after{ - display : none; - } - .noBrews{ - margin : 10px 0px; - font-size : 1.3em; - font-style : italic; - } - - } - } - .sort-container{ - font-family : 'Open Sans', sans-serif; - position : fixed; - top : 35px; - left : calc(50vw - 408px); - border : 2px solid #58180D; - width : 800px; - background-color : #EEE5CE; - padding : 2px; - text-align : center; - z-index : 15; - h6{ - text-transform : uppercase; - font-family : 'Open Sans', sans-serif; - font-size : 11px; - font-weight : bold; - color : #58180D; - } - table{ - margin : 0px; - vertical-align : middle; - tbody tr{ - background-color: transparent !important; - i{ - padding-right : 5px - } - button{ - background-color : transparent; - color : #58180D; - font-family : 'Open Sans', sans-serif; - font-size : 11px; - text-transform : uppercase; - font-weight : normal; - &.active{ - font-weight : bold; - border : 2px solid #58180D; - } - &.sortDir{ - width : 75px; - } - } - } - } - } -} From 00158c1894cfce17635e87c92bd82030d49bab09 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 4 Nov 2021 11:44:41 +1300 Subject: [PATCH 08/21] Rebase on new `master` --- .../pages/basePages/editorPage/editorPage.jsx | 578 ++++++++++++++++++ .../basePages/editorPage/editorPage.less | 99 +++ client/homebrew/pages/userPage/userPage.jsx | 1 - 3 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 client/homebrew/pages/basePages/editorPage/editorPage.jsx create mode 100644 client/homebrew/pages/basePages/editorPage/editorPage.less diff --git a/client/homebrew/pages/basePages/editorPage/editorPage.jsx b/client/homebrew/pages/basePages/editorPage/editorPage.jsx new file mode 100644 index 000000000..99a5c2b52 --- /dev/null +++ b/client/homebrew/pages/basePages/editorPage/editorPage.jsx @@ -0,0 +1,578 @@ +/* eslint-disable max-lines */ +require('./editorPage.less'); +const React = require('react'); +const createClass = require('create-react-class'); +const _ = require('lodash'); +const request = require('superagent'); +const { Meta } = require('vitreum/headtags'); + +const Nav = require('naturalcrit/nav/nav.jsx'); +const Navbar = require('../../../navbar/navbar.jsx'); + +const NewBrew = require('../../../navbar/newbrew.navitem.jsx'); +const ReportIssue = require('../../../navbar/issue.navitem.jsx'); +const PrintLink = require('../../../navbar/print.navitem.jsx'); +const Account = require('../../../navbar/account.navitem.jsx'); +const RecentNavItem = require('../../../navbar/recent.navitem.jsx').both; + +const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); +const Editor = require('../../../editor/editor.jsx'); +const BrewRenderer = require('../../../brewRenderer/brewRenderer.jsx'); + +const Markdown = require('naturalcrit/markdown.js'); + +const googleDriveActive = require('../../../googleDrive.png'); +const googleDriveInactive = require('../../../googleDriveMono.png'); + +const SAVE_TIMEOUT = 3000; + +const BREWKEY = 'homebrewery-new'; +const STYLEKEY = 'homebrewery-new-style'; +const METAKEY = 'homebrewery-new-meta'; + +const EditorPage = createClass({ + getDefaultProps : function() { + return { + brew : { + text : '', + style : '', + shareId : null, + editId : null, + createdAt : null, + updatedAt : null, + gDrive : false, + trashed : false, + + title : '', + description : '', + tags : '', + published : false, + authors : [], + systems : [], + renderer : 'legacy' + }, + pageType : 'edit', + googleDriveOptions : [ + 'DRIVE > HB', + 'HB > DRIVE' + ] + }; + }, + + getInitialState : function() { + return { + brew : this.props.brew, + isSaving : false, + isPending : false, + alertTrashedGoogleBrew : this.props.brew.trashed, + alertLoginToTransfer : false, + saveGoogle : this.props.brew.googleId ? true : false, + confirmGoogleTransfer : false, + errors : null, + htmlErrors : Markdown.validate(this.props.brew.text), + url : '' + }; + // return { + // brew : { + // text : brew.text || '', + // style : brew.style || undefined, + // gDrive : false, + // title : brew.title || '', + // description : brew.description || '', + // tags : brew.tags || '', + // published : false, + // authors : [], + // systems : brew.systems || [], + // renderer : brew.renderer || 'legacy' + // }, + + // isSaving : false, + // isPending : false, + // alertTrashedGoogleBrew : this.props.brew.trashed, + // alertLoginToTransfer : false, + // saveGoogle : (global.account && global.account.googleId ? true : false), + // confirmGoogleTransfer : false, + // errors : null, + // htmlErrors : Markdown.validate(brew.text), + // url : '' + // }; + }, + savedBrew : null, + + componentDidMount : function(){ + this.setState({ + url : window.location.href + }); + + this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy + + this.trySave(); + window.onbeforeunload = ()=>{ + if(this.state.isSaving || this.state.isPending){ + return 'You have unsaved changes!'; + } + }; + + this.setState((prevState)=>({ + htmlErrors : Markdown.validate(prevState.brew.text) + })); + + document.addEventListener('keydown', this.handleControlKeys); + }, + componentWillUnmount : function() { + window.onbeforeunload = function(){}; + document.removeEventListener('keydown', this.handleControlKeys); + }, + + handleControlKeys : function(e){ + if(!(e.ctrlKey || e.metaKey)) return; + const S_KEY = 83; + const P_KEY = 80; + if(e.keyCode == S_KEY) this.save(); + if(e.keyCode == P_KEY) window.open(`/print/${this.processShareId()}?dialog=true`, '_blank').focus(); + if(e.keyCode == P_KEY || e.keyCode == S_KEY){ + e.stopPropagation(); + e.preventDefault(); + } + }, + + isEdit : function(){ + return this.props.pageType == 'edit'; + }, + + isNew : function(){ + return this.props.pageType == 'new'; + }, + + handleSplitMove : function(){ + this.refs.editor.update(); + }, + + handleTextChange : function(text){ + //If there are errors, run the validator on every change to give quick feedback + let htmlErrors = this.state.htmlErrors; + if(htmlErrors.length) htmlErrors = Markdown.validate(text); + + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, { text: text }), + isPending : true, + htmlErrors : htmlErrors + }), ()=>this.trySave()); + }, + + handleStyleChange : function(style){ + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, { style: style }), + isPending : true + }), ()=>this.trySave()); + }, + + handleMetaChange : function(metadata){ + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, metadata), + isPending : true, + }), ()=>this.trySave()); + + }, + + hasChanges : function(){ + return !_.isEqual(this.state.brew, this.savedBrew); + }, + + trySave : function(){ + if(!this.isEdit()) return; + if(!this.debounceSave) this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT); + if(this.hasChanges()){ + this.debounceSave(); + } else { + this.debounceSave.cancel(); + } + }, + + handleGoogleClick : function(){ + if(!global.account?.googleId) { + this.setState({ + alertLoginToTransfer : true + }); + return; + } + this.setState((prevState)=>({ + confirmGoogleTransfer : !prevState.confirmGoogleTransfer + })); + this.clearErrors(); + }, + + closeAlerts : function(event){ + event.stopPropagation(); //Only handle click once so alert doesn't reopen + this.setState({ + alertTrashedGoogleBrew : false, + alertLoginToTransfer : false, + confirmGoogleTransfer : false + }); + }, + + toggleGoogleStorage : function(){ + this.setState((prevState)=>({ + saveGoogle : !prevState.saveGoogle, + isSaving : false, + errors : null + }), ()=>this.isEdit() && this.save()); + }, + + clearErrors : function(){ + this.setState({ + errors : null, + isSaving : false + + }); + }, + + save : async function(){ + this.setState((prevState)=>({ + isSaving : true, + errors : null, + htmlErrors : Markdown.validate(prevState.brew.text) + })); + + if(this.isEdit()){ + if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel(); + + const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId); + + const brew = this.state.brew; + brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; + + if(this.state.saveGoogle) { + if(transfer) { + const res = await request + .post('/api/newGoogle/') + .send(brew) + .catch((err)=>{ + console.log(err.status === 401 + ? 'Not signed in!' + : 'Error Transferring to Google!'); + this.setState({ errors: err, saveGoogle: false }); + }); + + if(!res) { return; } + + console.log('Deleting Local Copy'); + await request.delete(`/api/${brew.editId}`) + .send() + .catch((err)=>{ + console.log('Error deleting Local Copy'); + }); + + this.savedBrew = res.body; + history.replaceState(null, null, `/edit/${this.savedBrew.googleId}${this.savedBrew.editId}`); //update URL to match doc ID + } else { + const res = await request + .put(`/api/updateGoogle/${brew.editId}`) + .send(brew) + .catch((err)=>{ + console.log(err.status === 401 + ? 'Not signed in!' + : 'Error Saving to Google!'); + this.setState({ errors: err }); + return; + }); + + this.savedBrew = res.body; + } + } else { + if(transfer) { + const res = await request.post('/api') + .send(brew) + .catch((err)=>{ + console.log('Error creating Local Copy'); + this.setState({ errors: err }); + return; + }); + + await request.get(`/api/removeGoogle/${brew.googleId}${brew.editId}`) + .send() + .catch((err)=>{ + console.log('Error Deleting Google Brew'); + }); + + this.savedBrew = res.body; + history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); //update URL to match doc ID + } else { + const res = await request + .put(`/api/update/${brew.editId}`) + .send(brew) + .catch((err)=>{ + console.log('Error Updating Local Brew'); + this.setState({ errors: err }); + return; + }); + + this.savedBrew = res.body; + } + } + + this.setState((prevState)=>({ + brew : _.merge({}, prevState.brew, { + googleId : this.savedBrew.googleId ? this.savedBrew.googleId : null, + editId : this.savedBrew.editId, + shareId : this.savedBrew.shareId + }), + isPending : false, + isSaving : false, + })); + } + + if(this.isNew()){ + console.log('saving new brew'); + + let brew = this.state.brew; + // Split out CSS to Style if CSS codefence exists + if(brew.text.startsWith('```css') && brew.text.indexOf('```\n\n') > 0) { + const index = brew.text.indexOf('```\n\n'); + brew.style = `${brew.style ? `${brew.style}\n` : ''}${brew.text.slice(7, index - 1)}`; + brew.text = brew.text.slice(index + 5); + }; + + brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; + + if(this.state.saveGoogle) { + const res = await request + .post('/api/newGoogle/') + .send(brew) + .catch((err)=>{ + console.log(err.status === 401 + ? 'Not signed in!' + : 'Error Creating New Google Brew!'); + this.setState({ isSaving: false, errors: err }); + return; + }); + + brew = res.body; + localStorage.removeItem(BREWKEY); + localStorage.removeItem(STYLEKEY); + localStorage.removeItem(METAKEY); + window.location = `/edit/${brew.googleId}${brew.editId}`; + } else { + request.post('/api') + .send(brew) + .end((err, res)=>{ + if(err){ + this.setState({ + isSaving : false + }); + return; + } + window.onbeforeunload = function(){}; + brew = res.body; + localStorage.removeItem(BREWKEY); + localStorage.removeItem(STYLEKEY); + localStorage.removeItem(METAKEY); + window.location = `/edit/${brew.editId}`; + }); + } + } + }, + + renderGoogleDriveIcon : function(){ + return + {this.state.saveGoogle + ? googleDriveActive + : googleDriveInactive + } + + {this.state.confirmGoogleTransfer && +
+ { this.state.saveGoogle + ? this.props.googleDriveOptions[0] + : this.props.googleDriveOptions[1] + } +
+
+ Yes +
+
+ No +
+
+ } + + {this.state.alertLoginToTransfer && +
+ You must be signed in to a Google account to transfer + between the homebrewery and Google Drive! + +
+ Sign In +
+
+
+ Not Now +
+
+ } +
; + }, + + 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 saving...; + } + if(this.state.isPending && this.hasChanges()){ + return Save Now; + } + if(!this.state.isPending && !this.state.isSaving){ + return saved.; + } + }, + + processShareId : function() { + return this.state.brew.googleId ? + this.state.brew.googleId + this.state.brew.shareId : + this.state.brew.shareId; + }, + + getRedditLink : function(){ + + const shareLink = this.processShareId(); + const systems = this.props.brew.systems.length > 0 ? ` [${this.props.brew.systems.join(' - ')}]` : ''; + const title = `${this.props.brew.title} ${systems}`; + const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. + +**[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`; + + return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`; + }, + + renderNavbar : function(){ + const shareLink = this.processShareId(); + + return + + {this.state.alertTrashedGoogleBrew && +
+ This brew is currently in your Trash folder on Google Drive!
If you want to keep it, make sure to move it before it is deleted permanently!
+
+ OK +
+
+ } + + + {this.state.brew.title} + + + + {this.renderGoogleDriveIcon()} + {this.renderSaveButton()} + + + {this.isEdit() && <> + + + share + + + view + + {navigator.clipboard.writeText(`https://homebrewery.naturalcrit.com/share/${shareLink}`);}}> + copy url + + + post to reddit + + + + + } + + + + +
; + }, + + render : function(){ + return
+ + {this.renderNavbar()} + +
+ + + + +
+
; + } +}); + +module.exports = EditorPage; diff --git a/client/homebrew/pages/basePages/editorPage/editorPage.less b/client/homebrew/pages/basePages/editorPage/editorPage.less new file mode 100644 index 000000000..0cbfadcbd --- /dev/null +++ b/client/homebrew/pages/basePages/editorPage/editorPage.less @@ -0,0 +1,99 @@ +@keyframes glideDown { + 0% {transform : translate(-50% + 3px, 0px); + opacity : 0;} + 100% {transform : translate(-50% + 3px, 10px); + opacity : 1;} +} +.editPage{ + .navItem.save{ + width : 106px; + text-align : center; + position : relative; + &.saved{ + cursor : initial; + color : #666; + } + &.error{ + position : relative; + background-color : @red; + } + } + .googleDriveStorage { + position : relative; + } + .googleDriveStorage img{ + height : 20px; + padding : 0px; + margin : -5px; + } + .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/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index 498ff9df3..52744085e 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -1,4 +1,3 @@ -//require('./userPage.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); From eca12aae824090ad3f20590e4d8897d45ff5b4db Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Fri, 31 Dec 2021 13:06:21 +1300 Subject: [PATCH 09/21] Rebase on `master` --- .../pages/basePages/editorPage/editorPage.jsx | 578 ------------------ .../basePages/editorPage/editorPage.less | 99 --- client/homebrew/pages/editPage/editPage.jsx | 1 + client/homebrew/pages/newPage/newPage.jsx | 1 + 4 files changed, 2 insertions(+), 677 deletions(-) delete mode 100644 client/homebrew/pages/basePages/editorPage/editorPage.jsx delete mode 100644 client/homebrew/pages/basePages/editorPage/editorPage.less diff --git a/client/homebrew/pages/basePages/editorPage/editorPage.jsx b/client/homebrew/pages/basePages/editorPage/editorPage.jsx deleted file mode 100644 index 99a5c2b52..000000000 --- a/client/homebrew/pages/basePages/editorPage/editorPage.jsx +++ /dev/null @@ -1,578 +0,0 @@ -/* eslint-disable max-lines */ -require('./editorPage.less'); -const React = require('react'); -const createClass = require('create-react-class'); -const _ = require('lodash'); -const request = require('superagent'); -const { Meta } = require('vitreum/headtags'); - -const Nav = require('naturalcrit/nav/nav.jsx'); -const Navbar = require('../../../navbar/navbar.jsx'); - -const NewBrew = require('../../../navbar/newbrew.navitem.jsx'); -const ReportIssue = require('../../../navbar/issue.navitem.jsx'); -const PrintLink = require('../../../navbar/print.navitem.jsx'); -const Account = require('../../../navbar/account.navitem.jsx'); -const RecentNavItem = require('../../../navbar/recent.navitem.jsx').both; - -const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); -const Editor = require('../../../editor/editor.jsx'); -const BrewRenderer = require('../../../brewRenderer/brewRenderer.jsx'); - -const Markdown = require('naturalcrit/markdown.js'); - -const googleDriveActive = require('../../../googleDrive.png'); -const googleDriveInactive = require('../../../googleDriveMono.png'); - -const SAVE_TIMEOUT = 3000; - -const BREWKEY = 'homebrewery-new'; -const STYLEKEY = 'homebrewery-new-style'; -const METAKEY = 'homebrewery-new-meta'; - -const EditorPage = createClass({ - getDefaultProps : function() { - return { - brew : { - text : '', - style : '', - shareId : null, - editId : null, - createdAt : null, - updatedAt : null, - gDrive : false, - trashed : false, - - title : '', - description : '', - tags : '', - published : false, - authors : [], - systems : [], - renderer : 'legacy' - }, - pageType : 'edit', - googleDriveOptions : [ - 'DRIVE > HB', - 'HB > DRIVE' - ] - }; - }, - - getInitialState : function() { - return { - brew : this.props.brew, - isSaving : false, - isPending : false, - alertTrashedGoogleBrew : this.props.brew.trashed, - alertLoginToTransfer : false, - saveGoogle : this.props.brew.googleId ? true : false, - confirmGoogleTransfer : false, - errors : null, - htmlErrors : Markdown.validate(this.props.brew.text), - url : '' - }; - // return { - // brew : { - // text : brew.text || '', - // style : brew.style || undefined, - // gDrive : false, - // title : brew.title || '', - // description : brew.description || '', - // tags : brew.tags || '', - // published : false, - // authors : [], - // systems : brew.systems || [], - // renderer : brew.renderer || 'legacy' - // }, - - // isSaving : false, - // isPending : false, - // alertTrashedGoogleBrew : this.props.brew.trashed, - // alertLoginToTransfer : false, - // saveGoogle : (global.account && global.account.googleId ? true : false), - // confirmGoogleTransfer : false, - // errors : null, - // htmlErrors : Markdown.validate(brew.text), - // url : '' - // }; - }, - savedBrew : null, - - componentDidMount : function(){ - this.setState({ - url : window.location.href - }); - - this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy - - this.trySave(); - window.onbeforeunload = ()=>{ - if(this.state.isSaving || this.state.isPending){ - return 'You have unsaved changes!'; - } - }; - - this.setState((prevState)=>({ - htmlErrors : Markdown.validate(prevState.brew.text) - })); - - document.addEventListener('keydown', this.handleControlKeys); - }, - componentWillUnmount : function() { - window.onbeforeunload = function(){}; - document.removeEventListener('keydown', this.handleControlKeys); - }, - - handleControlKeys : function(e){ - if(!(e.ctrlKey || e.metaKey)) return; - const S_KEY = 83; - const P_KEY = 80; - if(e.keyCode == S_KEY) this.save(); - if(e.keyCode == P_KEY) window.open(`/print/${this.processShareId()}?dialog=true`, '_blank').focus(); - if(e.keyCode == P_KEY || e.keyCode == S_KEY){ - e.stopPropagation(); - e.preventDefault(); - } - }, - - isEdit : function(){ - return this.props.pageType == 'edit'; - }, - - isNew : function(){ - return this.props.pageType == 'new'; - }, - - handleSplitMove : function(){ - this.refs.editor.update(); - }, - - handleTextChange : function(text){ - //If there are errors, run the validator on every change to give quick feedback - let htmlErrors = this.state.htmlErrors; - if(htmlErrors.length) htmlErrors = Markdown.validate(text); - - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, { text: text }), - isPending : true, - htmlErrors : htmlErrors - }), ()=>this.trySave()); - }, - - handleStyleChange : function(style){ - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, { style: style }), - isPending : true - }), ()=>this.trySave()); - }, - - handleMetaChange : function(metadata){ - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, metadata), - isPending : true, - }), ()=>this.trySave()); - - }, - - hasChanges : function(){ - return !_.isEqual(this.state.brew, this.savedBrew); - }, - - trySave : function(){ - if(!this.isEdit()) return; - if(!this.debounceSave) this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT); - if(this.hasChanges()){ - this.debounceSave(); - } else { - this.debounceSave.cancel(); - } - }, - - handleGoogleClick : function(){ - if(!global.account?.googleId) { - this.setState({ - alertLoginToTransfer : true - }); - return; - } - this.setState((prevState)=>({ - confirmGoogleTransfer : !prevState.confirmGoogleTransfer - })); - this.clearErrors(); - }, - - closeAlerts : function(event){ - event.stopPropagation(); //Only handle click once so alert doesn't reopen - this.setState({ - alertTrashedGoogleBrew : false, - alertLoginToTransfer : false, - confirmGoogleTransfer : false - }); - }, - - toggleGoogleStorage : function(){ - this.setState((prevState)=>({ - saveGoogle : !prevState.saveGoogle, - isSaving : false, - errors : null - }), ()=>this.isEdit() && this.save()); - }, - - clearErrors : function(){ - this.setState({ - errors : null, - isSaving : false - - }); - }, - - save : async function(){ - this.setState((prevState)=>({ - isSaving : true, - errors : null, - htmlErrors : Markdown.validate(prevState.brew.text) - })); - - if(this.isEdit()){ - if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel(); - - const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId); - - const brew = this.state.brew; - brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; - - if(this.state.saveGoogle) { - if(transfer) { - const res = await request - .post('/api/newGoogle/') - .send(brew) - .catch((err)=>{ - console.log(err.status === 401 - ? 'Not signed in!' - : 'Error Transferring to Google!'); - this.setState({ errors: err, saveGoogle: false }); - }); - - if(!res) { return; } - - console.log('Deleting Local Copy'); - await request.delete(`/api/${brew.editId}`) - .send() - .catch((err)=>{ - console.log('Error deleting Local Copy'); - }); - - this.savedBrew = res.body; - history.replaceState(null, null, `/edit/${this.savedBrew.googleId}${this.savedBrew.editId}`); //update URL to match doc ID - } else { - const res = await request - .put(`/api/updateGoogle/${brew.editId}`) - .send(brew) - .catch((err)=>{ - console.log(err.status === 401 - ? 'Not signed in!' - : 'Error Saving to Google!'); - this.setState({ errors: err }); - return; - }); - - this.savedBrew = res.body; - } - } else { - if(transfer) { - const res = await request.post('/api') - .send(brew) - .catch((err)=>{ - console.log('Error creating Local Copy'); - this.setState({ errors: err }); - return; - }); - - await request.get(`/api/removeGoogle/${brew.googleId}${brew.editId}`) - .send() - .catch((err)=>{ - console.log('Error Deleting Google Brew'); - }); - - this.savedBrew = res.body; - history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); //update URL to match doc ID - } else { - const res = await request - .put(`/api/update/${brew.editId}`) - .send(brew) - .catch((err)=>{ - console.log('Error Updating Local Brew'); - this.setState({ errors: err }); - return; - }); - - this.savedBrew = res.body; - } - } - - this.setState((prevState)=>({ - brew : _.merge({}, prevState.brew, { - googleId : this.savedBrew.googleId ? this.savedBrew.googleId : null, - editId : this.savedBrew.editId, - shareId : this.savedBrew.shareId - }), - isPending : false, - isSaving : false, - })); - } - - if(this.isNew()){ - console.log('saving new brew'); - - let brew = this.state.brew; - // Split out CSS to Style if CSS codefence exists - if(brew.text.startsWith('```css') && brew.text.indexOf('```\n\n') > 0) { - const index = brew.text.indexOf('```\n\n'); - brew.style = `${brew.style ? `${brew.style}\n` : ''}${brew.text.slice(7, index - 1)}`; - brew.text = brew.text.slice(index + 5); - }; - - brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; - - if(this.state.saveGoogle) { - const res = await request - .post('/api/newGoogle/') - .send(brew) - .catch((err)=>{ - console.log(err.status === 401 - ? 'Not signed in!' - : 'Error Creating New Google Brew!'); - this.setState({ isSaving: false, errors: err }); - return; - }); - - brew = res.body; - localStorage.removeItem(BREWKEY); - localStorage.removeItem(STYLEKEY); - localStorage.removeItem(METAKEY); - window.location = `/edit/${brew.googleId}${brew.editId}`; - } else { - request.post('/api') - .send(brew) - .end((err, res)=>{ - if(err){ - this.setState({ - isSaving : false - }); - return; - } - window.onbeforeunload = function(){}; - brew = res.body; - localStorage.removeItem(BREWKEY); - localStorage.removeItem(STYLEKEY); - localStorage.removeItem(METAKEY); - window.location = `/edit/${brew.editId}`; - }); - } - } - }, - - renderGoogleDriveIcon : function(){ - return - {this.state.saveGoogle - ? googleDriveActive - : googleDriveInactive - } - - {this.state.confirmGoogleTransfer && -
- { this.state.saveGoogle - ? this.props.googleDriveOptions[0] - : this.props.googleDriveOptions[1] - } -
-
- Yes -
-
- No -
-
- } - - {this.state.alertLoginToTransfer && -
- You must be signed in to a Google account to transfer - between the homebrewery and Google Drive! - -
- Sign In -
-
-
- Not Now -
-
- } -
; - }, - - 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 saving...; - } - if(this.state.isPending && this.hasChanges()){ - return Save Now; - } - if(!this.state.isPending && !this.state.isSaving){ - return saved.; - } - }, - - processShareId : function() { - return this.state.brew.googleId ? - this.state.brew.googleId + this.state.brew.shareId : - this.state.brew.shareId; - }, - - getRedditLink : function(){ - - const shareLink = this.processShareId(); - const systems = this.props.brew.systems.length > 0 ? ` [${this.props.brew.systems.join(' - ')}]` : ''; - const title = `${this.props.brew.title} ${systems}`; - const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. - -**[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`; - - return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`; - }, - - renderNavbar : function(){ - const shareLink = this.processShareId(); - - return - - {this.state.alertTrashedGoogleBrew && -
- This brew is currently in your Trash folder on Google Drive!
If you want to keep it, make sure to move it before it is deleted permanently!
-
- OK -
-
- } - - - {this.state.brew.title} - - - - {this.renderGoogleDriveIcon()} - {this.renderSaveButton()} - - - {this.isEdit() && <> - - - share - - - view - - {navigator.clipboard.writeText(`https://homebrewery.naturalcrit.com/share/${shareLink}`);}}> - copy url - - - post to reddit - - - - - } - - - - -
; - }, - - render : function(){ - return
- - {this.renderNavbar()} - -
- - - - -
-
; - } -}); - -module.exports = EditorPage; diff --git a/client/homebrew/pages/basePages/editorPage/editorPage.less b/client/homebrew/pages/basePages/editorPage/editorPage.less deleted file mode 100644 index 0cbfadcbd..000000000 --- a/client/homebrew/pages/basePages/editorPage/editorPage.less +++ /dev/null @@ -1,99 +0,0 @@ -@keyframes glideDown { - 0% {transform : translate(-50% + 3px, 0px); - opacity : 0;} - 100% {transform : translate(-50% + 3px, 10px); - opacity : 1;} -} -.editPage{ - .navItem.save{ - width : 106px; - text-align : center; - position : relative; - &.saved{ - cursor : initial; - color : #666; - } - &.error{ - position : relative; - background-color : @red; - } - } - .googleDriveStorage { - position : relative; - } - .googleDriveStorage img{ - height : 20px; - padding : 0px; - margin : -5px; - } - .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/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index d2f21481d..fc378845e 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -351,6 +351,7 @@ const EditPage = createClass({ } if(this.state.errors.response.req.url.match(/^\/api\/.*Google.*$/m)){ + if(this.state.errors.status == '403' && this.state.errors.response.body.errors[0].reason == 'insufficientPermissions'){ return Oops!
diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 0c6b0aa6a..4f112c703 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -228,6 +228,7 @@ const NewPage = createClass({ } if(this.state.errors.response.req.url.match(/^\/api\/.*Google.*$/m)){ + if(this.state.errors.status == '403' && this.state.errors.response.body.errors[0].reason == 'insufficientPermissions'){ return Oops!
From 0beabc6c0c2bc4dc7e1262075a82a902f22d472d Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Fri, 31 Dec 2021 13:30:07 +1300 Subject: [PATCH 10/21] Fix New & EditPage issues after rebase --- client/homebrew/pages/editPage/editPage.jsx | 45 ++++++++++----------- client/homebrew/pages/newPage/newPage.jsx | 41 ++++++++++--------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index fc378845e..4567bff94 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -351,37 +351,38 @@ const EditPage = createClass({ } if(this.state.errors.response.req.url.match(/^\/api\/.*Google.*$/m)){ - if(this.state.errors.status == '403' && this.state.errors.response.body.errors[0].reason == 'insufficientPermissions'){ - return + 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 our log in page to sign out and sign back in with Google, then try saving again! - -
+ +
Sign In -
-
-
+
+ +
Not Now +
+ ; + } + + return + Oops! +
+ Looks like there was a problem saving.
+ Report the issue + here + .
; } - - return - Oops! -
- Looks like there was a problem saving.
- Report the issue - here - . -
-
; } if(this.state.isSaving){ @@ -406,9 +407,7 @@ const EditPage = createClass({ const shareLink = this.processShareId(); const systems = this.props.brew.systems.length > 0 ? ` [${this.props.brew.systems.join(' - ')}]` : ''; const title = `${this.props.brew.title} ${systems}`; - const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. - -**[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`; + const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. **[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`; return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`; }, diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 4f112c703..10b5061f0 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -228,37 +228,38 @@ const NewPage = createClass({ } if(this.state.errors.response.req.url.match(/^\/api\/.*Google.*$/m)){ - if(this.state.errors.status == '403' && this.state.errors.response.body.errors[0].reason == 'insufficientPermissions'){ - return + 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 our log in page to sign out and sign back in with Google, then try saving again! - -
+ +
Sign In -
-
-
+
+ +
Not Now +
+ ; + } + + return + Oops! +
+ Looks like there was a problem saving.
+ Report the issue + here + .
; } - - return - Oops! -
- Looks like there was a problem saving.
- Report the issue - here - . -
-
; } if(this.state.isSaving){ From 896d9ae2c77d6185c3d24e3384ceb850b9a830a3 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Fri, 31 Dec 2021 13:35:50 +1300 Subject: [PATCH 11/21] Lint fixes --- client/homebrew/pages/editPage/editPage.jsx | 3 +-- client/homebrew/pages/newPage/newPage.jsx | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 4567bff94..77c1834e4 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -376,8 +376,7 @@ const EditPage = createClass({ Oops!
Looks like there was a problem saving.
- Report the issue + Report the issue here .
diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 10b5061f0..9c1621287 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -253,8 +253,8 @@ const NewPage = createClass({ Oops!
Looks like there was a problem saving.
- Report the issue + Report the issue here .
From 605ea2aa62e767d66a712b3b3b32fece4e652230 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Fri, 31 Dec 2021 13:42:24 +1300 Subject: [PATCH 12/21] Revert New & EditPage to `master` status --- client/homebrew/pages/editPage/editPage.jsx | 43 +++++++++++---------- client/homebrew/pages/newPage/newPage.jsx | 40 +++++++++---------- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 77c1834e4..d2f21481d 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -351,37 +351,36 @@ const EditPage = createClass({ } if(this.state.errors.response.req.url.match(/^\/api\/.*Google.*$/m)){ - if(this.state.errors.status == '403' && this.state.errors.response.body.errors[0].reason == 'insufficientPermissions'){ - return + return Oops! -
+
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! - -
+ +
Sign In -
-
-
- Not Now
+ +
+ Not Now
- ; - } - - return - Oops! -
- Looks like there was a problem saving.
- Report the issue - here - .
; } + + return + Oops! +
+ Looks like there was a problem saving.
+ Report the issue + here + . +
+
; } if(this.state.isSaving){ @@ -406,7 +405,9 @@ const EditPage = createClass({ const shareLink = this.processShareId(); const systems = this.props.brew.systems.length > 0 ? ` [${this.props.brew.systems.join(' - ')}]` : ''; const title = `${this.props.brew.title} ${systems}`; - const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. **[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`; + const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. + +**[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`; return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`; }, diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 9c1621287..0c6b0aa6a 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -228,38 +228,36 @@ const NewPage = createClass({ } if(this.state.errors.response.req.url.match(/^\/api\/.*Google.*$/m)){ - if(this.state.errors.status == '403' && this.state.errors.response.body.errors[0].reason == 'insufficientPermissions'){ - return + return Oops! -
+
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! - -
+ +
Sign In -
-
-
- Not Now
+ +
+ Not Now
- ; - } - - return - Oops! -
- Looks like there was a problem saving.
- Report the issue - here - .
; } + + return + Oops! +
+ Looks like there was a problem saving.
+ Report the issue + here + . +
+
; } if(this.state.isSaving){ From 78e042cb9ae310d5357370e5caee2782899257e3 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Tue, 1 Feb 2022 22:40:06 -0500 Subject: [PATCH 13/21] Remove unused functions in userPage.jsx --- client/homebrew/pages/userPage/userPage.jsx | 144 ++------------------ 1 file changed, 13 insertions(+), 131 deletions(-) diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index 9fcddd497..c3ac93e43 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -5,14 +5,6 @@ const cx = require('classnames'); const ListPage = require('../basePages/listPage/listPage.jsx'); -// const brew = { -// title : 'SUPER Long title woah now', -// authors : [] -// }; - -//const BREWS = _.times(25, ()=>{ return brew;}); - - const UserPage = createClass({ displayName : 'UserPage', getDefaultProps : function() { @@ -22,146 +14,36 @@ const UserPage = createClass({ }; }, getInitialState : function() { - return { - sortType : 'alpha', - sortDir : 'asc', - filterString : '' - }; - }, - getUsernameWithS : function() { - if(this.props.username.endsWith('s')) - return `${this.props.username}'`; - return `${this.props.username}'s`; - }, + const usernameWithS = this.props.username + (this.props.username.endsWith('s') ? `'` : `'s`); - renderBrews : function(brews){ - if(!brews || !brews.length) return
No Brews.
; - - const sortedBrews = this.sortBrews(brews); - - return _.map(sortedBrews, (brew, idx)=>{ - return ; - }); - }, - - sortBrewOrder : function(brew){ - if(!brew.title){brew.title = 'No Title';} - const mapping = { - 'alpha' : _.deburr(brew.title.toLowerCase()), - 'created' : moment(brew.createdAt).format(), - 'updated' : moment(brew.updatedAt).format(), - 'views' : brew.views, - 'latest' : moment(brew.lastViewed).format() - }; - return mapping[this.state.sortType]; - }, - - sortBrews : function(brews){ - return _.orderBy(brews, (brew)=>{ return this.sortBrewOrder(brew); }, this.state.sortDir); - }, - - handleSortOptionChange : function(event){ - this.setState({ - sortType : event.target.value - }); - }, - - handleSortDirChange : function(event){ - this.setState({ - sortDir : `${(this.state.sortDir == 'asc' ? 'desc' : 'asc')}` - }); - }, - - renderSortOption : function(sortTitle, sortValue){ - return - - ; - }, - - handleFilterTextChange : function(e){ - this.setState({ - filterString : e.target.value - }); - return; - }, - - renderFilterOption : function(){ - return - - ; - }, - - renderSortOptions : function(){ - return
- - - - - {this.renderSortOption('Title', 'alpha')} - {this.renderSortOption('Created Date', 'created')} - {this.renderSortOption('Updated Date', 'updated')} - {this.renderSortOption('Views', 'views')} - {/* {this.renderSortOption('Latest', 'latest')} */} - - - {this.renderFilterOption()} - - -
-
Sort by :
-
-
Direction :
-
- -
-
; - }, - - getSortedBrews : function(){ - return _.groupBy(this.props.brews, (brew)=>{ + const brews = _.groupBy(this.props.brews, (brew)=>{ return (brew.published ? 'published' : 'private'); }); - }, - render : function(){ - const brews = this.getSortedBrews(); - - const brewCollections = [ + const brewCollection = [ { - title : `${this.getUsernameWithS()} published brews`, + title : `${usernameWithS} published brews`, class : 'published', brews : brews.published } ]; if(this.props.username == global.account?.username){ - brewCollections.push( + brewCollection.push( { - title : `${this.getUsernameWithS()} unpublished brews`, + title : `${usernameWithS} unpublished brews`, class : 'unpublished', brews : brews.private } ); } - return ; + return { + brewCollection : brewCollection + }; + }, + + render : function(){ + return ; } }); From 660004e34838789a58fb19fd93ec2896bc1d908b Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 5 Feb 2022 15:19:12 +1300 Subject: [PATCH 14/21] Remove UserPage BrewItem files --- .../pages/userPage/brewItem/brewItem.jsx | 145 ------------------ .../pages/userPage/brewItem/brewItem.less | 75 --------- 2 files changed, 220 deletions(-) delete mode 100644 client/homebrew/pages/userPage/brewItem/brewItem.jsx delete mode 100644 client/homebrew/pages/userPage/brewItem/brewItem.less diff --git a/client/homebrew/pages/userPage/brewItem/brewItem.jsx b/client/homebrew/pages/userPage/brewItem/brewItem.jsx deleted file mode 100644 index b0b647b86..000000000 --- a/client/homebrew/pages/userPage/brewItem/brewItem.jsx +++ /dev/null @@ -1,145 +0,0 @@ -require('./brewItem.less'); -const React = require('react'); -const createClass = require('create-react-class'); -const _ = require('lodash'); -const cx = require('classnames'); -const moment = require('moment'); -const request = require('superagent'); - -const googleDriveIcon = require('../../../googleDrive.png'); -const dedent = require('dedent-tabs').default; - -const BrewItem = createClass({ - displayName : 'BrewItem', - getDefaultProps : function() { - return { - brew : { - title : '', - description : '', - - authors : [] - } - }; - }, - - deleteBrew : function(){ - if(this.props.brew.authors.length <= 1){ - if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return; - if(!confirm('Are you REALLY sure? You will not be able to recover the document.')) return; - } else { - if(!confirm('Are you sure you want to remove this brew from your collection? This will remove you as an editor, but other owners will still be able to access the document.')) return; - if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return; - } - - if(this.props.brew.googleId) { - request.get(`/api/removeGoogle/${this.props.brew.googleId}${this.props.brew.editId}`) - .send() - .end(function(err, res){ - location.reload(); - }); - } else { - request.delete(`/api/${this.props.brew.editId}`) - .send() - .end(function(err, res){ - location.reload(); - }); - } - }, - - renderDeleteBrewLink : function(){ - if(!this.props.brew.editId) return; - - return - - ; - }, - - renderEditLink : function(){ - if(!this.props.brew.editId) return; - - let editLink = this.props.brew.editId; - if(this.props.brew.googleId) { - editLink = this.props.brew.googleId + editLink; - } - - return - - ; - }, - - renderShareLink : function(){ - if(!this.props.brew.shareId) return; - - let shareLink = this.props.brew.shareId; - if(this.props.brew.googleId) { - shareLink = this.props.brew.googleId + shareLink; - } - - return - - ; - }, - - renderDownloadLink : function(){ - if(!this.props.brew.shareId) return; - - let shareLink = this.props.brew.shareId; - if(this.props.brew.googleId) { - shareLink = this.props.brew.googleId + shareLink; - } - - return - - ; - }, - - renderGoogleDriveIcon : function(){ - if(!this.props.brew.gDrive) return; - - return - googleDriveIcon - ; - }, - - render : function(){ - const brew = this.props.brew; - const dateFormatString = 'YYYY-MM-DD HH:mm:ss'; - - return
-
-

{brew.title}

-

{brew.description}

-
-
-
- - {brew.authors.join(', ')} - -
- - {brew.views} - - {brew.pageCount && - - {brew.pageCount} - - } - - {moment(brew.updatedAt).fromNow()} - - {this.renderGoogleDriveIcon()} -
- -
- {this.renderShareLink()} - {this.renderEditLink()} - {this.renderDownloadLink()} - {this.renderDeleteBrewLink()} -
-
; - } -}); - -module.exports = BrewItem; diff --git a/client/homebrew/pages/userPage/brewItem/brewItem.less b/client/homebrew/pages/userPage/brewItem/brewItem.less deleted file mode 100644 index d323874f5..000000000 --- a/client/homebrew/pages/userPage/brewItem/brewItem.less +++ /dev/null @@ -1,75 +0,0 @@ - -.brewItem{ - position : relative; - display : inline-block; - vertical-align : top; - box-sizing : border-box; - box-sizing : border-box; - overflow : hidden; - width : 48%; - min-height : 105px; - margin-right : 15px; - margin-bottom : 15px; - padding : 5px 15px 2px 8px; - padding-right : 15px; - border : 1px solid #c9ad6a; - border-radius : 5px; - -webkit-column-break-inside : avoid; - page-break-inside : avoid; - break-inside : avoid; - .text { - min-height : 54px; - h4{ - margin-bottom : 5px; - font-size : 2.2em; - } - } - .info{ - position: initial; - bottom: 2px; - font-family : ScalySans; - font-size : 1.2em; - &>span{ - margin-right : 12px; - line-height : 1.5em; - } - } - &:hover{ - .links{ - opacity : 1; - } - } - &:nth-child(2n + 1){ - margin-right : 0px; - } - .links{ - .animate(opacity); - position : absolute; - top : 0px; - right : 0px; - height : 100%; - width : 2em; - opacity : 0; - background-color : fade(black, 60%); - text-align : center; - a{ - .animate(opacity); - display : block; - margin : 8px 0px; - opacity : 0.6; - font-size : 1.3em; - color : white; - &:hover{ - opacity : 1; - } - i{ - cursor : pointer; - } - } - } - .googleDriveIcon { - height : 20px; - padding : 0px; - margin : -5px; - } -} From 0611db1bdfc4299c83c38da8076054fe0f56bfa3 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 5 Feb 2022 17:01:52 +1300 Subject: [PATCH 15/21] Move Nav items to UserPage --- .../pages/basePages/listPage/listPage.jsx | 34 ++++++------------- client/homebrew/pages/userPage/userPage.jsx | 21 +++++++++++- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/client/homebrew/pages/basePages/listPage/listPage.jsx b/client/homebrew/pages/basePages/listPage/listPage.jsx index e4beb4a94..17a558735 100644 --- a/client/homebrew/pages/basePages/listPage/listPage.jsx +++ b/client/homebrew/pages/basePages/listPage/listPage.jsx @@ -4,16 +4,9 @@ const createClass = require('create-react-class'); const _ = require('lodash'); const cx = require('classnames'); -const moment = require('moment'); - -const Nav = require('naturalcrit/nav/nav.jsx'); -const Navbar = require('../../../navbar/navbar.jsx'); - -const RecentNavItem = require('../../../navbar/recent.navitem.jsx').both; -const Account = require('../../../navbar/account.navitem.jsx'); -const NewBrew = require('../../../navbar/newbrew.navitem.jsx'); const BrewItem = require('./brewItem/brewItem.jsx'); -const ReportIssue = require('../../../navbar/issue.navitem.jsx'); + +const moment = require('moment'); // const brew = { // title : 'SUPER Long title woah now', @@ -32,7 +25,8 @@ const ListPage = createClass({ class : '', brews : [] } - ] + ], + navItems : <> }; }, getInitialState : function() { @@ -146,16 +140,17 @@ const ListPage = createClass({ getSortedBrews : function(brewCollection){ const testString = _.deburr(this.state.filterString).toLowerCase(); - const brews = this.state.filterString ? _.filter(brewCollection.brews, (brew)=>{ + console.log(testString); + const brews = testString ? _.filter(brewCollection.brews, (brew)=>{ return (_.deburr(brew.title).toLowerCase().includes(testString)) || (_.deburr(brew.description).toLowerCase().includes(testString)); - }) : this.props.brewCollection.brews; - return _.groupBy(brews, (brew)=>{ - return (brew.published ? 'published' : 'private'); - }); + }) : brewCollection.brews; + console.log(brews); + return brews; }, renderBrewCollection : function(brewCollection){ + brewCollection.brews = this.getSortedBrews(brewCollection); return _.map(brewCollection, (brewItem, idx)=>{ return

{brewItem.title || 'No Title'}

@@ -167,14 +162,7 @@ const ListPage = createClass({ render : function(){ return
- - - - - - - - + {this.props.navItems}
diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index c3ac93e43..8c1c699f6 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -5,6 +5,14 @@ const cx = require('classnames'); const ListPage = require('../basePages/listPage/listPage.jsx'); +const Nav = require('naturalcrit/nav/nav.jsx'); +const Navbar = require('../../navbar/navbar.jsx'); + +const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; +const Account = require('../../navbar/account.navitem.jsx'); +const NewBrew = require('../../navbar/newbrew.navitem.jsx'); +const ReportIssue = require('../../navbar/issue.navitem.jsx'); + const UserPage = createClass({ displayName : 'UserPage', getDefaultProps : function() { @@ -42,8 +50,19 @@ const UserPage = createClass({ }; }, + navItems : function() { + return + + + + + + + ; + }, + render : function(){ - return ; + return ; } }); From 6299e87569f62b4a8677675815740705a9d22cfa Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 5 Feb 2022 17:14:41 +1300 Subject: [PATCH 16/21] Filtering now functional on ListPage --- client/homebrew/pages/basePages/listPage/listPage.jsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/client/homebrew/pages/basePages/listPage/listPage.jsx b/client/homebrew/pages/basePages/listPage/listPage.jsx index 17a558735..762ac3221 100644 --- a/client/homebrew/pages/basePages/listPage/listPage.jsx +++ b/client/homebrew/pages/basePages/listPage/listPage.jsx @@ -138,14 +138,12 @@ const ListPage = createClass({
; }, - getSortedBrews : function(brewCollection){ + getSortedBrews : function(brews){ const testString = _.deburr(this.state.filterString).toLowerCase(); - console.log(testString); - const brews = testString ? _.filter(brewCollection.brews, (brew)=>{ + brews = testString ? _.filter(brews, (brew)=>{ return (_.deburr(brew.title).toLowerCase().includes(testString)) || (_.deburr(brew.description).toLowerCase().includes(testString)); - }) : brewCollection.brews; - console.log(brews); + }) : brews; return brews; }, @@ -154,7 +152,7 @@ const ListPage = createClass({ return _.map(brewCollection, (brewItem, idx)=>{ return

{brewItem.title || 'No Title'}

- {this.renderBrews(brewItem.brews)} + {this.renderBrews(this.getSortedBrews(brewItem.brews))}
; }); }, From eccf5e15b115a541eabe208d16a72eca074d7a38 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 5 Feb 2022 17:29:41 +1300 Subject: [PATCH 17/21] Change ReportIssue navItem to Help navItem --- client/homebrew/pages/userPage/userPage.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index 8c1c699f6..a78ba11af 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -11,7 +11,7 @@ const Navbar = require('../../navbar/navbar.jsx'); const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; const Account = require('../../navbar/account.navitem.jsx'); const NewBrew = require('../../navbar/newbrew.navitem.jsx'); -const ReportIssue = require('../../navbar/issue.navitem.jsx'); +const HelpNavItem = require('../../navbar/help.navitem.jsx'); const UserPage = createClass({ displayName : 'UserPage', @@ -54,7 +54,7 @@ const UserPage = createClass({ return - + From c41141fe106f0f8bc91c05ded7daea526b3f5b21 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 5 Feb 2022 17:32:34 +1300 Subject: [PATCH 18/21] Add missing Help navItem file --- client/homebrew/navbar/help.navitem.jsx | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 client/homebrew/navbar/help.navitem.jsx diff --git a/client/homebrew/navbar/help.navitem.jsx b/client/homebrew/navbar/help.navitem.jsx new file mode 100644 index 000000000..b4be18319 --- /dev/null +++ b/client/homebrew/navbar/help.navitem.jsx @@ -0,0 +1,25 @@ +const React = require('react'); +const createClass = require('create-react-class'); +const _ = require('lodash'); + +const Nav = require('naturalcrit/nav/nav.jsx'); + +module.exports = function(props){ + return + + need help? + + + report issue + + + migrate + + ; +}; From 402301f201a4d113d1f50839a13b2ca985b45eb7 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 5 Feb 2022 19:04:53 +1300 Subject: [PATCH 19/21] Restore BrewItem action classNames --- .../pages/basePages/listPage/brewItem/brewItem.jsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx index c7c4d6f94..938913863 100644 --- a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx @@ -12,7 +12,8 @@ const dedent = require('dedent-tabs').default; const BrewItem = createClass({ getDefaultProps : function() { return { - brew : { + displayName : 'BrewItem', + brew : { title : '', description : '', @@ -48,7 +49,7 @@ const BrewItem = createClass({ renderDeleteBrewLink : function(){ if(!this.props.brew.editId) return; - return + return ; }, @@ -61,7 +62,7 @@ const BrewItem = createClass({ editLink = this.props.brew.googleId + editLink; } - return + return ; }, @@ -74,7 +75,7 @@ const BrewItem = createClass({ shareLink = this.props.brew.googleId + shareLink; } - return + return ; }, @@ -87,7 +88,7 @@ const BrewItem = createClass({ shareLink = this.props.brew.googleId + shareLink; } - return + return ; }, From 0b7fee0cc51cad1e353dc946295851eba547f026 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Sat, 5 Feb 2022 19:10:18 +1300 Subject: [PATCH 20/21] Add displayName properties --- .../homebrew/pages/basePages/listPage/brewItem/brewItem.jsx | 4 ++-- client/homebrew/pages/basePages/listPage/listPage.jsx | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx index 938913863..a89dad11e 100644 --- a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx @@ -10,10 +10,10 @@ const googleDriveIcon = require('../../../../googleDrive.png'); const dedent = require('dedent-tabs').default; const BrewItem = createClass({ + displayName : 'BrewItem', getDefaultProps : function() { return { - displayName : 'BrewItem', - brew : { + brew : { title : '', description : '', diff --git a/client/homebrew/pages/basePages/listPage/listPage.jsx b/client/homebrew/pages/basePages/listPage/listPage.jsx index 762ac3221..47b75480d 100644 --- a/client/homebrew/pages/basePages/listPage/listPage.jsx +++ b/client/homebrew/pages/basePages/listPage/listPage.jsx @@ -17,6 +17,7 @@ const moment = require('moment'); const ListPage = createClass({ + displayName : 'ListPage', getDefaultProps : function() { return { brewCollection : [ From d38bf3b4507d4f52285dbf4ba08e311d8851a626 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Sun, 6 Feb 2022 17:17:59 -0500 Subject: [PATCH 21/21] Clean up. --- .../pages/basePages/listPage/listPage.jsx | 42 ++++++------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/client/homebrew/pages/basePages/listPage/listPage.jsx b/client/homebrew/pages/basePages/listPage/listPage.jsx index 47b75480d..e1f300ab2 100644 --- a/client/homebrew/pages/basePages/listPage/listPage.jsx +++ b/client/homebrew/pages/basePages/listPage/listPage.jsx @@ -1,20 +1,10 @@ require('./listPage.less'); -const React = require('react'); +const React = require('react'); const createClass = require('create-react-class'); -const _ = require('lodash'); -const cx = require('classnames'); - -const BrewItem = require('./brewItem/brewItem.jsx'); - -const moment = require('moment'); - -// const brew = { -// title : 'SUPER Long title woah now', -// authors : [] -// }; - -//const BREWS = _.times(25, ()=>{ return brew;}); +const _ = require('lodash'); +const moment = require('moment'); +const BrewItem = require('./brewItem/brewItem.jsx'); const ListPage = createClass({ displayName : 'ListPage', @@ -41,9 +31,7 @@ const ListPage = createClass({ renderBrews : function(brews){ if(!brews || !brews.length) return
No Brews.
; - const sortedBrews = this.sortBrews(brews); - - return _.map(sortedBrews, (brew, idx)=>{ + return _.map(brews, (brew, idx)=>{ return ; }); }, @@ -60,10 +48,6 @@ const ListPage = createClass({ return mapping[this.state.sortType]; }, - sortBrews : function(brews){ - return _.orderBy(brews, (brew)=>{ return this.sortBrewOrder(brew); }, this.state.sortDir); - }, - handleSortOptionChange : function(event){ this.setState({ sortType : event.target.value @@ -141,19 +125,19 @@ const ListPage = createClass({ getSortedBrews : function(brews){ const testString = _.deburr(this.state.filterString).toLowerCase(); - brews = testString ? _.filter(brews, (brew)=>{ + brews = _.filter(brews, (brew)=>{ return (_.deburr(brew.title).toLowerCase().includes(testString)) || (_.deburr(brew.description).toLowerCase().includes(testString)); - }) : brews; - return brews; + }); + + return _.orderBy(brews, (brew)=>{ return this.sortBrewOrder(brew); }, this.state.sortDir); }, renderBrewCollection : function(brewCollection){ - brewCollection.brews = this.getSortedBrews(brewCollection); - return _.map(brewCollection, (brewItem, idx)=>{ - return
-

{brewItem.title || 'No Title'}

- {this.renderBrews(this.getSortedBrews(brewItem.brews))} + return _.map(brewCollection, (brewGroup, idx)=>{ + return
+

{brewGroup.title || 'No Title'}

+ {this.renderBrews(this.getSortedBrews(brewGroup.brews))}
; }); },