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

Collaboration on charts, for people and agents, in
[Issho](https://hex.pm/packages/issho)'s rooms: a chart's spec is a
shared state of the room (`Issho.State`), that everyone changes with
the same functions, and that is a valid chart after every change.

Fude draws charts on its own; this module is only for a chart that
others change too: a report two people build at once, a chart an agent
makes beside you. It is compiled when Issho is in your deps.

## The document

A shared chart is a document of a room. Define it in your policy, from
the chart your app keeps, and save it in `:on_change`:

    def document(_who, "report:" <> id = doc) do
      report = Reports.get!(id)
      Fude.Issho.document(report.chart, on_change: &Reports.save_chart(report, &1, &2))
    end

`Fude.Issho.document/2` gives Issho the chart's spec, with an id for
each layer and annotation (so that two changes to two layers at once
both stay), and a hook that refuses a change that would make the chart
invalid: the person or the agent who made it gets every error, with
where it is. `:on_change` gets the chart after each change and who made
it, and answers `:ok` or `{:error, reason}` to refuse it.

## In a LiveView

    def mount(%{"id" => id}, _session, socket) do
      room = "report:" <> id
      report = Reports.get!(id)

      if connected?(socket),
        do: {:ok, _} = Issho.Live.join(MyApp.Issho, socket.assigns.current_user, room)

      {:ok, Fude.Issho.assign(socket, :chart, room, room, report.chart)}
    end

    def handle_event("title", %{"title" => title}, socket) do
      :ok = Fude.Issho.change(socket, :chart, &Fude.title(&1, title))
      {:noreply, socket}
    end

```heex
<.chart id="revenue" spec={@chart} data={@sales} issho="report:7" />
```

`@chart` is the chart's spec, and changes as anyone changes it.
`change/3` takes a function of the chart, made with `Fude`'s own
functions (`Fude.title/3`, `Fude.annotate/3`, `Fude.layers/3`), and
sends only what it changed. The `issho` attribute of `<.chart>` is the
chart's document: the people in the room see each other's pointers on
its marks, at the same data on every screen (`Fude.Issho.Place`).

## Agents

An agent uses the same chart: `tools/0` gives `read_chart`, which says
what a chart shows (its spec, and the table of its data when your app
gives it), and `change_chart`, which takes the spec as the agent wants
it and sends what changed. Issho's `point` tool points at a mark, in
`Fude.Issho.Place`'s terms, and its `update_state` changes a spec by
paths too. Every change passes the same hook as a person's.

The data of a chart is your app's, read as the agent:

    config :fude, Fude.Issho, data: {MyApp.Reports, :chart_data}

    def chart_data("report:" <> id, ctx), do: {:ok, Reports.sales(id, ctx.opts[:on_behalf_of])}

# `assign`

```elixir
@spec assign(
  Phoenix.LiveView.Socket.t(),
  atom(),
  String.t(),
  String.t(),
  Fude.Chart.t() | Fude.Spec.t()
) :: Phoenix.LiveView.Socket.t()
```

Assigns a shared chart's spec as `key`, kept up to date as anyone
changes it (`Issho.State.assign/5`). Before the LiveView is in the
room (the static render), it is the spec of `chart`.

# `change`

```elixir
@spec change(Phoenix.LiveView.Socket.t(), atom(), (Fude.Chart.t() -&gt; Fude.Chart.t())) ::
  :ok | {:error, term()}
```

Changes the shared chart assigned as `key` with a function of the
chart (`Fude`'s functions), and sends what changed, as one change.
Gives `:ok`, or `{:error, reason}`: the chart it makes is not valid,
or the room refused it.

# `chart`

```elixir
@spec chart(Fude.Chart.t() | Fude.Spec.t()) ::
  {:ok, Fude.Chart.t()} | {:error, String.t()}
```

The chart of a shared state (or of a chart, as it is), or the errors of it in words.

# `document`

```elixir
@spec document(Fude.Chart.t() | Fude.Spec.t(), keyword()) ::
  {:ok, map()} | {:error, term()}
```

A shared chart's document, for your policy's `document/2`: the chart
(a `Fude.Chart`, or a spec) as the room's shared state, refused when a
change makes it invalid.

Options: `:on_change`, a function of the chart after a change and the
transaction's context (`:by`, `:document`, `:revision`), answering
`:ok` or `{:error, reason}`; `:store`, Issho's store of the document
(the instance's unless given).

# `ops`

```elixir
@spec ops(Fude.Spec.t(), Fude.Spec.t()) :: [map()]
```

The operations (`Issho.State`) that make one shared state into
another: a `set` or a `delete` for each key that changed, and, for the
layers and annotations, an `insert`, a `move` or a `remove` of each by
its id, and a `set` of each key of one that changed. Two people who
change two keys, or two layers, at once both have their change.

    iex> Fude.Issho.ops(%{"title" => "Q3"}, %{"title" => "Q4"})
    [%{"op" => "set", "path" => ["title"], "value" => "Q4"}]

# `place`

The place module of this binding, or `nil`.

# `state`

```elixir
@spec state(Fude.Chart.t()) :: Fude.Spec.t()
```

The chart as a shared state: its spec (`Fude.Spec.dump/2`), with its
layers in `"layers"` and an id for each layer and annotation that has
none.

# `tools`

```elixir
@spec tools() :: [module()]
```

Fude's tools for agents: `read_chart` and `change_chart`.

---

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