feat(ui): show new-message notifications via the gateway shell bridge#409
Conversation
Browser notifications silently stopped working when River moved into the gateway's sandboxed iframe (freenet-core#3254): the iframe has an opaque (null) origin, and browsers block the Notifications API there, so permission can never reach "granted" and notifications never fire (#408). They worked before that as a top-level page. Route notifications through the shell page (real origin) over the existing `__freenet_shell__` postMessage bridge — the same channel that already carries title/favicon. Paired with freenet-core#4801, which adds the shell-side handlers. - Detect the iframe context (`window.parent !== window`). When served directly (dev / `no-sync`, a real origin) the existing direct Notifications API path is used unchanged. - In the iframe, `request_permission_on_first_message` asks the shell to offer notifications (it owns the real-origin permission prompt), and `show_notification` postMessages the notification to the shell instead of calling the API directly. - Encode the room's owner key as the notification `tag`; the shell posts a `notification_click` reply carrying it back, and a window `message` listener (installed once from App, deferred signal write per the Dioxus signal-safety rules) routes CURRENT_ROOM to the clicked room. Tests: tag round-trip + malformed-tag rejection (the click-routing correctness). Native + wasm compile clean. Refs #408 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JyYK79ygyquHkDqnnBQ9Xe
…k, feature) Multi-model review (Codex + a signal-safety lens) of #409 found no signal-safety or correctness P1/P2 (the deferred CURRENT_ROOM write is correct and required). Addressed: - Codex P2: a notification click set CURRENT_ROOM but not MOBILE_VIEW, so on mobile the user stayed on the Rooms/Members panel instead of seeing the clicked conversation (and the room could be marked read without being shown). Set MOBILE_VIEW = Chat in the deferred callback, mirroring normal room selection. - Lens P3: declare `MessageEvent` explicitly in ui/Cargo.toml's web-sys features — it was only enabled transitively via dioxus-web/devtools, which is fragile (a dep bump or --no-default-features would drop it). - Lens P3: add an `event.source() == window.parent()` check to the notification_click listener (defense-in-depth against a stray postMessage forcing a room switch), and correct the two comments that overstated the guarantee / the iframe-detection specificity. Native (431) + wasm compile clean. Refs #408 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JyYK79ygyquHkDqnnBQ9Xe
Multi-model review — Codex + a signal-safety/correctness lensVerdict: no P1/P2 signal-safety or correctness bugs. The lens confirmed the deferred Fixed in the latest commit
Documented, not changed (justified)
Aside (separate cleanup, not this PR)The lens noted Paired with freenet-core#4801 (the shell-side handlers); this half is a harmless no-op until that ships and the gateway is rebuilt. [AI-assisted - Claude] |
UI-only republish of #409 (browser notifications via gateway shell bridge). No WASM/migration change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B49wBfvR8EjpfYTw5muNV9
Problem
Browser notifications for new messages silently stopped working when River moved into the gateway's sandboxed iframe (freenet-core#3254, Feb 2026): the iframe has an opaque (null) origin, and browsers block the Web Notifications API from opaque-origin iframes, so permission can never reach
grantedand notifications never fire (#408). They worked before that, as a top-level page (Dec 2025 → Feb 2026). Reported by Ivvor.Approach
Route notifications through the shell page (same-origin with the node, a real origin) over the existing
__freenet_shell__postMessage bridge — the same channel that already carries title/favicon updates. Paired with freenet-core#4801, which adds the shell-side handlers that actually show the notification.window.parent !== window). Served directly (dev /no-sync, a real origin), the existing direct Notifications API path is used unchanged.request_permission_on_first_messageasks the shell to offer notifications (the shell owns the real-origin permission prompt / affordance), andshow_notificationpostMessages the notification instead of calling the API directly.tag; the shell posts anotification_clickreply carrying it back, and a windowmessagelistener (installed once fromApp, with the signal write deferred per the Dioxus signal-safety rules) routesCURRENT_ROOMto the clicked room.Deployment / ordering
This is a no-op until a gateway serving the freenet-core#4801 shell is deployed, and neither side's deployment breaks the other (an old shell ignores the unknown message types; old River still calls the API directly, which no-ops in the iframe as it does today). The try.freenet.org gateway is a manual rebuild, so the feature lights up there once #4801 merges and that node is rebuilt.
Testing
taground-trips the room key, and malformed tags are rejected (the click-routing correctness).wasm32compile clean.Refs #408
🤖 Generated with Claude Code