diff --git a/api/pagination.py b/api/pagination.py index 7b12d87..6269cff 100644 --- a/api/pagination.py +++ b/api/pagination.py @@ -4,6 +4,8 @@ import urllib.parse from django.db import models from django.http import HttpRequest +from activities.models import PostInteraction + @dataclasses.dataclass class PaginationResult: @@ -68,7 +70,7 @@ class PaginationResult: class MastodonPaginator: """ - Paginates in the Mastodon style (max_id, min_id, etc) + Paginates in the Mastodon style (max_id, min_id, etc). """ def __init__( @@ -83,6 +85,22 @@ class MastodonPaginator: self.default_limit = default_limit self.max_limit = max_limit + def get_anchor(self, anchor_id: str): + """ + Gets an anchor object by ID. + It's possible that the anchor object might be an interaction, in which + case we recurse down to its post. + """ + if anchor_id.startswith("interaction-"): + try: + return PostInteraction.objects.get(pk=anchor_id[12:]) + except PostInteraction.DoesNotExist: + return PaginationResult.empty() + try: + return self.anchor_model.objects.get(pk=anchor_id) + except self.anchor_model.DoesNotExist: + return PaginationResult.empty() + def paginate( self, queryset, @@ -92,19 +110,13 @@ class MastodonPaginator: limit: int | None, ) -> PaginationResult: if max_id: - try: - anchor = self.anchor_model.objects.get(pk=max_id) - except self.anchor_model.DoesNotExist: - return PaginationResult.empty() + anchor = self.get_anchor(max_id) queryset = queryset.filter( **{self.sort_attribute + "__lt": getattr(anchor, self.sort_attribute)} ) if since_id: - try: - anchor = self.anchor_model.objects.get(pk=since_id) - except self.anchor_model.DoesNotExist: - return PaginationResult.empty() + anchor = self.get_anchor(since_id) queryset = queryset.filter( **{self.sort_attribute + "__gt": getattr(anchor, self.sort_attribute)} ) @@ -112,10 +124,7 @@ class MastodonPaginator: if min_id: # Min ID requires items _immediately_ newer than specified, so we # invert the ordering to accommodate - try: - anchor = self.anchor_model.objects.get(pk=min_id) - except self.anchor_model.DoesNotExist: - return PaginationResult.empty() + anchor = self.get_anchor(min_id) queryset = queryset.filter( **{self.sort_attribute + "__gt": getattr(anchor, self.sort_attribute)} ).order_by(self.sort_attribute) diff --git a/api/views/accounts.py b/api/views/accounts.py index f3328b2..0794470 100644 --- a/api/views/accounts.py +++ b/api/views/accounts.py @@ -122,7 +122,7 @@ def account_statuses( if tagged: queryset = queryset.tagged_with(tagged) - paginator = MastodonPaginator(Post) + paginator = MastodonPaginator(Post, sort_attribute="published") pager = paginator.paginate( queryset, min_id=min_id, diff --git a/api/views/timelines.py b/api/views/timelines.py index e767fde..8712044 100644 --- a/api/views/timelines.py +++ b/api/views/timelines.py @@ -19,7 +19,7 @@ def home( min_id: str | None = None, limit: int = 20, ): - paginator = MastodonPaginator(Post) + paginator = MastodonPaginator(Post, sort_attribute="published") queryset = TimelineService(request.identity).home() pager = paginator.paginate( queryset, @@ -64,7 +64,7 @@ def public( queryset = queryset.filter(local=False) if only_media: queryset = queryset.filter(attachments__id__isnull=True) - paginator = MastodonPaginator(Post) + paginator = MastodonPaginator(Post, sort_attribute="published") pager = paginator.paginate( queryset, min_id=min_id, @@ -105,7 +105,7 @@ def hashtag( queryset = queryset.filter(local=True) if only_media: queryset = queryset.filter(attachments__id__isnull=True) - paginator = MastodonPaginator(Post) + paginator = MastodonPaginator(Post, sort_attribute="published") pager = paginator.paginate( queryset, min_id=min_id,