from __future__ import annotations from quart import ( g, Blueprint, abort, render_template, render_template_string, make_response, current_app, ) from shared.config import config from .services.nav import category_context, get_nav from .services.blacklist.category import is_category_blocked from .services import ( _hx_fragment_request, _productInfo, _vary, _current_url_without_page, ) from shared.browser.app.redis_cacher import cache_page from shared.browser.app.utils.htmx import is_htmx_request def register(): browse_bp = Blueprint("browse", __name__) from .. import register_product browse_bp.register_blueprint( register_product(), ) @browse_bp.get("/") @cache_page(tag="browse") async def home(): """ Market landing page. Uses the post data hydrated by the app-level before_request (g.post_data). """ p_data = getattr(g, "post_data", None) or {} # Determine which template to use based on request type if not is_htmx_request(): # Normal browser request: full page with layout html = await render_template("_types/market/index.html", **p_data) else: # HTMX request: main panel + OOB elements html = await render_template("_types/market/_oob_elements.html", **p_data) return await make_response(html) @browse_bp.get("/all/") @cache_page(tag="browse") async def browse_all(): """ Browse all products across all categories. Renders full page or just product cards (HTMX pagination fragment). """ market = getattr(g, "market", None) market_id = market.id if market else None nav = await get_nav(g.s, market_id=market_id) ctx = { "category_label": "All Products", "top_slug": "all", "sub_slug": None, } product_info = await _productInfo() full_context = {**product_info, **ctx} # Determine which template to use based on request type and pagination if not is_htmx_request(): # Normal browser request: full page with layout html = await render_template("_types/browse/index.html", **full_context) elif product_info["page"] > 1: # HTMX pagination: just product cards + sentinel html = await render_template("_types/browse/_product_cards.html", **product_info) else: # HTMX navigation (page 1): main panel + OOB elements html = await render_template("_types/browse/_oob_elements.html", **full_context) resp = await make_response(html) resp.headers["Hx-Push-Url"] = _current_url_without_page() return _vary(resp) @browse_bp.get("//") @cache_page(tag="browse") async def browse_top(top_slug: str): """ Browse by top-level category (e.g. /fruit). 404 if category not in allowed list or is blocked. """ REVERSE_CATEGORY = {v: k for k, v in config()["categories"]["allow"].items()} if top_slug not in REVERSE_CATEGORY: abort(404) if is_category_blocked(top_slug): abort(404) market = getattr(g, "market", None) market_id = market.id if market else None nav = await get_nav(g.s, market_id=market_id) ctx = category_context(top_slug, None, nav) product_info = await _productInfo(top_slug) full_context = {**product_info, **ctx} # Determine which template to use based on request type and pagination if not is_htmx_request(): # Normal browser request: full page with layout html = await render_template("_types/browse/index.html", **full_context) elif product_info["page"] > 1: # HTMX pagination: just product cards + sentinel html = await render_template("_types/browse/_product_cards.html", **product_info) else: html = await render_template("_types/browse/_oob_elements.html", **full_context) resp = await make_response(html) resp.headers["Hx-Push-Url"] = _current_url_without_page() return _vary(resp) @browse_bp.get("///") @cache_page(tag="browse") async def browse_sub(top_slug: str, sub_slug: str): """ Browse by subcategory (e.g. /fruit/citrus). 404 if blocked or unknown. """ REVERSE_CATEGORY = {v: k for k, v in config()["categories"]["allow"].items()} if top_slug not in REVERSE_CATEGORY: abort(404) if is_category_blocked(top_slug, sub_slug): abort(404) market = getattr(g, "market", None) market_id = market.id if market else None nav = await get_nav(g.s, market_id=market_id) ctx = category_context(top_slug, sub_slug, nav) product_info = await _productInfo(top_slug, sub_slug) full_context = {**product_info, **ctx} # Determine which template to use based on request type and pagination if not is_htmx_request(): # Normal browser request: full page with layout html = await render_template("_types/browse/index.html", **full_context) elif product_info["page"] > 1: # HTMX pagination: just product cards + sentinel html = await render_template("_types/browse/_product_cards.html", **product_info) else: # HTMX navigation (page 1): main panel + OOB elements html = await render_template("_types/browse/_oob_elements.html", **full_context) resp = await make_response(html) resp.headers["Hx-Push-Url"] = _current_url_without_page() return _vary(resp) return browse_bp