Skip to content

Commit f973d33

Browse files
committed
use HTMX
1 parent 14eaa51 commit f973d33

File tree

11 files changed

+147
-55
lines changed

11 files changed

+147
-55
lines changed

adhocracy-plus/templates/base.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
<link rel="stylesheet" type="text/css" href="{% static 'adhocracy4.css' %}" />
4646
<script src="{% url 'javascript-catalog' %}"></script>
4747
<script src="{% static 'adhocracy4.js' %}"></script>
48+
<script src="https://unpkg.com/[email protected]"></script>
4849
<meta name="viewport" content="width=device-width" />
4950

5051
{% block extra_js %}
Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
{% load static notification_tags %}
22
{% unread_notifications_count as unread_count %}
3-
<a href="{% url 'userdashboard-notifications' %}" class="btn btn-default position-relative rounded-circle notifications_btn d-flex align-items-center justify-content-center" aria-label="Notifications">
3+
4+
<a href="{% url 'userdashboard-notifications' %}"
5+
class="btn btn-default position-relative rounded-circle notifications_btn d-flex align-items-center justify-content-center"
6+
aria-label="Notifications"
7+
hx-get="{% url 'notification-count-partial' %}"
8+
hx-trigger="updateNotificationCount from:body, every 15s"
9+
hx-target=".notification-badge-container"
10+
hx-swap="innerHTML">
11+
412
<i class="fas fa-bell"></i>
5-
{% if user.is_authenticated and unread_count > 0 %}
6-
<span class="position-absolute top-0 end-0 p-1 bg-danger border border-white rounded-circle">
7-
<span class="visually-hidden">Unread notifications</span>
8-
</span>
9-
{% endif %}
10-
</a>
13+
<span class="notification-badge-container">
14+
{% if user.is_authenticated and unread_count > 0 %}
15+
<span class="position-absolute top-0 end-0 p-1 bg-danger border border-white rounded-circle notification-badge">
16+
<span class="visually-hidden">Unread notifications</span>
17+
</span>
18+
{% endif %}
19+
</span>
20+
</a>

apps/notifications/strategies/moderation_strategies.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ def create_notification_data(self, feedback) -> dict:
2121
project = user_comment.project
2222

2323
if hasattr(project.organisation.site, "name"):
24-
site_name=project.organisation.site.name
24+
site_name = project.organisation.site.name
2525
else:
26-
site_name=""
27-
26+
site_name = ""
27+
2828
email_context = {
2929
"subject": _("Feedback for your contribution on {site_name}").format(
3030
site_name=site_name

apps/notifications/strategies/project_strategies.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def create_notification_data(self, project) -> dict:
127127
class ProjectInvitationCreated(ProjectNotificationStrategy):
128128
def get_organisation(self, invitation):
129129
return invitation.project.organisation
130-
130+
131131
def get_recipients(self, invitation) -> List[User]:
132132
user_email = invitation.email
133133
try:
@@ -150,9 +150,7 @@ def create_notification_data(self, invitation) -> dict:
150150
).format(project_type=project_type, project_name=project.name),
151151
"cta_url": f"https://{invitation.site}{invitation.get_absolute_url()}",
152152
"cta_label": _("Accept invitation"),
153-
"reason": _(
154-
"This email was sent to {receiver_email}."
155-
),
153+
"reason": _("This email was sent to {receiver_email}."),
156154
"content_template": "a4_candy_notifications/emails/content/project_invitation.en.email",
157155
"participantinvite": invitation,
158156
"project": project,

apps/notifications/templates/a4_candy_notifications/_notification_card.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ <h2 class="card-title text-decoration-underline mb-1 mb-md-0 mt-0">
1515
</a>
1616
{% endif %}
1717
{% if notifications_list and unread_count > 0 %}
18-
<form method="post" action="{% url 'mark_all_notifications_as_read' %}" class="m-0">
18+
<form
19+
class="m-0"
20+
method="post"
21+
action="{% url 'mark_all_notifications_as_read' %}"
22+
hx-post="{% url 'mark_all_notifications_as_read' %}"
23+
hx-target="#notifications-container"
24+
hx-swap="innerHTML">
1925
{% csrf_token %}
2026
<input type="hidden" name="section" value="{{ section_id }}">
2127
<button type="submit" class="btn btn-sm btn--transparent btn--transparent-border">
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{% if user.is_authenticated and unread_count > 0 %}
2+
<span class="position-absolute top-0 end-0 p-1 bg-danger border border-white rounded-circle notification-badge">
3+
<span class="visually-hidden">Unread notifications</span>
4+
</span>
5+
{% endif %}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{% load i18n notification_tags %}
2+
3+
{% unread_notifications_count as unread_count %}
4+
<h2>Notifications ({{ unread_count }})</h2>
5+
<p>{{ dashboard_description }}</p>
6+
7+
{% include "a4_candy_notifications/_notification_card.html" with section_id="interactions" unread_count=interactions_unread_count title=interactions_title description=interactions_description notifications_list=interactions_page.object_list empty_icon="fa-comments" empty_message=interactions_empty pagination_required=interactions_page.has_other_pages page_obj=interactions_page param_name="interactions_page" %}
8+
9+
{% include "a4_candy_notifications/_notification_card.html" with section_id="projects" unread_count=projects_unread_count title=projects_title description=projects_description notifications_list=projects_page.object_list empty_icon="fa-comments" empty_message=projects_empty pagination_required=projects_page.has_other_pages page_obj=projects_page param_name="projects_page" %}

apps/notifications/views.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
from django.contrib.auth.mixins import LoginRequiredMixin
66
from django.shortcuts import get_object_or_404
77
from django.shortcuts import redirect
8+
from django.shortcuts import render
89
from django.urls import reverse
910
from django.utils import timezone
1011
from django.views.generic import UpdateView
1112
from django.views.generic import View
1213

14+
from apps.userdashboard.views import UserDashboardNotificationsBaseView
15+
1316
from .forms import NotificationSettingsForm
1417
from .models import Notification
1518
from .models import NotificationSettings
@@ -53,6 +56,30 @@ def post(self, request):
5356
return redirect("account_notification_settings")
5457

5558

59+
class MarkAllNotificationsAsReadView(UserDashboardNotificationsBaseView):
60+
"""Mark all notifications as read with HTMX support"""
61+
62+
def post(self, request, *args, **kwargs):
63+
section = request.POST.get("section", "")
64+
notifications = Notification.objects.filter(recipient=request.user, read=False)
65+
66+
if section:
67+
notifications = get_notifications_by_section(notifications, section)
68+
notifications.update(read=True, read_at=timezone.now())
69+
70+
if request.headers.get("HX-Request"):
71+
context = self._get_notifications_context()
72+
response = render(
73+
request, "a4_candy_notifications/_notifications_partial.html", context
74+
)
75+
print("RESPOONDING WITH TRIGGER")
76+
# Add HTMX trigger header to update the button
77+
response["HX-Trigger"] = "updateNotificationCount"
78+
return response
79+
else:
80+
return redirect("userdashboard-notifications")
81+
82+
5683
class MarkNotificationAsReadView(LoginRequiredMixin, View):
5784
def get(self, request, *args, **kwargs):
5885
notification = get_object_or_404(
@@ -68,13 +95,16 @@ def get(self, request, *args, **kwargs):
6895
return redirect(request.META.get("HTTP_REFERER", "home"))
6996

7097

71-
class MarkAllNotificationsAsReadView(LoginRequiredMixin, View):
72-
def post(self, request):
73-
section = request.POST.get("section", "")
74-
notifications = Notification.objects.filter(recipient=request.user, read=False)
98+
class NotificationCountPartialView(UserDashboardNotificationsBaseView):
99+
"""HTMX partial for just the notification badge"""
75100

76-
if section:
77-
notifications = get_notifications_by_section(notifications, section)
78-
notifications.update(read=True, read_at=timezone.now())
79-
messages.success(request, "All notifications marked as read")
80-
return redirect(request.META.get("HTTP_REFERER", "home"))
101+
def get(self, request, *args, **kwargs):
102+
unread_count = 0
103+
if request.user.is_authenticated:
104+
unread_count = Notification.objects.unread_count_for_user(request.user)
105+
106+
return render(
107+
request,
108+
"a4_candy_notifications/_notifications_button_partial.html",
109+
{"user": request.user, "unread_count": unread_count},
110+
)

apps/userdashboard/templates/a4_candy_userdashboard/userdashboard_notifications.html

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,26 @@
44
{% block dashboard_content %}
55
<a class="btn btn--none px-0" href="{% url 'userdashboard-overview' %}">
66
<i class="fa fa-chevron-left" aria-hidden="true"></i>
7-
87
<span>{% trans 'Back to overview' %}</span>
98
</a>
109

1110
<h1 class="visually-hidden">
1211
{% trans 'Your Notifications' %}
1312
</h1>
1413

15-
{% unread_notifications_count as unread_count %}
1614
<div class="container mt-4">
1715
<div class="row">
18-
<div class="col-lg-10 mx-auto">
19-
<h2>Notifications ({{ unread_count }})</h2>
20-
<!-- todo: link in description -->
21-
<p>{{ dashboard_description }}</p>
22-
{% include "a4_candy_notifications/_notification_card.html" with section_id="interactions" unread_count=interactions_unread_count title=interactions_title description=interactions_description notifications_list=interactions_page.object_list empty_icon="fa-comments" empty_message=interactions_empty pagination_required=interactions_page.has_other_pages page_obj=interactions_page param_name="interactions_page" %}
23-
{% include "a4_candy_notifications/_notification_card.html" with section_id="projects" unread_count=projects_unread_count title=projects_title description=projects_description notifications_list=projects_page.object_list empty_icon="fa-comments" empty_message=projects_empty pagination_required=projects_page.has_other_pages page_obj=projects_page param_name="projects_page" %}
16+
<div class="col-lg-10 mx-auto"
17+
id="notifications-container"
18+
hx-get="{% url 'userdashboard-notifications-partial' %}"
19+
hx-trigger="load, every 10s, updateNotifications from:body"
20+
hx-swap="innerHTML">
21+
22+
<div class="text-center py-4">
23+
<i class="fas fa-spinner fa-spin"></i>
24+
{% trans 'Loading notifications...' %}
25+
</div>
2426
</div>
2527
</div>
2628
</div>
27-
28-
{% endblock dashboard_content %}
29+
{% endblock dashboard_content %}

apps/userdashboard/urls.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from django.urls import path
22
from django.urls import re_path
3-
from apps.notifications.views import MarkAllNotificationsAsReadView, MarkNotificationAsReadView
3+
4+
from apps.notifications.views import MarkAllNotificationsAsReadView
5+
from apps.notifications.views import MarkNotificationAsReadView
6+
from apps.notifications.views import NotificationCountPartialView
47

58
from . import views
69

@@ -20,8 +23,26 @@
2023
views.UserDashboardNotificationsView.as_view(),
2124
name="userdashboard-notifications",
2225
),
23-
path('overview/notifications/mark-as-read/<int:pk>/', MarkNotificationAsReadView.as_view(), name='mark_notification_as_read'),
24-
path('overview/notifications/mark-all-as-read/', MarkAllNotificationsAsReadView.as_view(), name='mark_all_notifications_as_read'),
26+
path(
27+
"overview/notifications/partial/",
28+
views.UserDashboardNotificationsPartialView.as_view(),
29+
name="userdashboard-notifications-partial",
30+
),
31+
path(
32+
"overview/notifications/mark-as-read/<int:pk>/",
33+
MarkNotificationAsReadView.as_view(),
34+
name="mark_notification_as_read",
35+
),
36+
path(
37+
"overview/notifications/mark-all-as-read/",
38+
MarkAllNotificationsAsReadView.as_view(),
39+
name="mark_all_notifications_as_read",
40+
),
41+
path(
42+
"overview/notifications/count/",
43+
NotificationCountPartialView.as_view(),
44+
name="notification-count-partial",
45+
),
2546
path(
2647
"overview/activities/",
2748
views.UserDashboardActivitiesView.as_view(),

0 commit comments

Comments
 (0)