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>
61 lines
1.9 KiB
Elixir
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
|