defmodule BerrypodWeb.ThemeHook do @moduledoc """ LiveView on_mount hook for theme settings, CSS, and media assigns. Mounted in the public_shop live_session alongside CartHook. Eliminates the identical theme-loading boilerplate from every shop LiveView. ## Actions - `:mount_theme` — loads theme settings, CSS, and media assigns - `:require_site_live` — redirects unauthenticated visitors to /coming-soon when the shop is not live """ import Phoenix.Component, only: [assign: 3] alias Berrypod.{Products, Settings, Site, Media} alias Berrypod.Theme.{CSSCache, CSSGenerator} @default_header_nav [ %{"label" => "Home", "href" => "/", "slug" => "home"}, %{ "label" => "Shop", "href" => "/collections/all", "slug" => "collection", "active_slugs" => ["collection", "pdp"] }, %{"label" => "About", "href" => "/about", "slug" => "about"}, %{"label" => "Contact", "href" => "/contact", "slug" => "contact"} ] @default_footer_nav [ %{"label" => "Delivery & returns", "href" => "/delivery", "slug" => "delivery"}, %{"label" => "Privacy policy", "href" => "/privacy", "slug" => "privacy"}, %{"label" => "Terms of service", "href" => "/terms", "slug" => "terms"}, %{"label" => "Contact", "href" => "/contact", "slug" => "contact"} ] def default_header_nav, do: @default_header_nav def default_footer_nav, do: @default_footer_nav def on_mount(:mount_theme, _params, _session, socket) do theme_settings = Settings.get_theme_settings() generated_css = case CSSCache.get() do {:ok, css} -> css :miss -> css = CSSGenerator.generate(theme_settings, &BerrypodWeb.Endpoint.static_path/1) CSSCache.put(css) css end socket = socket |> assign(:theme_settings, theme_settings) |> assign(:site_name, Settings.site_name()) |> assign(:site_description, Settings.site_description()) |> assign(:generated_css, generated_css) |> assign(:logo_image, Media.get_logo()) |> assign(:header_image, Media.get_header()) |> assign(:categories, Products.list_categories()) |> assign(:mode, :shop) |> assign( :is_admin, !!(socket.assigns[:current_scope] && socket.assigns.current_scope.user) ) |> assign(:header_nav_items, load_header_nav()) |> assign(:footer_nav_items, load_footer_nav()) |> assign(:social_links, Site.social_links_for_shop()) |> assign(:announcement_text, Site.announcement_text()) |> assign(:announcement_link, Site.announcement_link()) |> assign(:announcement_style, Site.announcement_style()) {:cont, socket} end def on_mount(:require_site_live, _params, _session, socket) do cond do Settings.site_live?() -> {:cont, socket} # mount_current_scope runs first, so current_scope is already validated socket.assigns[:current_scope] && socket.assigns.current_scope.user -> {:cont, socket} true -> {:halt, Phoenix.LiveView.redirect(socket, to: "/coming-soon")} end end defp load_header_nav do items = Site.nav_items_for_shop("header") if items == [], do: @default_header_nav, else: add_active_slugs(items) end defp load_footer_nav do items = Site.nav_items_for_shop("footer") if items == [], do: @default_footer_nav, else: items end # Add active_slugs for Shop nav item to highlight on collection and pdp pages defp add_active_slugs(items) do Enum.map(items, fn item -> if item["slug"] == "collection" do Map.put(item, "active_slugs", ["collection", "pdp"]) else item end end) end end