[READ-ONLY] Mirror of https://github.com/flo-bit/atproto-notify.
6.0 kB
atmo.pub redesign — implementation plan#
Redesign of the webapp (apps/web) into a v2-style installable PWA with
web push, an inbox, and per-category routing. Loose inspiration from
ref/design/project/v2.* (read v2.html → its imports for screen-level detail).
Backend work lands in the relay (apps/relay).
Locked decisions (2026-05-23)#
- Domains unchanged. Relay stays
relay.atmo.pub(did:webuntouched), webapp staysatmo.pub. This is a UI rebrand only → product name becomes atmo.pub. No OAuthORIGIN/client_id change. - Categories = free-form.
sendcarries an optionalcategorystring + optionalcategoryDescription. No sender "declare" step; the relay learns categories as they arrive. Routing keys on(recipient, sender, category). - No email channel. Channels are push + telegram only.
- Trusted apps, hardcoded. Replaces v2's "follows"/privacy/auto-allow: a
hardcoded list of sender DIDs in relay config that auto-grant at
requestPermission(skip pending). No Bluesky graph lookups, no privacy modes. - Inbox keeps everything. Every delivered notification is stored in the inbox (full history); per-category routing only decides which alert channels also fire (push / telegram). No pruning for now (revisit retention later).
- Keep the bell logo. Wordmark renders
atmo.pub(.pubmuted), bell in accent.
Reused as-is: the v2 color tokens are identical to apps/web/src/routes/layout.css,
so theming carries straight over.
Routing model#
Routes available (after cutting email): push, telegram, push+telegram,
inbox (inbox-only, no alert), off (alias of inbox-only for now), default
(inherit user-wide defaultRoute). Resolution per notification:
- Always insert a row into
notifications(inbox = canonical log). - Look up routing for
(recipient, sender, category); fall back to the user'sdefaultRoute. - Fan out alerts to the resolved channels (push and/or telegram).
Phases#
Phase 0 — Rebrand + foundation (no behavior change)#
- Rename product → atmo.pub in
apps/web,apps/homepage, relay Telegram copy (telegram/commands.ts, dashboard links), and docs. Wordmarkatmo.pub, keep bellLogomark. - Build a shared component layer in
apps/web/src/lib/componentsported from v2:Icon(glyph set),AppMark,RouteChip,IOSToggle,ActorStack, plus the Inbox/Apps/Settings shell (desktop split + mobile bottom-tab nav). - Remove the migrated landing (
/) and/docsfromapps/web(now inapps/homepage);/becomes the clean Login.
Phase 1 — PWA shell + restyle current features (frontend only)#
- Login (clean), Apps (today's grants + pending + denied, v2 styling), Settings (Telegram channel + existing toggle). Inbox tab present as a "coming soon" placeholder. Responsive desktop + mobile.
- Make
apps/weban installable PWA:manifest.webmanifest, icons, minimal service worker (install/offline shell only — no push yet). - Backend: none (rides existing
RelayRpcbinding).
Phase 2 — Web push (headline feature)#
- Relay:
- Generate VAPID keypair (public in config, private as
VAPID_PRIVATE_KEYsecret). - New
push_subscriptionstable:(did, endpoint PK, p256dh, auth, created_at)— multiple per user. Telegram stays inchannels. Migration0004_push. - Web Push delivery (aes128gcm encryption + VAPID JWT via WebCrypto, no node) in the dispatcher; dead endpoints (404/410) pruned.
- Binding methods
registerWebPush(did, subscription)/unregisterWebPush(did, endpoint). - Until Phase 4,
sendfans out globally to push + telegram.
- Generate VAPID keypair (public in config, private as
- Client: service worker
push→showNotification;PushManager.subscribewith the VAPID public key; subscription registered via web server → binding. Settings shows push devices (count + revoke).
Phase 3 — Inbox#
- Relay:
notificationstable:(id, recipient_did, sender_did, category, title, body, uri, actors_json, created_at, read_at). Migration0005_inbox.sendwrites a notification row for every delivery; gains optionalcategory,categoryDescription,actors[](additive lexicon change).- Binding methods
listNotifications(did, cursor)(paged) +markRead(did, ids|all).
- Client: Inbox screen — actor stacks, route chips, unread state, mark-read; swipe/grouped variants optional.
- Update
apps/example-sender+ homepage/docsfor the newsendfields.
Phase 4 — Per-category routing + trusted apps#
- Relay:
app_categoriestable: discovered(sender_did, category, description, count, last_seen)so the routing UI can list them.routingtable:(recipient_did, sender_did, category, routes_json); userdefault_route+ optionalquiet_hourscolumns onusers. Migration0006_routing.- Dispatcher resolves route per notification (see Routing model) and fans out.
- Trusted apps: hardcoded
TRUSTED_SENDERSin relay config;requestPermissionauto-grants for those DIDs. - (Optional) quiet hours applied at delivery; can be cut.
- Binding methods
getRouting(did)/setRouting(did, sender, category, routes); extendupdateSettingsfordefaultRoute(+ quiet hours).
- Client: app-detail per-category routing UI, Default-routing screen, Channels,
read-only Trusted-apps list. (v2 has 3 routing-UI variations in
v2-variations.jsx— pick one.)
Deferred / out of scope (for now)#
- Email channel. Bluesky-graph "follows" privacy. Notification retention/pruning.
- Native push (project is PWA-only — see
mobile-and-pushmemory).
Cross-cutting#
- Theming reuse (tokens already match v2); accessibility (focus rings, reduced
motion already in
layout.css); keep theRelayRpcservice-binding boundary — all new first-party methods go on the binding, never public XRPC. Onlysendchanges are public (and additive).