Notifications
Browser push notifications alert users when it is their turn or when notable events occur. In-app notifications appear in the navbar bell panel; users configure email, in-app, and push preferences in UserSettingsModal.js (settings.all.notifications for email/push categories, settings.all.inAppNotifications for the in-app feed).
Key files
| File | Role |
|---|---|
NotificationBell.js |
Navbar bell icon + dropdown panel (in-app feed) |
notificationMessages.js |
Renders notification body types with links and i18n |
useDismissNotification.js |
Per-item dismiss (dismiss_notification) |
useDismissAllNotifications.js |
Bulk dismiss (dismiss_all_notifications) |
useMarkNotificationsSeen.js |
Mark read (mark_notifications_seen) |
useUnreadNews.js |
News callout + badge when news-last-seen is stale |
subscription.js |
Per-device subscribe, delete, resync, and unregister-all |
public/sw.js |
Service worker (push handler) |
src/index.js |
Service worker registration and in-app toast for focused-tab pushes |
In-app bell panel
NotificationBell.js loads notifications via fetchNotifications() (list_notifications) when globalMe.notifications is unset. Me.js refetches the same query alongside me_dashboard on home / refresh-me (gap filler). New items also trigger a debounced refetch via WebSocket notification (scheduleNotificationsRefresh.js). Badge count includes only new items (status: 'new') plus unread news.
Panel layout (top to bottom):
- News callout (if unread news) — link to
/news - Mark all read — when any item is new (
mark_notifications_seenwith nosks) - Notification list — new items highlighted; per-row Mark read, Dismiss, and challenge View
- Dismiss all — two-click confirm (
dismiss_all_notifications)
English copy: notifications.* and me.notifications.* in public/locales/en/apfront.json.
Backend item shape and TTL: Notifications — In-app dashboard feed.
Per-device subscription flow
Each browser/device is registered independently. The settings checkbox applies to this device only.
- User checks "Push notifications on this device" in
UserSettingsModal.js. subscribeUser()requests notification permission and callspushManager.subscribe()with the VAPID public key from config (PUSH_VAPID_PUBLIC_KEYinglobal.js).- Subscription is sent to the backend via
save_push:
callAuthApi("save_push", { payload: subscription.toJSON() })
- On login,
ProfileBootstrap.jscallsresyncPushSubscription()once to refresh the server record if this browser already has a subscription (no permission prompt).
Disable on this device
Unchecking the box calls delete_push with the subscription endpoint, then pushManager.unsubscribe() locally.
Unregister all devices
A separate control calls set_push({ state: false }), which removes every PUSH record for the user on the server, then unsubscribes locally on the current browser.
Service worker
Registered on window load in index.js. When the tab is focused, the service worker posts a message to the page (shown as a toast); otherwise it displays an OS notification.
Backend delivery: Notifications.