Skip to content

[Feature]: First-run prompt to enable Background Notifications #336

@shukiv

Description

@shukiv

Problem / Motivation

After #233 shipped, Background Notifications are fully functional —
self-hosters can configure the relay URL in Settings → Notifications,
click Enable, and Web Push works end-to-end (verification handshake,
service-worker delivery, click-to-message).

The remaining UX gap is discovery: a user who just logged in to a
fresh install has no in-app signal that push is available. They have to
know push exists, know it lives under Settings, and remember to enable
it manually. In practice that means most users never turn it on, even
though the relay is sitting there ready to deliver.

Example flow today:

  1. User opens Bulwark for the first time after a self-host upgrade to
    v1.7.x.
  2. Mail loads, the UI looks complete, nothing hints that the browser
    could be receiving background notifications.
  3. User keeps the tab open all day for foreground notifications; closes
    it; misses new mail until next open.
  4. Months later: maybe stumbles onto Settings → Notifications.

Proposed Solution

Show a one-time onboarding affordance the first time the app loads on a
browser whose Notification.permission is default (never asked) and
where Web Push is supported ('serviceWorker' in navigator && 'PushManager' in window). Two reasonable shapes:

Option A — inline banner inside the mail view (preferred):

🔔 Get notified when new mail arrives, even when this tab is closed.
[ Enable ] [ Not now ] [ Don't ask again ]

  • Enable → triggers the same code path as the Settings toggle
    (Notification.requestPermission()pushManager.subscribe() with
    the VAPID public key → POST /api/push/register/web
    PushSubscription/set on JMAP). On success, also flips the
    Background Notifications toggle in Settings so the in-app state is
    consistent.
  • Not now → suppressed for the rest of the session.
  • Don't ask again → persistent localStorage flag.

Option B — guided callout from the existing notifications icon /
Settings entry
, e.g. a small badge / tooltip on the bell icon that
points to the toggle.

Suppression rules:

  • Skip if Notification.permission !== 'default' (don't pester users
    who already accepted, denied, or installed the PWA).
  • Skip on /login, /settings, and on the Bulwark setup flow.
  • Skip when no push relay URL is configured (don't show what we can't
    fulfil).
  • Skip when the Email Notifications toggle is OFF (user explicitly
    opted out of all mail notifications).

Why upstream rather than self-host injection

Self-hosters can absolutely paper over this with a nginx sub_filter
that injects a custom prompt — we did that briefly on our deployment —
but the result is awkward: the injected prompt runs outside Bulwark's
own subscription flow, so even when the user clicks Enable, the
in-app Background Notifications toggle reads "off" until they
revisit Settings. Doing this in Bulwark itself keeps a single source of
truth for the subscription state.

Alternatives Considered

  • Browser-native permission API auto-call on every load — too
    aggressive; Chromium quietly ignores promotional prompts and Firefox
    shows a low-contrast UI bar most users miss.
  • Documentation only — works in theory, but the gap between "feature
    shipped" and "users actually use it" stays wide. Especially on
    self-hosted instances where the operator may not be the same person
    as the end user.

Feature Area

Notifications / PWA

Mockups / Examples

Reeva.me, a self-host that ships Bulwark, ran a quick nginx sub_filter version of Option A as a stopgap. Banner copy and button
layout in the snippet at
https://git.linux-hosting.co.il/shukivaknin/Riva/commit/18b14a0
(reverted in favour of this upstream request). The visual treatment is
arbitrary — what matters is the trigger condition and the hand-off
into Bulwark's own subscribe flow.

Additional Context

If the maintainers are open to it, happy to put together a PR that
adds the banner component, a usePushOnboardingPrompt hook that
encapsulates the suppression rules, and a small piece of state in the
notifications store so the existing toggle reflects the result of the
banner's Enable click. Wanted to align on the approach (Option A vs B,
suppression rules) before sending code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions