# `PhiaUi.Collab.CollabRoom`
[🔗](https://github.com/charlenopires/PhiaUI/blob/v0.1.17/lib/phia_ui/collab/collab_room.ex#L1)

GenServer managing a single collaboration room session.

Started on-demand via `DynamicSupervisor` when the first user joins
(see `PhiaUi.Collab.RoomManager.join_or_create_room/2`).
Auto-terminates after an idle timeout (default 30 minutes) when all
users have left.

## Behaviour Callbacks

Implement the behaviour in your own module to persist room state:

    defmodule MyApp.CollabRoom do
      @behaviour PhiaUi.Collab.CollabRoom

      @impl true
      def on_join(room_id, user), do: {:ok, %{}}

      @impl true
      def on_leave(room_id, user), do: :ok

      @impl true
      def load_room_state(room_id), do: {:ok, %{}}

      @impl true
      def save_room_state(room_id, state), do: :ok
    end

## State Structure

The GenServer state is a `%PhiaUi.Collab.CollabRoom{}` struct containing:

  - `:room_id` — unique identifier for this room
  - `:callback_module` — optional module implementing this behaviour
  - `:users` — map of `user_id => user_entry` for all joined users
  - `:custom_state` — arbitrary map managed via `update_state/2`
  - `:created_at` / `:last_activity_at` — UTC timestamps
  - `:idle_timeout` — milliseconds before an empty room shuts down

# `load_room_state`
*optional* 

```elixir
@callback load_room_state(room_id :: String.t()) :: {:ok, map()} | {:error, term()}
```

Load persisted room state on startup. Return `{:ok, state_map}` or `{:error, reason}`.

# `on_join`
*optional* 

```elixir
@callback on_join(room_id :: String.t(), user :: map()) :: {:ok, map()} | {:error, term()}
```

Called when a user joins the room. Return `{:ok, meta}` to attach metadata.

# `on_leave`
*optional* 

```elixir
@callback on_leave(room_id :: String.t(), user :: map()) :: :ok
```

Called when a user leaves the room.

# `save_room_state`
*optional* 

```elixir
@callback save_room_state(room_id :: String.t(), state :: map()) :: :ok | {:error, term()}
```

Persist room state after each `update_state/2` call.

# `alive?`

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

Check whether a room process is running.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `get_state`

```elixir
@spec get_state(String.t()) :: {:ok, map()}
```

Get the room's custom state.

# `get_users`

```elixir
@spec get_users(String.t()) :: {:ok, [map()]}
```

Get all users currently in the room.

# `join`

```elixir
@spec join(String.t(), map()) :: {:ok, map()} | {:error, term()}
```

Join a user to the room. `user` must contain an `:id` key.

# `leave`

```elixir
@spec leave(String.t(), String.t()) :: :ok
```

Remove a user from the room by user ID.

# `start_link`

Start a `CollabRoom` process linked to the caller.

## Options
  - `:room_id` (required) — unique room identifier
  - `:callback_module` — module implementing `CollabRoom` behaviour
  - `:idle_timeout` — milliseconds before empty room shuts down (default 30 min)

# `touch`

```elixir
@spec touch(String.t()) :: :ok
```

Refresh the room's last-activity timestamp to prevent idle shutdown.

# `update_state`

```elixir
@spec update_state(String.t(), (map() -&gt; map())) :: :ok
```

Update the room's custom state by applying `fun` to the current state.

The function receives the current custom state map and must return the new state map.
If a callback module with `save_room_state/2` is configured, it is called after the update.

---

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