lib/nostr/client/subscriptions/timeline_subscription.ex

defmodule Nostr.Client.Subscriptions.TimelineSubscription do
  @moduledoc """
  A process creating and managing a live timeline subscription on a bunch of relays
  """

  use GenServer

  alias Nostr.RelaySocket
  alias Nostr.Event.Types.EndOfStoredEvents

  alias Nostr.Models.{ContactList}

  def start_link([relay_pids, pubkey, subscriber]) do
    GenServer.start_link(__MODULE__, %{
      relay_pids: relay_pids,
      pubkey: pubkey,
      subscriber: subscriber
    })
  end

  @impl true
  def init(%{relay_pids: relay_pids, pubkey: pubkey} = state) do
    subscriptions =
      relay_pids
      |> Enum.map(fn relay_pid ->
        RelaySocket.subscribe_contacts(relay_pid, pubkey)
      end)

    {
      :ok,
      state
      |> set_note_subscriptions(subscriptions)
    }
  end

  @impl true
  def handle_info(
        {_relay_url, %ContactList{contacts: contacts}},
        %{relay_pids: relay_pids, pubkey: _pubkey, subscriber: _subscriber} = state
      ) do
    pubkeys =
      contacts
      |> Enum.map(& &1.pubkey)

    unsubscribe_all_notes(state)

    new_subscriptions =
      relay_pids
      |> Enum.map(fn relay_pid ->
        subscription_id = RelaySocket.subscribe_notes(relay_pid, pubkeys, 1)
        {relay_pid, subscription_id}
      end)

    {:noreply, state |> set_note_subscriptions(new_subscriptions)}
  end

  @impl true
  def handle_info({relay_url, event}, %{subscriber: subscriber} = state) do
    send(subscriber, {relay_url, event})

    {:noreply, state}
  end

  @impl true
  def handle_info(%EndOfStoredEvents{}, state) do
    ## nothing to do

    {:noreply, state}
  end

  defp unsubscribe_all_notes(%{subscriptions: subscriptions}) do
    for {relay_pid, subscription_id} <- subscriptions do
      RelaySocket.unsubscribe(relay_pid, subscription_id)
    end
  end

  defp set_note_subscriptions(state, subscriptions) do
    Map.put(state, :subscriptions, subscriptions)
  end
end