site_name and site_description are shop identity, not theme concerns. They now live in the Settings table as first-class settings with their own assigns (@site_name, @site_description) piped through hooks and plugs. The setup wizard writes site_name on account creation, and the theme editor reads/writes via Settings.put_setting. Removed the "configure your shop" checklist item since currency/country aren't built yet. Also adds shop name field to setup wizard step 1. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
106 lines
2.8 KiB
Elixir
106 lines
2.8 KiB
Elixir
defmodule BerrypodWeb.FaviconController do
|
|
use BerrypodWeb, :controller
|
|
|
|
alias Berrypod.Media
|
|
alias Berrypod.Settings
|
|
|
|
@one_day 86_400
|
|
|
|
def favicon_svg(conn, _params) do
|
|
case Media.get_favicon_variants() do
|
|
%{svg: svg} when is_binary(svg) ->
|
|
conn
|
|
|> maybe_not_modified("svg")
|
|
|> serve("image/svg+xml", svg)
|
|
|
|
_ ->
|
|
send_resp(conn, 404, "")
|
|
end
|
|
end
|
|
|
|
def favicon_32(conn, _params), do: serve_variant(conn, :png_32)
|
|
def apple_touch_icon(conn, _params), do: serve_variant(conn, :png_180)
|
|
def icon_192(conn, _params), do: serve_variant(conn, :png_192)
|
|
def icon_512(conn, _params), do: serve_variant(conn, :png_512)
|
|
|
|
def webmanifest(conn, _params) do
|
|
settings = Settings.get_theme_settings()
|
|
site_name = Settings.site_name()
|
|
|
|
short_name =
|
|
case settings.favicon_short_name do
|
|
name when is_binary(name) and name != "" -> name
|
|
_ -> String.slice(site_name, 0, 12)
|
|
end
|
|
|
|
manifest = %{
|
|
name: site_name,
|
|
short_name: short_name,
|
|
theme_color: settings.accent_color || "#000000",
|
|
background_color: settings.icon_background_color || "#ffffff",
|
|
display: "minimal-ui",
|
|
start_url: "/",
|
|
icons: [
|
|
%{src: "/icon-192.png", sizes: "192x192", type: "image/png"},
|
|
%{src: "/icon-512.png", sizes: "512x512", type: "image/png", purpose: "maskable"}
|
|
]
|
|
}
|
|
|
|
conn
|
|
|> put_resp_content_type("application/manifest+json")
|
|
|> put_resp_header("cache-control", "public, max-age=#{@one_day}")
|
|
|> json(manifest)
|
|
end
|
|
|
|
defp serve_variant(conn, field) do
|
|
case Media.get_favicon_variants() do
|
|
nil ->
|
|
send_resp(conn, 404, "")
|
|
|
|
variants ->
|
|
case Map.get(variants, field) do
|
|
data when is_binary(data) ->
|
|
conn
|
|
|> maybe_not_modified(variants)
|
|
|> serve("image/png", data)
|
|
|
|
_ ->
|
|
send_resp(conn, 404, "")
|
|
end
|
|
end
|
|
end
|
|
|
|
defp maybe_not_modified(%{halted: true} = conn, _), do: conn
|
|
|
|
defp maybe_not_modified(conn, variants) do
|
|
etag = build_etag(variants)
|
|
|
|
case get_req_header(conn, "if-none-match") do
|
|
[^etag] ->
|
|
conn
|
|
|> put_resp_header("cache-control", "public, max-age=#{@one_day}")
|
|
|> put_resp_header("etag", etag)
|
|
|> send_resp(304, "")
|
|
|> halt()
|
|
|
|
_ ->
|
|
conn
|
|
|> put_resp_header("cache-control", "public, max-age=#{@one_day}")
|
|
|> put_resp_header("etag", etag)
|
|
end
|
|
end
|
|
|
|
defp serve(%{halted: true} = conn, _content_type, _data), do: conn
|
|
|
|
defp serve(conn, content_type, data) do
|
|
conn
|
|
|> put_resp_content_type(content_type)
|
|
|> send_resp(200, data)
|
|
end
|
|
|
|
defp build_etag(%{generated_at: %DateTime{} = dt}),
|
|
do: ~s("fav-#{DateTime.to_unix(dt)}")
|
|
|
|
defp build_etag(_), do: ~s("fav-0")
|
|
end
|