diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-02 12:44:04 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-02 12:44:04 +0200 |
| commit | a194b333169efb5e25bc05c94567600eec8bb823 (patch) | |
| tree | fc51135a85c71ce9e0f8b58d294fb914b90e1dd8 /packages/meshbay-hub/src | |
| parent | 2d8c6bc449e343a80569e45d4ceae0423616cedd (diff) | |
| download | meshbay-a194b333169efb5e25bc05c94567600eec8bb823.tar.gz | |
fix(hub): a dismissed notification stays dismissed across a restart
Clicking a notification navigated to the group and the entry disappeared —
the intended behaviour — and it was back on the next launch.
Neither half was wrong on its own, which is why it survived. `markRead` drops
the entry locally *and* marks it read on the hub, deliberately: "Reading it is
the point of clicking it: it goes, here and in the count, rather than sitting
there greyed out." The hub honoured that and persisted it. But the startup
fetch asked for `/v1/notifications?limit=20` with no filter, and the endpoint
returns read and unread alike, so every dismissed notification came straight
back. It asks for `unread_only=true` now — a parameter the endpoint already
had and already tested.
The feed still carried the fossil of the older intent:
`class="notif-item ${n.read ? '' : 'notif-unread'}"`, styling for a read entry
rendered greyed out, from before clicking meant dismissing. Nothing read
reaches the feed any more, so that branch was dead code describing behaviour
the application had abandoned — and noticing it is what made the two halves'
disagreement visible. Removed.
`unread_count` is computed server-side over the whole table and is unaffected
by the filter, so the bell is unchanged.
test_notification_dismissal.py holds both halves: the API round trip that is
the reported bug (list, read, list again), its mirror showing the unfiltered
endpoint still returns it — so the fix cannot read as a coincidence — and a
static check that the SPA asks for the filter, which is the only one of the
three that catches the defect that actually happened. Verified by dropping the
parameter again: that one fails, the API tests do not.
Read notifications now accumulate unread in the table rather than being
deleted. Purge removes them; the volume is small. Making dismissal a delete
would suit `purge_notifications`' own docstring — "these are signals, not a
record" — but it would leave `/read` a misnomer and `read-all` inconsistent,
so it is a separate decision.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014UtzVrzM7e2tG9fSpkR9ML
Diffstat (limited to 'packages/meshbay-hub/src')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/app.js | 10 |
1 files changed, 8 insertions, 2 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js index 06b6c10..7cc7c83 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js @@ -342,7 +342,7 @@ function NotificationFeed({ notifications, onMarkRead, onPurge }) { </button> </h3> ${notifications.map(n => html` - <div key=${n.id} class="notif-item ${n.read ? '' : 'notif-unread'}" + <div key=${n.id} class="notif-item notif-unread" onClick=${() => { // Reading it is the point of clicking it: it goes, here and in the // count, rather than sitting there greyed out. @@ -579,7 +579,13 @@ function App() { if (!user || notifDisabled) { setNotifications([]); setUnreadCount(0); return; } - hubFetch('/v1/notifications?limit=20', { token: user.token }) + // `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 + // removed it here and marked it read on the hub, and the next startup + // asked for everything and put it straight back. `unread_count` is + // computed server-side and is unaffected by the filter. + hubFetch('/v1/notifications?limit=20&unread_only=true', { token: user.token }) .then(data => { setNotifications(data.notifications || []); setUnreadCount(data.unread_count || 0); |