Convert social and federation profile from Jinja to SX rendering

Add primitives (replace, strip-tags, slice, csrf-token), convert all
social blueprint routes and federation profile to SX content builders,
delete 12 unused Jinja templates and social_lite layout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 17:43:47 +00:00
parent 0c9dbd6657
commit 0a81a2af01
18 changed files with 760 additions and 503 deletions

View File

@@ -57,6 +57,77 @@ def _is_aggregate(app_name: str) -> bool:
return app_name == "federation"
async def _render_profile_sx(actor, activities, total):
"""Render the federation actor profile page using SX."""
from markupsafe import escape
from shared.sx.page import get_template_context
from shared.sx.helpers import full_page_sx, oob_page_sx, sx_response
from shared.browser.app.utils.htmx import is_htmx_request
from shared.config import config
def _e(v):
s = str(v) if v else ""
return str(escape(s)).replace('"', '\\"')
username = _e(actor.preferred_username)
display_name = _e(actor.display_name or actor.preferred_username)
ap_domain = config().get("ap_domain", "rose-ash.com")
summary_el = ""
if actor.summary:
summary_el = f'(p :class "mt-2" "{_e(actor.summary)}")'
activity_items = []
for a in activities:
ts = ""
if a.published:
ts = a.published.strftime("%Y-%m-%d %H:%M")
obj_el = ""
if a.object_type:
obj_el = f'(span :class "text-sm text-stone-500" "{_e(a.object_type)}")'
activity_items.append(
f'(div :class "bg-white rounded-lg shadow p-4"'
f' (div :class "flex justify-between items-start"'
f' (span :class "font-medium" "{_e(a.activity_type)}")'
f' (span :class "text-sm text-stone-400" "{_e(ts)}"))'
f' {obj_el})')
if activities:
activities_el = ('(div :class "space-y-4" ' +
" ".join(activity_items) + ")")
else:
activities_el = '(p :class "text-stone-500" "No activities yet.")'
content = (
f'(div :id "main-panel"'
f' (div :class "py-8"'
f' (div :class "bg-white rounded-lg shadow p-6 mb-6"'
f' (h1 :class "text-2xl font-bold" "{display_name}")'
f' (p :class "text-stone-500" "@{username}@{_e(ap_domain)}")'
f' {summary_el})'
f' (h2 :class "text-xl font-bold mb-4" "Activities ({total})")'
f' {activities_el}))')
tctx = await get_template_context()
if is_htmx_request():
# Import federation layout for OOB headers
try:
from federation.sxc.pages import _social_oob
oob_headers = _social_oob(tctx)
except ImportError:
oob_headers = ""
return sx_response(oob_page_sx(oobs=oob_headers, content=content))
else:
try:
from federation.sxc.pages import _social_full
header_rows = _social_full(tctx)
except ImportError:
from shared.sx.helpers import root_header_sx
header_rows = root_header_sx(tctx)
return full_page_sx(tctx, header_rows=header_rows, content=content)
def create_activitypub_blueprint(app_name: str) -> Blueprint:
"""Return a Blueprint with AP endpoints for *app_name*."""
bp = Blueprint("activitypub", __name__)
@@ -272,16 +343,10 @@ def create_activitypub_blueprint(app_name: str) -> Blueprint:
# HTML: federation renders its own profile; other apps redirect there
if aggregate:
from quart import render_template
activities, total = await services.federation.get_outbox(
g._ap_s, username, page=1, per_page=20,
)
return await render_template(
"federation/profile.html",
actor=actor,
activities=activities,
total=total,
)
return await _render_profile_sx(actor, activities, total)
from quart import redirect
return redirect(f"https://{fed_domain}/users/{username}")