Fix following past 48 entries and make the e621 page size configurable

Follow lists were paginated at the API default of 48, but the SPA treats
them as complete sets: the tag/pool toggles read their state from page one
(so the 49th follow looked unfollowed and its spinner waited for a page that
could never contain it) and the Followed page rendered only 48 cards while
showing that as the count. Both follow endpoints are now unpaginated — they
are per-user sets and still restricted to the caller's rows — and the three
consumers take plain arrays.

Post visibility: the old J621-Django online view fetched limit=320 (e621's
maximum) while ours hard-coded 48, and fetchPostsByIds capped id batches at
100. The Online browser now has a 'Posts per page' setting (48/100/200/320)
in its sidebar, mirrored in Account -> Browsing preferences, stored per user
as e621_per_page and also used for pool loading; the id-batch cap is raised
to 320.

Tests: follow list shape/isolation (4) and preference validation/merge (3)
added; the full backend suite is 46 green. Live-checked the array response
shape and the preference bounds (200 accepted, 500 rejected).
This commit is contained in:
2026-09-18 19:06:18 -05:00
parent d16a77907a
commit 1adb761c8d
16 changed files with 237 additions and 25 deletions
+9 -2
View File
@@ -36,10 +36,16 @@ class FollowedTagViewSet(
mixins.DestroyModelMixin,
viewsets.GenericViewSet,
):
"""Tags the current user follows."""
"""Tags the current user follows.
Deliberately unpaginated: the SPA treats this as the complete set (follow
toggles read their state from it and the Followed page lists every card),
so a 48-item page silently broke following past 48 entries.
"""
serializer_class = FollowedTagSerializer
permission_classes = [IsAuthenticated]
pagination_class = None
http_method_names = ["get", "post", "delete", "head", "options"]
def get_queryset(self):
@@ -112,10 +118,11 @@ class FollowedPoolViewSet(
mixins.DestroyModelMixin,
viewsets.GenericViewSet,
):
"""Pools the current user follows."""
"""Pools the current user follows (unpaginated, like the tag list)."""
serializer_class = FollowedPoolSerializer
permission_classes = [IsAuthenticated]
pagination_class = None
http_method_names = ["get", "post", "delete", "head", "options"]
def get_queryset(self):