""" Base template context shared by all apps. This module no longer imports cart or menu_items services directly. Each app provides its own context_fn that calls this base and adds app-specific variables (cart data, menu_items, etc.). """ from __future__ import annotations from datetime import datetime from quart import request, g, current_app from shared.config import config from shared.utils import host_url from shared.browser.app.utils import current_route_relative_path from shared.infrastructure.urls import blog_url, market_url, cart_url, events_url def _qs_filter_fn(): """Build a qs_filter(dict) wrapper for sexp components, or None. Sexp components call ``qs_fn({"page": 2})``, ``qs_fn({"sort": "az"})``, ``qs_fn({"labels": ["organic", "local"]})``, etc. Simple keys (page, sort, search, liked, clear_filters) are forwarded to ``makeqs(**kwargs)``. List-valued keys (labels, stickers, brands) represent *replacement* sets, so we rebuild the querystring from the current base with those overridden. """ factory = getattr(g, "makeqs_factory", None) if not factory: return None makeqs = factory() def _qs(d: dict) -> str: from shared.browser.app.filters.qs_base import build_qs # Collect list-valued overrides list_overrides = {} for plural, singular in (("labels", "label"), ("stickers", "sticker"), ("brands", "brand")): if plural in d: list_overrides[singular] = list(d[plural] or []) simple = {k: v for k, v in d.items() if k in ("page", "sort", "search", "liked", "clear_filters")} if not list_overrides: return makeqs(**simple) # For list overrides: get the base qs, parse out the overridden keys, # then rebuild with the new values. base_qs = makeqs(**simple) from urllib.parse import parse_qsl, urlencode params = [(k, v) for k, v in parse_qsl(base_qs.lstrip("?")) if k not in list_overrides] for singular, vals in list_overrides.items(): for v in vals: params.append((singular, v)) return ("?" + urlencode(params)) if params else "" return _qs async def base_context() -> dict: """ Common template variables available in every app. Does NOT include cart, calendar_cart_entries, total, calendar_total, or menu_items — those are added by each app's context_fn. """ is_htmx = request.headers.get("HX-Request") == "true" search = request.headers.get("X-Search", "") zap_filter = is_htmx and search == "" def base_url(): return host_url() hx_select = "#main-panel" hx_select_search = ( hx_select + ", #search-mobile, #search-count-mobile, #search-desktop, #search-count-desktop, #menu-items-nav-wrapper" ) return { "is_htmx": is_htmx, "request": request, "now": datetime.now(), "current_local_href": current_route_relative_path(), "config": config(), "asset_url": current_app.jinja_env.globals.get("asset_url", lambda p: ""), "sort_options": [ ("az", "A\u2013Z", "order/a-z.svg"), ("za", "Z\u2013A", "order/z-a.svg"), ("price-asc", "\u00a3 low\u2192high", "order/l-h.svg"), ("price-desc", "\u00a3 high\u2192low", "order/h-l.svg"), ], "zap_filter": zap_filter, "qs_filter": _qs_filter_fn(), "print": print, "base_url": base_url, "app_label": current_app.name, "base_title": config()["title"], "hx_select": hx_select, "hx_select_search": hx_select_search, "blog_url": blog_url, "market_url": market_url, "cart_url": cart_url, "events_url": events_url, "rights": getattr(g, "rights", {}), }