berrypod/lib/berrypod_web/newsletter_hook.ex
jamey ad2e6d1e6d add newsletter and email campaigns
Subscribers with double opt-in confirmation, campaign composer with
draft/scheduled/sent lifecycle, admin dashboard with overview stats,
CSV export, and shop signup form wired into page builder blocks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 23:25:28 +00:00

61 lines
1.9 KiB
Elixir

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