From 13a575017cbbd2166bb56015f6e33ce370dedbcb Mon Sep 17 00:00:00 2001 From: Joseph Nuthalapati Date: Wed, 4 Feb 2026 00:15:16 +0530 Subject: [PATCH] ui: Dismiss notifications without page reload - Delete only the
  • of the notification using HTMX. - Notifications list stays open. User can dismiss another notification. - Decrement notification counter using JavaScript after removing notification from the list. - Added HTMX to every kind of notification. - Tested dismissing notifications from the top, middle and bottom of the list. Signed-off-by: Joseph Nuthalapati [sunil: Update comment format in .js file] Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- .../upgrades-dist-upgrade-notification.html | 3 +++ .../templates/upgrades-new-release.html | 3 +++ plinth/templates/notifications.html | 6 +++++- plinth/templates/operation-notification.html | 3 +++ plinth/views.py | 8 +++++++ static/themes/default/js/main.js | 21 +++++++++++++++++++ 6 files changed, 43 insertions(+), 1 deletion(-) diff --git a/plinth/modules/upgrades/templates/upgrades-dist-upgrade-notification.html b/plinth/modules/upgrades/templates/upgrades-dist-upgrade-notification.html index eb6cba373..0cd66fd3e 100644 --- a/plinth/modules/upgrades/templates/upgrades-dist-upgrade-notification.html +++ b/plinth/modules/upgrades/templates/upgrades-dist-upgrade-notification.html @@ -42,6 +42,9 @@ {% trans "Go to Distribution Update" %} {% trans "Dismiss" %} diff --git a/plinth/modules/upgrades/templates/upgrades-new-release.html b/plinth/modules/upgrades/templates/upgrades-new-release.html index d5fae3c3d..1cc1b7812 100644 --- a/plinth/modules/upgrades/templates/upgrades-new-release.html +++ b/plinth/modules/upgrades/templates/upgrades-new-release.html @@ -18,6 +18,9 @@

    {% trans "Dismiss" %} diff --git a/plinth/templates/notifications.html b/plinth/templates/notifications.html index 73ac7212b..0afd6e549 100644 --- a/plinth/templates/notifications.html +++ b/plinth/templates/notifications.html @@ -11,7 +11,8 @@

      {% for note in notifications %} -
    • +
    • @@ -54,6 +55,9 @@ {% for action in note.actions %} {% if action.type == "dismiss" %} {% trans "Dismiss" %} diff --git a/plinth/templates/operation-notification.html b/plinth/templates/operation-notification.html index 9fd3f571a..41559ad9f 100644 --- a/plinth/templates/operation-notification.html +++ b/plinth/templates/operation-notification.html @@ -19,6 +19,9 @@ {% if data.state == "completed" %}
      {% trans "Dismiss" %} diff --git a/plinth/views.py b/plinth/views.py index ece40a2e7..ca6bdd380 100644 --- a/plinth/views.py +++ b/plinth/views.py @@ -682,10 +682,18 @@ class AppLogsView(TemplateView): def notification_dismiss(request, id): """Dismiss a notification.""" + from django.http import HttpResponse from .notification import Notification notes = Notification.list(key=id, user=request.user) if notes: # If a notification is not found, no need to dismiss it. notes[0].dismiss() + # Don't redirect if the request is from HTMX. + if request.headers.get('HX-Request'): + response = HttpResponse(status=200) + # Trigger the notification-dismissed event to update the UI. + response['HX-Trigger'] = 'notification-dismissed' + return response + return HttpResponseRedirect(_get_redirect_url_from_param(request)) diff --git a/static/themes/default/js/main.js b/static/themes/default/js/main.js index b0a8018eb..c56a9a3a7 100644 --- a/static/themes/default/js/main.js +++ b/static/themes/default/js/main.js @@ -298,3 +298,24 @@ document.addEventListener('htmx:afterSwap', function (event) { window.location.reload(); } }); + +/* + * Decrement notification counter badge when a notification is dismissed via + * HTMX. + */ +document.addEventListener('notification-dismissed', function (evt) { + // There are 2 badges on the page. One for mobile navbar and one for desktop + // navbar. + const badges = document.querySelectorAll('.notifications-dropdown .badge'); + + badges.forEach(badge => { + const count = parseInt(badge.textContent.trim()); + + if (count > 1) { + badge.textContent = count - 1; + } else { + const dropdowns = document.querySelectorAll('.notifications-dropdown'); + dropdowns.forEach(dropdown => dropdown.remove()); + } + }); +});