Issho.Comments (Issho v0.4.0)

Copy Markdown View Source

Threads of comments on the places of a room's components: a range of a text, a node of a canvas, a cell of a grid, or the page as a whole. People and agents start them, reply, edit and delete their own, react, mention each other, resolve and reopen them.

A room's threads are a document of the room, "board:42/issho.comments" (document/1), with Issho's own engine (Issho.Engine.Comments): it is Issho's, whatever your policy's document/2 says of that id, kept in the instance's store, shared and live as any document is. Your policy says who may comment: a change to it asks for :comment, not :write, so a reader with a can: :read grant may comment when your policy allows it (Issho.Policy).

From your server

ada = %{id: "user:ada", name: "Ada"}
place = %{"doc" => "board:42", "at" => %{"node" => "n7"}}

{:ok, %{thread: thread}} =
  Issho.Comments.start(MyApp.Issho, "board:42", ada, place, "Should this go first?",
    mentions: ["agent:claude"])

{:ok, _} = Issho.Comments.reply(MyApp.Issho, "board:42", claude, thread, "Yes: it blocks the rest.")
{:ok, _} = Issho.Comments.resolve(MyApp.Issho, "board:42", ada, thread)
{:ok, threads} = Issho.Comments.threads(MyApp.Issho, "board:42")

An actor is an Issho.Actor, or an Issho.Identity (an agent and whom it acts for). From a LiveView, an agent's participant or a browser, the same changes are operations of the comments document (Issho.Comments.Ops), committed under the policy as any change.

Who wrote it, and when

Each comment names its author and its time. Before a change is accepted, Issho holds them to the writer: the author is the signed identity of whoever made the change (its key, name, kind, avatar, colour and whom it acts for), and the time is within a few minutes of the server's clock, so a browser cannot write as someone else, or in the past. A thread's place is in the room.

Events

After each change is saved, the room has its events (Issho.Events):

  • "thread.created": %{thread: id, comment: id, place: place, body: text, mentions: [key]};
  • "comment.created": every new comment, the first of a thread too, %{thread: id, comment: id, place: place, body: text, mentions: [key], reply: boolean};
  • "comment.edited" (body, mentions) and "comment.deleted";
  • "thread.resolved" and "thread.reopened": %{thread: id, place: place}.

An agent mentioned in a comment hears it (wait_for_events with mentions_me), and answers in the thread (the comment tool).

Summary

Functions

Deletes one's own comment; a thread with none left goes.

The comments document of a room.

Whether a document is a room's comments.

Edits one's own comment. Options: :mentions, whom the comment mentions after the edit (none unless given).

Adds the actor's reaction (an emoji) to a comment.

Reopens a resolved thread.

Replies in a thread. Options: :mentions. Gives {:ok, %{thread: id, comment: id}}.

Starts a thread at a place (nil for the page as a whole), with its first comment. Options: :mentions, the keys of the actors it mentions. Gives {:ok, %{thread: id, comment: id}}.

The threads of a room, in the order they began.

Functions

delete(instance, room, actor, thread, comment)

@spec delete(
  atom(),
  String.t(),
  Issho.Actor.t() | Issho.Identity.t(),
  String.t(),
  String.t()
) ::
  {:ok, map()} | {:error, term()}

Deletes one's own comment; a thread with none left goes.

document(room)

@spec document(String.t()) :: String.t()

The comments document of a room.

iex> Issho.Comments.document("board:42")
"board:42/issho.comments"

document?(id)

@spec document?(String.t()) :: boolean()

Whether a document is a room's comments.

iex> Issho.Comments.document?("board:42/issho.comments")
true
iex> Issho.Comments.document?("board:42/n7/issho.comments")
false

edit(instance, room, actor, thread, comment, body, opts \\ [])

@spec edit(
  atom(),
  String.t(),
  Issho.Actor.t() | Issho.Identity.t(),
  String.t(),
  String.t(),
  String.t(),
  keyword()
) :: {:ok, map()} | {:error, term()}

Edits one's own comment. Options: :mentions, whom the comment mentions after the edit (none unless given).

react(instance, room, actor, thread, comment, emoji)

@spec react(
  atom(),
  String.t(),
  Issho.Actor.t() | Issho.Identity.t(),
  String.t(),
  String.t(),
  String.t()
) :: {:ok, map()} | {:error, term()}

Adds the actor's reaction (an emoji) to a comment.

reopen(instance, room, actor, thread)

@spec reopen(atom(), String.t(), Issho.Actor.t() | Issho.Identity.t(), String.t()) ::
  {:ok, map()} | {:error, term()}

Reopens a resolved thread.

reply(instance, room, actor, thread, body, opts \\ [])

@spec reply(
  atom(),
  String.t(),
  Issho.Actor.t() | Issho.Identity.t(),
  String.t(),
  String.t(),
  keyword()
) :: {:ok, %{thread: String.t(), comment: String.t()}} | {:error, term()}

Replies in a thread. Options: :mentions. Gives {:ok, %{thread: id, comment: id}}.

resolve(instance, room, actor, thread)

@spec resolve(atom(), String.t(), Issho.Actor.t() | Issho.Identity.t(), String.t()) ::
  {:ok, map()} | {:error, term()}

Resolves a thread.

start(instance, room, actor, place, body, opts \\ [])

@spec start(
  atom(),
  String.t(),
  Issho.Actor.t() | Issho.Identity.t(),
  map() | nil,
  String.t(),
  keyword()
) :: {:ok, %{thread: String.t(), comment: String.t()}} | {:error, term()}

Starts a thread at a place (nil for the page as a whole), with its first comment. Options: :mentions, the keys of the actors it mentions. Gives {:ok, %{thread: id, comment: id}}.

threads(instance, room)

@spec threads(atom(), String.t()) :: {:ok, [map()]} | {:error, term()}

The threads of a room, in the order they began.

unreact(instance, room, actor, thread, comment, emoji)

@spec unreact(
  atom(),
  String.t(),
  Issho.Actor.t() | Issho.Identity.t(),
  String.t(),
  String.t(),
  String.t()
) :: {:ok, map()} | {:error, term()}

Takes the actor's reaction back.