diff --git a/lib/aether/chat.ex b/lib/aether/chat.ex index 59d9cdc..4a04d8e 100644 --- a/lib/aether/chat.ex +++ b/lib/aether/chat.ex @@ -35,6 +35,23 @@ defmodule Aether.Chat do def default_channel_slug, do: @default_channel_slug + def standalone_path(%Channel{slug: slug}), do: standalone_path(slug) + def standalone_path(slug) when is_binary(slug), do: "/aether/chat/" <> slug + + def embed_path(%Channel{slug: slug}), do: embed_path(slug) + def embed_path(slug) when is_binary(slug), do: "/aether/chat/embed/" <> slug + + def ensure_context_channel(provider, type, id, attrs \\ %{}, opts \\ []) + when is_binary(provider) and is_binary(type) and is_binary(id) and is_map(attrs) do + attrs + |> Map.merge(%{ + context_provider: provider, + context_type: type, + context_id: id + }) + |> ensure_channel(opts) + end + def ash_opts(context \\ nil) do [ authorize?: false, diff --git a/lib/aether_web/live/chat_live.ex b/lib/aether_web/live/chat_live.ex index f706f45..83b72fd 100644 --- a/lib/aether_web/live/chat_live.ex +++ b/lib/aether_web/live/chat_live.ex @@ -9,7 +9,7 @@ defmodule AetherWeb.ChatLive do @impl true def mount(_params, session, socket) do - slug = channel_slug(session) + {mode, slug} = channel_request(session) case Chat.ensure_channel(%{slug: slug, title: channel_title(slug)}) do {:ok, %Channel{} = channel} -> @@ -20,6 +20,7 @@ defmodule AetherWeb.ChatLive do socket |> assign(:page_title, channel.title) |> assign(:channel, channel) + |> assign(:embedded?, mode == :embedded) |> assign(:message_ids, message_ids) |> assign(:message_count, MapSet.size(message_ids)) |> stream_configure(:messages, dom_id: &("chat-message-" <> &1.id)) @@ -36,6 +37,7 @@ defmodule AetherWeb.ChatLive do socket |> assign(:page_title, "Chat") |> assign(:channel, nil) + |> assign(:embedded?, mode == :embedded) |> assign(:message_ids, MapSet.new()) |> assign(:message_count, 0) |> assign(:chat_error, inspect(reason)) @@ -73,6 +75,20 @@ defmodule AetherWeb.ChatLive do end @impl true + def render(%{embedded?: true} = assigns) do + ~H""" +
+ <.chat_panel + channel={@channel} + current_user={@current_user} + embedded?={@embedded?} + message_count={@message_count} + streams={@streams} + /> +
+ """ + end + def render(assigns) do ~H""" @@ -96,64 +112,104 @@ defmodule AetherWeb.ChatLive do -
-
- - -
-
- {author_initial(message)} -
-
-
- {author_label(message)} - - -
-

{message.body}

-
-
-
- - <%= if @current_user do %> -
-
- - - -
-
- <% else %> -
- Sign in to join the chat. -
- <% end %> -
+ <.chat_panel + channel={@channel} + current_user={@current_user} + embedded?={@embedded?} + message_count={@message_count} + streams={@streams} + />
""" end + defp chat_panel(assigns) do + ~H""" +
+
+
+

Chat

+

{if @channel, do: @channel.title, else: "Chat"}

+
+ {@message_count} +
+ +
+ + +
+
+ {author_initial(message)} +
+
+
+ {author_label(message)} + + +
+

{message.body}

+
+
+
+ + <%= if @current_user do %> +
+
+ + + +
+
+ <% else %> +
+ Sign in to join the chat. +
+ <% end %> +
+ """ + end + defp post_message(socket, body) do user = socket.assigns.current_user @@ -193,15 +249,17 @@ defmodule AetherWeb.ChatLive do end end - defp channel_slug(%{"plugin_path" => path}) when is_binary(path) do + defp channel_request(%{"plugin_path" => path}) when is_binary(path) do case String.split(path, "/", trim: true) do - ["aether", "chat", slug | _rest] -> slug - ["plugins", "aether", "chat", slug | _rest] -> slug - _other -> Chat.default_channel_slug() + ["aether", "chat", "embed", slug | _rest] -> {:embedded, slug} + ["plugins", "aether", "chat", "embed", slug | _rest] -> {:embedded, slug} + ["aether", "chat", slug | _rest] -> {:standalone, slug} + ["plugins", "aether", "chat", slug | _rest] -> {:standalone, slug} + _other -> {:standalone, Chat.default_channel_slug()} end end - defp channel_slug(_session), do: Chat.default_channel_slug() + defp channel_request(_session), do: {:standalone, Chat.default_channel_slug()} defp channel_title("general"), do: "General Chat" diff --git a/test/aether/chat_page_test.exs b/test/aether/chat_page_test.exs index 2a90823..f59edd9 100644 --- a/test/aether/chat_page_test.exs +++ b/test/aether/chat_page_test.exs @@ -28,6 +28,15 @@ defmodule Aether.ChatPageTest do assert has_element?(view, "#chat-messages", "Hello from chat test") end + test "renders compact embedded chat", %{conn: conn} do + slug = "embed-chat-#{System.unique_integer([:positive])}" + {:ok, view, _html} = live(conn, "/aether/chat/embed/#{slug}") + + assert has_element?(view, "#aether-chat-embed") + assert has_element?(view, "#chat-embed-header") + refute has_element?(view, "#aether-chat-page") + end + test "ensures context group channels with stable slugs" do attrs = %{ context_provider: "sender", @@ -42,5 +51,14 @@ defmodule Aether.ChatPageTest do assert first.id == second.id assert first.slug == "sender-stream-stream-123" assert first.conversation_kind == :context_group + assert Chat.embed_path(first) == "/aether/chat/embed/sender-stream-stream-123" + end + + test "ensures context group channels through provider helper" do + assert {:ok, channel} = + Chat.ensure_context_channel("sender", "stream", "stream-456", %{title: "Stream Chat"}) + + assert channel.slug == "sender-stream-stream-456" + assert channel.conversation_kind == :context_group end end