prop/lib/microwaveprop_web/live_helpers.ex
Graham McIntire b4b8d4ec47
Some checks failed
Build base image / Build and push base image (push) Successful in 12s
Build and Push / Build CI test image (push) Successful in 14s
Build and Push / Build and Push Docker Image (push) Failing after 14m7s
simplify: DRY up shared changesets, context helpers, LiveView helpers, and structural extraction
- Create MaidenheadChangesetHelpers: consolidate grid validation, callsign
  normalization, lat/lon validation, grid/latlon derivation across 6 schemas
- Create ContextHelpers: shared fetch_owned with admin bypass, safe_enqueue
  for Oban workers, UUID casting to replace CastError rescues
- Extend LiveHelpers: add current_user/1 (removes 7 duplicate definitions),
  subscribe/2 (replaces 13 inline PubSub sites), assign_url_params/2
- Extract Propagation.ScoreStore (528 lines): separate file I/O and cache
  management from scoring logic, 13 defdelegate passthroughs
- Split SubmitLive (942->475 lines): extract CSV/ADIF upload rendering into
  3 function component modules (csv_upload, adif_upload, preview)
- Update 16 LiveViews to use shared helpers
2026-08-06 18:06:50 -05:00

83 lines
3 KiB
Elixir

defmodule MicrowavepropWeb.LiveHelpers do
@moduledoc """
Small helpers shared across LiveView modules. Use sparingly — most
view-specific logic belongs in its own LiveView.
"""
# ── Parse helpers ────────────────────────────────────────────────────
alias Phoenix.LiveView.Socket
@doc """
Parses a value into a float, returning `default` for nil, empty, or
unparseable input. Accepts strings, numbers, or any value with a
`String.Chars` impl.
"""
@spec parse_float(term(), float()) :: float()
def parse_float(nil, default), do: default
def parse_float("", default), do: default
def parse_float(value, default) do
case value |> to_string() |> Float.parse() do
{n, _} -> n
:error -> default
end
end
@doc """
Parses a value into an integer, returning `default` for nil, empty,
or unparseable input.
"""
@spec parse_int(term(), integer()) :: integer()
def parse_int(nil, default), do: default
def parse_int("", default), do: default
def parse_int(value, default) do
case value |> to_string() |> Integer.parse() do
{n, _} -> n
:error -> default
end
end
# ── User helpers ─────────────────────────────────────────────────────
@doc """
Extracts the current user from a LiveView socket or assigns map.
Returns the `%User{}` struct or `nil` if not authenticated.
"""
@spec current_user(Socket.t() | map()) :: Ecto.Schema.t() | nil
def current_user(%Socket{assigns: assigns}) do
current_user(assigns)
end
@spec current_user(map()) :: Ecto.Schema.t() | nil
def current_user(%{current_scope: %{user: %_{} = user}}), do: user
def current_user(%{user: %_{} = user}), do: user
def current_user(_), do: nil
# ── PubSub helpers ───────────────────────────────────────────────────
@doc """
Subscribes the socket to a Phoenix.PubSub topic, but only when the
socket is connected (WebSocket). Safe to call unconditionally in mount/3.
"""
@spec subscribe(Socket.t(), String.t()) :: :ok
def subscribe(%Socket{} = socket, topic) when is_binary(topic) do
if Phoenix.LiveView.connected?(socket) do
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, topic)
end
:ok
end
# ── URL helpers ──────────────────────────────────────────────────────
@doc """
Merges URL params with defaults for handle_params/3. Returns a map of
merged values suitable for assigning to the socket.
"""
@spec assign_url_params(map(), map()) :: map()
def assign_url_params(params, defaults) when is_map(params) and is_map(defaults) do
Map.merge(defaults, Map.take(params, Map.keys(defaults)))
end
end