diff options
| author | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-06 15:25:36 -0700 |
|---|---|---|
| committer | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-06 15:25:36 -0700 |
| commit | 1bdf4ca8c0f51718124ffe5247ac133973d4f251 (patch) | |
| tree | 2904145bdbc5d2a2164cd3cb6c95346e48afd4a5 /routers/films.py | |
| parent | 051775337251b0c7036959901eacb58471100862 (diff) | |
Add authentication, public profile, and infinite scroll
- Implement session-based auth with argon2 password hashing
- Add login form and logout button in nav
- Create public /tyler profile page with curated stats
- Implement infinite scroll for film lists (load 20 at a time)
- Add lazy loading for poster images
- Fix profile page CSS to use dark theme variables
- Use consistent star character (✦) across all pages
- Add /films/partial endpoint for pagination
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Diffstat (limited to 'routers/films.py')
| -rw-r--r-- | routers/films.py | 59 |
1 files changed, 53 insertions, 6 deletions
diff --git a/routers/films.py b/routers/films.py index fc087b1..4bb7cc9 100644 --- a/routers/films.py +++ b/routers/films.py @@ -176,6 +176,17 @@ def _notice_context( } +def _get_shelf_query(db: Session, shelf: str): + """Get a query for films on a shelf, ordered appropriately.""" + query = db.query(Film).filter(Film.shelf == shelf) + if shelf == "diary": + return query.order_by(Film.date_watched.desc(), Film.created_at.desc(), Film.id.desc()) + elif shelf == "queue": + return query.order_by(Film.created_at.desc(), Film.id.desc()) + else: # abandoned + return query.order_by(Film.updated_at.desc(), Film.created_at.desc(), Film.id.desc()) + + def _group_films_by_month(films: list[Film]) -> list[dict]: def month_key(film: Film) -> str: return film.date_watched.strftime("%B %Y") if film.date_watched else "Unknown" @@ -192,15 +203,14 @@ def _render_shelf( db: Session, notices: dict, ): - query = db.query(Film).filter(Film.shelf == shelf) + query = _get_shelf_query(db, shelf) + total_films = query.count() + films = query.limit(20).all() + has_more = total_films > 20 + if shelf == "diary": - films = query.order_by(Film.date_watched.desc(), Film.created_at.desc(), Film.id.desc()).all() grouped_films = _group_films_by_month(films) - elif shelf == "queue": - films = query.order_by(Film.created_at.desc(), Film.id.desc()).all() - grouped_films = None else: - films = query.order_by(Film.updated_at.desc(), Film.created_at.desc(), Film.id.desc()).all() grouped_films = None return templates.TemplateResponse( @@ -212,6 +222,8 @@ def _render_shelf( "grouped_films": grouped_films, "active_shelf": shelf, "shelf_meta": SHELF_META[shelf], + "total_films": total_films, + "has_more": has_more, **notices, }, ) @@ -361,6 +373,41 @@ async def _film_tmdb_context(film: Film) -> dict | None: return context +@router.get("/films/partial") +def get_films_partial( + request: Request, + shelf: str = Query("diary"), + offset: int = Query(0), + limit: int = Query(20), + db: Session = Depends(get_db), +): + """Returns partial HTML for pagination. Used by infinite scroll.""" + if shelf not in ALLOWED_SHELVES: + raise HTTPException(status_code=400, detail="Invalid shelf.") + + query = _get_shelf_query(db, shelf) + total = query.count() + films = query.offset(offset).limit(limit).all() + has_more = (offset + limit) < total + + if shelf == "diary": + grouped_films = _group_films_by_month(films) + else: + grouped_films = None + + from fastapi.responses import HTMLResponse + html = templates.get_template("_feed_partial.html").render( + request=request, + films=films, + grouped_films=grouped_films, + active_shelf=shelf, + ) + + response = HTMLResponse(html) + response.headers["X-Has-More"] = "true" if has_more else "false" + return response + + @router.get("/films/{film_id}") async def film_detail(film_id: int, request: Request, db: Session = Depends(get_db)): film = _get_film_or_404(db, film_id) |
