Skip to content

Commit

Permalink
refactor admin-page
Browse files Browse the repository at this point in the history
  • Loading branch information
sorax committed Jan 23, 2024
1 parent a4186a3 commit 1200cca
Show file tree
Hide file tree
Showing 20 changed files with 265 additions and 133 deletions.
55 changes: 31 additions & 24 deletions assets/js/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const Hooks = {
const content = node.content
node.content = content?.substring(0, splitPos)

updateItem(node)
updateItem(node, container)

const newNode: Node = {
temp_id: self.crypto.randomUUID(),
Expand All @@ -91,7 +91,7 @@ export const Hooks = {

const prevNode = getNodeByItem(prevItem)
prevNode.content += node.content
updateItem(prevNode)
updateItem(prevNode, container)

item.parentNode?.removeChild(item)

Expand All @@ -107,24 +107,39 @@ export const Hooks = {

const nextNode = getNodeByItem(nextItem)
node.content += nextNode.content
updateItem(node)
updateItem(node, container)

nextItem.parentNode?.removeChild(nextItem)

focusItem(item)
this.pushEvent("delete_node", nextNode.uuid)
break

// case "Tab":
// event.preventDefault()
case "Tab":
event.preventDefault()

if (event.shiftKey) {
if (node.parent_id) {
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) {
node.parent_id = node.prev_id
node.prev_id = undefined

// if (event.shiftKey) {
// // outdentNode(node)
// // node.prev_id = node.parent_id
// } else {
// // indentNode(node)
// }
// break
updateItem(node, container)
focusItem(item)

this.pushEvent("update_node", node)
}
}
break
}
})

Expand All @@ -133,26 +148,18 @@ export const Hooks = {
// })

this.handleEvent("list", ({ nodes }) => {
// add all items
nodes.forEach(node => {
const item = createItem(node)
container.append(item)
})

// sort all items
// sort & indent all items
nodes.forEach(node => {
const item = getItemById(node.uuid)
const prevItem = getItemById(node.prev_id)
const parentItem = getItemById(node.parent_id)

if (prevItem) {
prevItem.after(item)
} else if (parentItem) {
parentItem.querySelector("ol")?.append(item)
} else {
container.append(item)
}
updateItem(node, container)
})

// focus last item
const lastItem = container.lastElementChild as HTMLLIElement
focusItem(lastItem)
})
Expand Down
19 changes: 15 additions & 4 deletions assets/js/hooks/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function createItem({ uuid, temp_id, content, parent_id, prev_id }: Node)
return item
}

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

Expand All @@ -33,12 +33,23 @@ export function updateItem({ uuid, temp_id, content, parent_id, prev_id }: Node)

item.setAttribute("data-parent", parent_id || "")
item.setAttribute("data-prev", prev_id || "")

const prevItem = getItemById(prev_id)
const parentItem = getItemById(parent_id)

if (prevItem) {
prevItem.after(item)
} else if (parentItem) {
parentItem.querySelector("ol")?.append(item)
} else {
container.append(item)
}
}

export function getItemById(uuid: string) {
const item = <HTMLLIElement>document.getElementById("outline-node-" + uuid)
export function getItemById(uuid: string | undefined) {
if (!uuid) return null

return item
return document.getElementById("outline-node-" + uuid)
}

export function getNodeByEvent(event: Event): Node {
Expand Down
19 changes: 19 additions & 0 deletions lib/radiator/outline.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,25 @@ defmodule Radiator.Outline do
|> Repo.get!(id)
end

@doc """
Gets a single node.
Returns `nil` if the Node does not exist.
## Examples
iex> get_node(123)
%Node{}
iex> get_node(456)
nil
"""
def get_node(id) do
Node
|> Repo.get(id)
end

@doc """
Creates a node.
Expand Down
10 changes: 7 additions & 3 deletions lib/radiator_web/components/layouts/app.html.heex
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<header class="text-white bg-fixed bg-center bg-cover bg-page-header">
<nav class="inset-x-0 py-6 overflow-hidden text-center">
<header class="text-center text-white bg-fixed bg-center bg-cover bg-page-header">
<nav class="inset-x-0 py-6 overflow-hidden">
<ul class={[
"relative inline-block px-4 border-l border-r border-page-border/40",
"before:border-page-border/40 before:left-full before:h-1 before:w-screen before:border-t before:border-b before:absolute before:top-1/2 before:-translate-y-1/2",
Expand Down Expand Up @@ -45,9 +45,13 @@
<% end %>
</ul>
</nav>
<div class="p-6">
<h1 class="text-4xl"><%= assigns[:page_title] %></h1>
<p class="p-2"><%= assigns[:page_description] %></p>
</div>
</header>
<main class="px-4 py-20 sm:px-6 lg:px-8">
<div class="max-w-2xl mx-auto">
<div class="max-w-6xl mx-auto">
<.flash_group flash={@flash} />
<%= @inner_content %>
</div>
Expand Down
26 changes: 26 additions & 0 deletions lib/radiator_web/live/admin_live/index.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
defmodule RadiatorWeb.AdminLive.Index do
use RadiatorWeb, :live_view

alias Radiator.Accounts
alias Radiator.Podcast
alias RadiatorWeb.Endpoint

@impl true
def mount(_params, _session, socket) do
socket
|> assign(:page_title, "Admin Dashboard")
|> assign(:page_description, "Tools to create and manage your prodcasts")
|> assign(:networks, Podcast.list_networks(preload: :shows))
|> assign(:bookmarklet, get_bookmarklet(Endpoint.url() <> "/api/v1/outline", socket))
|> reply(:ok)
end

defp get_bookmarklet(api_uri, socket) do
token =
socket.assigns.current_user
|> Accounts.generate_user_api_token()
|> Base.url_encode64(padding: false)

"""
javascript:(function(){
s=window.getSelection().toString();
c=s!=""?s:window.location.href;
xhr=new XMLHttpRequest();
xhr.open('POST','#{api_uri}',true);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send('content='+encodeURIComponent(c)+'&token=#{token}');
})()
"""
|> String.replace(["\n", " "], "")
end
end
40 changes: 27 additions & 13 deletions lib/radiator_web/live/admin_live/index.html.heex
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
<div>
<header>
<h1 class="text-4xl">Admin Dashboard</h1>
<p>Tools to create and manage your prodcasts</p>
</header>
<section class="my-12">
<div :for={network <- @networks}>
<h2 class="my-4 text-2xl"><%= network.title %></h2>
<div class="grid grid-cols-3 gap-6 sm:grid-cols-6">
<.link
:for={{show, i} <- Enum.with_index(network.shows)}
href={~p"/admin/podcast/#{show}"}
class="bg-[#f0f4f4] aspect-square"
>
<img src={"/images/pic1#{i}.jpg"} alt="" />
<div class="p-2 text-center"><%= show.title %></div>
</.link>
</div>
</div>
<button class="my-4 rounded text-white bg-[#df7366] px-8 py-2">Create Network</button>
</section>

<section class="grid grid-cols-1 gap-6 my-12">
<section class="my-12">
<h2 class="text-2xl">Bookmarklet</h2>
<p>
Drag & Drop this link in your Browser-Bookmarks:
<.icon name="hero-bookmark" class="w-5 h-5" />
<a href={@bookmarklet} class="underline">Save in Radiator (v2)</a>
</p>
</section>

<section class="my-12">
<h2 class="text-2xl">Tools</h2>
<.link href={~p"/admin/accounts"} class="flex bg-[#f0f4f4]">
<div class="bg-[#f0f4f4] p-2">
<.icon name="hero-users" class="w-12 h-12" />
Expand All @@ -13,13 +35,5 @@
Accounts
</div>
</.link>
<.link href={~p"/admin/outline"} class="flex bg-[#f0f4f4]">
<div class="bg-[#f0f4f4] p-2">
<.icon name="hero-list-bullet" class="w-12 h-12" />
</div>
<div class="p-2 my-auto">
Outline
</div>
</.link>
</section>
</div>
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
defmodule RadiatorWeb.OutlineLive.Index do
defmodule RadiatorWeb.EpisodeLive.Index do
use RadiatorWeb, :live_view

alias Radiator.Accounts
alias Radiator.Outline
alias Radiator.Podcast
alias RadiatorWeb.Endpoint

@topic "outline"

@impl true
def mount(_params, _session, socket) do
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
|> assign(:page_title, "Outline")
|> assign(:bookmarklet, get_bookmarklet(Endpoint.url() <> "/api/v1/outline", socket))
|> assign(:episode_id, get_episode_id())
|> push_event("list", %{nodes: Outline.list_nodes()})
|> assign(:page_title, show.title)
# |> assign(:page_description, "")
|> assign(:show, show)
|> assign(:episodes, show.episodes)
|> reply(:ok)
end

@impl true
def handle_params(params, _uri, socket) do
episode = get_selected_episode(params)

socket
|> assign(:selected_episode, episode)
|> push_event("list", %{nodes: Outline.list_nodes()})
|> reply(:noreply)
end

@impl true
def handle_event("set_focus", _node_id, socket) do
socket
Expand All @@ -34,7 +46,8 @@ defmodule RadiatorWeb.OutlineLive.Index do

def handle_event("create_node", %{"temp_id" => temp_id} = params, socket) do
user = socket.assigns.current_user
attrs = Map.put(params, "episode_id", socket.assigns.episode_id)
episode_id = socket.assigns.selected_episode.id
attrs = Map.put(params, "episode_id", episode_id)

socket =
case Outline.create_node(attrs, user) do
Expand All @@ -47,17 +60,21 @@ defmodule RadiatorWeb.OutlineLive.Index do
end

def handle_event("update_node", %{"uuid" => uuid} = params, socket) do
uuid
|> Outline.get_node!()
|> Outline.update_node(params)
attrs = Map.merge(%{"parent_id" => nil, "prev_id" => nil}, params)

case Outline.get_node(uuid) do
nil -> nil
node -> Outline.update_node(node, attrs)
end

socket
|> reply(:noreply)
end

def handle_event("delete_node", node_id, socket) do
node = Outline.get_node!(node_id)
Outline.delete_node(node)
node_id
|> Outline.get_node!()
|> Outline.delete_node()

socket
|> reply(:noreply)
Expand All @@ -82,32 +99,11 @@ defmodule RadiatorWeb.OutlineLive.Index do
|> reply(:noreply)
end

defp get_episode_id do
Radiator.Podcast.list_episodes()
|> Enum.sort_by(& &1.id)
|> List.last()
|> case do
nil -> nil
%{id: id} -> id
end
defp get_selected_episode(%{"episode" => episode_id}) do
Podcast.get_episode!(episode_id)
end

defp get_bookmarklet(api_uri, socket) do
token =
socket.assigns.current_user
|> Accounts.generate_user_api_token()
|> Base.url_encode64(padding: false)

"""
javascript:(function(){
s=window.getSelection().toString();
c=s!=""?s:window.location.href;
xhr=new XMLHttpRequest();
xhr.open('POST','#{api_uri}',true);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send('content='+encodeURIComponent(c)+'&token=#{token}');
})()
"""
|> String.replace(["\n", " "], "")
defp get_selected_episode(%{"show" => show_id}) do
Podcast.get_current_episode_for_show(show_id)
end
end
Loading

0 comments on commit 1200cca

Please sign in to comment.