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

People and agents seen moving things, with
[Issho](https://hex.pm/packages/issho): a list, a tree or a board
shows the item each person is on, the items someone is dragging (in
their color, with their name) and where they would land, and an agent
that moves items goes to them and drags them there before the move
lands, for the people who watch.

The order is not shared here: it is the order your app keeps (with
`Narabe.Ecto`, and a broadcast or a stream to the other viewers).
Narabe carries only who is moving what, and where to.

## The list

    <.board
      id="sprint"
      columns={@columns}
      cards={@cards}
      on_move="move"
      issho={[instance: MyApp.Issho, document: "board:7/cards"]}
      user={@current_user}
    />

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

## Locks

An item is moved by one at a time: the person who lifts it holds it
until they drop it or let go, and the others cannot lift it meanwhile
(they see who holds it). `lock: false` in the list's `issho` turns the
locks off. A lock is the people's way of not moving over each other,
not a check: two moves that reach the server both apply, in the order
they come (`Narabe.Ecto`'s `:serialize`).

## An agent

An agent's tool moves the items as the app does, and lets the people
see it drag them first:

    def call(%{"card" => card, "column" => column}, ctx) do
      {:ok, participant} = Issho.Tool.Context.join(ctx, "board:7")
      move = %Narabe.Move{ids: [card], parent: column, index: 0}

      Narabe.Issho.move(participant, "board:7/cards", move, fn ->
        Narabe.Ecto.move(Repo, Card, move)
      end)
    end

`move/5` takes the items' lock, goes to the first item, picks it up,
goes to the place it lands and waits a moment there (`:pace`), then
runs the function, then lets go. A person who paused or stopped the
agent (`Issho.Work`) stops it before the move lands.

## Places

A place in a list is `%{"item" => id}` (the item a person is on), or,
while they drag, `%{"item" => id, "count" => n, "drag" => gap}` with
the gap they would drop in: `%{"parent" => id, "after" => id,
"before" => id, "level" => n}` (the move's own words). Ids are text,
as the browser has them.

# `move`

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

Moves an agent through a list as it moves items (`Narabe.Move`), runs
`fun` (the app's move), and lets go; gives what `fun` gives.

When someone else holds one of the items, the agent does not move it:
`{:error, {:locked, key}}`, with the holder's key. An agent that a
person paused or stopped gives Issho's `{:error, {:interrupted, by}}`.

Options: `:pace`, how long the agent stays at the item and at the
place it drops 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 list, for `Issho.Motion` or a participant's place:
`%{"doc" => document, "at" => at}`.

    iex> Narabe.Issho.place("board:7/cards", %{"item" => "42"})
    %{"doc" => "board:7/cards", "at" => %{"item" => "42"}}

# `validate`

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

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

    iex> Narabe.Issho.validate(%{"item" => "42", "count" => 2, "drag" => %{"parent" => "7", "after" => nil, "before" => "9", "level" => 2}})
    :ok
    iex> Narabe.Issho.validate(%{"option" => "done"})
    {:error, :invalid_place}

---

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