defmodule PiDayWeb.SpectateLive do use PiDayWeb, :live_view alias PiDay.Game alias PiDayWeb.Presence @impl true def mount(_params, _session, socket) do if connected?(socket) do Phoenix.PubSub.subscribe(PiDay.PubSub, "leaderboard") # Subscribe to presence diffs for the hub PiDayWeb.Endpoint.subscribe("game:hub") :timer.send_interval(3000, self(), :refresh_leaderboard) :timer.send_interval(200, self(), :push_presence) end {:ok, assign(socket, page_title: "Pi Station - Leaderboard", leaderboard: Game.leaderboard(), pi_top: Game.top_scores("pi_memory", 5), mc_top: Game.top_scores("monte_carlo", 5), slice_top: Game.top_scores("slice_the_pi", 5), trivia_top: Game.top_scores("pi_trivia", 5), projectile_top: Game.top_scores("projectile_pi", 5) )} end defp refresh(socket) do assign(socket, leaderboard: Game.leaderboard(), pi_top: Game.top_scores("pi_memory", 5), mc_top: Game.top_scores("monte_carlo", 5), slice_top: Game.top_scores("slice_the_pi", 5), trivia_top: Game.top_scores("pi_trivia", 5), projectile_top: Game.top_scores("projectile_pi", 5) ) end @impl true def handle_info(:refresh_leaderboard, socket) do {:noreply, refresh(socket)} end def handle_info(:push_presence, socket) do players = Presence.list("game:hub") |> Enum.map(fn {id, %{metas: [meta | _]}} -> %{id: id, name: meta.name, avatar_key: meta.avatar_key, x: meta.x, y: meta.y, status: meta.status} end) {:noreply, push_event(socket, "hub_players", %{players: players})} end def handle_info({:score_updated, _score}, socket) do {:noreply, refresh(socket)} end # Ignore presence diffs — we poll with push_presence instead def handle_info(%Phoenix.Socket.Broadcast{}, socket), do: {:noreply, socket} @impl true def render(assigns) do ~H"""
Pi Day 2026 · Live Leaderboard
No scores yet — join the game!
<% end %>No scores yet
<% end %>