defmodule AshHqWeb.Pages.Forum do
@moduledoc "Forum page"
use Surface.LiveComponent
require Ash.Query
alias AshHqWeb.Components.Blog.Tag
alias AshHqWeb.Components.Forum.Attachment
alias Surface.Components.LivePatch
import AshHqWeb.Tails
prop params, :map, default: %{}
data thread, :any, default: nil
data threads, :any, default: []
data tag, :string, default: nil
data tags, :any, default: []
data channels, :any, default: []
data channel, :any, default: []
def render(assigns) do
~F"""
{#if @thread}
{@thread.name}
{@thread.author}
{@thread.create_timestamp |> DateTime.to_date()}
{#for tag <- @thread.tags}
{/for}
{#for message <- @thread.messages}
{message.author}:
{raw(message.content_html)}
{#for attachment <- message.attachments}
{/for}
{/for}
{#else}
Channels
{#for channel <- @channels}
{String.capitalize(channel.name)}
{/for}
{#if @threads.offset != 0}
Previous Page
{/if}
{#if @threads.more?}
Next Page
{/if}
{#if @tag}
Showing {page_info(@threads)} with tag: {@tag}
{#else}
Showing {page_info(@threads)}
{/if}
{#for thread <- @threads.results}
{thread.name}
{thread.author}
{thread.create_timestamp |> DateTime.to_date()}
{#for tag <- thread.tags}
{/for}
{/for}
{#if @threads.offset != 0}
Previous Page
{/if}
{#if @threads.more?}
Next Page
{/if}
{/if}
{#if !@thread}
All Tags:
{#for tag <- @tags}
{/for}
{/if}
"""
end
def update(assigns, socket) do
{
:ok,
socket
|> assign(assigns)
|> assign_channels()
|> assign_channel()
|> assign_tags()
|> assign_tag()
|> assign_thread()
|> assign_threads()
}
end
defp assign_tags(socket) do
tags =
AshHq.Discord.Tag
# TODO: use distinct
# |> Ash.Query.distinct(:name)
|> Ash.Query.filter(channel_id == ^socket.assigns.channel.id)
|> Ash.Query.select(:name)
|> Ash.Query.sort(:name)
|> AshHq.Discord.read!()
|> Enum.map(&to_string(&1.name))
|> Enum.uniq_by(&String.downcase/1)
assign(socket, :tags, tags)
end
defp assign_tag(socket) do
tag =
if socket.assigns.params["tag"] do
Enum.find(
socket.assigns.tags,
&Ash.Type.CiString.equal?(&1, socket.assigns.params["tag"])
)
end
assign(socket, :tag, tag)
end
defp assign_thread(socket) do
if socket.assigns.params["id"] do
messages_query =
AshHq.Discord.Message
|> Ash.Query.sort(timestamp: :asc)
|> Ash.Query.deselect(:content)
|> Ash.Query.load(:attachments)
assign(
socket,
:thread,
AshHq.Discord.Thread.by_id!(socket.assigns.params["id"],
load: [:tags, messages: messages_query]
)
)
else
assign(socket, :thread, nil)
end
end
defp assign_threads(socket) do
assign(
socket,
:threads,
AshHq.Discord.Thread.feed!(
socket.assigns.channel.id,
%{tag_name: socket.assigns.tag},
page: [offset: String.to_integer(socket.assigns.params["offset"] || "0"), count: true],
load: :tags
)
)
end
defp assign_channels(socket) do
assign(socket, :channels, AshHq.Discord.Channel.read!() |> Enum.sort_by(& &1.order))
end
defp assign_channel(socket) do
channel =
Enum.find(
socket.assigns.channels,
&(&1.name == socket.assigns.params["channel"])
)
if is_nil(channel) do
raise Ash.Error.Query.NotFound.exception(
primary_key: %{name: socket.assigns.params["channel"]}
)
end
assign(
socket,
:channel,
channel
)
end
defp page_info(%{results: []}) do
"no threads "
end
defp page_info(%{more?: false, offset: 0, count: count}) do
"all #{count} threads "
end
defp page_info(%{more?: false, results: results, count: count}) do
"the last #{Enum.count(results)} of #{count} threads "
end
defp page_info(%{offset: 0, limit: limit, count: count}) do
"the first #{limit} of #{count} threads "
end
defp page_info(%{offset: offset, limit: limit, count: count}) do
"threads #{offset + 1} to #{offset + limit} of #{count}"
end
end