Skip to content

Commit

Permalink
Merge pull request #562 from podlove/intend-outdent-nodes
Browse files Browse the repository at this point in the history
Indent  nodes
  • Loading branch information
electronicbites authored Sep 10, 2024
2 parents 90fc02b + b532c92 commit e09a5d5
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 69 deletions.
147 changes: 93 additions & 54 deletions lib/radiator/outline.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ defmodule Radiator.Outline do
@doc """
Returns a list of direct child nodes in correct order.
"""
def order_child_nodes(nil), do: []

def order_child_nodes(%Node{} = node) do
node
|> get_all_siblings()
Expand Down Expand Up @@ -56,13 +58,6 @@ defmodule Radiator.Outline do
|> List.flatten()
end

defp order_nodes_by_index(index, prev_id, collection) do
case index[prev_id] do
%{uuid: uuid} = node -> order_nodes_by_index(index, uuid, [node | collection])
_ -> Enum.reverse(collection)
end
end

@doc """
Inserts a node.
Expand Down Expand Up @@ -111,23 +106,27 @@ defmodule Radiator.Outline do
end)
end

defp episode_valid?(episode_id, %Node{episode_id: episode_id}, %Node{episode_id: episode_id}),
do: true

defp episode_valid?(episode_id, %Node{episode_id: episode_id}, nil), do: true
defp episode_valid?(episode_id, nil, %Node{episode_id: episode_id}), do: true
defp episode_valid?(_episode_id, nil, nil), do: true
defp episode_valid?(_episode_id, _parent_node, _prev_node), do: false
def indent_node(node_id) do
Repo.transaction(fn ->
case NodeRepository.get_node(node_id) do
nil ->
{:error, :not_found}

defp set_parent_id_if(attrs, nil), do: attrs
defp set_parent_id_if(attrs, %Node{uuid: uuid}), do: Map.put_new(attrs, "parent_id", uuid)
node ->
prev_node = get_prev_node(node)
do_indent_node(node, prev_node)
end
end)
|> case do
{:ok, {:error, error}} ->
{:error, error}

defp find_parent_node(%Node{parent_id: parent_id}, nil) do
NodeRepository.get_node_if(parent_id)
end
{:ok, {:ok, node_result}} ->
{:ok, node_result}

defp find_parent_node(_, parent_id) do
NodeRepository.get_node_if(parent_id)
{:error, error} ->
{:error, error}
end
end

@doc """
Expand Down Expand Up @@ -187,39 +186,6 @@ defmodule Radiator.Outline do
move_node(node_id, prev_id: new_prev_id, parent_id: parent_id)
end

# low level function to move a node
defp do_move_node(node, new_prev_id, new_parent_id, prev_node, parent_node) do
node_repo_result = %NodeRepoResult{node: node}

Repo.transaction(fn ->
old_next_node =
Node
|> where_prev_node_equals(node.uuid)
|> where_parent_node_equals(get_node_id(parent_node))
|> Repo.one()

new_next_node =
Node
|> where_prev_node_equals(new_prev_id)
|> where_parent_node_equals(new_parent_id)
|> Repo.one()

{:ok, node} = move_node_if(node, new_parent_id, new_prev_id)

{:ok, _old_next_node} =
move_node_if(old_next_node, get_node_id(parent_node), get_node_id(prev_node))

{:ok, _new_next_node} = move_node_if(new_next_node, new_parent_id, get_node_id(node))

Map.merge(node_repo_result, %{
node: node,
old_next_id: get_node_id(old_next_node),
old_prev_id: get_node_id(prev_node),
next_id: get_node_id(new_next_node)
})
end)
end

@doc """
Updates a nodes content.
Expand Down Expand Up @@ -453,6 +419,25 @@ defmodule Radiator.Outline do
{:ok, tree}
end

defp episode_valid?(episode_id, %Node{episode_id: episode_id}, %Node{episode_id: episode_id}),
do: true

defp episode_valid?(episode_id, %Node{episode_id: episode_id}, nil), do: true
defp episode_valid?(episode_id, nil, %Node{episode_id: episode_id}), do: true
defp episode_valid?(_episode_id, nil, nil), do: true
defp episode_valid?(_episode_id, _parent_node, _prev_node), do: false

defp set_parent_id_if(attrs, nil), do: attrs
defp set_parent_id_if(attrs, %Node{uuid: uuid}), do: Map.put_new(attrs, "parent_id", uuid)

defp find_parent_node(%Node{parent_id: parent_id}, nil) do
NodeRepository.get_node_if(parent_id)
end

defp find_parent_node(_, parent_id) do
NodeRepository.get_node_if(parent_id)
end

defp move_node_if(nil, _parent_node_id, _prev_node_id), do: {:ok, nil}

defp move_node_if(node, parent_id, prev_id) do
Expand All @@ -464,6 +449,53 @@ defmodule Radiator.Outline do
|> Repo.update()
end

# low level function to move a node
defp do_move_node(node, new_prev_id, new_parent_id, prev_node, parent_node) do
node_repo_result = %NodeRepoResult{node: node}

Repo.transaction(fn ->
old_next_node =
Node
|> where_prev_node_equals(node.uuid)
|> where_parent_node_equals(get_node_id(parent_node))
|> Repo.one()

new_next_node =
Node
|> where_prev_node_equals(new_prev_id)
|> where_parent_node_equals(new_parent_id)
|> Repo.one()

{:ok, node} = move_node_if(node, new_parent_id, new_prev_id)

if !is_nil(old_next_node) do
{:ok, _old_next_node} =
move_node_if(old_next_node, old_next_node.parent_id, get_node_id(prev_node))
end

{:ok, _new_next_node} = move_node_if(new_next_node, new_parent_id, get_node_id(node))

Map.merge(node_repo_result, %{
node: node,
old_next_id: get_node_id(old_next_node),
old_prev_id: get_node_id(prev_node),
next_id: get_node_id(new_next_node)
})
end)
end

defp do_indent_node(_node, nil), do: {:error, :no_prev_node}

defp do_indent_node(node, prev_node) do
new_previous_id =
prev_node
|> order_child_nodes()
|> List.last()
|> get_node_id()

move_node(node.uuid, prev_id: new_previous_id, parent_id: prev_node.uuid)
end

defp parent_and_prev_consistent?(_, nil), do: true
defp parent_and_prev_consistent?(nil, _), do: true

Expand All @@ -483,6 +515,13 @@ defmodule Radiator.Outline do
node.uuid
end

defp order_nodes_by_index(index, prev_id, collection) do
case index[prev_id] do
%{uuid: uuid} = node -> order_nodes_by_index(index, uuid, [node | collection])
_ -> Enum.reverse(collection)
end
end

defp binaray_uuid_to_ecto_uuid(nil), do: nil

defp binaray_uuid_to_ecto_uuid(uuid) do
Expand Down
9 changes: 9 additions & 0 deletions lib/radiator/outline/command.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule Radiator.Outline.Command do
alias Radiator.Outline.Command.{
ChangeNodeContentCommand,
DeleteNodeCommand,
IndentNodeCommand,
InsertNodeCommand,
MoveNodeCommand
}
Expand All @@ -24,6 +25,14 @@ defmodule Radiator.Outline.Command do
}
end

def build("indent_node", node_id, user_id, event_id) do
%IndentNodeCommand{
event_id: event_id,
user_id: user_id,
node_id: node_id
}
end

def build("change_node_content", node_id, content, user_id, event_id) do
%ChangeNodeContentCommand{
event_id: event_id,
Expand Down
12 changes: 12 additions & 0 deletions lib/radiator/outline/command/indent_node_command.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule Radiator.Outline.Command.IndentNodeCommand do
@moduledoc """
Command to indent a node.
"""
@type t() :: %__MODULE__{
event_id: binary(),
user_id: binary(),
node_id: binary()
}

defstruct [:event_id, :user_id, :node_id]
end
30 changes: 30 additions & 0 deletions lib/radiator/outline/command_processor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ defmodule Radiator.Outline.CommandProcessor do
alias Radiator.Outline.Command.{
ChangeNodeContentCommand,
DeleteNodeCommand,
IndentNodeCommand,
InsertNodeCommand,
MoveNodeCommand
}
Expand Down Expand Up @@ -68,6 +69,12 @@ defmodule Radiator.Outline.CommandProcessor do
|> handle_move_node_result(command)
end

defp process_command(%IndentNodeCommand{node_id: node_id} = command) do
node_id
|> Outline.indent_node()
|> handle_move_node_result(command)
end

defp process_command(%DeleteNodeCommand{node_id: node_id} = command) do
case NodeRepository.get_node(node_id) do
nil ->
Expand Down Expand Up @@ -118,6 +125,29 @@ defmodule Radiator.Outline.CommandProcessor do
node_id: node.uuid,
parent_id: command.parent_id,
prev_id: command.prev_id,
old_prev_id: result.old_prev_id,
old_next_id: result.old_next_id,
user_id: command.user_id,
uuid: command.event_id,
next_id: result.next_id,
episode_id: node.episode_id
}
|> EventStore.persist_event()
|> Dispatch.broadcast()

{:ok, node}
end

def handle_move_node_result(
{:ok, %NodeRepoResult{node: node} = result},
%IndentNodeCommand{} = command
) do
%NodeMovedEvent{
node_id: node.uuid,
parent_id: result.node.parent_id,
prev_id: result.node.prev_id,
old_prev_id: result.old_prev_id,
old_next_id: result.old_next_id,
user_id: command.user_id,
uuid: command.event_id,
next_id: result.next_id,
Expand Down
6 changes: 6 additions & 0 deletions lib/radiator/outline/dispatch.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ defmodule Radiator.Outline.Dispatch do
|> EventProducer.enqueue()
end

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

def move_node(node_id, user_id, event_id,
parent_id: parent_id,
prev_id: prev_id
Expand Down
5 changes: 1 addition & 4 deletions lib/radiator/outline/event.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,5 @@ defmodule Radiator.Outline.Event do
def event_type(%NodeDeletedEvent{} = _event), do: "NodeDeletedEvent"
def event_type(%NodeMovedEvent{} = _event), do: "NodeMovedEvent"

def episode_id(%NodeInsertedEvent{episode_id: episode_id}), do: episode_id
def episode_id(%NodeContentChangedEvent{episode_id: episode_id}), do: episode_id
def episode_id(%NodeDeletedEvent{episode_id: episode_id}), do: episode_id
def episode_id(%NodeMovedEvent{episode_id: episode_id}), do: episode_id
def episode_id(%{episode_id: episode_id}), do: episode_id
end
3 changes: 2 additions & 1 deletion lib/radiator_web/live/episode_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ 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)
Expand All @@ -71,7 +73,6 @@ defmodule RadiatorWeb.EpisodeLive.Index do

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
12 changes: 2 additions & 10 deletions lib/radiator_web/live/outline_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -226,24 +226,17 @@ defmodule RadiatorWeb.OutlineComponent do
)

socket
|> push_event("move_node", %{uuid: uuid, parent_id: prev_parent_id, prev_id: prev_prev_id})
end

defp move_down(socket, _uuid, _prev_id) do
socket
# |> push_event("move_node", %{uuid: uuid, parent_id: next_parent_id, prev_id: next_uuid})
end

defp indent(socket, uuid, prev_id) do
defp indent(socket, uuid, _prev_id) do
user_id = socket.assigns.user_id

Dispatch.move_node(uuid, user_id, generate_event_id(socket.id),
parent_id: prev_id,
prev_id: nil
)

Dispatch.indent_node(uuid, user_id, generate_event_id(socket.id))
socket
|> push_event("move_node", %{uuid: uuid, parent_id: prev_id, prev_id: nil})
end

defp outdent(socket, uuid, parent_id) do
Expand All @@ -255,6 +248,5 @@ defmodule RadiatorWeb.OutlineComponent do
)

socket
|> push_event("move_node", %{uuid: uuid, parent_id: nil, prev_id: parent_id})
end
end
Loading

0 comments on commit e09a5d5

Please sign in to comment.