From 5962536a11b6215b40fdc1a03e3d4e5a3835549f Mon Sep 17 00:00:00 2001 From: luelista Date: Tue, 27 May 2025 07:17:50 +0200 Subject: [PATCH] Dialog for cart renewal, async task without page refresh (#5148) * async_task: deduplicate response handling code * extend cart without full page reload * update dialog markup * fix error response from CartExtend * refactor asynctask, make sure waitingDialog.show() re-initializes dialog contents * add cart expiry notification * add aria references to other dialogs * improve error handling * fix error if max_extend=None * different message for expiring soon and expired carts * refactor dialog css * add classes to further dialog elements * switch extend-cart-dialog and loadingmodal to * Backport simple_block_tag from Django 5.2 * Use simple_block_tag for {% dialog %} tag * add alertdialog role * Update src/pretix/static/pretixbase/scss/_dialogs.scss Co-authored-by: Richard Schreiber * fix mobile dialog styles not being overwritten * asynctask dialog: prevent close by escape on chrome * remove dynamic aria-live from #cart-deadline dynamic aria-live is generally not well supported and as we have the dialog now anyways, we can remove it * move continue-button to right * Update src/pretix/static/pretixpresale/js/ui/cart.js Co-authored-by: Richard Schreiber * Fix CSS for old-style dialog * fix heading display/level * align dialogs at the top as they originally were * fix from merge-conflict * fix missing grow for dialog-content * improve cart-extend-button ui * do not show cart-extend-dialog onload * improve message if 0 minutes * do not save messae in session if ajax_dont_redirect * add ajax_dont_redirect to async_task_check_url * improve draw_deadline to only update #cart-deadline if necessary * add renew-confirmation-message --------- Co-authored-by: Richard Schreiber Co-authored-by: Raphael Michel --- src/pretix/base/services/cart.py | 4 +- src/pretix/base/templatetags/dialog.py | 33 +- src/pretix/base/views/tasks.py | 20 +- .../control/templates/pretixcontrol/base.html | 27 +- .../helpers/templatetags/simple_block_tag.py | 133 ++++++++ .../pretixpresale/event/fragment_cart.html | 11 +- .../event/fragment_cart_box.html | 2 +- .../pretixpresale/fragment_modals.html | 34 ++- src/pretix/presale/views/__init__.py | 3 + src/pretix/presale/views/cart.py | 10 +- src/pretix/static/pretixbase/js/asynctask.js | 288 ++++++++++-------- .../static/pretixbase/scss/_dialogs.scss | 174 +++++++++++ src/pretix/static/pretixbase/scss/_theme.scss | 114 ------- src/pretix/static/pretixcontrol/js/ui/main.js | 26 -- .../static/pretixcontrol/scss/main.scss | 86 +----- src/pretix/static/pretixpresale/js/ui/cart.js | 92 +++++- src/pretix/static/pretixpresale/js/ui/main.js | 2 - src/pretix/static/pretixpresale/js/ui/sso.js | 8 +- .../static/pretixpresale/scss/main.scss | 110 +------ 19 files changed, 621 insertions(+), 556 deletions(-) create mode 100644 src/pretix/helpers/templatetags/simple_block_tag.py create mode 100644 src/pretix/static/pretixbase/scss/_dialogs.scss diff --git a/src/pretix/base/services/cart.py b/src/pretix/base/services/cart.py index 14099b8c1..5212b1307 100644 --- a/src/pretix/base/services/cart.py +++ b/src/pretix/base/services/cart.py @@ -1655,7 +1655,7 @@ def clear_cart(self, event: Event, cart_id: str=None, locale='en', sales_channel @app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(CartError,)) -def extend_cart_reservation(self, event: Event, cart_id: str=None, locale='en', sales_channel='web', override_now_dt: datetime=None) -> None: +def extend_cart_reservation(self, event: Event, cart_id: str=None, locale='en', sales_channel='web', override_now_dt: datetime=None) -> dict: """ Resets the expiry time of a cart to the configured reservation time of this event. Limited to 11x the reservation time. @@ -1672,7 +1672,7 @@ def extend_cart_reservation(self, event: Event, cart_id: str=None, locale='en', try: cm = CartManager(event=event, cart_id=cart_id, sales_channel=sales_channel) cm.commit() - return cm.num_extended_positions + return {"success": cm.num_extended_positions, "expiry": cm._expiry, "max_expiry_extend": cm._max_expiry_extend} except LockTimeoutException: self.retry() except (MaxRetriesExceededError, LockTimeoutException): diff --git a/src/pretix/base/templatetags/dialog.py b/src/pretix/base/templatetags/dialog.py index 16805cecc..0cc422455 100644 --- a/src/pretix/base/templatetags/dialog.py +++ b/src/pretix/base/templatetags/dialog.py @@ -23,38 +23,39 @@ from django import template from django.utils.html import format_html from django.utils.safestring import mark_safe +from pretix.helpers.templatetags.simple_block_tag import ( + register_simple_block_tag, +) + from django.utils.translation import gettext_lazy as _ # NOQA + register = template.Library() -@register.simple_tag -def dialog(html_id, label, description, *args, **kwargs): +@register_simple_block_tag(register) +def dialog(content, html_id, title, description, *args, **kwargs): format_kwargs = { "id": html_id, - "label": label, + "title": title, "description": description, "icon": format_html('', kwargs["icon"]) if "icon" in kwargs else "", "alert": mark_safe('role="alertdialog"') if kwargs.get("alert", "False") != "False" else "", + "content": content, } result = """ - - """) + """ + return format_html(result, **format_kwargs) diff --git a/src/pretix/base/views/tasks.py b/src/pretix/base/views/tasks.py index 8a9e6e909..949cef639 100644 --- a/src/pretix/base/views/tasks.py +++ b/src/pretix/base/views/tasks.py @@ -68,7 +68,7 @@ class AsyncMixin: def get_check_url(self, task_id, ajax): return self.request.path + '?async_id=%s' % task_id + ('&ajax=1' if ajax else '') - def _ajax_response_data(self): + def _ajax_response_data(self, value): return {} def _return_ajax_result(self, res, timeout=.5): @@ -85,7 +85,7 @@ class AsyncMixin: logger.warning('Ignored ResponseError in AsyncResult.get()') except ConnectionError: # Redis probably just restarted, let's just report not ready and retry next time - data = self._ajax_response_data() + data = self._ajax_response_data(None) data.update({ 'async_id': res.id, 'ready': False @@ -93,7 +93,7 @@ class AsyncMixin: return data state, info = res.state, res.info - data = self._ajax_response_data() + data = self._ajax_response_data(info) data.update({ 'async_id': res.id, 'ready': ready, @@ -102,23 +102,21 @@ class AsyncMixin: if ready: if state == states.SUCCESS and not isinstance(info, Exception): smes = self.get_success_message(info) - if smes: + if smes and 'ajax_dont_redirect' not in self.request.GET and 'ajax_dont_redirect' not in self.request.POST: messages.success(self.request, smes) - # TODO: Do not store message if the ajax client states that it will not redirect - # but handle the message itself data.update({ 'redirect': self.get_success_url(info), 'success': True, - 'message': str(self.get_success_message(info)) + 'message': str(smes) }) else: - messages.error(self.request, self.get_error_message(info)) - # TODO: Do not store message if the ajax client states that it will not redirect - # but handle the message itself + smes = self.get_error_message(info) + if smes and 'ajax_dont_redirect' not in self.request.GET and 'ajax_dont_redirect' not in self.request.POST: + messages.error(self.request, smes) data.update({ 'redirect': self.get_error_url(), 'success': False, - 'message': str(self.get_error_message(info)) + 'message': str(smes) }) elif state == 'PROGRESS': data.update({ diff --git a/src/pretix/control/templates/pretixcontrol/base.html b/src/pretix/control/templates/pretixcontrol/base.html index 79d9c5f52..edd858c33 100644 --- a/src/pretix/control/templates/pretixcontrol/base.html +++ b/src/pretix/control/templates/pretixcontrol/base.html @@ -4,6 +4,8 @@ {% load statici18n %} {% load eventsignal %} {% load eventurl %} +{% load dialog %} +{% load icon %} @@ -463,25 +465,16 @@ -
+ -
-
-