refactor: apply functional programming patterns to accounts + map_live

- accounts.ex: replace if with with-chain in get_user_by_email_and_password
  for explicit pattern matching on User struct + valid_password? result.
  Collapse nested case into with in create_api_token.

- map_live.ex: extract connected? PubSub subscription block from mount into
  named subscribe_if_connected/1 function (cleaner than inline if with _ =).
  Deduplicate identical select_time + set_selected_time handlers into shared
  handle_set_time/2 helper. Extract toggle_grid + toggle_radar into shared
  handle_toggle/3 helper (functions-as-values pattern for assign key).
This commit is contained in:
Graham McIntire 2026-07-01 17:50:56 -05:00
parent c2efead65c
commit 5f154a20f3
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
2 changed files with 48 additions and 63 deletions

View file

@ -67,8 +67,12 @@ defmodule Microwaveprop.Accounts do
"""
@spec get_user_by_email_and_password(String.t(), String.t()) :: User.t() | nil
def get_user_by_email_and_password(email, password) when is_binary(email) and is_binary(password) do
user = Repo.get_by(User, email: email)
if User.valid_password?(user, password), do: user
with %User{} = user <- Repo.get_by(User, email: email),
true <- User.valid_password?(user, password) do
user
else
_ -> nil
end
end
@doc """
@ -443,15 +447,9 @@ defmodule Microwaveprop.Accounts do
@spec create_api_token(User.t(), map()) ::
{:ok, {String.t(), UserApiToken.t()}} | {:error, Ecto.Changeset.t()}
def create_api_token(%User{} = user, attrs) do
case UserApiToken.build(user, attrs) do
{:ok, {plaintext, changeset}} ->
case Repo.insert(changeset) do
{:ok, record} -> {:ok, {plaintext, record}}
{:error, changeset} -> {:error, changeset}
end
{:error, changeset} ->
{:error, changeset}
with {:ok, {plaintext, changeset}} <- UserApiToken.build(user, attrs),
{:ok, record} <- Repo.insert(changeset) do
{:ok, {plaintext, record}}
end
end

View file

@ -36,13 +36,7 @@ defmodule MicrowavepropWeb.MapLive do
@impl true
def mount(params, session, socket) do
_ =
if connected?(socket) do
:ok = Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "propagation:updated")
:ok = Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "propagation:pipeline")
_ = Process.send_after(self(), :refresh_pipeline_status, @pipeline_status_refresh_ms)
_ = Process.send_after(self(), :advance_now_cursor, @advance_now_cursor_ms)
end
socket = subscribe_if_connected(socket)
socket =
socket
@ -97,6 +91,17 @@ defmodule MicrowavepropWeb.MapLive do
)}
end
defp subscribe_if_connected(socket) do
if connected?(socket) do
:ok = Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "propagation:updated")
:ok = Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "propagation:pipeline")
_ = Process.send_after(self(), :refresh_pipeline_status, @pipeline_status_refresh_ms)
_ = Process.send_after(self(), :advance_now_cursor, @advance_now_cursor_ms)
end
socket
end
# Compact relative-time rendering of the build timestamp. Matches the
# format used by Layouts.deploy_stamp/1 so the nav and the map
# sidebar stay consistent.
@ -193,63 +198,22 @@ defmodule MicrowavepropWeb.MapLive do
end
def handle_event("select_time", %{"time" => time_str}, socket) do
# Same as set_selected_time — client owns the score fetch via HTTP.
# Kept as a separate event because the click target predates the
# split and cleaning every caller up isn't necessary.
case DateTime.from_iso8601(time_str) do
{:ok, time, _} ->
socket =
socket
|> assign(:selected_time, time)
|> assign(:tracking_now?, tracking_now?(time, socket.assigns.valid_times, DateTime.utc_now()))
|> patch_map_url()
{:noreply, socket}
_ ->
{:noreply, socket}
end
handle_set_time(time_str, socket)
end
# Fast path: the client already has the preloaded hour's scores in its
# local cache and only needs the server to remember which time is selected
# (used later for point_detail and forecast state on reconnect).
def handle_event("set_selected_time", %{"time" => time_str}, socket) do
case DateTime.from_iso8601(time_str) do
{:ok, time, _} ->
socket =
socket
|> assign(:selected_time, time)
|> assign(:tracking_now?, tracking_now?(time, socket.assigns.valid_times, DateTime.utc_now()))
|> patch_map_url()
{:noreply, socket}
_ ->
{:noreply, socket}
end
handle_set_time(time_str, socket)
end
def handle_event("toggle_grid", _params, socket) do
visible = !socket.assigns.grid_visible
socket =
socket
|> assign(:grid_visible, visible)
|> push_event("toggle_grid", %{visible: visible})
{:noreply, socket}
handle_toggle(socket, :grid_visible, "toggle_grid")
end
def handle_event("toggle_radar", _params, socket) do
visible = !socket.assigns.radar_visible
socket =
socket
|> assign(:radar_visible, visible)
|> push_event("toggle_radar", %{visible: visible})
{:noreply, socket}
handle_toggle(socket, :radar_visible, "toggle_radar")
end
def handle_event("point_detail", %{"lat" => lat, "lon" => lon}, socket) do
@ -313,6 +277,29 @@ defmodule MicrowavepropWeb.MapLive do
{:noreply, socket}
end
# -- event handler helpers (grouped after handle_event clauses) --
defp handle_set_time(time_str, socket) do
case DateTime.from_iso8601(time_str) do
{:ok, time, _} ->
socket =
socket
|> assign(:selected_time, time)
|> assign(:tracking_now?, tracking_now?(time, socket.assigns.valid_times, DateTime.utc_now()))
|> patch_map_url()
{:noreply, socket}
_ ->
{:noreply, socket}
end
end
defp handle_toggle(socket, assign_key, event_name) do
visible = !socket.assigns[assign_key]
{:noreply, socket |> assign(assign_key, visible) |> push_event(event_name, %{visible: visible})}
end
@impl true
def handle_info({:propagation_updated, _valid_times}, socket) do
band = socket.assigns.selected_band