Show current subdomain name (blog, cart, events, etc.) next to the site title in the root header row. Remove the redundant second "cart" menu row from cart overview and checkout error pages. Add dev-mode hot-reload for sexp templates: track file mtimes and re-read changed files per-request when RELOAD=true, so .sexp edits are picked up without restarting services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
106 lines
3.5 KiB
Python
106 lines
3.5 KiB
Python
"""
|
|
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
|
|
|
|
|
|
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,
|
|
}
|