Skip to content

Commit

Permalink
Feature/add more commands events (#523)
Browse files Browse the repository at this point in the history
  • Loading branch information
electronicbites committed Apr 25, 2024
1 parent 3acf3db commit f61fc25
Show file tree
Hide file tree
Showing 25 changed files with 450 additions and 148 deletions.
35 changes: 27 additions & 8 deletions assets/js/hooks/events/handler.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { Node } from "../types";
import { createItem, updateItem, deleteItem, focusItem } from "../item";
import { Node, UUID } from "../types";
import {
getItemByNode,
createItem,
updateItem,
deleteItem,
focusItem,
} from "../item";

export function handleList({ nodes }: { nodes: Node[] }) {
const container: HTMLOListElement = this.el;

if (nodes.length == 0) {
const node: Node = {
temp_id: self.crypto.randomUUID(),
uuid: self.crypto.randomUUID(),
content: "",
event_id: self.crypto.randomUUID(),
dirty: true,
};
nodes = [node];
Expand All @@ -29,19 +36,31 @@ export function handleList({ nodes }: { nodes: Node[] }) {
focusItem(lastItem);
}

export function handleInsert(node: Node) {
interface NodeEvent {
node: Node;
event_id: UUID;
}

export function handleInsert({ node, event_id }: NodeEvent) {
const container: HTMLOListElement = this.el;

const item = createItem(node);
container.append(item);
const item = getItemByNode(node);
if (item) {
node.dirty = false;
updateItem(node, container);
} else {
const newItem = createItem(node);
container.append(newItem);
}
}

export function handleUpdate(node: Node) {
export function handleUpdate({ node, event_id }: NodeEvent) {
const container: HTMLOListElement = this.el;

node.dirty = false;
updateItem(node, container);
}

export function handleDelete(node: Node) {
export function handleDelete({ node, event_id }: NodeEvent) {
deleteItem(node);
}
9 changes: 8 additions & 1 deletion assets/js/hooks/events/listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export function focusout(event: FocusEvent) {

export function input(event: Event) {
const node = getNodeByEvent(event);
node.event_id = self.crypto.randomUUID();
node.dirty = true;
this.pushEvent("update_node", node);
}
Expand Down Expand Up @@ -71,16 +72,18 @@ export function keydown(event: KeyboardEvent) {

const content = node.content;
node.content = content?.substring(0, splitPos);
node.event_id = self.crypto.randomUUID();
node.dirty = true;

updateItem(node, container);
this.pushEvent("update_node", node);

const newNode: Node = {
temp_id: self.crypto.randomUUID(),
uuid: self.crypto.randomUUID(),
content: content?.substring(splitPos),
parent_id: node.parent_id,
prev_id: node.uuid,
event_id: self.crypto.randomUUID(),
dirty: true,
};

Expand All @@ -101,10 +104,12 @@ export function keydown(event: KeyboardEvent) {
prevNode.content += node.content;
updateItem(prevNode, container);
focusItem(prevItem);
prevNode.event_id = self.crypto.randomUUID();
prevNode.dirty = true;
this.pushEvent("update_node", prevNode);

deleteItem(node);
node.event_id = self.crypto.randomUUID();
node.dirty = true;
this.pushEvent("delete_node", node);
break;
Expand All @@ -118,10 +123,12 @@ export function keydown(event: KeyboardEvent) {
node.content += nextNode.content;
updateItem(node, container);
focusItem(item);
node.event_id = self.crypto.randomUUID();
node.dirty = true;
this.pushEvent("update_node", node);

deleteItem(nextNode);
nextNode.event_id = self.crypto.randomUUID();
nextNode.dirty = true;
this.pushEvent("delete_node", nextNode);
break;
Expand Down
26 changes: 8 additions & 18 deletions assets/js/hooks/item.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { Node } from "./types";

export function createItem({
uuid,
temp_id,
content,
parent_id,
prev_id,
dirty,
}: Node) {
export function createItem({ uuid, content, parent_id, prev_id, dirty }: Node) {
const input = document.createElement("div");
input.textContent = content;
input.contentEditable = "true"; // firefox does not support "plaintext-only"
Expand All @@ -16,9 +9,7 @@ export function createItem({
ol.className = "list-disc";

const item = document.createElement("li");
temp_id && (item.id = "outline-node-" + temp_id);
uuid && (item.id = "outline-node-" + uuid);

item.id = "outline-node-" + uuid;
item.className = "my-1 ml-4";

item.setAttribute("data-parent", parent_id || "");
Expand All @@ -32,19 +23,18 @@ export function createItem({
}

export function updateItem(
{ uuid, temp_id, content, parent_id, prev_id }: Node,
container: HTMLOListElement,
{ uuid, content, parent_id, prev_id, dirty }: Node,
container: HTMLOListElement
) {
const item = getItemById(temp_id || uuid);
const item = getItemById(uuid);
if (!item) return;

temp_id && uuid && (item.id = "outline-node-" + uuid);

const input = item.firstChild!;
input.textContent = content;

item.setAttribute("data-parent", parent_id || "");
item.setAttribute("data-prev", prev_id || "");
item.setAttribute("data-dirty", dirty ? "true" : "false");

const prevItem = getItemById(prev_id);
const parentItem = getItemById(parent_id);
Expand All @@ -65,8 +55,8 @@ export function deleteItem({ uuid }: Node) {
item.parentNode!.removeChild(item);
}

export function getItemByNode({ uuid, temp_id }: Node) {
return getItemById(temp_id || uuid);
export function getItemByNode({ uuid }: Node) {
return getItemById(uuid);
}

function getItemById(uuid: string | undefined) {
Expand Down
4 changes: 2 additions & 2 deletions assets/js/hooks/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
export type UUID = `${string}-${string}-${string}-${string}-${string}`;

export interface Node {
uuid?: UUID;
temp_id?: UUID;
uuid: UUID;
content: string;
creator_id?: number;
parent_id?: UUID;
prev_id?: UUID;
event_id?: UUID;
dirty?: boolean;
}
2 changes: 1 addition & 1 deletion lib/radiator/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ defmodule Radiator.Application do
# Start to serve requests, typically the last entry
RadiatorWeb.Endpoint,
{EventProducer, name: EventProducer},
{EventConsumer, name: EventConsumer}
{EventConsumer, name: EventConsumer, subscribe_to: [{EventProducer, max_demand: 1}]}
]

# See https://hexdocs.pm/elixir/Supervisor.html
Expand Down
2 changes: 1 addition & 1 deletion lib/radiator/event_store.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ defmodule Radiator.EventStore do

def persist_event(event) do
# persist event
{:ok, event}
event
end
end
20 changes: 12 additions & 8 deletions lib/radiator/outline.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ defmodule Radiator.Outline do
# if no parent is given, the new node will be inserted as a root node
# if no previous node is given, the new node will be inserted as the first child of the parent node
def insert_node(attrs) do
# FIXME: missing test required episode_id . .it crashes without given
Repo.transaction(fn ->
prev_node_id = attrs["prev_node"]
parent_node_id = attrs["parent_node"]
Expand Down Expand Up @@ -62,17 +61,22 @@ defmodule Radiator.Outline do
## Examples
iex> update_node_content(node, %{content: new_value})
iex> update_node_content(node_id, %{content: new_value})
{:ok, %Node{}}
iex> update_node_content(node, %{content: nil})
iex> update_node_content(node_id, %{content: nil})
{:error, %Ecto.Changeset{}}
"""
def update_node_content(%Node{} = node, attrs, _socket_id \\ nil) do
node
|> Node.update_content_changeset(attrs)
|> Repo.update()
def update_node_content(node_id, content) do
case NodeRepository.get_node(node_id) do
nil ->
{:error, :not_found}

node ->
node
|> Node.update_content_changeset(%{content: content})
|> Repo.update()
end
end

@doc """
Expand Down
12 changes: 10 additions & 2 deletions lib/radiator/outline/command.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
defmodule Radiator.Outline.Command do
@moduledoc false

alias Radiator.Outline.Command.InsertNodeCommand
defstruct [:event_type, :event_id, :user_id, :payload]
alias Radiator.Outline.Command.{ChangeNodeContentCommand, InsertNodeCommand}

def build("insert_node", payload, user_id, event_id) do
%InsertNodeCommand{
Expand All @@ -11,4 +10,13 @@ defmodule Radiator.Outline.Command do
payload: payload
}
end

def build("change_node_content", node_id, content, user_id, event_id) do
%ChangeNodeContentCommand{
event_id: event_id,
user_id: user_id,
node_id: node_id,
content: content
}
end
end
13 changes: 13 additions & 0 deletions lib/radiator/outline/command/change_node_content_command.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule Radiator.Outline.Command.ChangeNodeContentCommand do
@moduledoc """
Command to move a nodeinside the outline to another place.
"""
@type t() :: %__MODULE__{
event_id: binary(),
user_id: binary(),
node_id: binary(),
content: String.t() | nil
}

defstruct [:event_id, :user_id, :node_id, :content]
end
23 changes: 23 additions & 0 deletions lib/radiator/outline/command/delete_node_command.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule Radiator.Outline.Command.DeleteNodeCommand do
@moduledoc """
Command to remove a node from the outline and delete it permantly.
"""
@type t() :: %__MODULE__{
event_id: binary(),
user_id: binary(),
node_id: binary()
}

defstruct [:event_id, :user_id, :node_id]

# def execute(%{id: id}) do
# case Radiator.Outline.Node.get(id) do
# nil ->
# {:error, "Node not found"}

# node ->
# Radiator.Outline.Node.delete(node)
# {:ok, %{}}
# end
# end
end
14 changes: 14 additions & 0 deletions lib/radiator/outline/command/move_node_command.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
defmodule Radiator.Outline.Command.MoveNodeCommand do
@moduledoc """
Command to move a nodeinside the outline to another place.
"""
@type t() :: %__MODULE__{
event_id: binary(),
user_id: binary(),
node_id: binary(),
parent_node_id: binary() | nil,
prev_node_id: binary() | nil
}

defstruct [:event_id, :user_id, :node_id, :parent_node_id, :prev_node_id]
end
24 changes: 19 additions & 5 deletions lib/radiator/outline/dispatch.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@ defmodule Radiator.Outline.Dispatch do
|> EventProducer.enqueue()
end

def change_node_content(node_id, content, user_id, event_id) do
# IO.inspect(node_id, label: "Dispatcher change_node_content")
"change_node_content"
|> Command.build(node_id, content, user_id, event_id)
|> EventProducer.enqueue()
end

# def move_node(attributes, user_id, event_id) do
# "move_node"
# |> Command.build(attributes, user_id, event_id)
# |> EventProducer.enqueue()
# end

# def delete_node(node_id, user_id, event_id) do
# "delete_node"
# |> Command.build(node_id, user_id, event_id)
# |> EventProducer.enqueue()
# end

def subscribe(_episode_id) do
Phoenix.PubSub.subscribe(Radiator.PubSub, "events")
end
Expand All @@ -18,10 +37,5 @@ defmodule Radiator.Outline.Dispatch do
Phoenix.PubSub.broadcast(Radiator.PubSub, "events", event)
end

# TODO
# update_node
# delete_node
# move_node

# list_node different case, sync call
end
5 changes: 5 additions & 0 deletions lib/radiator/outline/event/node_content_changed_event.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
defmodule Radiator.Outline.Event.NodeContentChangedEvent do
@moduledoc false

defstruct [:event_id, :node]
end
3 changes: 3 additions & 0 deletions lib/radiator/outline/event/node_deleted_event.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule Radiator.Outline.Event.NodeDeletedEvent do
@moduledoc false
end
3 changes: 3 additions & 0 deletions lib/radiator/outline/event/node_moved_event.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule Radiator.Outline.Event.NodeMovedEvent do
@moduledoc false
end
Loading

0 comments on commit f61fc25

Please sign in to comment.