You've already forked tribes-plugin-aether
forked from tribes/tribes-plugin-template
80101b7e78
Handle the NIP-67 EOSE tuple shape emitted by newer Parrhesia while keeping compatibility with the previous message format.
187 lines
6.2 KiB
Elixir
187 lines
6.2 KiB
Elixir
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"""
|
|
<div class="aether aether-chat h-full min-h-[28rem] p-2" id="aether-chat-embed">
|
|
<.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}
|
|
/>
|
|
</div>
|
|
"""
|
|
end
|
|
|
|
def render(assigns) do
|
|
~H"""
|
|
<Layouts.app flash={@flash} current_scope={@current_scope}>
|
|
<div class="aether aether-chat flex min-h-[calc(100vh-8rem)] flex-col p-4 sm:p-6" id="aether-chat-page">
|
|
<section class="mb-4 rounded-3xl border border-base-300 bg-base-100/95 p-5 shadow-sm">
|
|
<div class="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
|
|
<div>
|
|
<p class="text-xs font-semibold uppercase tracking-[0.3em] text-base-content/50">
|
|
Aether chat
|
|
</p>
|
|
<h1 class="mt-1 text-3xl font-semibold tracking-tight text-base-content">
|
|
{channel_title(@chat_request.slug)}
|
|
</h1>
|
|
<p class="mt-2 max-w-2xl text-sm leading-6 text-base-content/70">
|
|
Public synced rooms and NIP-17 direct messages share this chat surface.
|
|
</p>
|
|
</div>
|
|
<.live_component
|
|
:if={@current_user}
|
|
module={ChatRecipientPickerComponent}
|
|
id="aether-chat-recipient-picker"
|
|
current_user={@current_user}
|
|
/>
|
|
</div>
|
|
</section>
|
|
|
|
<.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}
|
|
/>
|
|
</div>
|
|
</Layouts.app>
|
|
"""
|
|
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
|