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}
Back to {String.capitalize(@channel.name)}
Discord App
Discord Web

{@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