From 0851fbd1ec09b142608667bf90ee806e59cafb28 Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Thu, 17 Nov 2022 18:52:00 -0700 Subject: Add search and better notifications --- activities/admin.py | 1 + activities/models/fan_out.py | 2 +- activities/models/timeline_event.py | 3 ++- activities/views/posts.py | 18 ++++++++++++++++++ activities/views/search.py | 32 ++++++++++++++++++++++++++++++++ activities/views/timelines.py | 17 +++++++++++++---- 6 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 activities/views/search.py (limited to 'activities') diff --git a/activities/admin.py b/activities/admin.py index 371aa7b..e24304d 100644 --- a/activities/admin.py +++ b/activities/admin.py @@ -36,6 +36,7 @@ class PostAdmin(admin.ModelAdmin): @admin.register(TimelineEvent) class TimelineEventAdmin(admin.ModelAdmin): list_display = ["id", "identity", "created", "type"] + readonly_fields = ["created"] raw_id_fields = [ "identity", "subject_post", diff --git a/activities/models/fan_out.py b/activities/models/fan_out.py index 771be19..6ebbe0a 100644 --- a/activities/models/fan_out.py +++ b/activities/models/fan_out.py @@ -37,7 +37,6 @@ class FanOutStates(StateGraph): private_key=post.author.private_key, key_id=post.author.public_key_id, ) - return cls.sent # Handle boosts/likes elif fan_out.type == FanOut.Types.interaction: interaction = await fan_out.subject_post_interaction.afetch_full() @@ -74,6 +73,7 @@ class FanOutStates(StateGraph): ) else: raise ValueError(f"Cannot fan out with type {fan_out.type}") + return cls.sent class FanOut(StatorModel): diff --git a/activities/models/timeline_event.py b/activities/models/timeline_event.py index 368fdad..cf93661 100644 --- a/activities/models/timeline_event.py +++ b/activities/models/timeline_event.py @@ -66,7 +66,7 @@ class TimelineEvent(models.Model): """ return cls.objects.get_or_create( identity=identity, - type=cls.Types.follow, + type=cls.Types.followed, subject_identity=source_identity, )[0] @@ -90,6 +90,7 @@ class TimelineEvent(models.Model): identity=identity, type=cls.Types.mentioned, subject_post=post, + subject_identity=post.author, )[0] @classmethod diff --git a/activities/views/posts.py b/activities/views/posts.py index 7b93e42..14da9ca 100644 --- a/activities/views/posts.py +++ b/activities/views/posts.py @@ -5,6 +5,7 @@ from django.utils.decorators import method_decorator from django.views.generic import FormView, TemplateView, View from activities.models import Post, PostInteraction, PostInteractionStates +from core.models import Config from users.decorators import identity_required from users.shortcuts import by_handle_or_404 @@ -112,6 +113,7 @@ class Compose(FormView): template_name = "activities/compose.html" class form_class(forms.Form): + text = forms.CharField( widget=forms.Textarea( attrs={ @@ -137,6 +139,22 @@ class Compose(FormView): help_text="Optional - Post will be hidden behind this text until clicked", ) + def clean_text(self): + text = self.cleaned_data.get("text") + if not text: + return text + length = len(text) + if length > Config.system.post_length: + raise forms.ValidationError( + f"Maximum post length is {Config.system.post_length} characters (you have {length})" + ) + return text + + def get_form_class(self): + form = super().get_form_class() + form.declared_fields["text"] + return form + def form_valid(self, form): Post.create_local( author=self.request.identity, diff --git a/activities/views/search.py b/activities/views/search.py new file mode 100644 index 0000000..b748348 --- /dev/null +++ b/activities/views/search.py @@ -0,0 +1,32 @@ +from django import forms +from django.views.generic import FormView + +from users.models import Identity + + +class Search(FormView): + + template_name = "activities/search.html" + + class form_class(forms.Form): + query = forms.CharField() + + def form_valid(self, form): + query = form.cleaned_data["query"].lstrip("@").lower() + results = {"identities": set()} + # Search identities + if "@" in query: + username, domain = query.split("@", 1) + for identity in Identity.objects.filter( + domain_id=domain, username=username + )[:20]: + results["identities"].add(identity) + else: + for identity in Identity.objects.filter(username=query)[:20]: + results["identities"].add(identity) + for identity in Identity.objects.filter(username__startswith=query)[:20]: + results["identities"].add(identity) + # Render results + context = self.get_context_data(form=form) + context["results"] = results + return self.render_to_response(context) diff --git a/activities/views/timelines.py b/activities/views/timelines.py index 02afc2c..38f9331 100644 --- a/activities/views/timelines.py +++ b/activities/views/timelines.py @@ -98,9 +98,18 @@ class Notifications(TemplateView): def get_context_data(self): context = super().get_context_data() - context["events"] = TimelineEvent.objects.filter( - identity=self.request.identity, - type__in=[TimelineEvent.Types.mentioned, TimelineEvent.Types.boosted], - ).select_related("subject_post", "subject_post__author", "subject_identity") + context["events"] = ( + TimelineEvent.objects.filter( + identity=self.request.identity, + type__in=[ + TimelineEvent.Types.mentioned, + TimelineEvent.Types.boosted, + TimelineEvent.Types.liked, + TimelineEvent.Types.followed, + ], + ) + .order_by("-created")[:50] + .select_related("subject_post", "subject_post__author", "subject_identity") + ) context["current_page"] = "notifications" return context -- cgit v1.2.3