Skip to content

Commit

Permalink
feature: split node-content on enter (new node)
Browse files Browse the repository at this point in the history
  • Loading branch information
sorax committed Sep 29, 2024
1 parent 199b9e4 commit 3bc2050
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 59 deletions.
10 changes: 5 additions & 5 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ let liveSocket = new LiveSocket("/live", Socket, {
longPollFallbackMs: 2500,
params: { _csrf_token: csrfToken },
metadata: {
// submit: (e, el) => {
// console.log(e)
// return {cursor: "123"}
// },
keydown: (e, el) => {
return {
key: e.key,
altKey: e.altKey,
shiftKey: e.shiftKey,
repeat: e.repeat
repeat: e.repeat,
selection: {
start: e.target.selectionStart,
end: e.target.selectionEnd,
},
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion assets/js/hooks/events/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ export function handleFocus({ uuid, user_name }: UserAction) {
addEditingUserLabel(node, user_name);
}

export function handleMoves({ nodes }: { nodes: NodeData[] }) {
export function handleFocusNode({ uuid }: NodeData) {
const input = document.getElementById(
`form-${uuid}_content`
) as HTMLDivElement;
input.focus();
}

export function handleMoveNodes({ nodes }: { nodes: NodeData[] }) {
nodes.forEach(({ uuid, parent_id, prev_id }: NodeData) => {
const node = getNodeById(uuid)!;
setValue(node, ".parent_id", parent_id || "");
Expand Down
6 changes: 4 additions & 2 deletions assets/js/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { moveNode } from "./node";
import {
handleBlur,
handleFocus,
handleMoves,
handleFocusNode,
handleMoveNodes,
handleSetContent,
} from "./events/handler";
import { keydown, toggleCollapse } from "./events/listener";
Expand All @@ -15,7 +16,8 @@ export const Hooks = {
mounted() {
this.handleEvent("blur", handleBlur.bind(this));
this.handleEvent("focus", handleFocus.bind(this));
this.handleEvent("move_nodes", handleMoves.bind(this));
this.handleEvent("focus_node", handleFocusNode.bind(this));
this.handleEvent("move_nodes", handleMoveNodes.bind(this));
this.handleEvent("set_content", handleSetContent.bind(this));

this.el.addEventListener("keydown", keydown.bind(this));
Expand Down
2 changes: 1 addition & 1 deletion assets/js/hooks/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function getUUID(node: HTMLDivElement) {
}

function getContent(node: HTMLDivElement) {
const input = node.querySelector("input") as HTMLInputElement;
const input = node.querySelector("input[type=text]") as HTMLInputElement;
return input.value;
}

Expand Down
9 changes: 1 addition & 8 deletions lib/radiator_web/live/episode_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,9 @@ defmodule RadiatorWeb.EpisodeLive.Index do
end

@impl true
def handle_info(%NodeMovedEvent{} = event, socket), do: proxy_event(event, socket)

def handle_info(%{uuid: <<_::binary-size(36)>> <> ":" <> id} = event, %{id: id} = socket) do
socket
|> stream_event(event)
|> reply(:noreply)
end

def handle_info(%NodeInsertedEvent{} = event, socket), do: proxy_event(event, socket)
def handle_info(%NodeContentChangedEvent{} = event, socket), do: proxy_event(event, socket)
def handle_info(%NodeMovedEvent{} = event, socket), do: proxy_event(event, socket)
def handle_info(%NodeDeletedEvent{} = event, socket), do: proxy_event(event, socket)

def handle_info(%{topic: "outline"} = event, socket) do
Expand Down
55 changes: 40 additions & 15 deletions lib/radiator_web/live/outline_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,20 @@ defmodule RadiatorWeb.OutlineComponent do
alias RadiatorWeb.OutlineComponents

@impl true
def update(%{event: %NodeInsertedEvent{node: node, next: nil}}, socket) do
def update(%{event: %NodeInsertedEvent{event_id: event_id, node: node, next: nil}}, socket) do
action = get_action(socket.id, event_id)

socket
|> stream_insert(:nodes, to_change_form(node, %{}))
|> stream_insert(:nodes, to_change_form(node, %{}, action))
|> reply(:ok)
end

def update(%{event: %NodeInsertedEvent{node: node, next: next}}, socket) do
def update(%{event: %NodeInsertedEvent{event_id: event_id, node: node, next: next}}, socket) do
action = get_action(socket.id, event_id)

socket
|> stream_insert(:nodes, to_change_form(node, %{}, action))
|> push_event("move_nodes", %{nodes: [next]})
|> stream_insert(:nodes, to_change_form(node, %{}))
|> reply(:ok)
end

Expand All @@ -53,6 +57,7 @@ defmodule RadiatorWeb.OutlineComponent do

socket
|> push_event("move_nodes", %{nodes: nodes})
|> push_event("focus_node", %{uuid: node.uuid})
|> reply(:ok)
end

Expand Down Expand Up @@ -88,10 +93,10 @@ defmodule RadiatorWeb.OutlineComponent do
|> reply(:ok)
end

def update(%{event: %Phoenix.Socket.Broadcast{event: event, payload: payload}}, socket)
def update(%{event: %Phoenix.Socket.Broadcast{event: event, payload: _payload}}, socket)
when event in ["focus", "blur"] do
socket
|> push_event(event, payload)
# |> push_event(event, payload)
|> reply(:ok)
end

Expand All @@ -115,15 +120,7 @@ defmodule RadiatorWeb.OutlineComponent do
|> reply(:noreply)
end

def handle_event("new", %{"node" => %{"uuid" => uuid}}, socket) do
new_uuid = Ecto.UUID.generate()
user_id = socket.assigns.user_id
episode_id = socket.assigns.episode_id

params = %{"uuid" => new_uuid, "prev_id" => uuid, "episode_id" => episode_id}

Dispatch.insert_node(params, user_id, generate_event_id(socket.id))

def handle_event("new", _params, socket) do
socket
|> reply(:noreply)
end
Expand All @@ -148,6 +145,31 @@ defmodule RadiatorWeb.OutlineComponent do
|> reply(:noreply)
end

def handle_event(
"keydown",
%{"key" => "Enter", "uuid" => uuid, "value" => value, "selection" => selection},
socket
) do
{first, _} = String.split_at(value, selection["start"])
{_, last} = String.split_at(value, selection["end"])

user_id = socket.assigns.user_id
Dispatch.change_node_content(uuid, first, user_id, generate_event_id(socket.id))

episode_id = socket.assigns.episode_id

params = %{
"prev_id" => uuid,
"content" => last,
"episode_id" => episode_id
}

Dispatch.insert_node(params, user_id, generate_event_id(socket.id))

socket
|> reply(:noreply)
end

def handle_event("keydown", %{"key" => key, "uuid" => uuid, "value" => ""}, socket)
when key in ["Backspace", "Delete"] do
user_id = socket.assigns.user_id
Expand Down Expand Up @@ -220,5 +242,8 @@ defmodule RadiatorWeb.OutlineComponent do
to_form(changeset, as: "node", id: "form-#{changeset.data.uuid}")
end

defp get_action(id, <<_::binary-size(36)>> <> ":" <> id), do: :self
defp get_action(_id, _event_id), do: nil

defp generate_event_id(id), do: Ecto.UUID.generate() <> ":" <> id
end
1 change: 1 addition & 0 deletions lib/radiator_web/live/outline_component.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
type="text"
field={form[:content]}
placeholder="Content..."
phx-mounted={form.action == :self && JS.focus()}
phx-focus="focus"
phx-blur="blur"
phx-keydown="keydown"
Expand Down
45 changes: 18 additions & 27 deletions test/radiator_web/live/outline_live_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule RadiatorWeb.OutlineLiveTest do
import Radiator.PodcastFixtures
import Radiator.OutlineFixtures

alias Radiator.Outline
# alias Radiator.Outline
# alias Radiator.Outline.NodeRepository

@additional_keep_alive 2000
Expand Down Expand Up @@ -52,44 +52,35 @@ defmodule RadiatorWeb.OutlineLiveTest do
assert html =~ node_3.content
end

# test "update node content", %{conn: conn, show_id: show_id, nodes: [node_1 | _]} do
# {:ok, live, _html} = live(conn, ~p"/admin/podcast/#{show_id}")
# {:ok, other_live, _other_html} = live(conn, ~p"/admin/podcast/#{show_id}")

# assert live
# |> form("#form-#{node_1.uuid}", node: %{content: "node_1_updated"})
# |> render_change()
test "update node content", %{conn: conn, show_id: show_id, nodes: [%{uuid: uuid} | _]} do
{:ok, live, _html} = live(conn, ~p"/admin/podcast/#{show_id}")
{:ok, other_live, _other_html} = live(conn, ~p"/admin/podcast/#{show_id}")

# keep_liveview_alive()
assert live
|> form("#form-#{uuid}", node: %{content: "node_1_updated"})
|> render_change()

# updated_node = NodeRepository.get_node!(node_1.uuid)
# assert updated_node.uuid == node_1.uuid
# assert updated_node.parent_id == node_1.parent_id
# assert updated_node.prev_id == node_1.prev_id
# assert updated_node.content == "node_1_updated"
keep_liveview_alive()

# assert other_live
# |> has_element?("#form-#{node_1.uuid}_content[value=node_1_updated]")
# end
assert_push_event(live, "set_content", %{uuid: ^uuid, content: "node_1_updated"})
assert_push_event(other_live, "set_content", %{uuid: ^uuid, content: "node_1_updated"})
end

test "insert a new node", %{conn: conn, show_id: show_id, nodes: [_, node_2 | _]} do
test "insert a new node", %{conn: conn, show_id: show_id, nodes: [_, %{uuid: uuid} | _]} do
{:ok, live, _html} = live(conn, ~p"/admin/podcast/#{show_id}")
{:ok, other_live, _html} = live(conn, ~p"/admin/podcast/#{show_id}")

assert live
|> form("#form-#{node_2.uuid}", node: %{content: "node_2_updated"})
|> render_submit()
|> element("#form-#{uuid}_content")
|> render_keydown(%{"key" => "Enter", "selection" => %{"start" => 2, "end" => 2}})

keep_liveview_alive()

siblings = Outline.get_all_siblings(nil)
node_2_1 = Enum.find(siblings, &(&1.prev_id == node_2.uuid))

assert node_2_1.parent_id == node_2.parent_id
assert node_2_1.prev_id == node_2.uuid
assert node_2_1.content == nil
assert_push_event(live, "set_content", %{uuid: ^uuid, content: "no"})
assert_push_event(other_live, "set_content", %{uuid: ^uuid, content: "no"})

assert other_live |> has_element?("#form-#{node_2_1.uuid}")
assert live |> has_element?("[value=de_2]")
assert other_live |> has_element?("[value=de_2]")
end

# test "move node", %{conn: conn, show: show, episode: episode, nodes: [node_1 | _]} do
Expand Down

0 comments on commit 3bc2050

Please sign in to comment.