add logo to newsletter email header
All checks were successful
deploy / deploy (push) Successful in 1m4s

Uses the shop logo image if configured, falls back to the favicon
icon (served as PNG) alongside the shop name, or plain text if
neither is available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey 2026-02-28 23:44:03 +00:00
parent c71d08bb5c
commit aa008f83b2

View File

@ -121,9 +121,10 @@ defmodule Berrypod.Newsletter.Notifier do
end
# Wraps HTML content in a branded email template.
# Keeps it simple: single column, shop name header, optional unsubscribe footer.
# Keeps it simple: single column, logo/shop name header, optional unsubscribe footer.
@doc false
def wrap_html(shop_name, content, unsubscribe_url \\ nil) do
logo_html = build_logo_html(shop_name)
footer =
if unsubscribe_url do
"""
@ -167,7 +168,7 @@ defmodule Berrypod.Newsletter.Notifier do
<table role="presentation" class="email-container" width="560" cellpadding="0" cellspacing="0" style="max-width: 560px; width: 100%; background-color: #ffffff; border-radius: 8px;">
<tr>
<td class="email-header" style="padding: 24px 32px 16px; border-bottom: 1px solid #e5e7eb;">
<strong style="font-size: 18px; color: #111827;">#{esc(shop_name)}</strong>
#{logo_html}
</td>
</tr>
<tr>
@ -186,6 +187,45 @@ defmodule Berrypod.Newsletter.Notifier do
"""
end
# Builds the header logo/name HTML. Uses the shop logo if configured,
# falls back to the favicon (served as PNG), or plain text.
defp build_logo_html(shop_name) do
base_url = BerrypodWeb.Endpoint.url()
theme = Berrypod.Settings.get_theme_settings()
cond do
# Logo image configured and not SVG (email clients don't support SVG)
theme.logo_image_id && !is_svg_image?(theme.logo_image_id) ->
src = "#{base_url}/image_cache/#{theme.logo_image_id}.webp"
height = theme.logo_size || 36
~s(<img src="#{esc(src)}" alt="#{esc(shop_name)}" height="#{height}" style="height: #{height}px; width: auto; display: block;">)
# Favicon icon exists (served as PNG at /apple-touch-icon.png)
has_favicon_icon?() ->
src = "#{base_url}/apple-touch-icon.png"
~s(<img src="#{esc(src)}" alt="#{esc(shop_name)}" width="36" height="36" style="height: 36px; width: 36px; display: inline-block; vertical-align: middle; border-radius: 4px; margin-right: 8px;">) <>
~s(<strong style="font-size: 18px; color: #111827; vertical-align: middle;">#{esc(shop_name)}</strong>)
# No image — text only
true ->
~s(<strong style="font-size: 18px; color: #111827;">#{esc(shop_name)}</strong>)
end
end
defp is_svg_image?(image_id) do
case Berrypod.Repo.get(Berrypod.Media.Image, image_id) do
%{is_svg: true} -> true
_ -> false
end
end
defp has_favicon_icon? do
require Ecto.Query
Berrypod.Repo.exists?(Ecto.Query.from(i in Berrypod.Media.Image, where: i.image_type == "icon"))
end
# Turns bare URLs in escaped text into clickable links.
defp linkify(escaped_text) do
Regex.replace(~r/(https?:\/\/[^\s<]+)/, escaped_text, fn url, _ ->