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
-
-
-
- No messages yet. Start the conversation.
-
-
-
-
- {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"""
+
+
+
+
+
+ No messages yet. Start the conversation.
+
+
+
+
+ {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