Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: upcoming events list component #463

Merged
merged 4 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions assets/javascripts/discourse/components/upcoming-events-list.gjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import DButton from "discourse/components/d-button";
import Component from "@glimmer/component";
import { ajax } from "discourse/lib/ajax";
import I18n from "discourse-i18n";
import { tracked } from "@glimmer/tracking";
import { inject as service } from "@ember/service";
import { action } from "@ember/object";
import { isNotFullDayEvent } from "../lib/guess-best-date-format";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import or from "truth-helpers/helpers/or";

export const DEFAULT_MONTH_FORMAT = "MMMM YYYY";
export const DEFAULT_DATE_FORMAT = "dddd, MMM D";
export const DEFAULT_TIME_FORMAT = "LT";

export default class UpcomingEventsList extends Component {
@service appEvents;
@service siteSettings;
@service router;

@tracked isLoading = true;
@tracked hasError = false;
@tracked eventsByMonth = {};

monthFormat = this.args.params?.monthFormat ?? DEFAULT_MONTH_FORMAT;
dateFormat = this.args.params?.dateFormat ?? DEFAULT_DATE_FORMAT;
timeFormat = this.args.params?.timeFormat ?? DEFAULT_TIME_FORMAT;

title = I18n.t("discourse_post_event.upcoming_events_list.title");
emptyMessage = I18n.t("discourse_post_event.upcoming_events_list.empty");
allDayLabel = I18n.t("discourse_post_event.upcoming_events_list.all_day");
errorMessage = I18n.t("discourse_post_event.upcoming_events_list.error");

constructor() {
super(...arguments);

this.appEvents.on("page:changed", this, this.updateEventsByMonth);
}

get shouldRender() {
if (!this.categoryId) {
return false;
}

const eventSettings =
this.siteSettings.events_calendar_categories.split("|");

return eventSettings.includes(this.categoryId.toString());
}

get categoryId() {
return this.router.currentRoute.attributes?.category?.id;
}

get hasEmptyResponse() {
return (
!this.isLoading &&
!this.hasError &&
Object.keys(this.eventsByMonth).length === 0
);
}

@action
async updateEventsByMonth() {
this.isLoading = true;
this.hasError = false;

try {
const { events } = await ajax("/discourse-post-event/events", {
data: { category_id: this.categoryId },
});

this.eventsByMonth = this.groupByMonthAndDay(events);
} catch {
this.hasError = true;
} finally {
this.isLoading = false;
}
}

@action
formatMonth(month) {
return moment(month, "YYYY-MM").format(this.monthFormat);
}

@action
formatDate(month, day) {
return moment(`${month}-${day}`, "YYYY-MM-DD").format(this.dateFormat);
}

@action
formatTime({ starts_at, ends_at }) {
return isNotFullDayEvent(moment(starts_at), moment(ends_at))
? moment(starts_at).format(this.timeFormat)
: this.allDayLabel;
}

groupByMonthAndDay(data) {
return data.reduce((result, item) => {
const date = new Date(item.starts_at);
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();

const monthKey = `${year}-${month}`;

result[monthKey] = result[monthKey] ?? {};
result[monthKey][day] = result[monthKey][day] ?? [];

result[monthKey][day].push(item);

return result;
}, {});
}

<template>
{{#if this.shouldRender}}
<div class="upcoming-events-list">
<h3 class="upcoming-events-list__heading">
{{this.title}}
</h3>

<div class="upcoming-events-list__container">
<ConditionalLoadingSpinner @condition={{this.isLoading}} />

{{#if this.hasEmptyResponse}}
<div class="upcoming-events-list__empty-message">
{{this.emptyMessage}}
</div>
{{/if}}

{{#if this.hasError}}
<div class="upcoming-events-list__error-message">
{{this.errorMessage}}
</div>
<DButton
@action={{this.updateEventsByMonth}}
@label="discourse_post_event.upcoming_events_list.try_again"
class="btn-link upcoming-events-list__try-again"
/>
{{/if}}

{{#unless this.isLoading}}
{{#each-in this.eventsByMonth as |month monthData|}}
{{#if this.monthFormat}}
<h4 class="upcoming-events-list__formatted-month">
{{this.formatMonth month}}
</h4>
{{/if}}

{{#each-in monthData as |day events|}}
<div class="upcoming-events-list__day-section">
<div class="upcoming-events-list__formatted-day">
{{this.formatDate month day}}
</div>

{{#each events as |event|}}
<a
class="upcoming-events-list__event"
href={{event.post.url}}
>
<div class="upcoming-events-list__event-time">
{{this.formatTime event}}
</div>
<div class="upcoming-events-list__event-name">
{{or event.name event.post.topic.title}}
</div>
</a>
{{/each}}
</div>
{{/each-in}}
{{/each-in}}
{{/unless}}
</div>
</div>
{{/if}}
</template>
}
2 changes: 2 additions & 0 deletions assets/stylesheets/common/upcoming-events-calendar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
background: var(--tertiary);
color: var(--secondary);
}

margin: 0.3em 0 0.3em 0.5em;
}

Expand Down Expand Up @@ -122,6 +123,7 @@
margin-left: 0;
}
}

.fc-list-item-add-to-calendar {
color: var(--tertiary);
font-size: var(--font-down-1);
Expand Down
36 changes: 36 additions & 0 deletions assets/stylesheets/common/upcoming-events-list.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.upcoming-events-list {
&__formatted-month {
font-weight: 600;
}

&__day-section {
display: flex;
flex-direction: column;
padding-bottom: 0.5rem;
gap: 0.5rem;
}

&__formatted-day {
margin-left: 0.5rem;
font-weight: 600;
font-size: var(--base-font-size);
}

&__event {
display: flex;
align-items: center;
margin-left: 1rem;
gap: 0.25rem;
font-size: var(--font-down-1);
line-height: var(--line-height-medium);
}

&__event-name {
width: 70%;
}

&__event-time {
width: 30%;
text-align: center;
}
}
6 changes: 6 additions & 0 deletions config/locales/client.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,12 @@ en:
creator: "Creator"
status: "Status"
starts_at: "Starts at"
upcoming_events_list:
title: "Upcoming events"
empty: "No upcoming events"
all_day: "All-day"
error: "Failed to retrieve events"
try_again: "Try again"
category:
sort_topics_by_event_start_date: "Sort topics by event start date."
disable_topic_resorting: "Disable topic resorting."
Expand Down
1 change: 1 addition & 0 deletions plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
register_asset "stylesheets/desktop/discourse-calendar.scss", :desktop
register_asset "stylesheets/colors.scss", :color_definitions
register_asset "stylesheets/common/user-preferences.scss"
register_asset "stylesheets/common/upcoming-events-list.scss"
register_svg_icon "fas fa-calendar-day"
register_svg_icon "fas fa-clock"
register_svg_icon "fas fa-file-csv"
Expand Down
Loading