# `Backpex.Preferences.LiveView`
[🔗](https://github.com/naymspace/backpex/blob/0.20.0/lib/backpex/preferences/live_view.ex#L1)

LiveView-side helpers for the preferences subsystem.

Emits preference-write push_events from a LiveView and owns the wire event
name that the `BackpexPreferences` JS hook listens for. The hook receives
the event, POSTs to the preferences controller, and the controller
persists through the configured adapter.

The event name is a browser contract — treat it as a stable wire protocol
and keep it aligned with `assets/js/hooks/_preferences.js`. The name is
returned from `event_name/0`.

# `client_cookie`

Name of the cookie carrying the browser's *unacknowledged* preference writes.

A browser contract — keep it aligned with `COOKIE_NAME` in
`assets/js/hooks/_preferences.js`.

# `client_manifest`

Builds the browser routing manifest for the configured preference adapters.

Each route carries an opaque token derived from the namespace its adapter
actually uses. This allows session-backed values to survive a tenant change
while values stored in a tenant-scoped adapter remain isolated. The manifest
contains no raw session or application scope values.

Returns `nil` when the endpoint or Phoenix session cannot provide the secrets
required to sign the route tokens.

# `event_name`

Name of the LiveView push_event used to signal a preference write to the
browser-side `BackpexPreferences` hook.

Exposed for tests that need to assert on the emitted event shape.

# `mount_context`

Builds the `Backpex.Preferences.Context` for a LiveView mount.

Combines the session and `socket.assigns` (what scope resolvers need)
with the preferences the browser is holding, which take precedence over
stored values.

The browser overrides the server exactly when it holds a write the server has
not acknowledged. Those writes reach us over the transport that rendered the
page, and each transport can only see one carrier:

  * CONNECTED mount — the `backpex_prefs` connect param. A LiveView
    reads the session snapshot taken when the websocket connected, so on a
    `live_redirect` re-mount it cannot see any preference written since and
    would render stale column/metric visibility. The browser mirrors those
    writes in `sessionStorage` and hands them back on every join, *before*
    mount renders.

  * DISCONNECTED mount — the `backpex_prefs` cookie (see `client_cookie/0`).
    The session cookie a document GET carries can be a full POST round-trip
    behind the user's last write, so the freshly-read session is *not*
    authoritative: it renders the pre-toggle state, which LiveView then
    patches away — the flash. The browser writes its unacknowledged writes to
    `backpex_prefs` synchronously, so entries that fit its 3072-byte budget
    ride the very next request. The cookie is skipped when namespace tokens
    are unavailable; in either degradation case the first paint may
    be stale until LiveView connects. Entries retire as soon as their POST
    responds, so the cookie cannot permanently shadow an adapter.

    Each cookie entry is only honored when its signed adapter namespace token
    matches the route that owns the key on this request. This is the one place
    an attacker-plantable (or simply outlived) cookie lands, so the check runs
    here and does not trust the browser to have discarded it already.

Both carriers feed the same `Backpex.Preferences.Context` client overlay.
When the pending value is available to both transports, the disconnected and
connected renders derive their state from the same value.

Only valid for calls during `mount/3` (including `on_mount` hooks), where
`Phoenix.LiveView.get_connect_params/1` is available.

# `push_write`

Pushes a preference-write event to the browser.

The `BackpexPreferences` JS hook listens for this event and persists the
value via the preferences controller.

This is the *transport primitive*: it hardcodes the browser round-trip and
never consults an adapter. Prefer `Backpex.Preferences.put/4`, which asks the
key's adapter first and only falls back here when the adapter cannot write
outside an HTTP request cycle (`{:error, :requires_http}` — the Session
adapter, and the zero-config default). An adapter that persists server-side
then costs no round-trip at all.

Returns the updated socket so it composes in pipelines.

## Options

  * `:mirror` - set to `:session` to additionally mirror the value into
    the browser's sessionStorage. Required for preferences that are read
    at mount and server-rendered (for example column and metric
    visibility): the Session adapter reads the websocket-connect session
    snapshot, which is frozen for the life of the socket, so without the
    mirror any write after connect silently reverts on the next
    `live_redirect` re-mount. The browser hands mirrored values back in the
    connect params of every join, where `mount_context/2` picks them up.

## Examples

    socket
    |> Backpex.Preferences.LiveView.push_write(Backpex.Preferences.Keys.theme(), "dark")

---

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