From aa008f83b2619ee1039930e3af3e15abae30f6bd Mon Sep 17 00:00:00 2001 From: jamey Date: Sat, 28 Feb 2026 23:44:03 +0000 Subject: [PATCH] add logo to newsletter email header 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 --- lib/berrypod/newsletter/notifier.ex | 44 +++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/lib/berrypod/newsletter/notifier.ex b/lib/berrypod/newsletter/notifier.ex index 73a19ad..3a82229 100644 --- a/lib/berrypod/newsletter/notifier.ex +++ b/lib/berrypod/newsletter/notifier.ex @@ -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 @@ -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(#{esc(shop_name)}) + + # Favicon icon exists (served as PNG at /apple-touch-icon.png) + has_favicon_icon?() -> + src = "#{base_url}/apple-touch-icon.png" + + ~s(#{esc(shop_name)}) <> + ~s(#{esc(shop_name)}) + + # No image — text only + true -> + ~s(#{esc(shop_name)}) + 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, _ ->