From 71b358857735393968c9b7ea18fb2ea7485cd4b0 Mon Sep 17 00:00:00 2001 From: imperosol Date: Wed, 19 Feb 2025 19:29:18 +0100 Subject: [PATCH] Add a "see more" button on news dates list --- .../components/upcoming-news-loader-index.ts | 67 ++++++ com/static/com/css/news-list.scss | 7 + com/templates/com/news_list.jinja | 192 ++++++++++++------ com/views.py | 26 ++- locale/fr/LC_MESSAGES/django.po | 18 +- sith/settings.py | 1 + 6 files changed, 239 insertions(+), 72 deletions(-) create mode 100644 com/static/bundled/com/components/upcoming-news-loader-index.ts diff --git a/com/static/bundled/com/components/upcoming-news-loader-index.ts b/com/static/bundled/com/components/upcoming-news-loader-index.ts new file mode 100644 index 00000000..ccc1e714 --- /dev/null +++ b/com/static/bundled/com/components/upcoming-news-loader-index.ts @@ -0,0 +1,67 @@ +import { type NewsDateSchema, newsFetchNewsDates } from "#openapi"; + +interface ParsedNewsDateSchema extends Omit { + // biome-ignore lint/style/useNamingConvention: api is snake_case + start_date: Date; + // biome-ignore lint/style/useNamingConvention: api is snake_case + end_date: Date; +} + +document.addEventListener("alpine:init", () => { + Alpine.data("upcomingNewsLoader", (startDate: Date) => ({ + startDate: startDate, + currentPage: 1, + pageSize: 6, + hasNext: true, + loading: false, + newsDates: [] as NewsDateSchema[], + + async loadMore() { + this.loading = true; + const response = await newsFetchNewsDates({ + query: { + after: this.startDate.toISOString(), + // biome-ignore lint/style/useNamingConvention: api is snake_case + text_format: "html", + page: this.currentPage, + // biome-ignore lint/style/useNamingConvention: api is snake_case + page_size: this.pageSize, + }, + }); + if (response.response.status === 404) { + this.hasNext = false; + } else if (response.data.next === null) { + this.newsDates.push(...response.data.results); + this.hasNext = false; + } else { + this.newsDates.push(...response.data.results); + this.currentPage += 1; + } + this.loading = false; + }, + + groupedDates(): Record { + return this.newsDates + .map( + (date: NewsDateSchema): ParsedNewsDateSchema => ({ + ...date, + // biome-ignore lint/style/useNamingConvention: api is snake_case + start_date: new Date(date.start_date), + // biome-ignore lint/style/useNamingConvention: api is snake_case + end_date: new Date(date.end_date), + }), + ) + .reduce( + (acc: Record, date: ParsedNewsDateSchema) => { + const key = date.start_date.toDateString(); + if (!acc[key]) { + acc[key] = []; + } + acc[key].push(date); + return acc; + }, + {}, + ); + }, + })); +}); diff --git a/com/static/com/css/news-list.scss b/com/static/com/css/news-list.scss index 5b8cc8d5..40da2157 100644 --- a/com/static/com/css/news-list.scss +++ b/com/static/com/css/news-list.scss @@ -56,6 +56,13 @@ #upcoming-events { max-height: 600px; overflow-y: scroll; + + #load-more-news-button { + text-align: center; + button { + width: 150px; + } + } } /* LINKS/BIRTHDAYS */ diff --git a/com/templates/com/news_list.jinja b/com/templates/com/news_list.jinja index beaae030..a3d39f25 100644 --- a/com/templates/com/news_list.jinja +++ b/com/templates/com/news_list.jinja @@ -16,6 +16,7 @@ {% block additional_js %} + {% endblock %} {% block content %} @@ -38,69 +39,140 @@
{% endif %}
- {% for day, dates_group in news_dates %} -
-
-
-
{{ day|date('D') }}
-
{{ day|date('d') }}
-
{{ day|date('b') }}
-
-
-
- {% for date in dates_group %} -
- {% if not date.news.is_moderated %} - {# if a non moderated news is in the object list, - the logged user is either an admin or the news author #} - {{ news_moderation_alert(date.news, user, "newsState") }} - {% endif %} -
-
- {% if date.news.club.logo %} - {{ date.news.club }} - {% else %} - {{ date.news.club }} - {% endif %} -
-

- - {{ date.news.title }} - -

- {{ date.news.club }} -
- - - -
-
-
-
- {{ date.news.summary|markdown }} -
-
-
- {% endfor %} -
-
- {% else %} + {% if not news_dates %}
{% trans %}Nothing to come...{% endtrans %}
- {% endfor %} + {% else %} + {% for day, dates_group in news_dates %} +
+
+
+
{{ day|date('D') }}
+
{{ day|date('d') }}
+
{{ day|date('b') }}
+
+
+
+ {% for date in dates_group %} +
+ {% if not date.news.is_moderated %} + {# if a non moderated news is in the object list, + the logged user is either an admin or the news author #} + {{ news_moderation_alert(date.news, user, "newsState") }} + {% endif %} +
+
+ {% if date.news.club.logo %} + {{ date.news.club }} + {% else %} + {{ date.news.club }} + {% endif %} +
+

+ + {{ date.news.title }} + +

+ {{ date.news.club }} +
+ - + +
+
+
+
+ {{ date.news.summary|markdown }} +
+
+
+ {% endfor %} +
+
+ {% endfor %} +
+ + +
+ +

+ + {% trans trimmed %} + It was too short. + You already reached the end of the upcoming events list. + {% endtrans %} + +

+
+
+ {% endif %}

diff --git a/com/views.py b/com/views.py index 7df0c1bd..ac29eb4a 100644 --- a/com/views.py +++ b/com/views.py @@ -253,33 +253,41 @@ class NewsListView(TemplateView): key=lambda u: u.date_of_birth.year, ) - def get_news_dates(self): - """Return the event dates to display. + def get_last_day(self) -> date: + """Get the last day when news will be displayed - The selected events are the ones that happens in the next 3 days - where something happen. + The returned day is the third one where something happen. For example, if there are 6 events : A on 15/03, B and C on 17/03, - D on 20/03, E on 21/03 and F on 22/03 ; then the displayed dates - will be A, B, C and D. + D on 20/03, E on 21/03 and F on 22/03 ; + then the result is 20/03. """ - last_date: date = list( + return list( NewsDate.objects.filter(end_date__gt=now()) .order_by("start_date") .values_list("start_date__date", flat=True) .distinct()[:4] )[-1] + + def get_news_dates(self, until: date): + """Return the event dates to display. + + The selected events are the ones that happens between + right now and the given day (included). + """ return itertools.groupby( NewsDate.objects.viewable_by(self.request.user) - .filter(end_date__gt=now(), start_date__date__lte=last_date) + .filter(end_date__gt=now(), start_date__date__lte=until) .order_by("start_date") .select_related("news", "news__club"), key=lambda d: d.start_date.date(), ) def get_context_data(self, **kwargs): + last_day = self.get_last_day() return super().get_context_data(**kwargs) | { - "news_dates": self.get_news_dates(), + "news_dates": self.get_news_dates(until=last_day), "birthdays": self.get_birthdays(), + "last_day": last_day, } diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index ea259ce0..bf44d1d5 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-18 15:03+0100\n" +"POT-Creation-Date: 2025-02-19 19:12+0100\n" "PO-Revision-Date: 2016-07-18\n" "Last-Translator: Maréchal \n" @@ -1429,11 +1429,11 @@ msgstr "" #: com/templates/com/macros.jinja #, python-format msgid "" -"This event will take place every week for %(nb)s weeks. If you moderate or " +"This event will take place every week for %%s weeks. If you moderate or " "delete this event, it will also be moderated (or deleted) for the following " "weeks." msgstr "" -"Cet événement se déroulera chaque semaine pendant %(nb)s semaines. Si vous " +"Cet événement se déroulera chaque semaine pendant %%s semaines. Si vous " "modérez ou supprimez cet événement, il sera également modéré (ou supprimé) " "pour les semaines suivantes." @@ -1578,6 +1578,18 @@ msgstr "Administrer les news" msgid "Nothing to come..." msgstr "Rien à venir..." +#: com/templates/com/news_list.jinja +msgid "See more" +msgstr "Voir plus" + +#: com/templates/com/news_list.jinja +msgid "" +"It was too short. You already reached the end of the upcoming events " +"list." +msgstr "" +"C'était trop court. Vous êtes déjà arrivés à la fin de la liste des " +"événements à venir." + #: com/templates/com/news_list.jinja msgid "All coming events" msgstr "Tous les événements à venir" diff --git a/sith/settings.py b/sith/settings.py index 9cd3ca89..8191251f 100644 --- a/sith/settings.py +++ b/sith/settings.py @@ -171,6 +171,7 @@ TEMPLATES = [ "timezone": "django.utils.timezone", "get_sith": "com.views.sith", "get_language": "django.utils.translation.get_language", + "timedelta": "datetime.timedelta", }, "bytecode_cache": { "name": "default",