Move blog index rendering from Python to .sx composition defcomps

BlogPageService.index_data() assembles all data (cards, filters, actions)
and 7 new .sx defcomps handle rendering: main content, aside, filter,
actions, tag groups filter, authors filter, and sentinel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 09:14:23 +00:00
parent 984e2ebed0
commit e75c8d16d1
3 changed files with 406 additions and 71 deletions

View File

@@ -133,85 +133,40 @@ def register(url_prefix, title):
@blogs_bp.get("/index/")
async def index():
"""Blog listing — moved from / to /index."""
q = decode()
content_type = request.args.get("type", "posts")
if content_type == "pages":
data = await pages_data(g.s, q.page, q.search)
context = {
**data,
"content_type": "pages",
"search": q.search,
"selected_tags": (),
"selected_authors": (),
"selected_groups": (),
"sort": None,
"view": None,
"drafts": None,
"draft_count": 0,
"tags": [],
"authors": [],
"tag_groups": [],
"posts": data.get("pages", []),
}
from shared.sx.page import get_template_context
from sx.sx_components import render_blog_page, render_blog_oob, render_blog_page_cards
tctx = await get_template_context()
tctx.update(context)
if not is_htmx_request():
html = await render_blog_page(tctx)
return await make_response(html)
elif q.page > 1:
sx_src = await render_blog_page_cards(tctx)
return sx_response(sx_src)
else:
sx_src = await render_blog_oob(tctx)
return sx_response(sx_src)
# Default: posts listing
# Drafts filter requires login; ignore if not logged in
show_drafts = bool(q.drafts and g.user)
is_admin = bool((g.get("rights") or {}).get("admin"))
drafts_user_id = None if (not show_drafts or is_admin) else g.user.id
# For the draft count badge: admin sees all drafts, non-admin sees own
count_drafts_uid = None if (g.user and is_admin) else (g.user.id if g.user else False)
data = await posts_data(
g.s, q.page, q.search, q.sort, q.selected_tags, q.selected_authors, q.liked,
drafts=show_drafts, drafts_user_id=drafts_user_id,
count_drafts_for_user_id=count_drafts_uid,
selected_groups=q.selected_groups,
from shared.services.registry import services
from shared.sx.helpers import (
render_to_sx, root_header_sx, full_page_sx, oob_page_sx,
)
from sx.sx_components import _blog_header_sx, _oob_header_sx
from shared.sx.parser import SxExpr
context = {
**data,
"content_type": "posts",
"selected_tags": q.selected_tags,
"selected_authors": q.selected_authors,
"selected_groups": q.selected_groups,
"sort": q.sort,
"search": q.search,
"view": q.view,
"drafts": q.drafts if show_drafts else None,
}
data = await services.get("blog_page").index_data(g.s)
# Render content, aside, and filter via .sx defcomps
content = await render_to_sx("blog-index-main-content", **data)
aside = await render_to_sx("blog-index-aside-content", **data)
filter_sx = await render_to_sx("blog-index-filter-content", **data)
from shared.sx.page import get_template_context
from sx.sx_components import render_blog_page, render_blog_oob, render_blog_cards
tctx = await get_template_context()
tctx.update(context)
if not is_htmx_request():
html = await render_blog_page(tctx)
root_hdr = await root_header_sx(tctx)
blog_hdr = await _blog_header_sx(tctx)
header_rows = "(<> " + root_hdr + " " + blog_hdr + ")"
html = await full_page_sx(tctx, header_rows=header_rows,
content=content, aside=aside, filter=filter_sx)
return await make_response(html)
elif q.page > 1:
# Sx wire format — client renders blog cards
sx_src = await render_blog_cards(tctx)
return sx_response(sx_src)
elif data.get("page", 1) > 1:
# Pagination — return just the cards
return sx_response(content)
else:
sx_src = await render_blog_oob(tctx)
root_hdr = await root_header_sx(tctx)
blog_hdr = await _blog_header_sx(tctx)
rows = "(<> " + root_hdr + " " + blog_hdr + ")"
header_oob = await _oob_header_sx("root-header-child", "blog-header-child", rows)
sx_src = await oob_page_sx(oobs=header_oob, content=content,
aside=aside, filter=filter_sx)
return sx_response(sx_src)
@blogs_bp.post("/new/")