From 6414e73e7db62b40745fe6debcc92d18225cf993 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Tue, 8 Jul 2025 15:50:27 -0400 Subject: [PATCH] Cleanup and better handling of pre-save snapshot --- client/homebrew/pages/editPage/editPage.jsx | 60 ++++++++++----------- server/homebrew.api.js | 30 +++++------ 2 files changed, 43 insertions(+), 47 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index dc2f0f2a5..c1448c497 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -200,13 +200,17 @@ const EditPage = createClass({ if(!this.debounceSave) this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT); if(this.state.isSaving) return; + + if(immediate) { + this.debounceSave(); + this.debounceSave.flush(); + return; + } if(this.hasChanges()) this.debounceSave(); else this.debounceSave.cancel(); - - if(immediate) this.debounceSave.flush(); }, handleGoogleClick : function(){ @@ -252,33 +256,17 @@ const EditPage = createClass({ await updateHistory(this.state.brew).catch(console.error); await versionHistoryGarbageCollection().catch(console.error); - const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId); - - const brew = { ...this.state.brew }; - - let jsonString = JSON.stringify(brew); - let bytes = new TextEncoder().encode(jsonString).length; - - console.log(`Before size: ${bytes} bytes (${(bytes / 1024).toFixed(2)} KB)`); + const preSaveSnapshot = { ...this.state.brew } + //Prepare content to send to server + const brew = { ...this.state.brew }; brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; + brew.patches = makePatches(this.savedBrew.text, brew.text); + brew.hash = await md5(this.savedBrew.text); + brew.text = undefined; + brew.textBin = undefined; - brew.patches = makePatches(this.savedBrew.text, brew.text); - brew.hash = await md5(this.savedBrew.text); - brew.text = undefined; - brew.textBin = undefined; - console.log('Saving Brew', brew); - - jsonString = JSON.stringify(brew); - bytes = new TextEncoder().encode(jsonString).length; - - console.log(`After size: ${bytes} bytes (${(bytes / 1024).toFixed(2)} KB)`); - - jsonString = JSON.stringify(brew.patches); - bytes = new TextEncoder().encode(jsonString).length; - - console.log(`Patch size: ${bytes} bytes (${(bytes / 1024).toFixed(2)} KB)`); - + const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId); const params = `${transfer ? `?${this.state.saveGoogle ? 'saveToGoogle' : 'removeFromGoogle'}=true` : ''}`; const res = await request .put(`/api/update/${brew.editId}${params}`) @@ -290,20 +278,28 @@ const EditPage = createClass({ if(!res) return; this.savedBrew = { - ...this.state.brew, + ...preSaveSnapshot, googleId : res.body.googleId ? res.body.googleId : null, editId : res.body.editId, shareId : res.body.shareId, version : res.body.version }; - history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); - this.setState(()=>({ - brew : this.savedBrew, - unsavedChanges : false, + this.setState((prevState) => ({ + brew: { + ...prevState.brew, + googleId : res.body.googleId ? res.body.googleId : null, + editId : res.body.editId, + shareId : res.body.shareId, + version : res.body.version + }, isSaving : false, unsavedTime : new Date() - })); + }), ()=>{ + this.setState({ unsavedChanges : this.hasChanges() }); + }); + + history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); }, renderGoogleDriveIcon : function(){ diff --git a/server/homebrew.api.js b/server/homebrew.api.js index c4feae16f..d0b1c43c8 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -339,30 +339,30 @@ const api = { // Initialize brew from request and body, destructure query params, and set the initial value for the after-save method const brewFromClient = api.excludePropsFromUpdate(req.body); const brewFromServer = req.brew; - const serverHash = md5(brewFromServer.text); - console.log({serverHash: serverHash}); - console.log({clientHash: brewFromClient.hash}); + splitTextStyleAndMetadata(brewFromServer); - if((brewFromServer?.version !== brewFromClient?.version)) { - console.log(`Version mismatch on brew ${brewFromClient.editId}`); + brewFromServer.hash = await md5(brewFromServer.text); + + if((brewFromServer?.version !== brewFromClient?.version) || (brewFromServer?.hash !== brewFromClient?.hash)) { + if(brewFromClient?.version !== brewFromClient?.version) + console.log(`Version mismatch on brew ${brewFromClient.editId}`); + if(brewFromServer?.hash !== brewFromClient?.hash) { + console.log(`Hash mismatch on brew ${brewFromClient.editId}`); + } res.setHeader('Content-Type', 'application/json'); return res.status(409).send(JSON.stringify({ message: `The server copy is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.` })); } + + let brew = _.assign(brewFromServer, brewFromClient); + brew.title = brew.title.trim(); + brew.description = brew.description.trim() || ''; + brew.text = applyPatches(brewFromClient.patches, brewFromServer.text)[0]; + brew.text = api.mergeBrewText(brew); - console.log(`Brewfromserver: ${JSON.stringify(brewFromServer)}`); - splitTextStyleAndMetadata(brewFromServer); - brewFromClient.text = applyPatches(brewFromClient.patches, brewFromServer.text)[0]; - console.log(`Server Text: ${brewFromServer.text}`); - console.log(`Brew text: ${brewFromClient.text}`); - let brew = _.assign(brewFromServer, brewFromClient); const googleId = brew.googleId; const { saveToGoogle, removeFromGoogle } = req.query; let afterSave = async ()=>true; - brew.title = brew.title.trim(); - brew.description = brew.description.trim() || ''; - brew.text = api.mergeBrewText(brew); - if(brew.googleId && removeFromGoogle) { // If the google id exists and we're removing it from google, set afterSave to delete the google brew and mark the brew's google id as undefined afterSave = async ()=>{