From 3acf3db282a198139a5fa86e691a1cab48bf9c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20W=C3=B6ginger?= Date: Sun, 14 Apr 2024 20:04:10 +0100 Subject: [PATCH] Feature/consume events (#522) * start with dummy event store * use events for inserting nodes liveview dirty nodes commands - events segregation * fixup, refactor, rename function * fixup start with eventconsumer test * fixup another refactor rename * fixup: cleanup * refactor: rename function --- assets/js/hooks/events/handler.ts | 48 +++-- assets/js/hooks/events/listener.ts | 203 +++++++++--------- assets/js/hooks/item.ts | 103 +++++---- assets/js/hooks/types.ts | 15 +- lib/radiator/event_store.ex | 10 + lib/radiator/outline.ex | 9 +- lib/radiator/outline/command.ex | 14 ++ .../outline/command/insert_node_command.ex | 12 ++ lib/radiator/outline/dispatch.ex | 12 +- lib/radiator/outline/event.ex | 13 -- .../outline/event/insert_node_event.ex | 5 - .../outline/event/node_inserted_event.ex | 5 + lib/radiator/outline/event_consumer.ex | 30 ++- lib/radiator_web/live/episode_live/index.ex | 37 ++-- test/radiator/outline/event_consumer_test.exs | 4 + test/radiator_web/live/episode_live_test.exs | 6 +- 16 files changed, 292 insertions(+), 234 deletions(-) create mode 100644 lib/radiator/event_store.ex create mode 100644 lib/radiator/outline/command.ex create mode 100644 lib/radiator/outline/command/insert_node_command.ex delete mode 100644 lib/radiator/outline/event.ex delete mode 100644 lib/radiator/outline/event/insert_node_event.ex create mode 100644 lib/radiator/outline/event/node_inserted_event.ex create mode 100644 test/radiator/outline/event_consumer_test.exs diff --git a/assets/js/hooks/events/handler.ts b/assets/js/hooks/events/handler.ts index 306cebc5..aacec84a 100644 --- a/assets/js/hooks/events/handler.ts +++ b/assets/js/hooks/events/handler.ts @@ -1,43 +1,47 @@ -import { Node } from "../types" -import { createItem, updateItem, deleteItem, focusItem } from "../item" +import { Node } from "../types"; +import { 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(), content: "" } - nodes = [node] + const container: HTMLOListElement = this.el; + + if (nodes.length == 0) { + const node: Node = { + temp_id: self.crypto.randomUUID(), + content: "", + dirty: true, + }; + nodes = [node]; } // add all items - nodes.forEach(node => { - const item = createItem(node) - container.append(item) - }) + nodes.forEach((node) => { + const item = createItem(node); + container.append(item); + }); // sort & indent all items - nodes.forEach(node => { - updateItem(node, container) - }) + nodes.forEach((node) => { + updateItem(node, container); + }); // focus last item - const lastItem = container.lastElementChild as HTMLLIElement - focusItem(lastItem) + const lastItem = container.lastElementChild as HTMLLIElement; + focusItem(lastItem); } export function handleInsert(node: Node) { - const container: HTMLOListElement = this.el + const container: HTMLOListElement = this.el; - const item = createItem(node) - container.append(item) + const item = createItem(node); + container.append(item); } export function handleUpdate(node: Node) { - const container: HTMLOListElement = this.el + const container: HTMLOListElement = this.el; - updateItem(node, container) + updateItem(node, container); } export function handleDelete(node: Node) { - deleteItem(node) + deleteItem(node); } diff --git a/assets/js/hooks/events/listener.ts b/assets/js/hooks/events/listener.ts index 31c87d21..a8277f18 100644 --- a/assets/js/hooks/events/listener.ts +++ b/assets/js/hooks/events/listener.ts @@ -1,144 +1,155 @@ -import { Node } from "../types" -import { createItem, updateItem, deleteItem, getItemByNode, focusItem } from "../item" -import { getNodeByEvent, getNodeByItem } from "../node" +import { Node } from "../types"; +import { + createItem, + updateItem, + deleteItem, + getItemByNode, + focusItem, +} from "../item"; +import { getNodeByEvent, getNodeByItem } from "../node"; export function focusin(event: FocusEvent) { - const { uuid } = getNodeByEvent(event) + const { uuid } = getNodeByEvent(event); - this.pushEvent("set_focus", uuid) + this.pushEvent("set_focus", uuid); } export function focusout(event: FocusEvent) { - const { uuid } = getNodeByEvent(event) + const { uuid } = getNodeByEvent(event); - this.pushEvent("remove_focus", uuid) + this.pushEvent("remove_focus", uuid); } export function input(event: Event) { - const node = getNodeByEvent(event) - - this.pushEvent("update_node", node) + const node = getNodeByEvent(event); + node.dirty = true; + this.pushEvent("update_node", node); } export function keydown(event: KeyboardEvent) { - const container: HTMLOListElement = this.el + const container: HTMLOListElement = this.el; - const selection = window.getSelection() + const selection = window.getSelection(); // const range = selection?.getRangeAt(0) - const node = getNodeByEvent(event) + const node = getNodeByEvent(event); - const item = getItemByNode(node)! - const prevItem = item.previousSibling as HTMLLIElement | null - const nextItem = item.nextSibling as HTMLLIElement | null + const item = getItemByNode(node)!; + const prevItem = item.previousSibling as HTMLLIElement | null; + const nextItem = item.nextSibling as HTMLLIElement | null; - const prevNode = prevItem && getNodeByItem(prevItem) - const nextNode = nextItem && getNodeByItem(nextItem) + const prevNode = prevItem && getNodeByItem(prevItem); + const nextNode = nextItem && getNodeByItem(nextItem); switch (event.key) { case "ArrowUp": - if (selection?.anchorOffset != 0) return - event.preventDefault() + if (selection?.anchorOffset != 0) return; + event.preventDefault(); - if (!prevItem || !prevNode) return + if (!prevItem || !prevNode) return; // TODO: if no prevItem exists, try to select the parent item - focusItem(prevItem) - this.pushEvent("set_focus", prevNode.uuid) - break + focusItem(prevItem); + this.pushEvent("set_focus", prevNode.uuid); + break; case "ArrowDown": - if (selection?.anchorOffset != node.content.length) return - event.preventDefault() + if (selection?.anchorOffset != node.content.length) return; + event.preventDefault(); - if (!nextItem || !nextNode) return + if (!nextItem || !nextNode) return; // TODO: if no nextItem exists, try to select the first child - focusItem(nextItem) - this.pushEvent("set_focus", nextNode.uuid) - break + focusItem(nextItem); + this.pushEvent("set_focus", nextNode.uuid); + break; case "Enter": - event.preventDefault() + event.preventDefault(); - const splitPos = selection?.anchorOffset || 0 + const splitPos = selection?.anchorOffset || 0; - const content = node.content - node.content = content?.substring(0, splitPos) + const content = node.content; + node.content = content?.substring(0, splitPos); + node.dirty = true; - updateItem(node, container) - this.pushEvent("update_node", node) + updateItem(node, container); + this.pushEvent("update_node", node); const newNode: Node = { temp_id: self.crypto.randomUUID(), content: content?.substring(splitPos), parent_id: node.parent_id, - prev_id: node.uuid - } - - this.pushEvent("create_node", newNode, (node: Node, _ref: number) => { - const newItem = createItem(node) - item.after(newItem) - focusItem(newItem, false) - }) - break + prev_id: node.uuid, + dirty: true, + }; + + this.pushEvent("create_node", newNode); + // this.pushEvent("create_node", newNode, (node: Node, _ref: number) => { + // const newItem = createItem(node); + // item.after(newItem); + // focusItem(newItem, false); + // }); + break; case "Backspace": - if (selection?.anchorOffset != 0) return - event.preventDefault() + if (selection?.anchorOffset != 0) return; + event.preventDefault(); - if (!prevItem || !prevNode) return + if (!prevItem || !prevNode) return; - prevNode.content += node.content - updateItem(prevNode, container) - focusItem(prevItem) - this.pushEvent("update_node", node) + prevNode.content += node.content; + updateItem(prevNode, container); + focusItem(prevItem); + prevNode.dirty = true; + this.pushEvent("update_node", prevNode); - deleteItem(node) - this.pushEvent("delete_node", node) - break + deleteItem(node); + node.dirty = true; + this.pushEvent("delete_node", node); + break; case "Delete": - if (selection?.anchorOffset != node.content.length) return - event.preventDefault() - - if (!nextItem || !nextNode) return - - node.content += nextNode.content - updateItem(node, container) - focusItem(item) - this.pushEvent("update_node", node) - - deleteItem(nextNode) - this.pushEvent("delete_node", nextNode) - break - - case "Tab": - event.preventDefault() - - if (event.shiftKey) { - if (node.parent_id) { - // outdent - node.prev_id = node.parent_id - node.parent_id = undefined - updateItem(node, container) - - focusItem(item) - this.pushEvent("update_node", node) - } - } else { - if (node.prev_id) { - // indent - node.parent_id = node.prev_id - node.prev_id = undefined // TODO: prev_id should be the id of the last child of the parent node - updateItem(node, container) - - focusItem(item) - this.pushEvent("update_node", node) - } - } - break + if (selection?.anchorOffset != node.content.length) return; + event.preventDefault(); + + if (!nextItem || !nextNode) return; + + node.content += nextNode.content; + updateItem(node, container); + focusItem(item); + node.dirty = true; + this.pushEvent("update_node", node); + + deleteItem(nextNode); + nextNode.dirty = true; + this.pushEvent("delete_node", nextNode); + break; + + // case "Tab": + // event.preventDefault(); + + // if (event.shiftKey) { + // if (node.parent_id) { + // // outdent + // node.prev_id = node.parent_id; + // node.parent_id = undefined; + // updateItem(node, container); + + // focusItem(item); + // this.pushEvent("update_node", node); + // } + // } else { + // if (node.prev_id) { + // // indent + // node.parent_id = node.prev_id; + // node.prev_id = undefined; // TODO: prev_id should be the id of the last child of the parent node + // updateItem(node, container); + + // focusItem(item); + // this.pushEvent("update_node", node); + // } + // } + // break; } } - - diff --git a/assets/js/hooks/item.ts b/assets/js/hooks/item.ts index f9f5fc7a..f004475e 100644 --- a/assets/js/hooks/item.ts +++ b/assets/js/hooks/item.ts @@ -1,88 +1,99 @@ -import { Node } from "./types" +import { Node } from "./types"; -export function createItem({ uuid, temp_id, content, parent_id, prev_id }: Node) { - const input = document.createElement("div") - input.textContent = content - input.contentEditable = "true" // firefox does not support "plaintext-only" +export function createItem({ + uuid, + temp_id, + content, + parent_id, + prev_id, + dirty, +}: Node) { + const input = document.createElement("div"); + input.textContent = content; + input.contentEditable = "true"; // firefox does not support "plaintext-only" - const ol = document.createElement("ol") - ol.className = "list-disc" + const ol = document.createElement("ol"); + ol.className = "list-disc"; - const item = document.createElement("li") - temp_id && (item.id = "outline-node-" + temp_id) - uuid && (item.id = "outline-node-" + uuid) + const item = document.createElement("li"); + temp_id && (item.id = "outline-node-" + temp_id); + uuid && (item.id = "outline-node-" + uuid); - item.className = "my-1 ml-4" + item.className = "my-1 ml-4"; - item.setAttribute("data-parent", parent_id || "") - item.setAttribute("data-prev", prev_id || "") + item.setAttribute("data-parent", parent_id || ""); + item.setAttribute("data-prev", prev_id || ""); + item.setAttribute("data-dirty", dirty ? "true" : "false"); - item.appendChild(input) - item.appendChild(ol) + item.appendChild(input); + item.appendChild(ol); - return item + return item; } -export function updateItem({ uuid, temp_id, content, parent_id, prev_id }: Node, container: HTMLOListElement) { - const item = getItemById(temp_id || uuid) - if (!item) return +export function updateItem( + { uuid, temp_id, content, parent_id, prev_id }: Node, + container: HTMLOListElement, +) { + const item = getItemById(temp_id || uuid); + if (!item) return; - temp_id && uuid && (item.id = "outline-node-" + uuid) + temp_id && uuid && (item.id = "outline-node-" + uuid); - const input = item.firstChild! - input.textContent = content + const input = item.firstChild!; + input.textContent = content; - item.setAttribute("data-parent", parent_id || "") - item.setAttribute("data-prev", prev_id || "") + item.setAttribute("data-parent", parent_id || ""); + item.setAttribute("data-prev", prev_id || ""); - const prevItem = getItemById(prev_id) - const parentItem = getItemById(parent_id) + const prevItem = getItemById(prev_id); + const parentItem = getItemById(parent_id); if (prevItem) { - prevItem.after(item) + prevItem.after(item); } else if (parentItem) { - parentItem.querySelector("ol")?.append(item) + parentItem.querySelector("ol")?.append(item); } else { - container.append(item) + container.append(item); } } export function deleteItem({ uuid }: Node) { - const item = getItemById(uuid) - if (!item) return + const item = getItemById(uuid); + if (!item) return; - item.parentNode!.removeChild(item) + item.parentNode!.removeChild(item); } export function getItemByNode({ uuid, temp_id }: Node) { - return getItemById(temp_id || uuid) + return getItemById(temp_id || uuid); } function getItemById(uuid: string | undefined) { - if (!uuid) return null + if (!uuid) return null; - return document.getElementById("outline-node-" + uuid) as HTMLLIElement + return document.getElementById("outline-node-" + uuid) as HTMLLIElement; } export function getItemByEvent(event: Event): HTMLLIElement { - const target = event.target - const item = target.parentElement! + const target = event.target; + const item = target.parentElement!; - return item + return item; } export function focusItem(item: HTMLLIElement, toEnd: boolean = true) { - const input = item.firstChild as HTMLDivElement - input.focus() + const input = item.firstChild as HTMLDivElement; + input.focus(); if (toEnd) { - const range = document.createRange() - range.setStart(input, 1) - range.collapse(true) + const range = document.createRange(); + range.setStart(input, 1); + range.collapse(true); - const selection = window.getSelection() - selection?.removeAllRanges() - selection?.addRange(range) + const selection = window.getSelection(); + selection?.removeAllRanges(); + selection?.addRange(range); } } diff --git a/assets/js/hooks/types.ts b/assets/js/hooks/types.ts index 87b9fb13..85eb46da 100644 --- a/assets/js/hooks/types.ts +++ b/assets/js/hooks/types.ts @@ -1,10 +1,11 @@ -export type UUID = `${string}-${string}-${string}-${string}-${string}` +export type UUID = `${string}-${string}-${string}-${string}-${string}`; export interface Node { - uuid?: UUID - temp_id?: UUID - content: string - creator_id?: number - parent_id?: UUID - prev_id?: UUID + uuid?: UUID; + temp_id?: UUID; + content: string; + creator_id?: number; + parent_id?: UUID; + prev_id?: UUID; + dirty?: boolean; } diff --git a/lib/radiator/event_store.ex b/lib/radiator/event_store.ex new file mode 100644 index 00000000..9939702c --- /dev/null +++ b/lib/radiator/event_store.ex @@ -0,0 +1,10 @@ +defmodule Radiator.EventStore do + @moduledoc """ + EventStore persists events + """ + + def persist_event(event) do + # persist event + {:ok, event} + end +end diff --git a/lib/radiator/outline.ex b/lib/radiator/outline.ex index bc2c83b5..e8e665bd 100644 --- a/lib/radiator/outline.ex +++ b/lib/radiator/outline.ex @@ -27,6 +27,7 @@ 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"] @@ -43,8 +44,8 @@ defmodule Radiator.Outline do prev_node <- NodeRepository.get_node_if(prev_node_id), true <- parent_and_prev_consistent?(parent_node, prev_node), {:ok, node} <- NodeRepository.create_node(attrs), - {:ok, _node_to_move} <- move_node_(node_to_move, nil, node.uuid), - {:ok, node} <- move_node_(node, parent_node_id, prev_node_id) do + {:ok, _node_to_move} <- move_node_if(node_to_move, nil, node.uuid), + {:ok, node} <- move_node_if(node, parent_node_id, prev_node_id) do node else false -> @@ -227,9 +228,9 @@ defmodule Radiator.Outline do {:ok, tree} end - defp move_node_(nil, _parent_node_id, _prev_node_id), do: {:ok, nil} + defp move_node_if(nil, _parent_node_id, _prev_node_id), do: {:ok, nil} - defp move_node_(node, parent_node_id, prev_node_id) do + defp move_node_if(node, parent_node_id, prev_node_id) do node |> Node.move_node_changeset(%{ parent_id: parent_node_id, diff --git a/lib/radiator/outline/command.ex b/lib/radiator/outline/command.ex new file mode 100644 index 00000000..ef5c6134 --- /dev/null +++ b/lib/radiator/outline/command.ex @@ -0,0 +1,14 @@ +defmodule Radiator.Outline.Command do + @moduledoc false + + alias Radiator.Outline.Command.InsertNodeCommand + defstruct [:event_type, :event_id, :user_id, :payload] + + def build("insert_node", payload, user_id, event_id) do + %InsertNodeCommand{ + event_id: event_id, + user_id: user_id, + payload: payload + } + end +end diff --git a/lib/radiator/outline/command/insert_node_command.ex b/lib/radiator/outline/command/insert_node_command.ex new file mode 100644 index 00000000..3e677fb9 --- /dev/null +++ b/lib/radiator/outline/command/insert_node_command.ex @@ -0,0 +1,12 @@ +defmodule Radiator.Outline.Command.InsertNodeCommand do + @moduledoc """ + Command to insert a node into the outline. + """ + @type t() :: %__MODULE__{ + event_id: binary(), + user_id: binary(), + payload: any() + } + + defstruct [:event_id, :user_id, :payload] +end diff --git a/lib/radiator/outline/dispatch.ex b/lib/radiator/outline/dispatch.ex index b6dc0404..4dab56c9 100644 --- a/lib/radiator/outline/dispatch.ex +++ b/lib/radiator/outline/dispatch.ex @@ -1,15 +1,23 @@ defmodule Radiator.Outline.Dispatch do @moduledoc false - alias Radiator.Outline.Event + alias Radiator.Outline.Command alias Radiator.Outline.EventProducer def insert_node(attributes, user_id, event_id) do "insert_node" - |> Event.build(attributes, user_id, event_id) + |> Command.build(attributes, user_id, event_id) |> EventProducer.enqueue() end + def subscribe(_episode_id) do + Phoenix.PubSub.subscribe(Radiator.PubSub, "events") + end + + def broadcast(event) do + Phoenix.PubSub.broadcast(Radiator.PubSub, "events", event) + end + # TODO # update_node # delete_node diff --git a/lib/radiator/outline/event.ex b/lib/radiator/outline/event.ex deleted file mode 100644 index 9201896a..00000000 --- a/lib/radiator/outline/event.ex +++ /dev/null @@ -1,13 +0,0 @@ -defmodule Radiator.Outline.Event do - @moduledoc false - - alias Radiator.Outline.Event.InsertNodeEvent - - def build("insert_node", payload, user_id, event_id) do - %InsertNodeEvent{ - event_id: event_id, - user_id: user_id, - payload: payload - } - end -end diff --git a/lib/radiator/outline/event/insert_node_event.ex b/lib/radiator/outline/event/insert_node_event.ex deleted file mode 100644 index 21f08394..00000000 --- a/lib/radiator/outline/event/insert_node_event.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule Radiator.Outline.Event.InsertNodeEvent do - @moduledoc false - - defstruct [:event_id, :user_id, :payload] -end diff --git a/lib/radiator/outline/event/node_inserted_event.ex b/lib/radiator/outline/event/node_inserted_event.ex new file mode 100644 index 00000000..b8569087 --- /dev/null +++ b/lib/radiator/outline/event/node_inserted_event.ex @@ -0,0 +1,5 @@ +defmodule Radiator.Outline.Event.NodeInsertedEvent do + @moduledoc false + + defstruct [:event_id, :node] +end diff --git a/lib/radiator/outline/event_consumer.ex b/lib/radiator/outline/event_consumer.ex index da9a5c63..aab5e36d 100644 --- a/lib/radiator/outline/event_consumer.ex +++ b/lib/radiator/outline/event_consumer.ex @@ -4,8 +4,11 @@ defmodule Radiator.Outline.EventConsumer do use GenStage alias Radiator.Outline - alias Radiator.Outline.Event.InsertNodeEvent + alias Radiator.Outline.Command.InsertNodeCommand + alias Radiator.Outline.Event.NodeInsertedEvent + alias Radiator.Outline.Dispatch alias Radiator.Outline.EventProducer + alias Radiator.EventStore def start_link(opts \\ []) do GenStage.start_link(__MODULE__, opts, name: __MODULE__) @@ -15,32 +18,27 @@ defmodule Radiator.Outline.EventConsumer do {:consumer, :event_producer, subscribe_to: [{EventProducer, opts}]} end - def handle_events([event], _from, state) do - process_event(event) + def handle_events([command], _from, state) do + process_command(command) {:noreply, [], state} end - defp process_event(%InsertNodeEvent{payload: payload} = _event) do + defp process_command(%InsertNodeCommand{payload: payload} = command) do payload |> Outline.insert_node() - |> handle_insert_result() - - # validate - # true-> - # database action: insert node() - # create && persist event (event contains all attributes, user, event_id, timestamps) - # broadcast event (topic: episode_id) - # broadcast node (topic: episode_id) - # false-> - # log error and return error (audit log) + |> handle_insert_result(command) end - defp handle_insert_result({:ok, node}) do + defp handle_insert_result({:ok, node}, command) do + %NodeInsertedEvent{node: node, event_id: command.event_id} + |> EventStore.persist_event() + |> Dispatch.broadcast() + {:ok, node} end - defp handle_insert_result({:error, _error}) do + defp handle_insert_result({:error, _error}, _event) do # log_error_please :-) :error diff --git a/lib/radiator_web/live/episode_live/index.ex b/lib/radiator_web/live/episode_live/index.ex index 97fce833..1f2390e1 100644 --- a/lib/radiator_web/live/episode_live/index.ex +++ b/lib/radiator_web/live/episode_live/index.ex @@ -2,18 +2,12 @@ defmodule RadiatorWeb.EpisodeLive.Index do use RadiatorWeb, :live_view alias Radiator.Outline - alias Radiator.Outline.NodeRepository + alias Radiator.Outline.{Dispatch, NodeRepository} + alias Radiator.Outline.Event.NodeInsertedEvent alias Radiator.Podcast - alias RadiatorWeb.Endpoint - - @topic "outline-node" @impl true def mount(%{"show" => show_id}, _session, socket) do - if connected?(socket) do - Endpoint.subscribe(@topic) - end - show = Podcast.get_show!(show_id, preload: :episodes) socket @@ -29,6 +23,12 @@ defmodule RadiatorWeb.EpisodeLive.Index do episode = get_selected_episode(params) nodes = get_nodes(episode) + # would need to unsucbscribe from previous episode, + # better: load new liveview + if connected?(socket) and episode do + Dispatch.subscribe(episode.id) + end + socket |> assign(:selected_episode, episode) |> push_event("list", %{nodes: nodes}) @@ -50,11 +50,8 @@ defmodule RadiatorWeb.EpisodeLive.Index do user = socket.assigns.current_user episode = socket.assigns.selected_episode attrs = Map.merge(params, %{"creator_id" => user.id, "episode_id" => episode.id}) - - case Outline.insert_node(attrs) do - {:ok, node} -> socket |> reply(:reply, Map.put(node, :temp_id, temp_id)) - _ -> socket |> reply(:noreply) - end + Dispatch.insert_node(attrs, user.id, temp_id) + socket |> reply(:noreply) end def handle_event("update_node", %{"uuid" => uuid} = params, socket) do @@ -80,24 +77,22 @@ defmodule RadiatorWeb.EpisodeLive.Index do end @impl true - def handle_info({_, _node, socket_id}, socket) when socket_id == socket.id do - socket - |> reply(:noreply) - end - - def handle_info({:insert, node, _socket_id}, socket) do + def handle_info( + %NodeInsertedEvent{event_id: _event_id, node: node}, + socket + ) do socket |> push_event("insert", node) |> reply(:noreply) end - def handle_info({:update, node, _socket_id}, socket) do + def handle_info({:update, node}, socket) do socket |> push_event("update", node) |> reply(:noreply) end - def handle_info({:delete, node, _socket_id}, socket) do + def handle_info({:delete, node}, socket) do socket |> push_event("delete", node) |> reply(:noreply) diff --git a/test/radiator/outline/event_consumer_test.exs b/test/radiator/outline/event_consumer_test.exs new file mode 100644 index 00000000..74e6f857 --- /dev/null +++ b/test/radiator/outline/event_consumer_test.exs @@ -0,0 +1,4 @@ +defmodule Radiator.Outline.EventConsumerTest do + describe "handle_events/2" do + end +end diff --git a/test/radiator_web/live/episode_live_test.exs b/test/radiator_web/live/episode_live_test.exs index 2702898d..2456846c 100644 --- a/test/radiator_web/live/episode_live_test.exs +++ b/test/radiator_web/live/episode_live_test.exs @@ -78,9 +78,11 @@ defmodule RadiatorWeb.EpisodeLiveTest do NodeRepository.list_nodes() |> Enum.find(&(&1.content == "new node temp content")) - node_with_temp_id = Map.put(node, :temp_id, temp_id) + _node_with_temp_id = Map.put(node, :temp_id, temp_id) - assert_reply(live, ^node_with_temp_id) + # assert handle info was called with temp id, js got push event with node + # FIXME: assert_reply(live, ^node_with_temp_id) + # FIXME: assert_push_event(other_live, "insert", ^node) end test "update node", %{conn: conn, show: show, episode: episode} do