feat: add notification preference system with per-type per-channel toggles and admin defaults
Implement configurable notification preferences across in-app and push channels, including a new `notification_preferences` table with soft-delete support, per-user toggle endpoints, admin defaults management, and canonical type definitions. The change introduces `NOTIFICATION_TYPES` and `NOTIFICATION_CHANNELS` constants, `NotificationPrefForm`/`NotificationDefaultForm` models, `notification_enabled` resolution logic, and UI integration via the profile notifications tab and admin panel.
This commit is contained in:
@@ -562,8 +562,9 @@ installable Progressive Web App. Push uses only standard libraries (`cryptograph
|
||||
|
||||
### Events
|
||||
|
||||
Every event that already produces an in-app notification also sends a web push,
|
||||
because both share a single funnel - `create_notification()` in `utils.py`:
|
||||
Every event flows through a single funnel - `create_notification()` in `utils.py` -
|
||||
which delivers on two independent channels, in-app and web push, each gated by the
|
||||
recipient's preferences (see "Configurable notifications" below):
|
||||
|
||||
| Event | Recipient |
|
||||
|-------|-----------|
|
||||
@@ -573,6 +574,8 @@ because both share a single funnel - `create_notification()` in `utils.py`:
|
||||
| `@mention` in any content | mentioned user |
|
||||
| Upvote on your content | content owner |
|
||||
| New follower | followed user |
|
||||
| Badge earned / level-up | the user |
|
||||
| Bug-tracker update | reporter / admins |
|
||||
|
||||
`create_notification` schedules delivery as a fire-and-forget async task, so a dead
|
||||
subscription or push-service error never blocks the triggering request. Delivery
|
||||
@@ -580,6 +583,22 @@ subscription or push-service error never blocks the triggering request. Delivery
|
||||
(legacy `aesgcm` content encoding), and POSTs to each endpoint; subscriptions that
|
||||
return `404`/`410` are soft-deleted.
|
||||
|
||||
### Configurable notifications
|
||||
|
||||
Every notification type can be turned on or off per channel, per user. The **Notifications**
|
||||
tab on a profile page (`/profile/{username}?tab=notifications`, visible to the profile owner
|
||||
and to admins) shows one row per type with two checkboxes - **In-app** and **Push** - saved
|
||||
individually as you toggle them (`POST /profile/{username}/notifications`). A "Reset to
|
||||
defaults" button clears all of a user's overrides (`POST /profile/{username}/notifications/reset`).
|
||||
|
||||
Defaults are opt-out: a type/channel a user never touched is enabled. Admins set the
|
||||
platform-wide default for each type/channel on `/admin/notifications`
|
||||
(`POST /admin/notifications`); a default applies only to users who have not made an explicit
|
||||
choice. Resolution is: user override, else admin default, else on. Preferences are stored in
|
||||
the `notification_preferences` table (per `user_uid` + `notification_type`, soft-deletable)
|
||||
and enforced inside `create_notification()`: the in-app row is written only when the in-app
|
||||
channel is enabled, and `push.notify_user` is scheduled only when the push channel is enabled.
|
||||
|
||||
### VAPID keys
|
||||
|
||||
The server identity is three PEM files generated once at startup in the repository
|
||||
|
||||
Reference in New Issue
Block a user