From f17fbaff49d84e6ed4e120df2d14a26b10001f45 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Fri, 7 Feb 2025 21:44:54 +1300 Subject: [PATCH 01/88] Add event-stream route --- server/app.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/server/app.js b/server/app.js index 4dec6b4c4..f586b760c 100644 --- a/server/app.js +++ b/server/app.js @@ -37,6 +37,8 @@ import bodyParser from 'body-parser'; import cookieParser from 'cookie-parser'; import forceSSL from './forcessl.mw.js'; +import EventEmitter from 'events'; + const sanitizeBrew = (brew, accessType)=>{ brew._id = undefined; @@ -352,7 +354,7 @@ app.get('/user/:username', async (req, res, next)=>{ app.put('/api/user/rename', async (req, res)=>{ const { username, newUsername } = req.body; const ownAccount = req.account && (req.account.username == newUsername); - + if(!username || !newUsername) return res.status(400).json({ error: 'Username and newUsername are required.' }); if(!ownAccount) @@ -512,6 +514,27 @@ app.get('/account', asyncHandler(async (req, res, next)=>{ return next(); })); +// Event Stream +const Stream = new EventEmitter; + +// After Stream starts, send initStream event +setTimeout(()=>{ + Stream.emit('push', 'initStream', { time: (new Date).toString() }); +}, 1000); + +app.get('/stream', (req, res)=>{ + res.writeHead(200, { + 'Content-Type' : 'text/event-stream', + 'Cache-Control' : 'no-cache', + 'Connection' : 'keep-alive' + }); + + Stream.on('push', (event, data)=>{ + // console.log('Event:', event, 'Data:', data); + res.write(`data: ${JSON.stringify({ ...data, eventType: event })}\n\n`); + }); +}); + // Local only if(isLocalEnvironment){ // Login From 4669c5d48922a5adf7cd7e91b3b327ac5fcdd745 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Fri, 7 Feb 2025 21:45:27 +1300 Subject: [PATCH 02/88] Add reload when initStream message is received --- client/homebrew/homebrew.jsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 415390498..b8e8aa474 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -66,6 +66,18 @@ const Homebrew = createClass({ return {}; }, + componentDidMount : function(){ + const eventSource = new EventSource('/stream'); + + eventSource.addEventListener('message', (evt)=>{ + const messageData = JSON.parse(evt.data); + + if(messageData.eventType == 'initStream'){ + window.location.reload(); + } + }); + }, + render : function (){ return ( From 0a6434fbf347ef9284caf212cf123953ebb3158f Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Fri, 7 Feb 2025 21:46:28 +1300 Subject: [PATCH 03/88] Increase max-lines on app.js --- server/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app.js b/server/app.js index f586b760c..11a109843 100644 --- a/server/app.js +++ b/server/app.js @@ -1,4 +1,4 @@ -/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/ +/*eslint max-lines: ["warn", {"max": 750, "skipBlankLines": true, "skipComments": true}]*/ // Set working directory to project root import { dirname } from 'path'; import { fileURLToPath } from 'url'; From 4ce1cf5eece6fd13a3e7a16ee3fa175870775d1f Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Tue, 11 Feb 2025 09:58:00 +1300 Subject: [PATCH 04/88] Move EventStream to external module --- server/app.js | 28 ++++++++++++++-------------- server/eventStreamSource.js | 9 +++++++++ 2 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 server/eventStreamSource.js diff --git a/server/app.js b/server/app.js index 11a109843..e06fda2a5 100644 --- a/server/app.js +++ b/server/app.js @@ -37,7 +37,7 @@ import bodyParser from 'body-parser'; import cookieParser from 'cookie-parser'; import forceSSL from './forcessl.mw.js'; -import EventEmitter from 'events'; +import Stream from './eventStreamSource.js'; const sanitizeBrew = (brew, accessType)=>{ @@ -514,27 +514,27 @@ app.get('/account', asyncHandler(async (req, res, next)=>{ return next(); })); -// Event Stream -const Stream = new EventEmitter; - -// After Stream starts, send initStream event -setTimeout(()=>{ - Stream.emit('push', 'initStream', { time: (new Date).toString() }); -}, 1000); - +// Create Event Stream source for pages to listen to app.get('/stream', (req, res)=>{ res.writeHead(200, { - 'Content-Type' : 'text/event-stream', - 'Cache-Control' : 'no-cache', - 'Connection' : 'keep-alive' + 'Content-Type' : 'text/event-stream', + 'Cache-Control' : 'no-cache', + 'Connection' : 'keep-alive', + 'Content-Encoding' : 'none' }); - Stream.on('push', (event, data)=>{ - // console.log('Event:', event, 'Data:', data); + Stream.on('sendUpdate', (event, data)=>{ + console.log('Event:', event, '\nData:', data); res.write(`data: ${JSON.stringify({ ...data, eventType: event })}\n\n`); }); }); +// After Stream starts, send initStream event +setTimeout(()=>{ + Stream.emit('sendUpdate', 'initStream', { time: new Date }); +}, 1000); + + // Local only if(isLocalEnvironment){ // Login diff --git a/server/eventStreamSource.js b/server/eventStreamSource.js new file mode 100644 index 000000000..66fdb3354 --- /dev/null +++ b/server/eventStreamSource.js @@ -0,0 +1,9 @@ +import { EventEmitter } from 'events'; + +const Stream = new EventEmitter; + +export default { + emit : function(event) {return Stream.emit(event, ...([...arguments].slice(1)));}, // Arguments doesn't work for arrow functions + on : (event, listener)=>{return Stream.on(event, listener);}, + off : (event, listener)=>{return Stream.off(event, listener);} +}; \ No newline at end of file From 97ebdf7ae679e86b6881e36027a7aa031cb843d1 Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Tue, 11 Feb 2025 09:59:14 +1300 Subject: [PATCH 05/88] Add brew update detection to Share Page --- client/homebrew/pages/sharePage/sharePage.jsx | 12 ++++++++++++ server/homebrew.api.js | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index e9c5540a2..a3fdcdf34 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -46,6 +46,18 @@ const SharePage = (props)=>{ brew.theme ); + const eventSource = new EventSource('/stream'); + + eventSource.addEventListener('message', (evt)=>{ + const messageData = JSON.parse(evt.data); + + if(messageData.eventType == 'brewUpdated'){ + if(messageData.shareId == brew.shareId && messageData.version != brew.version) { + console.log(`brew has been updated, viewing ${brew.version}, new version is ${messageData.version}`); + } + } + }); + return ()=>{ document.removeEventListener('keydown', handleControlKeys); }; diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 9a479732e..57815e384 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -16,6 +16,8 @@ const router = express.Router(); import { DEFAULT_BREW, DEFAULT_BREW_LOAD } from './brewDefaults.js'; import Themes from '../themes/themes.json' with { type: 'json' }; +import Stream from './eventStreamSource.js'; + const isStaticTheme = (renderer, themeName)=>{ return Themes[renderer]?.[themeName] !== undefined; }; @@ -398,6 +400,8 @@ const api = { const after = await afterSave(); if(!after) return; + Stream.emit('sendUpdate', 'brewUpdated', { time: new Date, shareId: brew.shareId, version: brew.version }); + res.status(200).send(saved); }, deleteGoogleBrew : async (account, id, editId, res)=>{ From a237bac9dcb4da8cc8126338ee15763abd292d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 26 Jul 2026 19:51:26 +0200 Subject: [PATCH 06/88] fix the issue --- client/homebrew/pages/editPage/editPage.jsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index bc18bc5e5..4cb3ba522 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -71,6 +71,7 @@ const EditPage = (props)=>{ const [alertTrashedGoogleBrew, setAlertTrashedGoogleBrew] = useState(props.brew.trashed); const [alertLoginToTransfer, setAlertLoginToTransfer] = useState(false); const [confirmGoogleTransfer, setConfirmGoogleTransfer] = useState(false); + const [notOwnerToTransfer, setNotOwnerToTransfer] = useState(false); const [autoSaveEnabled, setAutoSaveEnabled] = useState(true); const [warnUnsavedChanges, setWarnUnsavedChanges] = useState(true); @@ -162,6 +163,9 @@ const EditPage = (props)=>{ }; const handleGoogleClick = ()=>{ + if(global.account !== brew.authors[0]) { + setNotOwnerToTransfer(true); + } if(!global.account?.googleId) { setAlertLoginToTransfer(true); return; @@ -176,6 +180,7 @@ const EditPage = (props)=>{ setAlertTrashedGoogleBrew(false); setAlertLoginToTransfer(false); setConfirmGoogleTransfer(false); + setNotOwnerToTransfer(false); }; const toggleGoogleStorage = ()=>{ @@ -271,9 +276,19 @@ const EditPage = (props)=>{ )} + {notOwnerToTransfer && ( +
+ You must be the Owner to transfer between the Homebrewery and Google Drive! + +
Sign In
+
+
Not Now
+
+ )} + {alertLoginToTransfer && (
- You must be signed in to a Google account to transfer between the homebrewery and Google Drive! + You must be signed in to a Google account to transfer between the Homebrewery and Google Drive!
Sign In
From 089e880b88ba87e5d0aea2f459ae4131d0725107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Tue, 28 Jul 2026 23:46:31 +0200 Subject: [PATCH 07/88] suggested changes --- client/homebrew/pages/editPage/editPage.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 4cb3ba522..ebed5fa72 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -70,8 +70,8 @@ const EditPage = (props)=>{ const [unsavedChanges, setUnsavedChanges] = useState(false); const [alertTrashedGoogleBrew, setAlertTrashedGoogleBrew] = useState(props.brew.trashed); const [alertLoginToTransfer, setAlertLoginToTransfer] = useState(false); + const [alertOwnershipToTransfer, setalertOwnershipToTransfer] = useState(false); const [confirmGoogleTransfer, setConfirmGoogleTransfer] = useState(false); - const [notOwnerToTransfer, setNotOwnerToTransfer] = useState(false); const [autoSaveEnabled, setAutoSaveEnabled] = useState(true); const [warnUnsavedChanges, setWarnUnsavedChanges] = useState(true); @@ -164,7 +164,7 @@ const EditPage = (props)=>{ const handleGoogleClick = ()=>{ if(global.account !== brew.authors[0]) { - setNotOwnerToTransfer(true); + setalertOwnershipToTransfer(true); } if(!global.account?.googleId) { setAlertLoginToTransfer(true); @@ -180,7 +180,7 @@ const EditPage = (props)=>{ setAlertTrashedGoogleBrew(false); setAlertLoginToTransfer(false); setConfirmGoogleTransfer(false); - setNotOwnerToTransfer(false); + setalertOwnershipToTransfer(false); }; const toggleGoogleStorage = ()=>{ @@ -276,13 +276,13 @@ const EditPage = (props)=>{
)} - {notOwnerToTransfer && ( + {alertOwnershipToTransfer && (
You must be the Owner to transfer between the Homebrewery and Google Drive! + The owner of this file is ${brew.authors[0]} -
Sign In
+
Okay
-
Not Now
)} From 9275ac4d12cd279902c69d3bbed0cfd3b38fcece Mon Sep 17 00:00:00 2001 From: durstann Date: Sat, 1 Aug 2026 00:05:05 -0700 Subject: [PATCH 08/88] Make metadata editor more accessible. --- .../editor/metadataEditor/metadataEditor.jsx | 18 ++++++++++-------- .../editor/metadataEditor/metadataEditor.less | 2 +- client/homebrew/editor/tagInput/tagInput.jsx | 1 + shared/naturalcrit/styles/core.less | 1 - 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index 14ea97305..9377aa41c 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -157,11 +157,11 @@ const MetadataEditor = createReactClass({ renderPublish : function(){ if(this.props.metadata.published){ return