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