From 118d32241fed8910a22a9b5d236b01909a394cfb Mon Sep 17 00:00:00 2001 From: Steffen Beyer Date: Fri, 10 Apr 2026 18:38:06 +0200 Subject: [PATCH] refactor: use typed plugin nostr data --- lib/aether_web/live/timeline_live.ex | 72 ++++++++++++++-------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/lib/aether_web/live/timeline_live.ex b/lib/aether_web/live/timeline_live.ex index 79ebeef..68e40f0 100644 --- a/lib/aether_web/live/timeline_live.ex +++ b/lib/aether_web/live/timeline_live.ex @@ -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
@@ -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"