# `PhoenixKit.Notifications.Prefs`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.102/lib/phoenix_kit/notifications/prefs.ex#L1)

Per-user notification preferences.

Preferences live inside the user's existing `custom_fields` JSONB column
under the `"notification_preferences"` key — a flat `%{type_key =>
boolean}` map. Unset keys default to the type's own `:default` flag via
`PhoenixKit.Notifications.Types.default_for/1`, so behaviour before a
user has opted in anywhere is unchanged.

The filter function `user_wants?/2` is called once per notification
fan-out from `PhoenixKit.Notifications.maybe_create_from_activity/1`. It's
designed to fail open: any lookup error, unknown action, or malformed
prefs map returns `true` so the system never silently drops a
notification due to a bad row.

# `get`

```elixir
@spec get(PhoenixKit.Users.Auth.User.t() | String.t()) :: %{
  optional(String.t()) =&gt; boolean()
}
```

Returns the user's raw preference map.

Accepts either a loaded `%User{}` (zero DB work) or a UUID (one lookup).
Missing preferences return `%{}`.

# `update`

```elixir
@spec update(PhoenixKit.Users.Auth.User.t(), %{optional(String.t()) =&gt; boolean()}) ::
  {:ok, PhoenixKit.Users.Auth.User.t()} | {:error, Ecto.Changeset.t()}
```

Saves a preference map into the user's `custom_fields`.

`prefs` is the full map of `%{type_key => boolean}` — callers should
include entries for every rendered toggle, since the storage is a
replace, not a merge-at-the-key level. Other custom-field keys are
preserved (we merge at the `custom_fields` level, not inside it).

# `user_wants?`

```elixir
@spec user_wants?(String.t(), String.t()) :: boolean()
```

Answers "would this user want a notification for this action?"

Returns `true` on any ambiguity so new actions, unknown prefs, or a
failing user lookup never cause a silent drop.

Order of checks:

  1. Unknown action (no registered type) → `true` (fail open)
  2. Prefs map has the type key set to `true` or `false` → that value
  3. No entry → the type's `:default` from `Types.default_for/1`
  4. Any raise → logged as a warning and returns `true`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
