diff --git a/hypha/apply/activity/context_processors.py b/hypha/apply/activity/context_processors.py deleted file mode 100644 index 0a807d21ba7430e35b8b6735fc12d42ac48b9b00..0000000000000000000000000000000000000000 --- a/hypha/apply/activity/context_processors.py +++ /dev/null @@ -1,13 +0,0 @@ -from .models import Activity - - -def notification_context(request): - context_data = {} - if hasattr(request, "user"): - if request.user.is_authenticated and request.user.is_apply_staff: - context_data["latest_notifications"] = ( - Activity.objects.filter(current=True) - .latest() - .order_by("-timestamp")[:5] - ) - return context_data diff --git a/hypha/apply/activity/templates/activity/include/notifications_dropdown.html b/hypha/apply/activity/templates/activity/include/notifications_dropdown.html index 0866fcae53ba8bbf9217d138922d0fdbb1950636..03003c18c772ca556b43fe837d56797e59b42a32 100644 --- a/hypha/apply/activity/templates/activity/include/notifications_dropdown.html +++ b/hypha/apply/activity/templates/activity/include/notifications_dropdown.html @@ -1,20 +1,15 @@ {% load i18n activity_tags nh3_tags markdown_tags submission_tags apply_tags %} -<div class="notifications notifications--dropdown"> - <div class="notifications__content zeta" role="activity"> - <div class="notifications__header"> - <span>{% trans "Notifications" %}</span> - <a class="notifications__more" href="{% url "activity:notifications" %}">{% trans "Show All" %}</a> - </div> +{% for activity in object_list %} + <p class="notifications__item"> + <strong>{{ activity.source_content_type.name|source_type }} </strong> + <a href="{{ activity.source.get_absolute_url }}{% if activity.type == 'comment' %}#communications{% endif %}">{{ activity.source.title|capfirst|truncatechars:15 }}</a> + : {{ activity.user.get_full_name }} {% if activity.type == 'comment' %}{% trans "made a comment" %}{% else %} {{ activity|display_for:request.user }}{% endif %} + {% if activity.related_object %}<a href="{{ activity.related_object.get_absolute_url }}">{{ activity.related_object|model_verbose_name }}</a>{% endif %} + </p> +{% empty %} + <p class="notifications__item"> + {% trans "No notifications available." %} + </p> +{% endfor %} - {% for activity in latest_notifications %} - <p class="notifications__item"> - <strong>{{ activity.source_content_type.name|source_type }} </strong> - <a href="{{ activity.source.get_absolute_url }}{% if activity.type == 'comment' %}#communications{% endif %}">{{ activity.source.title|capfirst|truncatechars:15 }}</a> - : {{ activity.user.get_full_name }} {% if activity.type == 'comment' %}{% trans "made a comment" %}{% else %} {{ activity|display_for:request.user }}{% endif %} - {% if activity.related_object %}<a href="{{ activity.related_object.get_absolute_url }}">{{ activity.related_object|model_verbose_name }}</a>{% endif %} - </p> - {% endfor %} - - </div> -</div> diff --git a/hypha/apply/activity/views.py b/hypha/apply/activity/views.py index 573928dd84cfafbc40f4d64aad5db709feb02953..6f14dad78628c1a5cd5b44c16704ef84cbb0ef11 100644 --- a/hypha/apply/activity/views.py +++ b/hypha/apply/activity/views.py @@ -88,11 +88,21 @@ class NotificationsView(ListView): template_name = "activity/notifications.html" filterset_class = NotificationFilter + def get_template_names(self): + if self.request.htmx and self.request.GET.get("type") == "header_dropdown": + return ["activity/include/notifications_dropdown.html"] + return super().get_template_names() + def get_queryset(self): # List only last 30 days' activities queryset = Activity.objects.filter(current=True).latest() + self.filterset = self.filterset_class(self.request.GET, queryset=queryset) - return self.filterset.qs.distinct().order_by("-timestamp") + qs = self.filterset.qs.distinct().order_by("-timestamp") + + if self.request.htmx and self.request.GET.get("type") == "header_dropdown": + qs = qs[:5] + return qs def get_context_data(self, *, object_list=None, **kwargs): context = super(NotificationsView, self).get_context_data() diff --git a/hypha/settings/base.py b/hypha/settings/base.py index e5fb607357c61b36dde7fe2fef3adf6cab60a6eb..908b37b7810ab6e73f89657967650b424a1cb373 100644 --- a/hypha/settings/base.py +++ b/hypha/settings/base.py @@ -216,7 +216,6 @@ TEMPLATES = [ "social_django.context_processors.login_redirect", "hypha.apply.projects.context_processors.projects_enabled", "hypha.cookieconsent.context_processors.cookies_accepted", - "hypha.apply.activity.context_processors.notification_context", "hypha.core.context_processors.global_vars", ], "builtins": [ diff --git a/hypha/templates/base-apply.html b/hypha/templates/base-apply.html index d10900a315c0f02c9f13105c15ec0ec1133f9988..efb63428bb556c6fd3a7711776816375cd7926bd 100644 --- a/hypha/templates/base-apply.html +++ b/hypha/templates/base-apply.html @@ -101,8 +101,12 @@ </section> <div class="header__button-container flex gap-4"> - {% if latest_notifications %} - <div x-data="{open: false}"> + {% comment %} Notifications {% endcomment %} + {% if request.user.is_authenticated and request.user.is_apply_staff %} + <div + x-data="{open: false}" + x-init="$watch('open', value => { if (value) { document.getElementById('id-notification-list').dispatchEvent(new Event('htmx:fetch')); } })" + > <a href="{% url "activity:notifications" %}" class="p-2 flex items-center hover:opacity-70 transition-opacity" aria-label="{% trans "Notifications" %}" @@ -114,9 +118,41 @@ > {% heroicon_outline "bell-alert" class="inline me-1 text-black" aria_hidden="true" %} </a> - <div x-cloak x-show="open" x-transition @click.outside="open = false">{% include "activity/include/notifications_dropdown.html" %}</div> + <div x-cloak x-show="open" x-transition @click.outside="open = false"> + <div class="notifications notifications--dropdown"> + <div class="notifications__content zeta" role="activity"> + <div class="notifications__header"> + <span>{% trans "Notifications" %}</span> + <a class="notifications__more" href="{% url "activity:notifications" %}">{% trans "Show All" %}</a> + </div> + + <div + id="id-notification-list" + hx-get="{% url "activity:notifications" %}?type=header_dropdown" + hx-swap="innerHTML" + hx-trigger="htmx:fetch" + > + <div class="min-h-4 mx-4 my-3 rounded-lg bg-gray-200 animate-pulse"></div> + <div class="min-h-4 w-2/3 mx-4 my-3 rounded-lg bg-gray-200 animate-pulse"></div> + <hr> + <div class="min-h-4 mx-4 my-3 rounded-lg bg-gray-200 animate-pulse"></div> + <div class="min-h-4 w-2/3 mx-4 my-3 rounded-lg bg-gray-200 animate-pulse"></div> + <hr> + <div class="min-h-4 mx-4 my-3 rounded-lg bg-gray-200 animate-pulse"></div> + <div class="min-h-4 w-2/3 mx-4 my-3 rounded-lg bg-gray-200 animate-pulse"></div> + <hr> + <div class="min-h-4 mx-4 my-3 rounded-lg bg-gray-200 animate-pulse"></div> + <div class="min-h-4 w-2/3 mx-4 my-3 rounded-lg bg-gray-200 animate-pulse"></div> + <hr> + <div class="min-h-4 mx-4 my-3 rounded-lg bg-gray-200 animate-pulse"></div> + <div class="min-h-4 w-2/3 mx-4 my-3 rounded-lg bg-gray-200 animate-pulse"></div> + </div> + </div> + </div> + </div> </div> {% endif %} + {% comment %} Notifications End{% endcomment %} {% if request.path != '/auth/' and request.path != '/login/' %} {% include "includes/login_button.html" %}