# `Erabi.Issho`
[🔗](https://github.com/hattoriai/erabi/blob/v0.1.0/lib/erabi/issho.ex#L2)

People and agents seen choosing, with
[Issho](https://hex.pm/packages/issho): a picker shows who has it
open and the option they are on, and an agent that changes a value
goes to the picker, opens it, and moves to the option before the
value lands, for the people who watch.

A picker's value is not shared here: it is the value of the document
that holds it (a page's property, a row, a form), which your app saves
and shares as it does. Erabi carries only who is choosing, and where.

## The picker

    <.select
      field={@form[:status]}
      options={@statuses}
      issho={[instance: MyApp.Issho, document: "page:42/status"]}
      user={@current_user}
    />

`issho` takes the keys of every component of the family
(`Issho.Join`): the picker's document names it in its room (the part
before the first `"/"`), and the LiveView joins that room for it. The
`user` is the person, as `Issho.Actor` says them.

The people of the room who have the picker open show on its field,
each in their color; the option each is on is marked in the list, and
when the list is closed on a screen, a peek of it shows the option they
move to. Issho's pointers find a picker's places by their options.

## An agent

An agent's tool changes the value as the app does, in its own
document, and lets the people see it choose first:

    def call(%{"status" => status}, ctx) do
      {:ok, participant} = Issho.Tool.Context.join(ctx, "page:42")

      Erabi.Issho.choose(participant, "page:42/status", status, fn ->
        Pages.set_property(page, :status, status)
      end)
    end

`choose/4` moves the agent to the picker (its list opens on the
screens that show it), then to the option, and waits a moment there
(`:pace`), then runs the function, then leaves the picker. The agent
moves only for someone who sees the picker, within Issho's time budget
(`Issho.Motion`); else the value lands at once.

## Locks

A picker is chosen in by one at a time: the person who opens its list
holds its lock, until they close it or leave. The others see who is
choosing, and their picker does not open; if two open it at once, the
one who opened it first keeps it, and the other's list closes with
the reason. An agent takes the lock as it chooses, and `choose/5`
gives `{:error, {:locked, key}}` when someone else holds it.
`lock: false` in the picker's `issho` turns the locks off.

The lock is the people's way of not choosing over each other, not a
check: a form posts as it does, and the app's document keeps the last
value saved.

## Places

A place in a picker is `%{"open" => true, "option" => value}` (the
option a person or an agent is on, its list open), or `%{}` (the
field), in the room as the `"at"` of the picker's document:

    %{"doc" => "page:42/status", "at" => %{"open" => true, "option" => "done"}}

The option is its value as the picker posts it (text).

# `choose`

```elixir
@spec choose(GenServer.server(), String.t(), term(), (-&gt; result), keyword()) ::
  result | {:error, {:locked, String.t()} | term()}
when result: term()
```

Moves an agent through a picker as it chooses `value` (or several,
for a picker of several), runs `fun` (the app's change), and leaves the
picker; gives what `fun` gives.

When someone else holds the picker's lock, the agent does not choose:
`{:error, {:locked, key}}`, with the holder's key. An agent that a
person paused or stopped (`Issho.Work`) does not choose either: it
gives Issho's `{:error, {:interrupted, by}}`.

Options: `:pace`, how long the agent stays on each option before it
takes it (500 ms; 0 for none), and `:travel` (`false`: the agent
is there at once, as `Issho.Motion` says).

# `place`

```elixir
@spec place(String.t(), map()) :: map()
```

A place of a picker, for `Issho.Motion` or a participant's place:
`%{"doc" => document, "at" => at}`.

    iex> Erabi.Issho.place("page:42/status", %{"open" => true, "option" => "done"})
    %{"doc" => "page:42/status", "at" => %{"open" => true, "option" => "done"}}

# `validate`

```elixir
@spec validate(term()) :: :ok | {:error, :invalid_place}
```

Checks a place in a picker (its `"at"`): `:ok`, or an error.

    iex> Erabi.Issho.validate(%{"open" => true, "option" => "done"})
    :ok
    iex> Erabi.Issho.validate(%{"cell" => [1, "a"]})
    {:error, :invalid_place}

---

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