People and agents seen moving things, with 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)
endmove/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.
Summary
Functions
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.
A place of a list, for Issho.Motion or a participant's place:
%{"doc" => document, "at" => at}.
Checks a place in a list (its "at"): :ok, or an error.
Functions
@spec move(GenServer.server(), String.t(), Narabe.Move.t(), (-> 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).
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"}}
@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}