/* eslint-disable max-lines */ 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 HelpNavItem = require('../../navbar/help.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({ displayName : 'EditPage', 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' } }; }, 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 : '', autoSave : true, autoSaveWarning : false, unsavedTime : new Date() }; }, savedBrew : null, componentDidMount : function(){ this.setState({ url : window.location.href }); this.savedBrew = JSON.parse(JSON.stringify(this.props.brew)); //Deep copy this.setState({ autoSave: JSON.parse(localStorage.getItem('AUTOSAVE_ON')) }, ()=>{ if(this.state.autoSave){ this.trySave(); } else { this.setState({ autoSaveWarning: true }); } }); 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 : { ...prevState.brew, text: text }, isPending : true, htmlErrors : htmlErrors }), ()=>{if(this.state.autoSave) this.trySave();}); }, handleStyleChange : function(style){ this.setState((prevState)=>({ brew : { ...prevState.brew, style: style }, isPending : true }), ()=>{if(this.state.autoSave) this.trySave();}); }, handleMetaChange : function(metadata){ this.setState((prevState)=>({ brew : { ...prevState.brew, ...metadata }, isPending : true, }), ()=>{if(this.state.autoSave) 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; const params = `${transfer ? `?${this.state.saveGoogle ? 'saveToGoogle' : 'removeFromGoogle'}=true` : ''}`; const res = await request .put(`/api/update/${brew.editId}${params}`) .send(brew) .catch((err)=>{ console.log('Error Updating Local Brew'); this.setState({ errors: err }); }); this.savedBrew = res.body; history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); this.setState((prevState)=>({ brew : { ...prevState.brew, googleId : this.savedBrew.googleId ? this.savedBrew.googleId : null, editId : this.savedBrew.editId, shareId : this.savedBrew.shareId }, isPending : false, isSaving : false, unsavedTime : new Date() })); }, 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.response.req.url.match(/^\/api.*Google.*$/m)){ 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 .
; } if(this.state.autoSaveWarning && this.hasChanges()){ this.setAutosaveWarning(); const elapsedTime = Math.round((new Date() - this.state.unsavedTime) / 1000 / 60); const text = elapsedTime == 0 ? 'Autosave is OFF.' : `Autosave is OFF, and you haven't saved for ${elapsedTime} minutes.`; return Reminder...
{text}
; } if(this.state.isSaving){ return saving...; } if(this.state.isPending && this.hasChanges()){ return Save Now; } if(!this.state.isPending && !this.state.isSaving && this.state.autoSave){ return auto-saved.; } if(!this.state.isPending && !this.state.isSaving){ return saved.; } }, handleAutoSave : function(){ if(this.warningTimer) clearTimeout(this.warningTimer); this.setState((prevState)=>({ autoSave : !prevState.autoSave, autoSaveWarning : prevState.autoSave }), ()=>{ localStorage.setItem('AUTOSAVE_ON', JSON.stringify(this.state.autoSave)); }); }, setAutosaveWarning : function(){ setTimeout(()=>this.setState({ autoSaveWarning: false }), 4000); // 4 seconds to display this.warningTimer = setTimeout(()=>{this.setState({ autoSaveWarning: true });}, 900000); // 15 minutes between warnings this.warningTimer; }, renderAutoSaveButton : function(){ return Autosave ; }, processShareId : function() { return this.state.brew.googleId && !this.state.brew.stubbed ? 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](${global.config.publicUrl}/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.renderAutoSaveButton()} share view {navigator.clipboard.writeText(`${global.config.publicUrl}/share/${shareLink}`);}}> copy url post to reddit
; }, render : function(){ return
{this.renderNavbar()}
; } }); module.exports = EditPage;