diff --git a/src/argus_htmx/incidents/views.py b/src/argus_htmx/incidents/views.py index 4223163..e0c07d6 100644 --- a/src/argus_htmx/incidents/views.py +++ b/src/argus_htmx/incidents/views.py @@ -9,14 +9,15 @@ from django.views.decorators.http import require_POST, require_GET from django.core.paginator import Paginator -from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest -from django_htmx.middleware import HtmxDetails +from django.http import HttpResponse, HttpResponseBadRequest from django_htmx.http import HttpResponseClientRefresh from argus.auth.utils import get_preference, save_preference from argus.incident.models import Incident from argus.util.datetime_utils import make_aware +from ..request import HtmxHttpRequest + from .constants import ALLOWED_PAGE_SIZES from .customization import get_incident_table_columns from .utils import get_filter_function @@ -52,10 +53,6 @@ def prefetch_incident_daughters(): ) -class HtmxHttpRequest(HttpRequest): - htmx: HtmxDetails - - # fetch with htmx def incident_detail(request, pk: int): incident = get_object_or_404(Incident, id=pk) diff --git a/src/argus_htmx/middleware.py b/src/argus_htmx/middleware.py index ad352fb..953b456 100644 --- a/src/argus_htmx/middleware.py +++ b/src/argus_htmx/middleware.py @@ -1,9 +1,11 @@ from django.conf import settings from django.contrib.auth.views import redirect_to_login -from django.http import HttpRequest, HttpResponse +from django.http import HttpResponse from django.template import loader from django.utils.deprecation import MiddlewareMixin from django.utils.encoding import force_str +from django.contrib import messages +from .request import HtmxHttpRequest class LoginRequiredMiddleware: @@ -54,7 +56,7 @@ class HtmxMessageMiddleware(MiddlewareMixin): TEMPLATE = "messages/_notification_messages_htmx_append.html" - def process_response(self, request: HttpRequest, response: HttpResponse) -> HttpResponse: + def process_response(self, request: HtmxHttpRequest, response: HttpResponse) -> HttpResponse: if not request.htmx: return response @@ -65,5 +67,23 @@ def process_response(self, request: HttpRequest, response: HttpResponse) -> Http if not response.writable(): return response + # For HTMX error responses, the view should make sure to write the appropriate message to + # django messages framework. However, if it doesn't, we add a (generic) message so that we + # can at least send some indication to the user that something has gone wrong. + if response.status_code >= 400: + storage = messages.get_messages(request) + has_error_message = any("error" in message.tags for message in storage) + storage.used = False + if not has_error_message: + messages.error(request, "An error occured while processing your request, please try again") + # HTMX doesn't swap content for response codes >=400. However, we do want to show + # the new messages, so we need to rewrite the response to 200, and make sure it only + # swaps the oob notification content + response = HttpResponse( + headers={ + "HX-Retarget": "#notification-messages .toast", + "HX-Reswap": "beforeend", + } + ) response.write(loader.render_to_string(self.TEMPLATE, request=request)) return response diff --git a/src/argus_htmx/request.py b/src/argus_htmx/request.py new file mode 100644 index 0000000..178c49d --- /dev/null +++ b/src/argus_htmx/request.py @@ -0,0 +1,6 @@ +from django.http import HttpRequest +from django_htmx.middleware import HtmxDetails + + +class HtmxHttpRequest(HttpRequest): + htmx: HtmxDetails