diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx
index 65de45177..75a07c7d1 100644
--- a/client/homebrew/brewRenderer/brewRenderer.jsx
+++ b/client/homebrew/brewRenderer/brewRenderer.jsx
@@ -23,7 +23,6 @@ import safeHTML from './safeHTML.js';
const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m;
const PAGEBREAK_REGEX_LEGACY = /\\page(?:break)?/m;
const COLUMNBREAK_REGEX_LEGACY = /\\column(:?break)?/m;
-const PAGE_HEIGHT = 1056;
const TOOLBAR_STATE_KEY = 'HB_renderer_toolbarState';
@@ -47,31 +46,25 @@ const BrewPage = (props)=>{
};
const pageRef = useRef(null);
const cleanText = safeHTML(props.contents);
+ const pageNum = props.index + 1;
useEffect(()=>{
if(!pageRef.current) return;
- // Observer for tracking pages within the `.pages` div
+ // Observer for tracking which pages are at least 30% visible in the iframe
const visibleObserver = new IntersectionObserver(
- (entries)=>{
- entries.forEach((entry)=>{
- if(entry.isIntersecting)
- props.onVisibilityChange(props.index + 1, true, false); // add page to array of visible pages.
- else
- props.onVisibilityChange(props.index + 1, false, false);
- });
- },
+ (entries)=>entries.forEach((entry)=>{
+ props.onVisibilityChange(pageNum, entry.isIntersecting, false); // add page to array of visible pages.
+ }),
{ threshold: .3, rootMargin: '0px 0px 0px 0px' } // detect when >30% of page is within bounds.
);
// Observer for tracking the page at the center of the iframe.
const centerObserver = new IntersectionObserver(
- (entries)=>{
- entries.forEach((entry)=>{
- if(entry.isIntersecting)
- props.onVisibilityChange(props.index + 1, true, true); // Set this page as the center page
- });
- },
+ (entries)=>entries.forEach((entry)=>{
+ if(entry.isIntersecting)
+ props.onVisibilityChange(pageNum, true, true); // Set this page as the center page
+ }),
{ threshold: 0, rootMargin: '-50% 0px -50% 0px' } // Detect when the page is at the center
);
@@ -92,7 +85,7 @@ const BrewPage = (props)=>{
//v=====--------------------< Brew Renderer Component >-------------------=====v//
let renderedPages = [];
-let pageTemplates = [];
+const pageTemplates = [];
let rawPages = [];
const BrewRenderer = (props)=>{
@@ -100,22 +93,21 @@ const BrewRenderer = (props)=>{
text : '',
style : '',
renderer : 'legacy',
- theme : '5ePHB',
lang : '',
errors : [],
currentEditorCursorPageNum : 1,
- currentEditorViewPageNum : 1,
currentBrewRendererPageNum : 1,
themeBundle : {},
onPageChange : ()=>{},
...props
};
+ const [visiblePages, setVisiblePages] = useState([]);
+ const [centerPage , setCenterPage ] = useState(1);
+
const [state, setState] = useState({
- isMounted : false,
- visibility : 'hidden',
- visiblePages : [],
- centerPage : 1
+ isMounted : false,
+ visibility : 'hidden'
});
const [displayOptions, setDisplayOptions] = useState({
@@ -135,9 +127,7 @@ const BrewRenderer = (props)=>{
const [headerState, setHeaderState] = useState(false);
- const mainRef = useRef(null);
const pagesRef = useRef(null);
- const urlRef = useRef('');
if(props.renderer == 'legacy') {
rawPages = props.text.split(PAGEBREAK_REGEX_LEGACY);
@@ -146,20 +136,16 @@ const BrewRenderer = (props)=>{
}
const handlePageVisibilityChange = (pageNum, isVisible, isCenter)=>{
- setState((prevState)=>{
- const updatedVisiblePages = new Set(prevState.visiblePages);
- if(!isCenter)
- isVisible ? updatedVisiblePages.add(pageNum) : updatedVisiblePages.delete(pageNum);
-
- return {
- ...prevState,
- visiblePages : [...updatedVisiblePages].sort((a, b)=>a - b),
- centerPage : isCenter ? pageNum : prevState.centerPage
- };
+ setVisiblePages((prev)=>{
+ const updatedVisiblePages = new Set(prev);
+ isVisible ? updatedVisiblePages.add(pageNum) : updatedVisiblePages.delete(pageNum);
+ return [...updatedVisiblePages].sort((a, b)=>a - b);
});
- if(isCenter)
+ if(isCenter) {
+ setCenterPage(pageNum);
props.onPageChange(pageNum);
+ }
};
const isInView = (index)=>{
@@ -341,19 +327,19 @@ const BrewRenderer = (props)=>{
: null}
-
+
-
0 ? state.visiblePages : [state.centerPage]} totalPages={rawPages.length} headerState={headerState} setHeaderState={setHeaderState}/>
+ 0 ? visiblePages : [centerPage]} totalPages={rawPages.length} headerState={headerState} setHeaderState={setHeaderState}/>
{/*render in iFrame so broken code doesn't crash the site.*/}
- {emitClick();}}
- sandbox="allow-same-origin allow-modals allow-top-navigation"
+ sandbox='allow-same-origin allow-modals allow-top-navigation'
>
{
+ const version = global.version || '0.0.0';
- render : function(){
- return
+ return (
+
The Homebrewery
- {`v${this.state.ver}`}
+ {`v${version}`}
- {/*this.renderChromeWarning()*/}
+ {/* this.renderChromeWarning() */}
- {this.props.children}
- ;
- }
-});
+ {children}
+
+ );
+};
-export default Navbar;
+export default Navbar;
\ No newline at end of file
diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx
index 205cf3fcb..57667ca6e 100644
--- a/client/homebrew/pages/editPage/editPage.jsx
+++ b/client/homebrew/pages/editPage/editPage.jsx
@@ -289,12 +289,10 @@ const EditPage = (props)=>{
text={currentBrew.text}
style={currentBrew.style}
renderer={currentBrew.renderer}
- theme={currentBrew.theme}
themeBundle={themeBundle}
errors={HTMLErrors}
lang={currentBrew.lang}
onPageChange={setCurrentBrewRendererPageNum}
- currentEditorViewPageNum={currentEditorViewPageNum}
currentEditorCursorPageNum={currentEditorCursorPageNum}
currentBrewRendererPageNum={currentBrewRendererPageNum}
allowPrint={true}
diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx
index 0132a1147..0ac78f0e1 100644
--- a/client/homebrew/pages/homePage/homePage.jsx
+++ b/client/homebrew/pages/homePage/homePage.jsx
@@ -138,11 +138,10 @@ const HomePage =(props)=>{
text={currentBrew.text}
style={currentBrew.style}
renderer={currentBrew.renderer}
+ themeBundle={themeBundle}
onPageChange={setCurrentBrewRendererPageNum}
- currentEditorViewPageNum={currentEditorViewPageNum}
currentEditorCursorPageNum={currentEditorCursorPageNum}
currentBrewRendererPageNum={currentBrewRendererPageNum}
- themeBundle={themeBundle}
/>
diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx
index 48d589c1e..c7b6c7160 100644
--- a/client/homebrew/pages/newPage/newPage.jsx
+++ b/client/homebrew/pages/newPage/newPage.jsx
@@ -183,12 +183,10 @@ const NewPage = (props)=>{
text={currentBrew.text}
style={currentBrew.style}
renderer={currentBrew.renderer}
- theme={currentBrew.theme}
themeBundle={themeBundle}
errors={HTMLErrors}
lang={currentBrew.lang}
onPageChange={setCurrentBrewRendererPageNum}
- currentEditorViewPageNum={currentEditorViewPageNum}
currentEditorCursorPageNum={currentEditorCursorPageNum}
currentBrewRendererPageNum={currentBrewRendererPageNum}
allowPrint={true}
diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx
index 8df241d7b..d81b4d878 100644
--- a/client/homebrew/pages/sharePage/sharePage.jsx
+++ b/client/homebrew/pages/sharePage/sharePage.jsx
@@ -39,6 +39,18 @@ const SharePage = (props)=>{
document.addEventListener('keydown', handleControlKeys);
fetchThemeBundle(undefined, setThemeBundle, brew.renderer, brew.theme);
+ // listen for changes in the brew version
+ 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/app.js b/server/app.js
index eb078a877..714c4beba 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": 400, "skipBlankLines": true, "skipComments": true}]*/
// Set working directory to project root
import { dirname } from 'path';
import { fileURLToPath } from 'url';
@@ -30,6 +30,8 @@ import contentNegotiation from './middleware/content-negotiation.js';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import forceSSL from './forcessl.mw.js';
+
+import Stream from './eventStreamSource.js';
import dbCheck from './middleware/dbCheck.js';
import cors from 'cors';
@@ -124,10 +126,10 @@ export default async function createApp(vite) {
};
app.use(pageRoutes({
- defaultMetaTags,
+ defaultMetaTags,
HomebrewModel,
sanitizeBrew,
- }));
+ }));
//Robots.txt
app.get('/robots.txt', (req, res)=>{
@@ -182,6 +184,27 @@ export default async function createApp(vite) {
}
});
+ // 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-Encoding' : 'none'
+ });
+
+ 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
diff --git a/server/homebrew.api.js b/server/homebrew.api.js
index 4cca76193..90543ceaa 100644
--- a/server/homebrew.api.js
+++ b/server/homebrew.api.js
@@ -21,6 +21,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;
};
@@ -168,8 +170,7 @@ const api = {
const googleBrew = await GoogleActions.getGoogleBrew(oAuth2Client, googleId, id, accessType)
.catch((googleError)=>{
- const reason = googleError.errors?.[0].reason;
- if(reason == 'notFound')
+ if(googleError.code === 404 || googleError.status === 404)
throw { ...googleError, HBErrorCode: '02', authors: stub?.authors, account: req.account?.username };
else
throw { ...googleError, HBErrorCode: '01' };
@@ -501,6 +502,8 @@ const api = {
saved.textBin = undefined; // Remove textBin from the saved object to save bandwidth
+ Stream.emit('sendUpdate', 'brewUpdated', { time: new Date, shareId: brew.shareId, version: brew.version });
+
res.status(200).send(saved);
},
deleteGoogleBrew : async (account, id, editId, res)=>{