Accept interactions as pagination anchors

Fixes #316
This commit is contained in:
Andrew Godwin 2022-12-29 14:00:37 -07:00
parent eb7297d743
commit 4d3b76612b
3 changed files with 26 additions and 17 deletions

View File

@ -4,6 +4,8 @@ import urllib.parse
from django.db import models from django.db import models
from django.http import HttpRequest from django.http import HttpRequest
from activities.models import PostInteraction
@dataclasses.dataclass @dataclasses.dataclass
class PaginationResult: class PaginationResult:
@ -68,7 +70,7 @@ class PaginationResult:
class MastodonPaginator: class MastodonPaginator:
""" """
Paginates in the Mastodon style (max_id, min_id, etc) Paginates in the Mastodon style (max_id, min_id, etc).
""" """
def __init__( def __init__(
@ -83,6 +85,22 @@ class MastodonPaginator:
self.default_limit = default_limit self.default_limit = default_limit
self.max_limit = max_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( def paginate(
self, self,
queryset, queryset,
@ -92,19 +110,13 @@ class MastodonPaginator:
limit: int | None, limit: int | None,
) -> PaginationResult: ) -> PaginationResult:
if max_id: if max_id:
try: anchor = self.get_anchor(max_id)
anchor = self.anchor_model.objects.get(pk=max_id)
except self.anchor_model.DoesNotExist:
return PaginationResult.empty()
queryset = queryset.filter( queryset = queryset.filter(
**{self.sort_attribute + "__lt": getattr(anchor, self.sort_attribute)} **{self.sort_attribute + "__lt": getattr(anchor, self.sort_attribute)}
) )
if since_id: if since_id:
try: anchor = self.get_anchor(since_id)
anchor = self.anchor_model.objects.get(pk=since_id)
except self.anchor_model.DoesNotExist:
return PaginationResult.empty()
queryset = queryset.filter( queryset = queryset.filter(
**{self.sort_attribute + "__gt": getattr(anchor, self.sort_attribute)} **{self.sort_attribute + "__gt": getattr(anchor, self.sort_attribute)}
) )
@ -112,10 +124,7 @@ class MastodonPaginator:
if min_id: if min_id:
# Min ID requires items _immediately_ newer than specified, so we # Min ID requires items _immediately_ newer than specified, so we
# invert the ordering to accommodate # invert the ordering to accommodate
try: anchor = self.get_anchor(min_id)
anchor = self.anchor_model.objects.get(pk=min_id)
except self.anchor_model.DoesNotExist:
return PaginationResult.empty()
queryset = queryset.filter( queryset = queryset.filter(
**{self.sort_attribute + "__gt": getattr(anchor, self.sort_attribute)} **{self.sort_attribute + "__gt": getattr(anchor, self.sort_attribute)}
).order_by(self.sort_attribute) ).order_by(self.sort_attribute)

View File

@ -122,7 +122,7 @@ def account_statuses(
if tagged: if tagged:
queryset = queryset.tagged_with(tagged) queryset = queryset.tagged_with(tagged)
paginator = MastodonPaginator(Post) paginator = MastodonPaginator(Post, sort_attribute="published")
pager = paginator.paginate( pager = paginator.paginate(
queryset, queryset,
min_id=min_id, min_id=min_id,

View File

@ -19,7 +19,7 @@ def home(
min_id: str | None = None, min_id: str | None = None,
limit: int = 20, limit: int = 20,
): ):
paginator = MastodonPaginator(Post) paginator = MastodonPaginator(Post, sort_attribute="published")
queryset = TimelineService(request.identity).home() queryset = TimelineService(request.identity).home()
pager = paginator.paginate( pager = paginator.paginate(
queryset, queryset,
@ -64,7 +64,7 @@ def public(
queryset = queryset.filter(local=False) queryset = queryset.filter(local=False)
if only_media: if only_media:
queryset = queryset.filter(attachments__id__isnull=True) queryset = queryset.filter(attachments__id__isnull=True)
paginator = MastodonPaginator(Post) paginator = MastodonPaginator(Post, sort_attribute="published")
pager = paginator.paginate( pager = paginator.paginate(
queryset, queryset,
min_id=min_id, min_id=min_id,
@ -105,7 +105,7 @@ def hashtag(
queryset = queryset.filter(local=True) queryset = queryset.filter(local=True)
if only_media: if only_media:
queryset = queryset.filter(attachments__id__isnull=True) queryset = queryset.filter(attachments__id__isnull=True)
paginator = MastodonPaginator(Post) paginator = MastodonPaginator(Post, sort_attribute="published")
pager = paginator.paginate( pager = paginator.paginate(
queryset, queryset,
min_id=min_id, min_id=min_id,