Whether a user is online, and when they were last seen.
Split out of Gamend.Accounts, which still exposes every function here under
the same name. The writes themselves are coalesced by
Gamend.Accounts.PresenceWriter.
Summary
Functions
Mark a user as offline and update last_seen_at.
Mark a user as online and update last_seen_at.
Updates last_seen_at to now for the given user. Fire-and-forget — errors are ignored.
Call on login (session or JWT) to track activity. Also records the UTC day
for Gamend.Analytics (DAU / retention).
Lightweight version of touch_last_seen/1 that accepts a user ID directly.
Performs a single UPDATE without loading the full struct first, setting
last_seen_at to now and is_online to true, then invalidates the cache.
Fire-and-forget — errors are ignored.
Functions
@spec set_user_offline(Ecto.UUID.t()) :: {:ok, Gamend.Accounts.User.t()} | {:error, term()}
Mark a user as offline and update last_seen_at.
Writes only on a real online→offline transition (see set_user_online/1).
Returns {:ok, user} on success.
@spec set_user_online(Ecto.UUID.t()) :: {:ok, Gamend.Accounts.User.t()} | {:error, term()}
Mark a user as online and update last_seen_at.
Writes only on a real offline→online transition: reconnects and extra
tabs/sockets while already online are no-ops, so reconnect storms don't
hammer the users table (and the after_user_online hook fires once per
session, not once per socket).
Returns {:ok, user} on success.
@spec touch_last_seen(Gamend.Accounts.User.t()) :: :ok
Updates last_seen_at to now for the given user. Fire-and-forget — errors are ignored.
Call on login (session or JWT) to track activity. Also records the UTC day
for Gamend.Analytics (DAU / retention).
@spec touch_last_seen_by_id(Ecto.UUID.t()) :: :ok
Lightweight version of touch_last_seen/1 that accepts a user ID directly.
Performs a single UPDATE without loading the full struct first, setting
last_seen_at to now and is_online to true, then invalidates the cache.
Fire-and-forget — errors are ignored.