defmodule BerrypodWeb.NewsletterHook do @moduledoc """ LiveView on_mount hook for newsletter signup across all shop pages. Uses `attach_hook/4` to intercept `newsletter_subscribe` events globally without modifying individual shop LiveViews. """ import Phoenix.Component, only: [assign: 3] import Phoenix.LiveView, only: [attach_hook: 4] alias Berrypod.Newsletter def on_mount(:mount_newsletter, _params, _session, socket) do enabled = Newsletter.newsletter_enabled?() socket = socket |> assign(:newsletter_enabled, enabled) |> assign(:newsletter_state, :idle) |> assign(:newsletter_ip_hash, hash_ip(socket)) |> attach_hook(:newsletter, :handle_event, &handle_newsletter_event/3) {:cont, socket} end defp handle_newsletter_event("newsletter_subscribe", %{"email" => email}, socket) do if socket.assigns.newsletter_enabled do case Newsletter.subscribe(email, consent_text: "Newsletter signup on website", ip_hash: socket.assigns.newsletter_ip_hash ) do {:ok, _} -> {:halt, assign(socket, :newsletter_state, :submitted)} {:already_confirmed, _} -> {:halt, assign(socket, :newsletter_state, :submitted)} {:error, _} -> {:halt, assign(socket, :newsletter_state, :error)} end else {:halt, assign(socket, :newsletter_state, :disabled)} end end defp handle_newsletter_event(_event, _params, socket), do: {:cont, socket} # connect_info is only available during mount, so we hash and store it early defp hash_ip(socket) do case Phoenix.LiveView.get_connect_info(socket, :peer_data) do %{address: ip} -> daily_salt = Date.utc_today() |> Date.to_iso8601() :crypto.hash(:sha256, :inet.ntoa(ip) ++ [daily_salt]) |> Base.encode16(case: :lower) _ -> nil end end end