feat: add generic paginate helper and cursor-based load-more across feed, gists, news, and projects

Extract shared PAGE_SIZE constant and paginate() function into database.py, replacing inline cursor logic in feed, gists, news, and projects routers. Introduce `_load_more.html` partial template to unify "Load More" button rendering. Update all affected route handlers to accept `before` query parameter and return `next_cursor` for seamless infinite scroll. Adjust projects count display to use filtered length instead of total. Add test helper `_seed_posts` for pagination coverage.
This commit is contained in:
2026-06-05 03:36:18 +00:00
parent 02b07dce8e
commit 9f4bead896
16 changed files with 256 additions and 55 deletions
+11 -10
View File
@@ -3,7 +3,7 @@ from typing import Annotated
from fastapi import APIRouter, Request, Form
from devplacepy.models import GistForm, GistEditForm
from fastapi.responses import HTMLResponse, RedirectResponse
from devplacepy.database import get_table, get_users_by_uids, get_gist_languages
from devplacepy.database import get_table, get_users_by_uids, get_gist_languages, paginate
from devplacepy.content import load_detail, edit_content_item, delete_content_item, enrich_items, create_content_item, detail_context
from devplacepy.templating import templates
from devplacepy.utils import get_current_user, require_user, not_found, XP_GIST
@@ -23,7 +23,7 @@ LANGUAGES = [
]
def get_gists_list(user_uid=None, language=None, viewer=None):
def get_gists_list(user_uid=None, language=None, before=None, viewer=None):
gists_table = get_table("gists")
filters = {}
if user_uid:
@@ -31,20 +31,20 @@ def get_gists_list(user_uid=None, language=None, viewer=None):
if language:
filters["language"] = language
all_gists = list(gists_table.find(**filters, order_by=["-created_at"]))
total = gists_table.count(**filters)
gists, next_cursor = paginate(gists_table, before=before, **filters)
if not all_gists:
return []
if not gists:
return [], next_cursor, total
users_map = get_users_by_uids([g["user_uid"] for g in all_gists])
return enrich_items(all_gists, "gist", users_map, user=viewer)
users_map = get_users_by_uids([g["user_uid"] for g in gists])
return enrich_items(gists, "gist", users_map, user=viewer), next_cursor, total
@router.get("", response_class=HTMLResponse)
async def gists_page(request: Request, language: str = None, user_uid: str = None):
async def gists_page(request: Request, language: str = None, user_uid: str = None, before: str = None):
user = get_current_user(request)
gists_data = get_gists_list(user_uid, language, viewer=user)
total_count = len(gists_data)
gists_data, next_cursor, total_count = get_gists_list(user_uid, language, before, viewer=user)
seo_ctx = list_page_seo(
request,
title="Gists",
@@ -60,6 +60,7 @@ async def gists_page(request: Request, language: str = None, user_uid: str = Non
"user": user,
"gists": gists_data,
"total_count": total_count,
"next_cursor": next_cursor,
"current_language": language,
"languages": LANGUAGES,
"gist_language_codes": get_gist_languages(),