From bdd9751f0e5c440d1c8b56c933db2288e4014f4c Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Sat, 10 Jul 2021 21:37:27 +0200 Subject: [PATCH] Widget: remove limit of 50 events in list-view by adding a load-more-button (#2144) --- src/pretix/base/models/event.py | 5 +++++ src/pretix/presale/views/widget.py | 16 +++++++++++++- .../static/pretixpresale/js/widget/widget.js | 22 ++++++++++++++++++- .../static/pretixpresale/scss/widget.scss | 13 +++++++++++ src/tests/presale/test_widget.py | 1 + 5 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py index b02c343af..b35893a3d 100644 --- a/src/pretix/base/models/event.py +++ b/src/pretix/base/models/event.py @@ -1004,6 +1004,11 @@ class Event(EventMixin, LoggedModel): | Q(date_to__gte=now() - timedelta(hours=24)) ) ) # order_by doesn't make sense with I18nField + if ordering in ("date_ascending", "date_descending"): + # if primary order is by date, then order in database + # this allows to limit/slice results + return subevs.order_by(*orderfields) + for f in reversed(orderfields): if f.startswith('-'): subevs = sorted(subevs, key=attrgetter(f[1:]), reverse=True) diff --git a/src/pretix/presale/views/widget.py b/src/pretix/presale/views/widget.py index 03ac67b60..69ba1c906 100644 --- a/src/pretix/presale/views/widget.py +++ b/src/pretix/presale/views/widget.py @@ -422,7 +422,10 @@ class WidgetAPIProductList(EventListMixin, View): data['list_type'] = list_type if hasattr(self.request, 'event') and data['list_type'] not in ("calendar", "week"): - if self.request.event.subevents.filter(date_from__gt=now()).count() > 50: + # only allow list-view of more than 50 subevents if ordering is by data as this can be done in the database + # ordering by name is currently not supported in database due to I18NField-JSON + ordering = self.request.event.settings.get('frontpage_subevent_ordering', default='date_ascending', as_type=str) + if ordering not in ("date_ascending", "date_descending") and self.request.event.subevents.filter(date_from__gt=now()).count() > 50: if self.request.event.settings.event_list_type not in ("calendar", "week"): self.request.event.settings.event_list_type = "calendar" data['list_type'] = list_type = 'calendar' @@ -537,10 +540,21 @@ class WidgetAPIProductList(EventListMixin, View): for d in data['days']: d['events'] = self._serialize_events(d['events'] or []) else: + offset = int(self.request.GET.get("offset", 0)) + limit = 50 if hasattr(self.request, 'event'): evs = self.request.event.subevents_sorted( filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier), self.request) ) + ordering = self.request.event.settings.get('frontpage_subevent_ordering', default='date_ascending', as_type=str) + data['has_more_events'] = False + if ordering in ("date_ascending", "date_descending"): + # fetch one more result than needed to check if more events exist + evs = list(evs[offset:offset + limit + 1]) + if len(evs) > limit: + data['has_more_events'] = True + evs = evs[:limit] + tz = pytz.timezone(request.event.settings.timezone) if self.request.event.settings.event_list_available_only: evs = [ diff --git a/src/pretix/static/pretixpresale/js/widget/widget.js b/src/pretix/static/pretixpresale/js/widget/widget.js index 5bf266d28..fd15d132a 100644 --- a/src/pretix/static/pretixpresale/js/widget/widget.js +++ b/src/pretix/static/pretixpresale/js/widget/widget.js @@ -53,6 +53,7 @@ var strings = { 'next_week': django.pgettext('widget', 'Next week'), 'previous_week': django.pgettext('widget', 'Previous week'), 'show_seating': django.pgettext('widget', 'Open seat selection'), + 'load_more': django.pgettext('widget', 'Load more'), 'days': { 'MO': django.gettext('Mo'), 'TU': django.gettext('Tu'), @@ -854,6 +855,8 @@ Vue.component('pretix-widget-event-form', { this.$root.target_url = this.$root.parent_stack.pop(); this.$root.error = null; this.$root.subevent = null; + this.$root.offset = 0; + this.$root.append_events = false; this.$root.trigger_load_callback(); if (this.$root.events !== undefined && this.$root.events !== null) { this.$root.view = "events"; @@ -938,9 +941,12 @@ Vue.component('pretix-widget-event-list', { + '' + '
' + '' + + '

' + ''), methods: { back_to_calendar: function () { + this.$root.offset = 0; + this.$root.append_events = false; if (this.$root.weeks) { this.$root.events = undefined; this.$root.view = "weeks"; @@ -953,6 +959,12 @@ Vue.component('pretix-widget-event-list', { this.$root.reload(); } }, + load_more: function () { + this.$root.append_events = true; + this.$root.offset += 50; + this.$root.loading++; + this.$root.reload(); + } } }); @@ -1353,6 +1365,9 @@ var shared_root_methods = { } else { url = this.$root.target_url + 'widget/product_list?lang=' + lang; } + if (this.$root.offset) { + url += '&offset=' + this.$root.offset; + } if (this.$root.filter) { url += '&' + this.$root.filter; } @@ -1408,11 +1423,13 @@ var shared_root_methods = { root.name = data.name; root.frontpage_text = data.frontpage_text; } else if (data.events !== undefined) { - root.events = data.events; + root.events = root.append_events && root.events ? root.events.concat(data.events) : data.events; + root.append_events = false; root.weeks = undefined; root.view = "events"; root.name = data.name; root.frontpage_text = data.frontpage_text; + root.has_more_events = data.has_more_events; } else { root.view = "event"; root.name = data.name; @@ -1647,6 +1664,9 @@ var create_widget = function (element) { currency: null, name: null, date_range: null, + offset: 0, + has_more_events: false, + append_events: false, frontpage_text: null, filter: filter, item_filter: items, diff --git a/src/pretix/static/pretixpresale/scss/widget.scss b/src/pretix/static/pretixpresale/scss/widget.scss index a80da38db..aaa16bb88 100644 --- a/src/pretix/static/pretixpresale/scss/widget.scss +++ b/src/pretix/static/pretixpresale/scss/widget.scss @@ -172,6 +172,15 @@ .pretix-widget-loading svg { margin: 40px; + /*Fallback*/ + position: absolute; + top: 50%; + margin-top: -64px; + /*Sticky*/ + position: -webkit-sticky; + position: sticky; + top: Min(50vh, 50%);/* use uppercase M to use CSS-min and not SASS-min*/ + -webkit-animation: pretix-widget-spin 6s linear infinite; -moz-animation: pretix-widget-spin 6s linear infinite; animation: pretix-widget-spin 6s linear infinite; @@ -699,6 +708,10 @@ fill: $brand-primary; } +.pretix-widget-event-list-load-more { + text-align: center; +} + .pretix-widget.pretix-widget-mobile { .pretix-widget-event-week-table { display: block; diff --git a/src/tests/presale/test_widget.py b/src/tests/presale/test_widget.py index 387b1f884..6dc61af5b 100644 --- a/src/tests/presale/test_widget.py +++ b/src/tests/presale/test_widget.py @@ -520,6 +520,7 @@ class WidgetCartTest(CartTestMixin, TestCase): 'name': '30C3', 'frontpage_text': '', 'poweredby': 'ticketing powered by pretix', + 'has_more_events': False, 'events': [ {'name': 'Present', 'date_range': 'Jan. 1, 2019 11:00', 'availability': {'color': 'none', 'text': '', 'reason': 'unknown'}, 'event_url': 'http://example.com/ccc/30c3/', 'subevent': se1.pk, 'location': ''},