Merge pull request #2524 from chdorner/feature/tag-support
Initial hashtag support
This commit is contained in:
commit
12af5992a3
20 changed files with 635 additions and 13 deletions
|
@ -130,6 +130,7 @@ from .group import (
|
|||
accept_membership,
|
||||
reject_membership,
|
||||
)
|
||||
from .hashtag import Hashtag
|
||||
from .inbox import Inbox
|
||||
from .interaction import Favorite, Unfavorite, Boost, Unboost
|
||||
from .isbn import Isbn
|
||||
|
|
54
bookwyrm/views/hashtag.py
Normal file
54
bookwyrm/views/hashtag.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
""" listing statuses for a given hashtag """
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Q
|
||||
from django.views import View
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template.response import TemplateResponse
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.settings import PAGE_LENGTH
|
||||
from bookwyrm.views.helpers import maybe_redirect_local_path
|
||||
|
||||
|
||||
# pylint: disable= no-self-use
|
||||
class Hashtag(View):
|
||||
"""listing statuses for a given hashtag"""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def get(self, request, hashtag_id, slug=None):
|
||||
"""show hashtag with related statuses"""
|
||||
hashtag = get_object_or_404(models.Hashtag, id=hashtag_id)
|
||||
|
||||
if redirect_local_path := maybe_redirect_local_path(request, hashtag):
|
||||
return redirect_local_path
|
||||
|
||||
activities = (
|
||||
models.Status.privacy_filter(
|
||||
request.user,
|
||||
)
|
||||
.filter(
|
||||
Q(mention_hashtags=hashtag),
|
||||
)
|
||||
.exclude(
|
||||
privacy__in=["direct", "unlisted"],
|
||||
)
|
||||
.select_related(
|
||||
"user",
|
||||
"reply_parent",
|
||||
"review__book",
|
||||
"comment__book",
|
||||
"quotation__book",
|
||||
)
|
||||
.prefetch_related(
|
||||
"mention_books",
|
||||
"mention_users",
|
||||
"attachments",
|
||||
)
|
||||
)
|
||||
paginated = Paginator(activities, PAGE_LENGTH)
|
||||
|
||||
data = {
|
||||
"hashtag": hashtag.name,
|
||||
"activities": paginated.get_page(request.GET.get("page", 1)),
|
||||
}
|
||||
return TemplateResponse(request, "hashtag.html", data)
|
|
@ -115,6 +115,19 @@ class CreateStatus(View):
|
|||
if status.reply_parent:
|
||||
status.mention_users.add(status.reply_parent.user)
|
||||
|
||||
# inspect the text for hashtags
|
||||
for (mention_text, mention_hashtag) in find_or_create_hashtags(content).items():
|
||||
# add them to status mentions fk
|
||||
status.mention_hashtags.add(mention_hashtag)
|
||||
|
||||
# turn the mention into a link
|
||||
content = re.sub(
|
||||
rf"{mention_text}\b(?!@)",
|
||||
rf'<a href="{mention_hashtag.remote_id}" data-mention="hashtag">'
|
||||
+ rf"{mention_text}</a>",
|
||||
content,
|
||||
)
|
||||
|
||||
# deduplicate mentions
|
||||
status.mention_users.set(set(status.mention_users.all()))
|
||||
|
||||
|
@ -237,6 +250,38 @@ def find_mentions(user, content):
|
|||
return username_dict
|
||||
|
||||
|
||||
def find_or_create_hashtags(content):
|
||||
"""detect #hashtags in raw status content
|
||||
|
||||
it stores hashtags case-sensitive, but ensures that an existing
|
||||
hashtag with different case are found and re-used. for example,
|
||||
an existing #BookWyrm hashtag will be found and used even if the
|
||||
status content is using #bookwyrm.
|
||||
"""
|
||||
if not content:
|
||||
return {}
|
||||
|
||||
found_hashtags = {t.lower(): t for t in re.findall(regex.HASHTAG, content)}
|
||||
if len(found_hashtags) == 0:
|
||||
return {}
|
||||
|
||||
known_hashtags = {
|
||||
t.name.lower(): t
|
||||
for t in models.Hashtag.objects.filter(
|
||||
Q(name__in=found_hashtags.keys())
|
||||
).distinct()
|
||||
}
|
||||
|
||||
not_found = found_hashtags.keys() - known_hashtags.keys()
|
||||
for lower_name in not_found:
|
||||
tag_name = found_hashtags[lower_name]
|
||||
mention_hashtag = models.Hashtag(name=tag_name)
|
||||
mention_hashtag.save()
|
||||
known_hashtags[lower_name] = mention_hashtag
|
||||
|
||||
return {found_hashtags[k]: v for k, v in known_hashtags.items()}
|
||||
|
||||
|
||||
def format_links(content):
|
||||
"""detect and format links"""
|
||||
validator = URLValidator()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue