pwa: installable manifest and Web Push stages 0-1 (dark by default) #121
Loading…
Reference in a new issue
No description provided.
Delete branch "void/pwa-push-stage-0-1"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What changed
Stages 0 and 1 of
/opt/commons/handoff/ratatoskr-app-push-gap-20261110.md. Nothing is deployed and the homeserver config is untouched.Stage 0
public/manifest.json:id: "/",scope: "./", a 512 maskable icon (public/res/android/android-chrome-maskable-512x512.png, the mark inside the 80% safe zone on a full-bleed gradient of the icon's own colours), and theorientation: portraitlock dropped (it locked tablets and desktop windows; nothing in the app needs it).id: "/"is what Chrome already derives fromstart_url: "./"at a site root, so the installed app keeps its identity. At a sub-path deployment (CINNY_BASE) it would not; chat.gamecult.org is at the root.What is stored. One IndexedDB record,
ratatoskr-sw/session/ keysession:{ baseUrl, accessToken }. Nothing else. It is written only while the browser holds a push subscription (the worker checkspushManager.getSubscription()on every session hand-off), and deleted when the page hands the worker no session (logout) or the subscription is gone. A browser that never turned push on stores nothing. Only the push handler reads it, and sends the token only to thatbaseUrl(two reads: the event and the room name).Stage 1 (
src/app/features/push/, wired insrc/sw.ts)pushhandler: always shows a notification (userVisibleOnly), except when a window is visible and focused. Ids-only pushes are resolved through the homeserver with the stored token (clear-text rooms; an encrypted event or any failure shows "New message"). One notification per room (tag,renotify).notificationclick: focuses an open window and posts the room to it; with none open, starts the app with?pushRoom=&pushEvent=, whichusePushOpen(mounted inClientNonUIFeatures) turns intonavigateRoomand strips from the URL.PushNotification.tsx): shown only whenconfig.jsonpushhasvapidPublicKey,gatewayUrlandappIdall filled and the browser supports it. On means a subscription AND this account's pusher for it. Enable subscribes then sets the pusher (a subscription under another VAPID key is replaced); disable deletes the pusher, then unsubscribes.config.jsonshipspush: { vapidPublicKey: "", gatewayUrl: "", appId: "" }: off. The repoconfig.jsonalready differs from the live one (live isterrainwithout the newer blocks), sobuild-prod-dist.shneedsALLOW_CONFIG_DRIFT=1until the live file is reconciled; this adds one more block to that diff.webpushtype:pushkey= the subscription's p256dh,data.endpoint/authread by Sygnal (I readsygnal/webpushpushkin.py),data.url/formatread by Synapse,format: event_id_only.Verification
vitest runon a freshnpm ci: 94 files, 1083 tests; 1082 pass. The one failure issrc/app/features/scene/index.test.tstiming out in the full run; it passes alone (14/14), on this branch and on base. 34 new tests insrc/app/features/push/: payload parsing and description against a mock homeserver (token, URLs, 401, network down, encrypted, clipping), the push and click handlers against a mock worker port fed the Matrix gateway body, the pusher against a mock homeserver (shape, order, failure, toggle truth, stale key), the session store through a fake IndexedDB, the manifest and its icon files, and the shipped-empty config.eslintclean on touched files,prettierclean,vite buildpasses.tsc --noEmitis noisy at base (matrix-js-sdk types); nothing new beyond the sameIPusherRequestimport error class.dist/sw.json a localhost origin: registered; a session hand-off with no subscription stores nothing, and a logout leaves nothing; with a stored session a CDP-delivered push event calledshowNotification('Ratatoskr', {body: 'New message', tag: '!r:x', renotify, data: {roomId, eventId}})with the homeserver unreachable. Not run on a phone, and not against Sygnal.Not done (Stages 2 and 3, follow-ups filed)
Sygnal, VAPID keys, nginx location,
push.include_contentand the liveconfig.jsonpush block are Eureka's host work; the phone test needs the operator's phone.🤖 Generated with Claude Code
Stage 0: manifest gains id "/" (what Chrome already derives from start_url), scope, a 512 maskable icon, and drops the portrait lock. The service worker keeps the session ({baseUrl, accessToken}) in IndexedDB `ratatoskr-sw` so a push can wake a worker with no tab; the page's existing logout message deletes it. Stage 1: `push` and `notificationclick` handlers (src/app/features/push), a pusher toggle in notification settings, `push` block in config.json shipped empty (off). Tested against a mock homeserver and Matrix gateway body. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>