Skip to content

Updated the bones for the notifications module#579

Merged
ddon merged 4 commits into
BeamLabEU:mainfrom
alexdont:dev
Jun 1, 2026
Merged

Updated the bones for the notifications module#579
ddon merged 4 commits into
BeamLabEU:mainfrom
alexdont:dev

Conversation

@alexdont

@alexdont alexdont commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

No description provided.

Alexander Don and others added 4 commits June 1, 2026 20:59
Notifications were strictly per-user projections of an activity —
activity_uuid was NOT NULL, so an activity always had to exist. Allow
notifications to stand on their own:

- V126: drop NOT NULL on phoenix_kit_notifications.activity_uuid and add
  `metadata JSONB NOT NULL DEFAULT '{}'`. The (activity_uuid,
  recipient_uuid) unique index still holds — Postgres treats NULLs as
  distinct, so a recipient can hold many standalone rows.
  @current_version 125 -> 126.
- Schema: add :metadata field; drop activity_uuid from validate_required
  (only recipient_uuid is required now).
- Render: when there's no activity, read notification_text/icon/link from
  the notification's own metadata instead of the generic fallback.
- New Notifications.create/1 for app-driven, activity-less notifications
  (recipient_uuid + text/icon/link or raw metadata). Honors the
  notifications_enabled kill-switch, broadcasts {:notification_created};
  pins activity: nil before broadcast so Render takes the metadata path
  (a fresh insert otherwise carries a NotLoaded assoc the activity clause
  would choke on). Skips the per-type prefs filter (no action to map).

Version -> 1.7.127.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two genericity additions for standalone notifications:

1. create/1 takes an optional :type (notification type key) or :action
   (action string) to opt the send into the recipient's per-type
   preference filter — :type via the new Prefs.user_wants_type?/2,
   :action via the existing user_wants?/2. With neither, the send stays
   unconditional (app-driven). Filtered-out sends return {:ok, :skipped}.

2. create_many/2 — multi-recipient fan-out primitive: one standalone
   notification per recipient uuid (caller supplies the list, e.g. an
   author's followers), de-duped, each filtered independently by
   :type/:action prefs, kill-switch checked once. Returns
   {:ok, created_count}. The recipient list is the caller's job — this
   is the fan-out building block, not an audience resolver.

Folded into unreleased 1.7.127.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Notifications was a working subsystem with no presence on the admin
Modules page and no admin route. Wire it up like Jobs/Storage:

- PhoenixKit.Notifications now `use PhoenixKit.Module` — module_key
  "notifications", enable_system/disable_system flip the existing
  notifications_enabled kill-switch (so the module toggle IS the
  kill-switch), get_config returns enabled + admin_stats, and
  permission_metadata + admin_tabs register the "notifications"
  permission and the /admin/notifications nav tab. Registered in
  ModuleRegistry.internal_modules so it's discovered. Added
  Notifications.admin_stats/0 (total / unread / dismissed counts).
- Modules page: a Notifications card (toggle + counts + "View
  Notifications" link), gated like the others on the accessible-modules
  set (Owner gets it automatically via all_module_keys).
- New route live "/admin/notifications" -> Live.Modules.Notifications.Index,
  a simple read-only overview LiveView (status, retention, counts),
  redirecting to /admin/modules when the module is disabled.

Folded into unreleased 1.7.127.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The bell LiveView existed but was never embedded, so notifications
surfaced to nobody. Render it in the admin header, to the right of the
theme switcher, gated on the Notifications module being enabled and a
logged-in user.

The bell is a sticky nested LiveView (own process + PubSub
subscription), so it needs the parent socket. app_layout — a function
component — gained an optional `socket` attr (also threaded into the
internal admin-chrome assigns). It's passed `socket={@socket}` from the
admin.html.heex layout (plugin pages) and the ~25 core admin
app_layout call sites; where it's absent (public/auth pages, controller
dead-views) the bell simply isn't rendered (no crash). A stable id +
sticky keep the bell's subscription alive across admin navigation.

Folded into unreleased 1.7.127.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ddon ddon merged commit 7dfb74e into BeamLabEU:main Jun 1, 2026
mdon added a commit to mdon/phoenix_kit that referenced this pull request Jun 1, 2026
Core schema support for the phoenix_kit_projects nested-sub-projects +
project-assignee feature. Renumbered from V126/V127 → V127/V128 after
rebasing onto main, which took V126 for standalone notifications (BeamLabEU#579).
Bumps @current_version 126 → 128.

V127 — Sub-projects as tasks: adds child_project_uuid to
phoenix_kit_project_assignments (ON DELETE RESTRICT) so an assignment can
embed a child project instead of a task; task_uuid-XOR-child CHECK; partial
unique index (one parent per child); drops task_uuid NOT NULL. down/1 deletes
the child-link rows before restoring NOT NULL.

V128 — Assignee on projects: adds assigned_team/department/person_uuid to
phoenix_kit_projects (ON DELETE SET NULL) with a single-assignee CHECK +
partial indexes. A sub-project is a project, so this covers it too.
ddon pushed a commit that referenced this pull request Jun 1, 2026
Sub-projects + project assignees migrations (V127/V128) and post-#579
notification fixes (kill-switch freshness, admin_stats single query).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants