Improve hashtag case handling and implement /api/v1/tags/<hashtag> endpoint (#554)
* Lowercase hashtag before loading its timeline * Implement /api/v1/tags/<hashtag> endpoint * Lower hashtag before un-/following * Fix field name for hashtag following/followed boolean
This commit is contained in:
parent
216915ddb8
commit
b31c5156ff
|
@ -168,14 +168,14 @@ class Hashtag(StatorModel):
|
||||||
results[date(year, month, day)] = val
|
results[date(year, month, day)] = val
|
||||||
return dict(sorted(results.items(), reverse=True)[:num])
|
return dict(sorted(results.items(), reverse=True)[:num])
|
||||||
|
|
||||||
def to_mastodon_json(self, followed: bool | None = None):
|
def to_mastodon_json(self, following: bool | None = None):
|
||||||
value = {
|
value = {
|
||||||
"name": self.hashtag,
|
"name": self.hashtag,
|
||||||
"url": self.urls.view.full(), # type: ignore
|
"url": self.urls.view.full(), # type: ignore
|
||||||
"history": [],
|
"history": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
if followed is not None:
|
if following is not None:
|
||||||
value["followed"] = followed
|
value["following"] = following
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -276,15 +276,15 @@ class Tag(Schema):
|
||||||
name: str
|
name: str
|
||||||
url: str
|
url: str
|
||||||
history: dict
|
history: dict
|
||||||
followed: bool | None
|
following: bool | None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_hashtag(
|
def from_hashtag(
|
||||||
cls,
|
cls,
|
||||||
hashtag: activities_models.Hashtag,
|
hashtag: activities_models.Hashtag,
|
||||||
followed: bool | None = None,
|
following: bool | None = None,
|
||||||
) -> "Tag":
|
) -> "Tag":
|
||||||
return cls(**hashtag.to_mastodon_json(followed=followed))
|
return cls(**hashtag.to_mastodon_json(following=following))
|
||||||
|
|
||||||
|
|
||||||
class FollowedTag(Tag):
|
class FollowedTag(Tag):
|
||||||
|
@ -295,7 +295,7 @@ class FollowedTag(Tag):
|
||||||
cls,
|
cls,
|
||||||
follow: users_models.HashtagFollow,
|
follow: users_models.HashtagFollow,
|
||||||
) -> "FollowedTag":
|
) -> "FollowedTag":
|
||||||
return cls(id=follow.id, **follow.hashtag.to_mastodon_json(followed=True))
|
return cls(id=follow.id, **follow.hashtag.to_mastodon_json(following=True))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def map_from_follows(
|
def map_from_follows(
|
||||||
|
|
|
@ -96,6 +96,7 @@ urlpatterns = [
|
||||||
path("v1/statuses/<id>/unbookmark", statuses.unbookmark_status),
|
path("v1/statuses/<id>/unbookmark", statuses.unbookmark_status),
|
||||||
# Tags
|
# Tags
|
||||||
path("v1/followed_tags", tags.followed_tags),
|
path("v1/followed_tags", tags.followed_tags),
|
||||||
|
path("v1/tags/<hashtag>", tags.hashtag),
|
||||||
path("v1/tags/<id>/follow", tags.follow),
|
path("v1/tags/<id>/follow", tags.follow),
|
||||||
path("v1/tags/<id>/unfollow", tags.unfollow),
|
path("v1/tags/<id>/unfollow", tags.unfollow),
|
||||||
# Timelines
|
# Timelines
|
||||||
|
|
|
@ -9,6 +9,22 @@ from api.pagination import MastodonPaginator, PaginatingApiResponse, PaginationR
|
||||||
from users.models import HashtagFollow
|
from users.models import HashtagFollow
|
||||||
|
|
||||||
|
|
||||||
|
@api_view.get
|
||||||
|
def hashtag(request: HttpRequest, hashtag: str) -> schemas.Tag:
|
||||||
|
tag = get_object_or_404(
|
||||||
|
Hashtag,
|
||||||
|
pk=hashtag.lower(),
|
||||||
|
)
|
||||||
|
following = None
|
||||||
|
if request.identity:
|
||||||
|
following = tag.followers.filter(identity=request.identity).exists()
|
||||||
|
|
||||||
|
return schemas.Tag.from_hashtag(
|
||||||
|
tag,
|
||||||
|
following=following,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@scope_required("read:follows")
|
@scope_required("read:follows")
|
||||||
@api_view.get
|
@api_view.get
|
||||||
def followed_tags(
|
def followed_tags(
|
||||||
|
@ -42,12 +58,12 @@ def follow(
|
||||||
) -> schemas.Tag:
|
) -> schemas.Tag:
|
||||||
hashtag = get_object_or_404(
|
hashtag = get_object_or_404(
|
||||||
Hashtag,
|
Hashtag,
|
||||||
pk=id,
|
pk=id.lower(),
|
||||||
)
|
)
|
||||||
request.identity.hashtag_follows.get_or_create(hashtag=hashtag)
|
request.identity.hashtag_follows.get_or_create(hashtag=hashtag)
|
||||||
return schemas.Tag.from_hashtag(
|
return schemas.Tag.from_hashtag(
|
||||||
hashtag,
|
hashtag,
|
||||||
followed=True,
|
following=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,10 +75,10 @@ def unfollow(
|
||||||
) -> schemas.Tag:
|
) -> schemas.Tag:
|
||||||
hashtag = get_object_or_404(
|
hashtag = get_object_or_404(
|
||||||
Hashtag,
|
Hashtag,
|
||||||
pk=id,
|
pk=id.lower(),
|
||||||
)
|
)
|
||||||
request.identity.hashtag_follows.filter(hashtag=hashtag).delete()
|
request.identity.hashtag_follows.filter(hashtag=hashtag).delete()
|
||||||
return schemas.Tag.from_hashtag(
|
return schemas.Tag.from_hashtag(
|
||||||
hashtag,
|
hashtag,
|
||||||
followed=False,
|
following=False,
|
||||||
)
|
)
|
||||||
|
|
|
@ -101,7 +101,7 @@ def hashtag(
|
||||||
) -> ApiResponse[list[schemas.Status]]:
|
) -> ApiResponse[list[schemas.Status]]:
|
||||||
if limit > 40:
|
if limit > 40:
|
||||||
limit = 40
|
limit = 40
|
||||||
queryset = TimelineService(request.identity).hashtag(hashtag)
|
queryset = TimelineService(request.identity).hashtag(hashtag.lower())
|
||||||
if local:
|
if local:
|
||||||
queryset = queryset.filter(local=True)
|
queryset = queryset.filter(local=True)
|
||||||
if only_media:
|
if only_media:
|
||||||
|
|
Loading…
Reference in New Issue