You've already forked tribes-plugin-aether
forked from tribes/tribes-plugin-template
b4b8c83ddb
Adopt canonical plugin id/slug manifest fields, vendor-prefixed OTP app naming, and fully-qualified capability ids for Aether.
140 lines
4.7 KiB
Elixir
140 lines
4.7 KiB
Elixir
defmodule TribeOne.TribesPlugin.AetherWeb.ChatRecipientPickerComponent do
|
|
@moduledoc """
|
|
Recipient picker exposed as part of Aether's `org.tribe-one.caps.chat@1` UI contract.
|
|
"""
|
|
|
|
use Phoenix.LiveComponent
|
|
|
|
alias Tribes.Plugin.Services.Alliance
|
|
alias Tribes.Plugin.User
|
|
alias Tribes.Scope
|
|
|
|
@impl true
|
|
def update(assigns, socket) do
|
|
recipients = Map.get(assigns, :recipients) || load_recipients(Map.get(assigns, :current_user))
|
|
|
|
{:ok,
|
|
socket
|
|
|> assign(assigns)
|
|
|> assign_new(:query, fn -> "" end)
|
|
|> assign(:recipients, recipients)}
|
|
end
|
|
|
|
@impl true
|
|
def handle_event("search", %{"recipient" => %{"query" => query}}, socket) do
|
|
{:noreply, assign(socket, :query, String.trim(query || ""))}
|
|
end
|
|
|
|
def handle_event("select", %{"pubkey" => pubkey}, socket) do
|
|
case Enum.find(socket.assigns.recipients, &(&1.pubkey_hex == pubkey)) do
|
|
%User{} = user -> send(self(), {:aether_chat, :recipient_selected, user})
|
|
_other -> :ok
|
|
end
|
|
|
|
{:noreply, socket}
|
|
end
|
|
|
|
@impl true
|
|
def render(assigns) do
|
|
~H"""
|
|
<details id="chat-recipient-picker" class="dropdown dropdown-end">
|
|
<summary id="chat-recipient-picker-button" class="btn btn-primary btn-sm">
|
|
New message
|
|
</summary>
|
|
<div class="dropdown-content z-30 mt-2 w-80 rounded-box border border-base-300 bg-base-100 p-3 shadow-xl">
|
|
<form id="chat-recipient-search-form" phx-change="search" phx-target={@myself}>
|
|
<label class="input input-sm input-bordered flex items-center gap-2">
|
|
<span class="text-xs text-base-content/50">To</span>
|
|
<input
|
|
id="chat-recipient-search"
|
|
name="recipient[query]"
|
|
value={@query}
|
|
type="search"
|
|
class="grow"
|
|
placeholder="Search tribe users"
|
|
/>
|
|
</label>
|
|
</form>
|
|
|
|
<div id="chat-recipient-options" class="mt-3 max-h-72 space-y-1 overflow-y-auto">
|
|
<button
|
|
:for={recipient <- filtered_recipients(@recipients, @query, @current_user)}
|
|
id={"chat-recipient-#{recipient.id}"}
|
|
type="button"
|
|
class="flex w-full items-center gap-3 rounded-box px-3 py-2 text-left hover:bg-base-200"
|
|
phx-click="select"
|
|
phx-target={@myself}
|
|
phx-value-pubkey={recipient.pubkey_hex}
|
|
>
|
|
<span class="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-primary/10 text-sm font-semibold text-primary">
|
|
{recipient_initial(recipient)}
|
|
</span>
|
|
<span class="min-w-0">
|
|
<span class="block truncate text-sm font-medium text-base-content">{recipient_label(recipient)}</span>
|
|
<span class="block truncate text-xs text-base-content/50">{recipient_pubkey(recipient)}</span>
|
|
</span>
|
|
</button>
|
|
|
|
<p
|
|
:if={filtered_recipients(@recipients, @query, @current_user) == []}
|
|
id="chat-recipient-empty"
|
|
class="rounded-box border border-dashed border-base-300 p-4 text-center text-sm text-base-content/60"
|
|
>
|
|
No matching users.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
"""
|
|
end
|
|
|
|
defp load_recipients(_current_user) do
|
|
scope = Scope.new()
|
|
|
|
with {:ok, tribe} when is_map(tribe) <- Alliance.local_tribe(scope),
|
|
{:ok, users} <- Alliance.list_tribe_users(scope, tribe.id) do
|
|
Enum.filter(users, &is_binary(&1.pubkey_hex))
|
|
else
|
|
_other -> []
|
|
end
|
|
end
|
|
|
|
defp filtered_recipients(recipients, query, current_user) do
|
|
current_pubkey = current_user && current_user.pubkey_hex
|
|
query = String.downcase(query || "")
|
|
|
|
recipients
|
|
|> Enum.reject(&(&1.pubkey_hex == current_pubkey))
|
|
|> Enum.filter(&matches_query?(&1, query))
|
|
end
|
|
|
|
defp matches_query?(_recipient, ""), do: true
|
|
|
|
defp matches_query?(recipient, query) do
|
|
[recipient.username, recipient.display_name, recipient.npub, recipient.pubkey_hex]
|
|
|> Enum.filter(&is_binary/1)
|
|
|> Enum.any?(&String.contains?(String.downcase(&1), query))
|
|
end
|
|
|
|
defp recipient_label(%User{display_name: display_name})
|
|
when is_binary(display_name) and display_name != "" do
|
|
display_name
|
|
end
|
|
|
|
defp recipient_label(%User{username: username}) when is_binary(username) and username != "",
|
|
do: username
|
|
|
|
defp recipient_label(%User{pubkey_hex: pubkey}), do: String.slice(pubkey, 0, 8) <> "…"
|
|
|
|
defp recipient_pubkey(%User{npub: npub}) when is_binary(npub) and npub != "", do: npub
|
|
defp recipient_pubkey(%User{pubkey_hex: pubkey}), do: pubkey
|
|
|
|
defp recipient_initial(recipient) do
|
|
recipient
|
|
|> recipient_label()
|
|
|> String.first()
|
|
|> Kernel.||("?")
|
|
|> String.upcase()
|
|
end
|
|
end
|