diff --git a/activities/services/timeline.py b/activities/services/timeline.py index d761d21..f034abe 100644 --- a/activities/services/timeline.py +++ b/activities/services/timeline.py @@ -1,6 +1,6 @@ from django.db import models -from activities.models import Hashtag, Post, TimelineEvent +from activities.models import Hashtag, Post, PostInteraction, TimelineEvent from activities.services import PostService from users.models import Identity @@ -82,3 +82,16 @@ class TimelineService: .unlisted(include_replies=True) .order_by("-id") ) + + def likes(self) -> models.QuerySet[Post]: + """ + Return all liked posts for an identity + """ + return ( + PostService.queryset() + .filter( + interactions__identity=self.identity, + interactions__type=PostInteraction.Types.like, + ) + .order_by("-id") + ) diff --git a/api/views/timelines.py b/api/views/timelines.py index 973ccb2..70d7d3b 100644 --- a/api/views/timelines.py +++ b/api/views/timelines.py @@ -144,3 +144,33 @@ def conversations( ): # We don't implement this yet return [] + + +@api_router.get("/v1/favourites", response=list[schemas.Status]) +@identity_required +def favourites( + request: HttpRequest, + response: HttpResponse, + max_id: str | None = None, + since_id: str | None = None, + min_id: str | None = None, + limit: int = 20, +): + queryset = TimelineService(request.identity).likes() + + paginator = MastodonPaginator() + pager = paginator.paginate( + queryset, + min_id=min_id, + max_id=max_id, + since_id=since_id, + limit=limit, + ) + # Convert those to the JSON form + pager.jsonify_posts(identity=request.identity) + + # Add the link header if needed + if pager.results: + response.headers["Link"] = pager.link_header(request, ["limit"]) + + return pager.json_results diff --git a/tests/api/test_likes.py b/tests/api/test_likes.py new file mode 100644 index 0000000..db31d8c --- /dev/null +++ b/tests/api/test_likes.py @@ -0,0 +1,35 @@ +import pytest + + +@pytest.mark.django_db +def test_likes_flow(api_token, client): + # Add a post + response = client.post( + "/api/v1/statuses", + HTTP_AUTHORIZATION=f"Bearer {api_token.token}", + HTTP_ACCEPT="application/json", + content_type="application/json", + data={ + "status": "Like test.", + "visibility": "public", + }, + ).json() + assert response["content"] == "

Like test.

" + + status_id = response["id"] + + # Like it + response = client.post( + f"/api/v1/statuses/{status_id}/favourite", + HTTP_AUTHORIZATION=f"Bearer {api_token.token}", + HTTP_ACCEPT="application/json", + ).json() + assert response["favourited"] is True + + # Check if it's displaying at likes endpoint + response = client.get( + "/api/v1/favourites", + HTTP_AUTHORIZATION=f"Bearer {api_token.token}", + HTTP_ACCEPT="application/json", + ).json() + assert response[0]["id"] == status_id