Skip to content

Commit

Permalink
feat: client-side (js) node editing
Browse files Browse the repository at this point in the history
  • Loading branch information
sorax committed Dec 10, 2023
1 parent 8afde37 commit 142a831
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 73 deletions.
13 changes: 9 additions & 4 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,20 @@
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
import "phoenix_html"
// Establish Phoenix Socket and LiveView configuration.
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import { Socket } from "phoenix"
import { LiveSocket } from "phoenix_live_view"
import topbar from "../vendor/topbar"

import { Hooks } from "./hooks"

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
let liveSocket = new LiveSocket("/live", Socket, {
hooks: Hooks,
params: { _csrf_token: csrfToken }
})

// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" })
window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
window.addEventListener("phx:page-loading-stop", _info => topbar.hide())

Expand Down
22 changes: 22 additions & 0 deletions assets/js/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createNode } from "./node";

export const Hooks = {
outline: {
mounted() {
const container: HTMLElement = this.el

container.addEventListener("keydown", (event) => {
})

container.addEventListener("keyup", (event) => {
})

this.handleEvent("insert", ({ nodes }) => {
nodes.forEach(node => {
const li = createNode(node)
container.prepend(li)
});
})
}
}
}
20 changes: 20 additions & 0 deletions assets/js/hooks/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
interface Node {
uuid?: string
content?: string
creator_id?: number
parent_id?: string
prev_id?: string
}

export function createNode({ uuid, content }: Node) {
const input = document.createElement("div")
input.innerText = content || ""
input.contentEditable = "plaintext-only"

const node = document.createElement("li")
node.className = "my-2 ml-2"
node.appendChild(input)
uuid && (node.id = "outline-node-" + uuid)

return node
}
1 change: 0 additions & 1 deletion lib/radiator/outline.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ defmodule Radiator.Outline do
"""
def list_nodes do
Node
|> order_by(desc: :updated_at)
|> Repo.all()
end

Expand Down
2 changes: 2 additions & 0 deletions lib/radiator/outline/node.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ defmodule Radiator.Outline.Node do
use Ecto.Schema
import Ecto.Changeset

@derive {Jason.Encoder, only: [:uuid, :content, :creator_id, :parent_id, :prev_id]}

@primary_key {:uuid, :binary_id, autogenerate: true}
schema "outline_nodes" do
field :content, :string
Expand Down
53 changes: 21 additions & 32 deletions lib/radiator_web/live/outline_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ defmodule RadiatorWeb.OutlineLive.Index do

alias Radiator.Accounts
alias Radiator.Outline
alias Radiator.Outline.Node

alias RadiatorWeb.Endpoint

@topic "outline"
Expand All @@ -15,57 +13,48 @@ defmodule RadiatorWeb.OutlineLive.Index do
Endpoint.subscribe(@topic)
end

node = %Node{}
node = %Outline.Node{}
changeset = Outline.change_node(node)

socket
|> assign(:page_title, "Outline")
|> assign(:bookmarklet, get_bookmarklet(Endpoint.url() <> "/api/v1/outline", socket))
|> assign(:node, node)
|> assign(:form, to_form(changeset))
|> stream_configure(:nodes, dom_id: &"node-#{&1.uuid}")
|> stream(:nodes, Outline.list_nodes())
|> push_event("insert", %{nodes: Outline.list_nodes()})
|> reply(:ok)
end

@impl true
def handle_event("update", %{"node" => _params}, socket) do
socket
|> reply(:noreply)
end

@impl true
def handle_event("next", %{"node" => params}, socket) do
user = socket.assigns.current_user
Outline.create_node(params, user)
{:ok, node} = Outline.create_node(params, user)

socket
# |> stream_insert(:nodes, node, at: 0)
|> push_event("insert", %{nodes: [node]})
|> reply(:noreply)
end

@impl true
def handle_event("delete", %{"uuid" => uuid}, socket) do
node = Outline.get_node!(uuid)
Outline.delete_node(node)
# def handle_event("delete", %{"uuid" => uuid}, socket) do
# node = Outline.get_node!(uuid)
# Outline.delete_node(node)

socket
# |> stream_delete(:nodes, node)
|> reply(:noreply)
end
# socket
# |> reply(:noreply)
# end

@impl true
def handle_info({:insert, node}, socket) do
socket
|> stream_insert(:nodes, node, at: 0)
|> reply(:noreply)
end
# @impl true
# def handle_info({:insert, node}, socket) do
# socket
# |> push_event("insert", node)
# |> reply(:noreply)
# end

def handle_info({:delete, node}, socket) do
socket
|> stream_delete(:nodes, node)
|> reply(:noreply)
end
# def handle_info({:delete, node}, socket) do
# socket
# |> push_event("delete", node)
# |> reply(:noreply)
# end

defp get_bookmarklet(api_uri, socket) do
token =
Expand Down
31 changes: 8 additions & 23 deletions lib/radiator_web/live/outline_live/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,14 @@
<section class="my-12">
<h2 class="text-2xl">Inbox</h2>

<.focus_wrap id="input-form-wrap">
<.form id="inbox-form" for={@form} phx-change="update" phx-submit="next">
<.input type="text" field={@form[:content]} />
<.input type="hidden" field={@form[:uuid]} />
</.form>
</.focus_wrap>
<.form id="inbox-form" for={@form} phx-submit="next">
<.input
type="text"
field={@form[:content]}
placeholder="this input will be removed in the future"
/>
</.form>

<ul id="inbox" class="mt-8 list-disc list-inside" phx-update="stream">
<li :for={{dom_id, node} <- @streams.nodes} id={dom_id} class="my-2">
<div>
<%= node.content %>
<.link
phx-click={JS.push("delete", value: %{uuid: node.uuid}) |> hide("##{dom_id}")}
data-confirm="Delete?"
>
<.icon name="hero-x-circle" class="w-5 h-5" />
<div class="sr-only">Delete</div>
</.link>
</div>
<div class="pl-4 text-xs italic">
Creator: <%= node.creator_id %> | Last Update: <%= node.updated_at %>
</div>
</li>
</ul>
<ol id="outline" class="mt-8 list-disc list-inside" phx-hook="outline"></ol>
</section>
</div>
30 changes: 17 additions & 13 deletions test/radiator_web/live/outline_live_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ defmodule RadiatorWeb.OutlineLiveTest do
%{conn: log_in_user(conn, user), node: node}
end

test "lists all nodes", %{conn: conn, node: node} do
{:ok, _live, html} = live(conn, ~p"/admin/outline")
test "lists all nodes", %{conn: conn, node: _node} do
{:ok, live, _html} = live(conn, ~p"/admin/outline")

assert live |> element("h2", "Inbox")

assert html =~ "Inbox</h2>"
assert html =~ node.content
assert_push_event(live, "insert", %{nodes: [%{content: "some content"}]})

# assert html =~ node.content
end

test "save new node", %{conn: conn} do
Expand All @@ -45,18 +48,19 @@ defmodule RadiatorWeb.OutlineLiveTest do

assert live
|> element("ul#inbox")
|> render() =~ "new node content"

assert_push_event(live, "insert", %{nodes: [%{content: "new node content"}]})
end

test "delete existing node", %{conn: conn, node: node} do
{:ok, live, _html} = live(conn, ~p"/admin/outline")
# test "delete existing node", %{conn: conn, node: node} do
# {:ok, live, _html} = live(conn, ~p"/admin/outline")

assert live
|> element("#node-#{node.uuid} a", "Delete")
|> render_click()
# assert live
# |> element("#node-#{node.uuid} a", "Delete")
# |> render_click()

refute live
|> has_element?("#node-#{node.uuid}")
end
# refute live
# |> has_element?("#node-#{node.uuid}")
# end
end
end

0 comments on commit 142a831

Please sign in to comment.