defmodule AprsmeWeb.BadPacketsLive.Index do @moduledoc false use AprsmeWeb, :live_view import Ecto.Query alias Aprsme.BadPacket alias Aprsme.Repo @debounce_ms 2_000 @impl true def mount(_params, _session, socket) do # Load data unconditionally on the dead render so crawlers and link # previews see content — guarding with connected?/1 breaks SEO. bad_packets = fetch_bad_packets() limited_packets = Enum.take(bad_packets, 100) _ = if connected?(socket) do :ok = Phoenix.PubSub.subscribe(Aprsme.PubSub, "postgres:aprsme_events") end {:ok, assign(socket, bad_packets: limited_packets, loading: false, refresh_timer: nil )} end @impl true def handle_params(params, _url, socket) do {:noreply, apply_action(socket, socket.assigns.live_action, params)} end defp apply_action(socket, :index, _params) do assign(socket, :page_title, "Bad Packets") end @impl true def handle_event("refresh", _params, socket) do _ = cancel_timer(socket.assigns.refresh_timer) socket = assign(socket, loading: true) {:noreply, do_debounced_refresh(socket)} end @impl true def handle_info({:postgres_notify, _payload}, socket) do {:noreply, debounce_refresh(socket)} end @impl true def handle_info(:do_refresh, socket) do bad_packets = fetch_bad_packets() limited_packets = Enum.take(bad_packets, 100) {:noreply, assign(socket, bad_packets: limited_packets, loading: false, refresh_timer: nil )} end defp debounce_refresh(socket) do _ = cancel_timer(socket.assigns.refresh_timer) timer_ref = Process.send_after(self(), :do_refresh, @debounce_ms) assign(socket, refresh_timer: timer_ref) end defp do_debounced_refresh(socket) do timer_ref = Process.send_after(self(), :do_refresh, @debounce_ms) assign(socket, refresh_timer: timer_ref) end defp cancel_timer(nil), do: :ok defp cancel_timer(ref) do Process.cancel_timer(ref) end defp fetch_bad_packets(limit \\ 100) do actual_limit = min(limit, 100) BadPacket |> order_by([b], desc: b.attempted_at) |> limit(^actual_limit) |> Repo.all() end end