defmodule MvWeb.JoinConfirmController do @moduledoc """ Handles GET /confirm_join/:token for the public join flow (double opt-in). Renders a full HTML page with public header and hero layout (success, expired, or invalid). Calls a configurable callback (default Mv.Membership) so tests can stub the dependency. Public route; no authentication required. """ use MvWeb, :controller use Gettext, backend: MvWeb.Gettext def confirm(conn, %{"token" => token}) when is_binary(token) do callback = Application.get_env(:mv, :join_confirm_callback, Mv.Membership) case callback.confirm_join_request(token, actor: nil) do {:ok, _request} -> success_response(conn) {:error, :token_expired} -> expired_response(conn) {:error, _} -> invalid_response(conn) end end def confirm(conn, _params), do: invalid_response(conn) defp success_response(conn) do conn |> assign_confirm_assigns(:success) |> put_view(MvWeb.JoinConfirmHTML) |> render("confirm.html") end defp expired_response(conn) do conn |> assign_confirm_assigns(:expired) |> put_view(MvWeb.JoinConfirmHTML) |> render("confirm.html") end defp invalid_response(conn) do conn |> put_status(404) |> assign_confirm_assigns(:invalid) |> put_view(MvWeb.JoinConfirmHTML) |> render("confirm.html") end defp assign_confirm_assigns(conn, result) do page_title = page_title_for_result(result) conn |> assign(:result, result) |> assign(:page_title, page_title) |> assign(:flash, conn.assigns[:flash] || conn.flash || %{}) end defp page_title_for_result(:success), do: gettext("Join confirmation") defp page_title_for_result(:expired), do: gettext("Link expired") defp page_title_for_result(:invalid), do: gettext("Invalid link") end