aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-09 14:01:08 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-09 14:02:19 +0200
commitf2517faca8d35772fb03272ee7f8056421ce2198 (patch)
treee345e2291a99e29acfafc755251cd7925ad96ca9 /packages/meshbay-hub/src/meshbay_hub/static
parent4ae61dc4e1dcca6ae24131fcd9acc55124e3c0af (diff)
downloadmeshbay-f2517faca8d35772fb03272ee7f8056421ce2198.tar.gz
fix(hub): an open application looks for new notifications when you come back
The page asked for the notification list at sign-in and at a token renewal, and at no other time. A notification created after the application started, such as a chat line the hub wrote 20 ms after the message, stayed unseen until the next launch. The hub has no channel to the page and is not polled on a timer, so the list is asked for again on a gesture: the application returning to the foreground (an Android phone included) and the home page, where the list is shown. Two requests less than 30 s apart count as one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/app.js29
1 files changed, 29 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js
index 2254a11..50bd8ac 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js
@@ -43,6 +43,9 @@ import { InvitePage, JoinByLink } from './invite-page.js';
// How often to look. Cheap — it reads a timestamp out of the token and almost
// always does nothing.
const TOKEN_CHECK_MS = 60000;
+// The least time between two askings for the notification list on somebody's
+// return: a gesture, never a timer, and two in a row are one question.
+const NOTIF_REFRESH_MS = 30000;
const THEME_KEY = 'mb_theme';
// ── Theme ────────────────────────────────────────────────────────────────────
@@ -980,10 +983,12 @@ function App() {
hubFetch('/v1/hub/info').then(setHubInfo).catch(() => {});
}, []);
+ const notifAskedAtRef = useRef(0);
const fetchNotifications = useCallback(() => {
if (!user || notifDisabled) {
setNotifications([]); setUnreadCount(0); return;
}
+ notifAskedAtRef.current = Date.now();
// `unread_only`: clicking one is what dismisses it (see markRead), so a
// read notification is a dismissed notification and must not come back on
// the next launch. Without this the two halves disagreed — the click
@@ -1046,6 +1051,30 @@ function App() {
syncPush(user).catch(() => {});
}, [user]);
+ // The list used to be asked for at sign-in and then never, so an application
+ // left open showed nothing created after it started: a chat line the hub
+ // wrote within 20 ms of the message stayed unseen. The hub has no channel to
+ // the page, and is not polled on a timer, so the list is asked for again
+ // when somebody comes back to look: the application returning to the
+ // foreground (a phone included), and the home page, where it is shown.
+ const refreshNotifications = useCallback(() => {
+ if (Date.now() - notifAskedAtRef.current < NOTIF_REFRESH_MS) return;
+ fetchNotifications();
+ }, [fetchNotifications]);
+
+ useEffect(() => {
+ if (!user) return;
+ const onVisible = () => {
+ if (document.visibilityState === 'visible') refreshNotifications();
+ };
+ document.addEventListener('visibilitychange', onVisible);
+ return () => document.removeEventListener('visibilitychange', onVisible);
+ }, [user, refreshNotifications]);
+
+ useEffect(() => {
+ if (user && route === '/') refreshNotifications();
+ }, [user, route, refreshNotifications]);
+
// The node this application ships, set up, started and linked for whoever
// signs in (main.js `ensureNode`): installing it is all it should take.
// Keyed on the account, not the session object -- the token rotates, and a