defmodule TribeOne.TribesPlugin.AetherWeb.ChatLive do use Phoenix.LiveView on_mount({Tribes.Plugin.LiveUserAuth, :live_user_optional}) alias TribeOne.TribesPlugin.Aether.Chat alias TribeOne.TribesPlugin.AetherWeb.{ChatPanelComponent, ChatRecipientPickerComponent} alias Tribes.Plugin.Layouts @component_id "aether-chat-panel-component" @impl true def mount(_params, session, socket) do request = channel_request(session) {:ok, socket |> assign(:page_title, channel_title(request.slug)) |> assign(:chat_request, request) |> assign(:component_id, @component_id)} end @impl true def handle_info({:aether_chat, :message, message}, socket) do send_update(ChatPanelComponent, id: @component_id, incoming_message: message) {:noreply, socket} end def handle_info({:aether_chat, :recipient_selected, recipient}, socket) do current_user = socket.assigns.current_user if current_user && is_binary(recipient.pubkey_hex) do attrs = %{ title: recipient_label(recipient), metadata: %{"recipient_username" => recipient.username} } case Chat.ensure_direct_conversation(current_user.pubkey_hex, recipient.pubkey_hex, attrs, session_privkey: socket.assigns[:session_privkey] ) do {:ok, channel} -> {:noreply, push_navigate(socket, to: Chat.standalone_path(channel))} {:error, reason} -> {:noreply, put_flash(socket, :error, "Could not open conversation: #{inspect(reason)}")} end else {:noreply, put_flash(socket, :error, "Sign in to start a direct message")} end end def handle_info({:parrhesia, :events, _ref, _subscription_id, events}, socket) when is_list(events) do Enum.each(events, &send_unwrapped_message(socket, &1)) {:noreply, socket} end def handle_info({:parrhesia, :event, _ref, _subscription_id, event}, socket) do send_unwrapped_message(socket, event) {:noreply, socket} end def handle_info({:parrhesia, :eose, _ref, _subscription_id, _hints}, socket), do: {:noreply, socket} def handle_info({:parrhesia, :eose, _ref, _subscription_id}, socket), do: {:noreply, socket} @impl true def render(%{chat_request: %{mode: :embedded}} = assigns) do ~H"""
<.live_component module={ChatPanelComponent} id={@component_id} slug={@chat_request.slug} backend={@chat_request.backend} embedded?={true} current_user={@current_user} session_privkey={@session_privkey} />
""" end def render(assigns) do ~H"""

Aether chat

{channel_title(@chat_request.slug)}

Public synced rooms and NIP-17 direct messages share this chat surface.

<.live_component :if={@current_user} module={ChatRecipientPickerComponent} id="aether-chat-recipient-picker" current_user={@current_user} />
<.live_component module={ChatPanelComponent} id={@component_id} slug={@chat_request.slug} backend={@chat_request.backend} embedded?={false} current_user={@current_user} session_privkey={@session_privkey} />
""" end defp channel_request(%{"plugin_path" => path}) when is_binary(path) do case String.split(path, "/", trim: true) do ["aether", "chat", "embed", slug | _rest] -> request(:embedded, slug, :public_sync) ["plugins", "aether", "chat", "embed", slug | _rest] -> request(:embedded, slug, :public_sync) ["aether", "chat", "marmot", slug | _rest] -> request(:standalone, slug, :marmot) ["plugins", "aether", "chat", "marmot", slug | _rest] -> request(:standalone, slug, :marmot) ["aether", "chat", slug | _rest] -> request(:standalone, slug, :public_sync) ["plugins", "aether", "chat", slug | _rest] -> request(:standalone, slug, :public_sync) _other -> request(:standalone, Chat.default_channel_slug(), :public_sync) end end defp channel_request(_session), do: request(:standalone, Chat.default_channel_slug(), :public_sync) defp request(mode, slug, backend), do: %{mode: mode, slug: slug, backend: backend} defp send_unwrapped_message(socket, event) do request = socket.assigns.chat_request with {:ok, %TribeOne.TribesPlugin.Aether.Chat.Channel{} = channel} <- Chat.get_channel_by_slug(request.slug, Chat.ash_opts()), backend <- Chat.backend_module(channel.backend), true <- function_exported?(backend, :unwrap_event, 3), {:ok, message} <- backend.unwrap_event(channel, event, session_privkey: socket.assigns[:session_privkey]) do send_update(ChatPanelComponent, id: @component_id, incoming_message: message) end end defp recipient_label(%{display_name: display_name}) when is_binary(display_name) and display_name != "" do display_name end defp recipient_label(%{username: username}) when is_binary(username) and username != "", do: username defp recipient_label(%{pubkey_hex: pubkey}), do: String.slice(pubkey, 0, 8) <> "…" defp channel_title("general"), do: "General Chat" defp channel_title(slug) do slug |> String.replace(["-", "_"], " ") |> String.split(" ", trim: true) |> Enum.map_join(" ", &String.capitalize/1) end end