- Message actions (reply/react/edit/delete) consolidated into ONE hover pill anchored to the
bubble's top-right, overlapping it so on short messages the icons no longer float off into
empty space and vanish before you can click.
- Desktop toast + mobile(FCM)/web-background push now show the sender's real DP:
* renderer passes the DP URL through; desktop shell downloads it for the toast icon
(canvas-drawing an external DP tainted it → initials). Desktop bumped to 0.1.4.
* DM push payload carries icon=sender avatar; sw.js already uses it (web background),
sendFcm sets notification.image (Android).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /api/v1/devices (register) + /api/v1/devices/remove — auth-required, validates
platform (ios|android), upserts by token; e2e covers register/validation/auth/remove.
- db device_tokens table + deviceTokens repo.
- push.js: FCM HTTP v1 (Android) and APNs token-based over HTTP/2 (iOS) folded into
the single push.sendToUser path alongside Web Push; each transport independently
config-gated and a silent no-op without creds. Dead tokens pruned on 404/410.
- docs: CLIENTS.md Phase B updated; DEPLOY.md env table adds FCM/APNs vars.
e2e 117/117.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Page-level Notifications can't fire when a tab is frozen/closed (and never on
mobile), which is why recipients on another tab/app got nothing. Adds a
notification-only service worker (sw.js, no caching) + Web Push:
- push.js: optional web-push wrapper (no-op unless web-push installed AND
VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY set -> app unaffected if unconfigured).
- push_subscriptions table + R.pushSubs repo (upsert by endpoint, prune dead).
- /api/push/vapid|subscribe|unsubscribe; DM + group message routes also send a
Web Push to recipients.
- Client registers /sw.js, subscribes when permission granted; hidden-tab popups
are left to push to avoid double-notifying (pushActive flag); SW suppresses the
OS popup when a tab is visible. Removes the old code that unregistered SWs.
Requires (prod, once): npm install + VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY/VAPID_SUBJECT env.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>