refactor: use typed plugin nostr data

This commit is contained in:
2026-04-10 18:38:06 +02:00
parent 6fc8be20bf
commit 118d32241f
+35 -37
View File
@@ -4,6 +4,7 @@ defmodule AetherWeb.TimelineLive do
on_mount({Tribes.Plugin.LiveUserAuth, :live_user_optional})
alias Tribes.Plugin.Layouts
alias Tribes.Plugin.Nostr.Profile
alias Tribes.Plugin.Services.Cluster
alias Tribes.Plugin.Services.Communities
alias Tribes.Plugin.Services.Nostr
@@ -15,7 +16,7 @@ defmodule AetherWeb.TimelineLive do
relay_urls = load_relay_urls()
notes = load_notes(feed.member_pubkeys)
profile_cache = load_profile_cache(notes)
note_ids = notes |> Enum.map(& &1["id"]) |> MapSet.new()
note_ids = notes |> Enum.map(& &1.id) |> MapSet.new()
socket =
socket
@@ -29,7 +30,7 @@ defmodule AetherWeb.TimelineLive do
|> assign(:note_ids, note_ids)
|> assign(:note_count, MapSet.size(note_ids))
|> assign(:subscription_ref, nil)
|> stream_configure(:notes, dom_id: &("note-" <> &1["id"]))
|> stream_configure(:notes, dom_id: &("note-" <> &1.id))
|> stream(:notes, notes)
if connected?(socket) do
@@ -69,24 +70,28 @@ defmodule AetherWeb.TimelineLive do
end
@impl true
def handle_info({:parrhesia, :event, _ref, _sub_id, %{"kind" => 1} = event}, socket) do
if note_in_scope?(event, socket.assigns.tribe_member_pubkeys) do
profile_cache = maybe_put_profile(socket.assigns.profile_cache, event["pubkey"])
def handle_info({:parrhesia, :event, _ref, _sub_id, %{"kind" => 1} = raw_event}, socket) do
with {:ok, event} <- Nostr.to_event(raw_event) do
if note_in_scope?(event, socket.assigns.tribe_member_pubkeys) do
profile_cache = maybe_put_profile(socket.assigns.profile_cache, event.pubkey)
if MapSet.member?(socket.assigns.note_ids, event["id"]) do
{:noreply, assign(socket, :profile_cache, profile_cache)}
if MapSet.member?(socket.assigns.note_ids, event.id) do
{:noreply, assign(socket, :profile_cache, profile_cache)}
else
note_ids = MapSet.put(socket.assigns.note_ids, event.id)
{:noreply,
socket
|> assign(:profile_cache, profile_cache)
|> assign(:note_ids, note_ids)
|> assign(:note_count, MapSet.size(note_ids))
|> stream_insert(:notes, event, at: 0)}
end
else
note_ids = MapSet.put(socket.assigns.note_ids, event["id"])
{:noreply,
socket
|> assign(:profile_cache, profile_cache)
|> assign(:note_ids, note_ids)
|> assign(:note_count, MapSet.size(note_ids))
|> stream_insert(:notes, event, at: 0)}
{:noreply, socket}
end
else
{:noreply, socket}
{:error, _reason} -> {:noreply, socket}
end
end
@@ -171,20 +176,20 @@ defmodule AetherWeb.TimelineLive do
<article :for={{id, note} <- @streams.notes} id={id} class="card bg-base-100 shadow-sm">
<div class="card-body gap-2">
<div class="flex items-center justify-between gap-4 text-xs text-base-content/60">
<a href={Routes.profile_path(note["pubkey"])} class="link link-hover font-medium">
{author_label(@profile_cache, note["pubkey"])}
<a href={Routes.profile_path(note.pubkey)} class="link link-hover font-medium">
{author_label(@profile_cache, note.pubkey)}
</a>
<span
phx-hook="DateTime"
id={"time-#{note["id"]}"}
id={"time-#{note.id}"}
phx-update="ignore"
data-timestamp={note["created_at"] * 1000}
data-timestamp={note.created_at * 1000}
data-relative="true"
data-format="LLL"
>
</span>
</div>
<p class="whitespace-pre-wrap text-sm leading-6">{note["content"]}</p>
<p class="whitespace-pre-wrap text-sm leading-6">{note.content}</p>
</div>
</article>
</div>
@@ -195,7 +200,7 @@ defmodule AetherWeb.TimelineLive do
defp handle_published_note(socket, event) do
if note_in_scope?(event, socket.assigns.tribe_member_pubkeys) do
note_ids = MapSet.put(socket.assigns.note_ids, event["id"])
note_ids = MapSet.put(socket.assigns.note_ids, event.id)
{:noreply,
socket
@@ -262,7 +267,7 @@ defmodule AetherWeb.TimelineLive do
end
defp note_in_scope?(event, member_pubkeys) do
MapSet.member?(member_pubkeys, event["pubkey"])
MapSet.member?(member_pubkeys, event.pubkey)
end
defp filter_notes(notes, member_pubkeys) do
@@ -271,13 +276,13 @@ defmodule AetherWeb.TimelineLive do
defp sort_notes(notes) do
Enum.sort_by(notes, fn note ->
{-Map.get(note, "created_at", 0), Map.get(note, "id", "")}
{-note.created_at, note.id}
end)
end
defp load_profile_cache(notes) do
notes
|> Enum.map(& &1["pubkey"])
|> Enum.map(& &1.pubkey)
|> Enum.uniq()
|> Enum.reduce(%{}, &maybe_put_profile(&2, &1))
end
@@ -288,24 +293,17 @@ defmodule AetherWeb.TimelineLive do
else
case Nostr.get_profile(pubkey) do
{:ok, nil} -> Map.put(cache, pubkey, nil)
{:ok, event} -> Map.put(cache, pubkey, parse_profile(event))
{:ok, %Profile{} = profile} -> Map.put(cache, pubkey, profile)
{:error, _reason} -> Map.put(cache, pubkey, nil)
end
end
end
defp parse_profile(%{"content" => content}) when is_binary(content) do
case JSON.decode(content) do
{:ok, profile} when is_map(profile) -> profile
_other -> %{}
end
end
defp parse_profile(_event), do: %{}
defp author_label(profile_cache, pubkey) do
profile = Map.get(profile_cache, pubkey, %{}) || %{}
profile["name"] || "#{String.slice(pubkey, 0, 8)}"
case Map.get(profile_cache, pubkey) do
%Profile{name: name} when is_binary(name) and name != "" -> name
_other -> "#{String.slice(pubkey, 0, 8)}"
end
end
defp timeline_title(nil), do: "Aether"