Fix page ordering (#535)
This commit is contained in:
parent
6e8149675c
commit
61830a9a9c
|
@ -216,57 +216,45 @@ class MastodonPaginator:
|
|||
max_id: str | None,
|
||||
since_id: str | None,
|
||||
limit: int | None,
|
||||
home: bool = False,
|
||||
) -> PaginationResult[TM]:
|
||||
limit = min(limit or self.default_limit, self.max_limit)
|
||||
filters = {}
|
||||
id_field = "id"
|
||||
reverse = False
|
||||
if home:
|
||||
# The home timeline interleaves Post IDs and PostInteraction IDs in an
|
||||
# annotated field called "subject_id".
|
||||
id_field = "subject_id"
|
||||
queryset = queryset.annotate(
|
||||
subject_id=Case(
|
||||
When(type=TimelineEvent.Types.post, then=F("subject_post_id")),
|
||||
default=F("subject_post_interaction"),
|
||||
)
|
||||
)
|
||||
|
||||
# These "does not start with interaction" checks can be removed after a
|
||||
# couple months, when clients have flushed them out.
|
||||
if max_id and not max_id.startswith("interaction"):
|
||||
queryset = queryset.filter(id__lt=max_id)
|
||||
filters[f"{id_field}__lt"] = max_id
|
||||
if since_id and not since_id.startswith("interaction"):
|
||||
queryset = queryset.filter(id__gt=since_id)
|
||||
filters[f"{id_field}__gt"] = since_id
|
||||
if min_id and not min_id.startswith("interaction"):
|
||||
# Min ID requires items _immediately_ newer than specified, so we
|
||||
# invert the ordering to accommodate
|
||||
queryset = queryset.filter(id__gt=min_id).order_by("id")
|
||||
else:
|
||||
queryset = queryset.order_by("-id")
|
||||
filters[f"{id_field}__gt"] = min_id
|
||||
reverse = True
|
||||
|
||||
# Default is to order by ID descending (newest first), except for min_id
|
||||
# queries, which should order by ID for limiting, then reverse the results to be
|
||||
# consistent. The clearest explanation of this I've found so far is this:
|
||||
# https://mastodon.social/@Gargron/100846335353411164
|
||||
ordering = id_field if reverse else f"-{id_field}"
|
||||
results = list(queryset.filter(**filters).order_by(ordering)[:limit])
|
||||
if reverse:
|
||||
results.reverse()
|
||||
|
||||
limit = min(limit or self.default_limit, self.max_limit)
|
||||
return PaginationResult(
|
||||
results=list(queryset[:limit]),
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
def paginate_home(
|
||||
self,
|
||||
queryset,
|
||||
min_id: str | None,
|
||||
max_id: str | None,
|
||||
since_id: str | None,
|
||||
limit: int | None,
|
||||
) -> PaginationResult:
|
||||
"""
|
||||
The home timeline requires special handling where we mix Posts and
|
||||
PostInteractions together.
|
||||
"""
|
||||
queryset = queryset.annotate(
|
||||
event_id=Case(
|
||||
When(type=TimelineEvent.Types.post, then=F("subject_post_id")),
|
||||
default=F("subject_post_interaction"),
|
||||
)
|
||||
)
|
||||
if max_id and not max_id.startswith("interaction"):
|
||||
queryset = queryset.filter(event_id__lt=max_id)
|
||||
if since_id and not since_id.startswith("interaction"):
|
||||
queryset = queryset.filter(event_id__gt=since_id)
|
||||
if min_id and not min_id.startswith("interaction"):
|
||||
# Min ID requires items _immediately_ newer than specified, so we
|
||||
# invert the ordering to accommodate
|
||||
queryset = queryset.filter(event_id__gt=min_id).order_by("event_id")
|
||||
else:
|
||||
queryset = queryset.order_by("-event_id")
|
||||
|
||||
limit = min(limit or self.default_limit, self.max_limit)
|
||||
return PaginationResult(
|
||||
results=list(queryset[:limit]),
|
||||
results=results,
|
||||
limit=limit,
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.http import HttpRequest
|
||||
from hatchway import ApiError, ApiResponse, api_view
|
||||
|
||||
from activities.models import Post
|
||||
from activities.models import Post, TimelineEvent
|
||||
from activities.services import TimelineService
|
||||
from api import schemas
|
||||
from api.decorators import scope_required
|
||||
|
@ -34,12 +34,13 @@ def home(
|
|||
"subject_post_interaction__post__mentions__domain",
|
||||
"subject_post_interaction__post__author__posts",
|
||||
)
|
||||
pager = paginator.paginate_home(
|
||||
pager: PaginationResult[TimelineEvent] = paginator.paginate(
|
||||
queryset,
|
||||
min_id=min_id,
|
||||
max_id=max_id,
|
||||
since_id=since_id,
|
||||
limit=limit,
|
||||
home=True,
|
||||
)
|
||||
return PaginatingApiResponse(
|
||||
schemas.Status.map_from_timeline_event(pager.results, request.identity),
|
||||
|
|
Loading…
Reference in New Issue