From b0da77d6f4096a961b7f442e45aa24a909283154 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 4 Sep 2024 07:12:32 -0400 Subject: [PATCH 01/30] convert Event Form Component to Glimmer gjs --- .../discourse/components/event-form.gjs | 277 ++++++++++++++++++ .../discourse/components/event-form.hbs | 132 --------- .../discourse/components/event-form.js | 140 --------- .../discourse/components/modal/add-event.hbs | 2 +- .../discourse/components/modal/add-event.js | 42 +-- 5 files changed, 300 insertions(+), 293 deletions(-) create mode 100644 assets/javascripts/discourse/components/event-form.gjs delete mode 100644 assets/javascripts/discourse/components/event-form.hbs delete mode 100644 assets/javascripts/discourse/components/event-form.js diff --git a/assets/javascripts/discourse/components/event-form.gjs b/assets/javascripts/discourse/components/event-form.gjs new file mode 100644 index 00000000..e3cc2fad --- /dev/null +++ b/assets/javascripts/discourse/components/event-form.gjs @@ -0,0 +1,277 @@ +// app/components/event-form.gjs +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { hash } from "rsvp"; +import { tracked } from '@glimmer/tracking'; +import { service } from "@ember/service"; +import { fn, on } from "@ember/modifier"; +import concatClass from "discourse/helpers/concat-class"; +// import moment from 'moment'; +import i18n from "discourse-common/helpers/i18n"; +import ComboBox from "select-kit/components/combo-box"; +import InputTip from "discourse/components/input-tip"; +import DateInput from "discourse/components/date-input"; +import TimeInput from "discourse/components/time-input"; +import EmailGroupUserChooser from "select-kit/components/email-group-user-chooser"; + +import { compileEvent, nextInterval, setupEventForm, timezoneLabel } from '../lib/date-utilities'; + +export default class EventForm extends Component { + @service siteSettings; + @service site; + @tracked endEnabled = false; + @tracked allDay = false; + @tracked showTimezone = false; + @tracked startDate; + @tracked startTime; + @tracked endDate; + @tracked endTime; + @tracked timezone; + @tracked rsvpEnabled = false; + @tracked goingMax; + @tracked usersGoing; + + constructor() { + super(...arguments); + this.setupProperties(); + } + + @action + setupProperties() { + const props = setupEventForm(this.args.event, { + siteSettings: this.siteSettings, + }); + Object.assign(this, props); + if ( + this.siteSettings.events_add_default_end_time && + !this.args.event && + !this.endDate && + !this.endTime + ) { + this.toggleEndEnabled(true); + } + } + + get timezones() { + const eventTimezones = this.args.event?.eventTimezones || this.site.event_timezones; + return eventTimezones.map((tz) => ({ + value: tz.value, + name: timezoneLabel(tz.value, { siteSettings: this.siteSettings }), + })); + } + + @action + onChangeStartDate(date) { + this.startDate = moment(date); + this.updateEvent(); + } + + @action + onChangeEndDate(date) { + this.endDate = moment(date); + this.updateEvent(); + } + + @action + onChangeStartTime(time) { + this.startTime = moment(time); + this.updateEvent(); + } + + @action + onChangeEndTime(time) { + this.endTime = moment(time); + this.updateEvent(); + } + + @action + toggleEndEnabled(event) { + this.endEnabled = event.target.checked; + if (this.endEnabled) { + if (!this.endDate) { + this.endDate = this.startDate; + } + if (!this.allDay && !this.endTime) { + const start = moment( + `${moment(this.startDate).format('YYYY-MM-DD')} ${this.startTime.format('HH:mm')}` + ); + this.endTime = moment(start).add(1, 'hours'); + } + } else { + this.endDate = undefined; + this.endTime = undefined; + } + this.updateEvent(); + } + + @action + toggleAllDay(event) { + this.allDay = event.target.checked; + if (!this.allDay) { + const start = nextInterval(); + this.startTime = start; + if (this.endEnabled) { + this.endTime = moment(start).add(1, 'hours'); + } + } + this.updateEvent(); + } + + @action + updateEvent() { + const event = compileEvent({ + startDate: this.startDate, + startTime: this.startTime, + endDate: this.endDate, + endTime: this.endTime, + endEnabled: this.endEnabled, + allDay: this.allDay, + timezone: this.timezone, + rsvpEnabled: this.rsvpEnabled, + goingMax: this.goingMax, + usersGoing: this.usersGoing, + }); + this.args.updateEvent(event, this.eventValid(event)); + } + + @action + eventValid(event) { + return !event || !event.end || moment(event.end).isSameOrAfter(event.start); + } + +} diff --git a/assets/javascripts/discourse/components/event-form.hbs b/assets/javascripts/discourse/components/event-form.hbs deleted file mode 100644 index 3397fa7e..00000000 --- a/assets/javascripts/discourse/components/event-form.hbs +++ /dev/null @@ -1,132 +0,0 @@ -
-
- - {{i18n "add_event.end_enabled"}} -
- -
- - {{i18n "add_event.all_day"}} -
- - {{#unless allDay}} -
- -
- {{/unless}} -
- -
-
- - {{i18n "add_event.event_start"}} - - - - -
-
- - - -
- - {{#unless allDay}} -
- - - -
- {{/unless}} -
-
- -
- - {{i18n "add_event.event_end"}} - - - - -
-
- - - -
- - {{#unless allDay}} -
- - - -
- {{/unless}} -
-
-
- -{{#if siteSettings.events_rsvp}} -
-
- {{input type="checkbox" checked=rsvpEnabled}} - {{i18n "add_event.rsvp_enabled"}} -
- - {{#if rsvpEnabled}} -
-
- {{i18n "add_event.going_max"}} - {{input type="number" value=goingMax}} -
- -
- {{i18n "add_event.going"}} - -
-
- {{/if}} -
-{{/if}} \ No newline at end of file diff --git a/assets/javascripts/discourse/components/event-form.js b/assets/javascripts/discourse/components/event-form.js deleted file mode 100644 index 7cd948a0..00000000 --- a/assets/javascripts/discourse/components/event-form.js +++ /dev/null @@ -1,140 +0,0 @@ -import Component from "@ember/component"; -import { - default as discourseComputed, - observes, -} from "discourse-common/utils/decorators"; -import { - compileEvent, - nextInterval, - setupEventForm, - timezoneLabel, -} from "../lib/date-utilities"; - -export default Component.extend({ - classNames: "event-form", - endEnabled: false, - allDay: false, - showTimezone: false, - - didReceiveAttrs() { - this._super(...arguments); - const props = setupEventForm(this.event, { - siteSettings: this.siteSettings, - }); - this.setProperties(props); - if ( - this.siteSettings.events_add_default_end_time && - !this.event && - !this.endDate && - !this.endTime - ) { - this.send("toggleEndEnabled", true); - } - }, - - eventValid(event) { - return !event || !event.end || moment(event.end).isSameOrAfter(event.start); - }, - - @observes( - "startDate", - "startTime", - "endDate", - "endTime", - "endEnabled", - "allDay", - "timezone", - "rsvpEnabled", - "goingMax", - "usersGoing" - ) - eventUpdated() { - let event = compileEvent({ - startDate: this.startDate, - startTime: this.startTime, - endDate: this.endDate, - endTime: this.endTime, - endEnabled: this.endEnabled, - allDay: this.allDay, - timezone: this.timezone, - rsvpEnabled: this.rsvpEnabled, - goingMax: this.goingMax, - usersGoing: this.usersGoing, - }); - this.updateEvent(event, this.eventValid(event)); - }, - - @discourseComputed() - timezones() { - const eventTimezones = - this.get("eventTimezones") || this.site.event_timezones; - return eventTimezones.map((tz) => { - return { - value: tz.value, - name: timezoneLabel(tz.value, { siteSettings: this.siteSettings }), - }; - }); - }, - - @discourseComputed("endEnabled") - endClass(endEnabled) { - return endEnabled ? "" : "disabled"; - }, - - actions: { - onChangeStartDate(date) { - this.set("startDate", moment(date)); - }, - - onChangeEndDate(date) { - this.set("endDate", moment(date)); - }, - - onChangeStartTime(time) { - this.set("startTime", moment(time)); - }, - - onChangeEndTime(time) { - this.set("endTime", moment(time)); - }, - - toggleEndEnabled(value) { - this.set("endEnabled", value); - - if (value) { - if (!this.endDate) { - this.set("endDate", this.startDate); - } - - if (!this.allDay) { - if (!this.endTime) { - let start = moment( - moment(this.startDate).format("YYYY-MM-DD") + - " " + - this.startTime.format("HH:mm") - ); - this.set("endTime", moment(start).add(1, "hours")); - } - } - } else { - this.setProperties({ - endDate: undefined, - endTime: undefined, - }); - } - }, - - toggleAllDay(value) { - this.set("allDay", value); - - if (!value) { - const start = nextInterval(); - this.set("startTime", start); - - if (this.endEnabled) { - this.set("endTime", moment(start).add(1, "hours")); - } - } - }, - }, -}); diff --git a/assets/javascripts/discourse/components/modal/add-event.hbs b/assets/javascripts/discourse/components/modal/add-event.hbs index 4667fccc..47429e85 100644 --- a/assets/javascripts/discourse/components/modal/add-event.hbs +++ b/assets/javascripts/discourse/components/modal/add-event.hbs @@ -5,7 +5,7 @@ @flash={{this.flash}} > <:body> - + <:footer> Date: Wed, 4 Sep 2024 12:17:05 -0400 Subject: [PATCH 02/30] FEATURE: add deadline event option behind setting --- .../discourse/components/event-form.gjs | 244 ++++++++++-------- .../discourse/components/modal/add-event.js | 2 +- .../discourse/lib/date-utilities.js.es6 | 12 +- config/locales/client.en.yml | 3 + config/locales/server.en.yml | 1 + config/settings.yml | 3 + 6 files changed, 158 insertions(+), 107 deletions(-) diff --git a/assets/javascripts/discourse/components/event-form.gjs b/assets/javascripts/discourse/components/event-form.gjs index e3cc2fad..ec32c0e3 100644 --- a/assets/javascripts/discourse/components/event-form.gjs +++ b/assets/javascripts/discourse/components/event-form.gjs @@ -21,6 +21,7 @@ export default class EventForm extends Component { @service site; @tracked endEnabled = false; @tracked allDay = false; + @tracked deadline = false; @tracked showTimezone = false; @tracked startDate; @tracked startTime; @@ -48,7 +49,12 @@ export default class EventForm extends Component { !this.endDate && !this.endTime ) { - this.toggleEndEnabled(true); + this.endEnabled = true; + } + if ( + this.siteSettings.events_support_deadlines + ) { + this.toggleDeadlineEnabled; } } @@ -60,6 +66,10 @@ export default class EventForm extends Component { })); } + get showDeadlineToggle() { + return this.siteSettings.events_support_deadlines + } + @action onChangeStartDate(date) { this.startDate = moment(date); @@ -117,6 +127,19 @@ export default class EventForm extends Component { this.updateEvent(); } + @action + toggleDeadline(event) { + this.deadline = event.target.checked; + // if (!this.allDay) { + // const start = nextInterval(); + // this.startTime = start; + // if (this.endEnabled) { + // this.endTime = moment(start).add(1, 'hours'); + // } + // } + this.updateEvent(); + } + @action updateEvent() { const event = compileEvent({ @@ -126,6 +149,7 @@ export default class EventForm extends Component { endTime: this.endTime, endEnabled: this.endEnabled, allDay: this.allDay, + deadline: this.deadline, timezone: this.timezone, rsvpEnabled: this.rsvpEnabled, goingMax: this.goingMax, @@ -140,138 +164,150 @@ export default class EventForm extends Component { } } diff --git a/assets/javascripts/discourse/components/modal/add-event.js b/assets/javascripts/discourse/components/modal/add-event.js index 42bcb784..3f17068e 100644 --- a/assets/javascripts/discourse/components/modal/add-event.js +++ b/assets/javascripts/discourse/components/modal/add-event.js @@ -9,7 +9,7 @@ export default class AddEvent extends Component { @action clear() { - this.args.model.event?.preventDefault(); + event?.preventDefault(); this.bufferedEvent = null; } diff --git a/assets/javascripts/discourse/lib/date-utilities.js.es6 b/assets/javascripts/discourse/lib/date-utilities.js.es6 index 2f97db51..526c3b7a 100644 --- a/assets/javascripts/discourse/lib/date-utilities.js.es6 +++ b/assets/javascripts/discourse/lib/date-utilities.js.es6 @@ -295,6 +295,8 @@ function compileEvent(params) { } } + event.deadline = params.deadline || false; + return event; } @@ -374,12 +376,14 @@ function setupEvent(event, args = {}) { let start; let end; let allDay; + let deadline; let multiDay; let timezone; if (event) { start = moment(event["start"]); allDay = isAllDay(event); + deadline = event["deadline"] || false; if (event["end"]) { end = moment(event["end"]); @@ -399,7 +403,7 @@ function setupEvent(event, args = {}) { } } - return { start, end, allDay, multiDay, timezone }; + return { start, end, allDay, deadline, multiDay, timezone }; } function timezoneLabel(tz, args = {}) { @@ -429,7 +433,7 @@ function timezoneLabel(tz, args = {}) { } function setupEventForm(event, args = {}) { - const { start, end, allDay, timezone } = setupEvent( + const { start, end, allDay, deadline, timezone } = setupEvent( event, Object.assign(args, { useEventTimezone: true }) ); @@ -475,6 +479,10 @@ function setupEventForm(event, args = {}) { } } + if (event && event.deadline) { + props["deadline"] = deadline; + } + return props; } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index d1f9b3af..07dc5e3f 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -11,6 +11,9 @@ en: event_clear: Clear end_enabled: "Event end" all_day: "All day" + deadline: + label: "Is a deadline" + title: "Will show countdown in Topic and Topic List" timezone: "Timezone" no_timezone: "Select a timezone" rsvp_enabled: "Rsvp enabled" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 89193b12..d418f575 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -36,6 +36,7 @@ en: events_split_series_into_different_topics: "A seperate topic will be created for each event in a series." events_allow_moderator_management: "Allow moderators to administer events." events_add_default_end_time: "Add a default end time to events." + events_support_deadlines: "Offer option to create an event as a deadline, which will display a countdown in both Topic and Topic Lists" rss_description: events: "Events" diff --git a/config/settings.yml b/config/settings.yml index 6c2e987e..208ebd97 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -87,3 +87,6 @@ plugins: events_add_default_end_time: default: false client: true + events_support_deadlines: + default: false + client: true From 3186301d73b8ed863f7d9679526f99ce7d795b2f Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 4 Sep 2024 13:59:12 -0400 Subject: [PATCH 03/30] linting --- .../discourse/components/event-form.gjs | 15 +++++++-------- .../discourse/components/modal/add-event.js | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/assets/javascripts/discourse/components/event-form.gjs b/assets/javascripts/discourse/components/event-form.gjs index ec32c0e3..5e161048 100644 --- a/assets/javascripts/discourse/components/event-form.gjs +++ b/assets/javascripts/discourse/components/event-form.gjs @@ -1,19 +1,18 @@ // app/components/event-form.gjs import Component from '@glimmer/component'; -import { action } from '@ember/object'; -import { hash } from "rsvp"; import { tracked } from '@glimmer/tracking'; +import { on } from "@ember/modifier"; +import { action } from '@ember/object'; import { service } from "@ember/service"; -import { fn, on } from "@ember/modifier"; +import { hash } from "rsvp"; +import DateInput from "discourse/components/date-input"; +import InputTip from "discourse/components/input-tip"; +import TimeInput from "discourse/components/time-input"; import concatClass from "discourse/helpers/concat-class"; // import moment from 'moment'; import i18n from "discourse-common/helpers/i18n"; import ComboBox from "select-kit/components/combo-box"; -import InputTip from "discourse/components/input-tip"; -import DateInput from "discourse/components/date-input"; -import TimeInput from "discourse/components/time-input"; import EmailGroupUserChooser from "select-kit/components/email-group-user-chooser"; - import { compileEvent, nextInterval, setupEventForm, timezoneLabel } from '../lib/date-utilities'; export default class EventForm extends Component { @@ -67,7 +66,7 @@ export default class EventForm extends Component { } get showDeadlineToggle() { - return this.siteSettings.events_support_deadlines + return this.siteSettings.events_support_deadlines; } @action diff --git a/assets/javascripts/discourse/components/modal/add-event.js b/assets/javascripts/discourse/components/modal/add-event.js index 3f17068e..cbec7def 100644 --- a/assets/javascripts/discourse/components/modal/add-event.js +++ b/assets/javascripts/discourse/components/modal/add-event.js @@ -1,7 +1,7 @@ +import { tracked } from "@glimmer/tracking"; import Component from "@ember/component"; import { action } from "@ember/object"; import I18n from "I18n"; -import { tracked } from '@glimmer/tracking'; export default class AddEvent extends Component { @tracked bufferedEvent = this.args.model.event; @@ -28,4 +28,4 @@ export default class AddEvent extends Component { this.bufferedEvent = event; this.valid = valid; } -}; +} From b9590bae4aab5ca5b23710e707d1aaabc21b669e Mon Sep 17 00:00:00 2001 From: merefield Date: Sun, 8 Sep 2024 06:55:38 -0400 Subject: [PATCH 04/30] first working --- .../basic_event_serializer.rb | 1 + .../discourse/lib/date-utilities.js.es6 | 19 ++++++++++++++++++- config/locales/client.en.yml | 10 ++++++++++ config/locales/server.en.yml | 1 + config/settings.yml | 4 ++++ lib/discourse_events/event_creator.rb | 2 ++ lib/discourse_events/event_revisor.rb | 5 +++++ plugin.rb | 5 +++++ 8 files changed, 46 insertions(+), 1 deletion(-) diff --git a/app/serializers/discourse_events/basic_event_serializer.rb b/app/serializers/discourse_events/basic_event_serializer.rb index 33617319..ff1a103b 100644 --- a/app/serializers/discourse_events/basic_event_serializer.rb +++ b/app/serializers/discourse_events/basic_event_serializer.rb @@ -5,6 +5,7 @@ class BasicEventSerializer < ApplicationSerializer attributes :id, :start_time, :end_time, + :deadline, :name, :description, :status, diff --git a/assets/javascripts/discourse/lib/date-utilities.js.es6 b/assets/javascripts/discourse/lib/date-utilities.js.es6 index 526c3b7a..604b6c01 100644 --- a/assets/javascripts/discourse/lib/date-utilities.js.es6 +++ b/assets/javascripts/discourse/lib/date-utilities.js.es6 @@ -313,6 +313,7 @@ function eventLabel(event, args = {}) { iconClass += "no-date"; } let label = renderIcon("string", icon, { class: iconClass }); + let passedDue = false; if (!args.noText) { const { start, end, allDay, timezone } = setupEvent(event, args); @@ -363,10 +364,26 @@ function eventLabel(event, args = {}) { } } } + + passedDue = moment() > start; + + if (siteSettings.events_support_deadlines && event.deadline) { + const countdownIcon = siteSettings.events_event_countdown_icon; + const duration = passedDue ? 0 : moment.duration(start - moment()); + + let d = Math.floor(duration / (1000 * 60 * 60 * 24)); + let h = Math.floor((duration % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + let m = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60)); + + const timeLeft = `${d} ${I18n.t("add_event.deadline.units.day", { count: d })}, ${h} ${I18n.t("add_event.deadline.units.hour", { count: h})}, ${m} ${I18n.t("add_event.deadline.units.minute", { count: m })}`; + + label += renderIcon("string", countdownIcon); + label += `${timeLeft}`; + } } if (!args.noContainer) { - label = `${label}`; + label = `${label}`; } return label; diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 07dc5e3f..abe4aca7 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -14,6 +14,16 @@ en: deadline: label: "Is a deadline" title: "Will show countdown in Topic and Topic List" + units: + day: + one: "day" + other: "days" + hour: + one: "hour" + other: "hours" + minute: + one: "minute" + other: "minutes" timezone: "Timezone" no_timezone: "Select a timezone" rsvp_enabled: "Rsvp enabled" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index d418f575..71147fde 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -37,6 +37,7 @@ en: events_allow_moderator_management: "Allow moderators to administer events." events_add_default_end_time: "Add a default end time to events." events_support_deadlines: "Offer option to create an event as a deadline, which will display a countdown in both Topic and Topic Lists" + events_event_countdown_icon: "Icon for countdown" rss_description: events: "Events" diff --git a/config/settings.yml b/config/settings.yml index 208ebd97..dff0c10c 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -90,3 +90,7 @@ plugins: events_support_deadlines: default: false client: true + events_event_countdown_icon: + default: "hourglass" + client: true + diff --git a/lib/discourse_events/event_creator.rb b/lib/discourse_events/event_creator.rb index 55f7b025..f649bba3 100644 --- a/lib/discourse_events/event_creator.rb +++ b/lib/discourse_events/event_creator.rb @@ -38,6 +38,7 @@ def create event_start = event["start"] event_end = event["end"] event_all_day = event["all_day"] + event_deadline = event["deadline"] timezone = event["timezone"] rsvp = event["rsvp"] going_max = event["going_max"] @@ -47,6 +48,7 @@ def create topic.custom_fields["event_start"] = event_start.to_datetime.to_i if event_start topic.custom_fields["event_end"] = event_end.to_datetime.to_i if event_end topic.custom_fields["event_all_day"] = event_all_day === "true" if event_all_day + topic.custom_fields["event_deadline"] = event_deadline === "true" if event_deadline topic.custom_fields["event_timezone"] = timezone if timezone topic.custom_fields["event_rsvp"] = rsvp if rsvp topic.custom_fields["event_going_max"] = going_max if going_max diff --git a/lib/discourse_events/event_revisor.rb b/lib/discourse_events/event_revisor.rb index 1cfc3421..a0c308ee 100644 --- a/lib/discourse_events/event_revisor.rb +++ b/lib/discourse_events/event_revisor.rb @@ -25,6 +25,11 @@ def revise! @tc.record_change("event_all_day", @tc.topic.custom_fields["event_all_day"], all_day) @tc.topic.custom_fields["event_all_day"] = all_day if all_day_change + deadline = !!@event["deadline"] + deadline_change = + @tc.record_change("event_deadline", @tc.topic.custom_fields["event_deadline"], deadline) + @tc.topic.custom_fields["event_deadline"] = deadline if deadline_change + timezone = @event["timezone"] timezone_change = @tc.record_change("event_timezone", @tc.topic.custom_fields["event_timezone"], timezone) diff --git a/plugin.rb b/plugin.rb index e7842b64..be9f8abc 100644 --- a/plugin.rb +++ b/plugin.rb @@ -132,6 +132,7 @@ register_topic_custom_field_type("event_start", :integer) register_topic_custom_field_type("event_end", :integer) register_topic_custom_field_type("event_all_day", :boolean) + register_topic_custom_field_type("event_deadline", :boolean) register_topic_custom_field_type("event_rsvp", :boolean) register_topic_custom_field_type("event_going", :json) register_topic_custom_field_type("event_going_max", :integer) @@ -142,6 +143,7 @@ event_start event_end event_all_day + event_deadline event_timezone event_rsvp event_going @@ -173,6 +175,8 @@ event[:all_day] = custom_fields["event_all_day"] if custom_fields["event_all_day"].present? + event[:deadline] = custom_fields["event_deadline"] if custom_fields["event_deadline"].present? + event[:version] = custom_fields["event_version"] if custom_fields["event_version"].present? if event_rsvp @@ -459,6 +463,7 @@ event_params["event_end"] = event["end"].to_datetime.to_i if event["end"].present? event_params["event_all_day"] = event["all_day"] === "true" if event["all_day"].present? + event_params["deadline"] = event["deadline"] === "true" if event["deadline"].present? event_params["event_timezone"] = event["timezone"] if event["timezone"].present? event_params["event_rsvp"] = event["rsvp"] if event["rsvp"].present? event_params["event_going_max"] = event["going_max"] if event["going_max"].present? From de3a3d155d1673da17745612c38a78ec4a053219 Mon Sep 17 00:00:00 2001 From: merefield Date: Sun, 8 Sep 2024 06:56:20 -0400 Subject: [PATCH 05/30] add deadline attribute migration --- ...0240905012439_add_deadline_to_discourse_events_events.rb | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb diff --git a/db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb b/db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb new file mode 100644 index 00000000..3915ec50 --- /dev/null +++ b/db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +class AddDeadlineToDiscourseEventsEvents < ActiveRecord::Migration[7.1] + def change + add_column :discourse_events_events, :deadline, :boolean, default: false + end +end From 3ca6fc8d668540118c6e9f403248cec90a22b6bf Mon Sep 17 00:00:00 2001 From: merefield Date: Sun, 8 Sep 2024 07:20:21 -0400 Subject: [PATCH 06/30] add different icons for passed due or in progress --- assets/javascripts/discourse/lib/date-utilities.js.es6 | 4 +++- config/locales/server.en.yml | 3 ++- config/settings.yml | 7 +++++-- plugin.rb | 2 ++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/assets/javascripts/discourse/lib/date-utilities.js.es6 b/assets/javascripts/discourse/lib/date-utilities.js.es6 index 604b6c01..1e647a9f 100644 --- a/assets/javascripts/discourse/lib/date-utilities.js.es6 +++ b/assets/javascripts/discourse/lib/date-utilities.js.es6 @@ -368,7 +368,9 @@ function eventLabel(event, args = {}) { passedDue = moment() > start; if (siteSettings.events_support_deadlines && event.deadline) { - const countdownIcon = siteSettings.events_event_countdown_icon; + const countdownIconPending = siteSettings.events_event_countdown_icon_pending || "hourglass-half"; + const countdownIconPassedDue = siteSettings.events_event_countdown_icon_passed_due || "hourglass-end"; + const countdownIcon = passedDue ? countdownIconPassedDue : countdownIconPending; const duration = passedDue ? 0 : moment.duration(start - moment()); let d = Math.floor(duration / (1000 * 60 * 60 * 24)); diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 71147fde..1bce0dbc 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -37,7 +37,8 @@ en: events_allow_moderator_management: "Allow moderators to administer events." events_add_default_end_time: "Add a default end time to events." events_support_deadlines: "Offer option to create an event as a deadline, which will display a countdown in both Topic and Topic Lists" - events_event_countdown_icon: "Icon for countdown" + events_event_countdown_icon_pending: "Icon for countdown" + events_event_countdown_icon_passed_due: "Icon for countdown once passed due" rss_description: events: "Events" diff --git a/config/settings.yml b/config/settings.yml index dff0c10c..8bc41605 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -90,7 +90,10 @@ plugins: events_support_deadlines: default: false client: true - events_event_countdown_icon: - default: "hourglass" + events_event_countdown_icon_pending: + default: "hourglass-half" + client: true + events_event_countdown_icon_passed_due: + default: "hourglass-end" client: true diff --git a/plugin.rb b/plugin.rb index be9f8abc..51074f0f 100644 --- a/plugin.rb +++ b/plugin.rb @@ -43,6 +43,8 @@ register_svg_icon "rss" register_svg_icon "fingerprint" register_svg_icon "save" +register_svg_icon "hourglass-half" +register_svg_icon "hourglass-end" require_relative "lib/discourse_events_client_site_setting.rb" require_relative "lib/discourse_events_timezone_default_site_setting.rb" From 50dfef5e3ad564960ac01aee7f104ec70fa50e87 Mon Sep 17 00:00:00 2001 From: merefield Date: Sun, 8 Sep 2024 07:29:44 -0400 Subject: [PATCH 07/30] clean up --- assets/javascripts/discourse/components/event-form.gjs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/assets/javascripts/discourse/components/event-form.gjs b/assets/javascripts/discourse/components/event-form.gjs index 5e161048..56ef5f8a 100644 --- a/assets/javascripts/discourse/components/event-form.gjs +++ b/assets/javascripts/discourse/components/event-form.gjs @@ -1,4 +1,3 @@ -// app/components/event-form.gjs import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { on } from "@ember/modifier"; @@ -9,7 +8,6 @@ import DateInput from "discourse/components/date-input"; import InputTip from "discourse/components/input-tip"; import TimeInput from "discourse/components/time-input"; import concatClass from "discourse/helpers/concat-class"; -// import moment from 'moment'; import i18n from "discourse-common/helpers/i18n"; import ComboBox from "select-kit/components/combo-box"; import EmailGroupUserChooser from "select-kit/components/email-group-user-chooser"; @@ -129,13 +127,6 @@ export default class EventForm extends Component { @action toggleDeadline(event) { this.deadline = event.target.checked; - // if (!this.allDay) { - // const start = nextInterval(); - // this.startTime = start; - // if (this.endEnabled) { - // this.endTime = moment(start).add(1, 'hours'); - // } - // } this.updateEvent(); } From 4b42ff7b843ffedb8ffe6cbb146ba07aa2e41431 Mon Sep 17 00:00:00 2001 From: merefield Date: Mon, 9 Sep 2024 07:17:53 -0400 Subject: [PATCH 08/30] add past due format --- .../discourse/lib/date-utilities.js.es6 | 16 +++++++++------- config/locales/client.en.yml | 19 +++++++++++-------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/assets/javascripts/discourse/lib/date-utilities.js.es6 b/assets/javascripts/discourse/lib/date-utilities.js.es6 index 1e647a9f..8d401436 100644 --- a/assets/javascripts/discourse/lib/date-utilities.js.es6 +++ b/assets/javascripts/discourse/lib/date-utilities.js.es6 @@ -313,7 +313,7 @@ function eventLabel(event, args = {}) { iconClass += "no-date"; } let label = renderIcon("string", icon, { class: iconClass }); - let passedDue = false; + let pastDue = false; if (!args.noText) { const { start, end, allDay, timezone } = setupEvent(event, args); @@ -365,19 +365,21 @@ function eventLabel(event, args = {}) { } } - passedDue = moment() > start; + pastDue = moment() > start; if (siteSettings.events_support_deadlines && event.deadline) { const countdownIconPending = siteSettings.events_event_countdown_icon_pending || "hourglass-half"; - const countdownIconPassedDue = siteSettings.events_event_countdown_icon_passed_due || "hourglass-end"; - const countdownIcon = passedDue ? countdownIconPassedDue : countdownIconPending; - const duration = passedDue ? 0 : moment.duration(start - moment()); + const countdownIconpastDue = siteSettings.events_event_countdown_icon_passed_due || "hourglass-end"; + const countdownIcon = pastDue ? countdownIconpastDue : countdownIconPending; + const duration = pastDue ? 0 : moment.duration(start - moment()); let d = Math.floor(duration / (1000 * 60 * 60 * 24)); let h = Math.floor((duration % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); let m = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60)); - const timeLeft = `${d} ${I18n.t("add_event.deadline.units.day", { count: d })}, ${h} ${I18n.t("add_event.deadline.units.hour", { count: h})}, ${m} ${I18n.t("add_event.deadline.units.minute", { count: m })}`; + const timeLeft = + pastDue ? `${I18n.t("event_label.deadline.past_due")}: ${moment(start).locale(I18n.locale).fromNow()}` + : `${d} ${I18n.t("event_label.deadline.units.day", { count: d })}, ${h} ${I18n.t("event_label.deadline.units.hour", { count: h})}, ${m} ${I18n.t("event_label.deadline.units.minute", { count: m })}`; label += renderIcon("string", countdownIcon); label += `${timeLeft}`; @@ -385,7 +387,7 @@ function eventLabel(event, args = {}) { } if (!args.noContainer) { - label = `${label}`; + label = `${label}`; } return label; diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index abe4aca7..1860965b 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -14,6 +14,16 @@ en: deadline: label: "Is a deadline" title: "Will show countdown in Topic and Topic List" + timezone: "Timezone" + no_timezone: "Select a timezone" + rsvp_enabled: "Rsvp enabled" + rsvp_enabled_label: "Rsvp" + going: "Guests" + going_max: "Maximum guests" + going_max_label: "{{goingMax}} Guests" + error: "Event end should be same as or after event start" + event_label: + deadline: units: day: one: "day" @@ -24,14 +34,7 @@ en: minute: one: "minute" other: "minutes" - timezone: "Timezone" - no_timezone: "Select a timezone" - rsvp_enabled: "Rsvp enabled" - rsvp_enabled_label: "Rsvp" - going: "Guests" - going_max: "Maximum guests" - going_max_label: "{{goingMax}} Guests" - error: "Event end should be same as or after event start" + past_due: "Past Due" category: events_setting_heading: "Events" enable_events: "Allow events to be added to topics in this category." From d6b6cbba7326192cf07c46affc6b9bcfc529f550 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 07:55:06 +0100 Subject: [PATCH 09/30] add tests, fix dependency --- .../discourse/components/event-form.gjs | 2 +- .../discourse/lib/date-utilities.js.es6 | 6 ++- plugin.rb | 4 +- .../acceptance/events-topic-test.js | 26 +++++++++++++ .../javascripts/components/event-form-test.js | 39 +++++++++++++++++++ 5 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 test/javascripts/components/event-form-test.js diff --git a/assets/javascripts/discourse/components/event-form.gjs b/assets/javascripts/discourse/components/event-form.gjs index 56ef5f8a..a98c822a 100644 --- a/assets/javascripts/discourse/components/event-form.gjs +++ b/assets/javascripts/discourse/components/event-form.gjs @@ -174,7 +174,7 @@ export default class EventForm extends Component { {{#if this.showDeadlineToggle}} -
+
start; if (siteSettings.events_support_deadlines && event.deadline) { + deadline = true; const countdownIconPending = siteSettings.events_event_countdown_icon_pending || "hourglass-half"; const countdownIconpastDue = siteSettings.events_event_countdown_icon_passed_due || "hourglass-end"; const countdownIcon = pastDue ? countdownIconpastDue : countdownIconPending; @@ -382,12 +384,12 @@ function eventLabel(event, args = {}) { : `${d} ${I18n.t("event_label.deadline.units.day", { count: d })}, ${h} ${I18n.t("event_label.deadline.units.hour", { count: h})}, ${m} ${I18n.t("event_label.deadline.units.minute", { count: m })}`; label += renderIcon("string", countdownIcon); - label += `${timeLeft}`; + label += `${timeLeft}`; } } if (!args.noContainer) { - label = `${label}`; + label = `${label}`; } return label; diff --git a/plugin.rb b/plugin.rb index 51074f0f..c37df674 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # name: discourse-events # about: Allows you to manage events in Discourse -# version: 0.7.0 +# version: 0.7.1 # authors: Angus McLeod # contact_emails: angus@pavilion.tech # url: https://github.com/paviliondev/discourse-events @@ -21,7 +21,7 @@ gem "icalendar", "2.8.0" gem "icalendar-recurrence", "1.1.3" gem "date", "3.3.4" -gem "time", "0.2.0" +gem "time", "0.2.2" gem "stringio", "3.1.1" gem "omnievent", "0.1.0.pre3", require_name: "omnievent" gem "omnievent-icalendar", "0.1.0.pre5", require_name: "omnievent/icalendar" diff --git a/test/javascripts/acceptance/events-topic-test.js b/test/javascripts/acceptance/events-topic-test.js index ecddcdd6..14048d2e 100644 --- a/test/javascripts/acceptance/events-topic-test.js +++ b/test/javascripts/acceptance/events-topic-test.js @@ -54,3 +54,29 @@ acceptance("Events | topic with an event", function (needs) { ); }); }); + +acceptance("Events | topic with an event that is a deadline", function (needs) { + needs.user(); + needs.site({ event_timezones: Timezones["event_timezones"] }); + setupServer(needs, { + event: { + start: "2022-11-06T12:00:00.000Z", + deadline: true, + timezone: "Australia/Perth", + }, + }); + + test("shows event", async function (assert) { + this.siteSettings.events_timezone_include_in_topic_list = true; + this.siteSettings.events_timezone_display = "event"; + this.siteSettings.events_support_deadlines = true; + await visit("/t/280"); + + assert.ok(exists(".event-label.deadline.past-due"), "the event-label is visible"); + assert.strictEqual( + query(".event-label .deadline").innerText.trim().split(":")[0], + I18n.t("event_label.deadline.past_due"), + "the event-label shows the Past Due element" + ); + }); +}); diff --git a/test/javascripts/components/event-form-test.js b/test/javascripts/components/event-form-test.js new file mode 100644 index 00000000..4917a2ad --- /dev/null +++ b/test/javascripts/components/event-form-test.js @@ -0,0 +1,39 @@ +import { render } from "@ember/test-helpers"; +import hbs from "htmlbars-inline-precompile"; +import { module, test } from "qunit"; +import { setupRenderingTest } from "discourse/tests/helpers/component-test"; +import { query } from "discourse/tests/helpers/qunit-helpers"; +import I18n from "discourse-i18n"; +import { default as Timezones } from "../fixtures/timezone-fixtures"; + +module("Poll | Component | event-form", function (hooks) { + setupRenderingTest(hooks); + + test("open the form without a defined event", async function (assert) { + this.siteSettings.events_support_deadlines = true; + this.site.event_timezones = Timezones["event_timezones"]; + this.setProperties({ + event: { + deadline: true + }, + updateEvent: () => {} + }); + + await render(hbs``); + + assert.strictEqual( + query(".event-form .control.deadline span").textContent.trim(), + I18n.t("add_event.deadline.label"), + "displays the deadline checkbox" + ); + + assert.strictEqual( + query(".event-form .control.deadline input").checked, + true, + "the checkbox is checked" + ); + }); +}); From 324fa2cc9bd52f66c110c2a8e8e52cf9a178c205 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 08:33:40 +0100 Subject: [PATCH 10/30] linting --- .../discourse/components/event-form.gjs | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/assets/javascripts/discourse/components/event-form.gjs b/assets/javascripts/discourse/components/event-form.gjs index a98c822a..0c0f2a12 100644 --- a/assets/javascripts/discourse/components/event-form.gjs +++ b/assets/javascripts/discourse/components/event-form.gjs @@ -1,7 +1,7 @@ -import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; +import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; import { on } from "@ember/modifier"; -import { action } from '@ember/object'; +import { action } from "@ember/object"; import { service } from "@ember/service"; import { hash } from "rsvp"; import DateInput from "discourse/components/date-input"; @@ -11,7 +11,12 @@ import concatClass from "discourse/helpers/concat-class"; import i18n from "discourse-common/helpers/i18n"; import ComboBox from "select-kit/components/combo-box"; import EmailGroupUserChooser from "select-kit/components/email-group-user-chooser"; -import { compileEvent, nextInterval, setupEventForm, timezoneLabel } from '../lib/date-utilities'; +import { + compileEvent, + nextInterval, + setupEventForm, + timezoneLabel, +} from "../lib/date-utilities"; export default class EventForm extends Component { @service siteSettings; @@ -48,15 +53,14 @@ export default class EventForm extends Component { ) { this.endEnabled = true; } - if ( - this.siteSettings.events_support_deadlines - ) { + if (this.siteSettings.events_support_deadlines) { this.toggleDeadlineEnabled; } } get timezones() { - const eventTimezones = this.args.event?.eventTimezones || this.site.event_timezones; + const eventTimezones = + this.args.event?.eventTimezones || this.site.event_timezones; return eventTimezones.map((tz) => ({ value: tz.value, name: timezoneLabel(tz.value, { siteSettings: this.siteSettings }), @@ -100,9 +104,11 @@ export default class EventForm extends Component { } if (!this.allDay && !this.endTime) { const start = moment( - `${moment(this.startDate).format('YYYY-MM-DD')} ${this.startTime.format('HH:mm')}` + `${moment(this.startDate).format( + "YYYY-MM-DD" + )} ${this.startTime.format("HH:mm")}` ); - this.endTime = moment(start).add(1, 'hours'); + this.endTime = moment(start).add(1, "hours"); } } else { this.endDate = undefined; @@ -118,7 +124,7 @@ export default class EventForm extends Component { const start = nextInterval(); this.startTime = start; if (this.endEnabled) { - this.endTime = moment(start).add(1, 'hours'); + this.endTime = moment(start).add(1, "hours"); } } this.updateEvent(); @@ -181,7 +187,9 @@ export default class EventForm extends Component { title={{i18n "add_event.deadline.title"}} {{on "click" this.toggleDeadline}} /> - {{i18n "add_event.deadline.label"}} + {{i18n + "add_event.deadline.label" + }}
{{/if}} @@ -235,7 +243,12 @@ export default class EventForm extends Component {
-
+
{{i18n "add_event.event_end"}} @@ -291,7 +304,9 @@ export default class EventForm extends Component { @value={{this.usersGoing}} @onChange={{action (mut this.usersGoing)}} class="user-selector" - @options={{hash filterPlaceholder="composer.users_placeholder"}} + @options={{hash + filterPlaceholder="composer.users_placeholder" + }} />
From 074dc01359abe1b222cd9effb30972072345e081 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 08:40:46 +0100 Subject: [PATCH 11/30] linting --- .../discourse/lib/date-utilities.js.es6 | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/assets/javascripts/discourse/lib/date-utilities.js.es6 b/assets/javascripts/discourse/lib/date-utilities.js.es6 index 9f5bc921..12b6d2c4 100644 --- a/assets/javascripts/discourse/lib/date-utilities.js.es6 +++ b/assets/javascripts/discourse/lib/date-utilities.js.es6 @@ -370,18 +370,30 @@ function eventLabel(event, args = {}) { if (siteSettings.events_support_deadlines && event.deadline) { deadline = true; - const countdownIconPending = siteSettings.events_event_countdown_icon_pending || "hourglass-half"; - const countdownIconpastDue = siteSettings.events_event_countdown_icon_passed_due || "hourglass-end"; - const countdownIcon = pastDue ? countdownIconpastDue : countdownIconPending; + const countdownIconPending = + siteSettings.events_event_countdown_icon_pending || "hourglass-half"; + const countdownIconpastDue = + siteSettings.events_event_countdown_icon_passed_due || "hourglass-end"; + const countdownIcon = pastDue + ? countdownIconpastDue + : countdownIconPending; const duration = pastDue ? 0 : moment.duration(start - moment()); let d = Math.floor(duration / (1000 * 60 * 60 * 24)); let h = Math.floor((duration % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); let m = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60)); - const timeLeft = - pastDue ? `${I18n.t("event_label.deadline.past_due")}: ${moment(start).locale(I18n.locale).fromNow()}` - : `${d} ${I18n.t("event_label.deadline.units.day", { count: d })}, ${h} ${I18n.t("event_label.deadline.units.hour", { count: h})}, ${m} ${I18n.t("event_label.deadline.units.minute", { count: m })}`; + const timeLeft = pastDue + ? `${I18n.t("event_label.deadline.past_due")}: ${moment(start) + .locale(I18n.locale) + .fromNow()}` + : `${d} ${I18n.t("event_label.deadline.units.day", { + count: d, + })}, ${h} ${I18n.t("event_label.deadline.units.hour", { + count: h, + })}, ${m} ${I18n.t("event_label.deadline.units.minute", { + count: m, + })}`; label += renderIcon("string", countdownIcon); label += `${timeLeft}`; @@ -389,7 +401,9 @@ function eventLabel(event, args = {}) { } if (!args.noContainer) { - label = `${label}`; + label = `${label}`; } return label; From f98ac1441c57d8623fca762653ddf2e4b810de19 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 08:42:59 +0100 Subject: [PATCH 12/30] linting --- .../20240905012439_add_deadline_to_discourse_events_events.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb b/db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb index 3915ec50..855b2965 100644 --- a/db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb +++ b/db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -class AddDeadlineToDiscourseEventsEvents < ActiveRecord::Migration[7.1] +class AddDeadlineToDiscourseEventsEvents < ActiveRecord::Migration[7.1] def change add_column :discourse_events_events, :deadline, :boolean, default: false end From 3fb31f5aad1b834665ea3ef91cc9b60d345e90d6 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 08:45:06 +0100 Subject: [PATCH 13/30] test linting --- test/javascripts/acceptance/events-topic-test.js | 5 ++++- test/javascripts/components/event-form-test.js | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/test/javascripts/acceptance/events-topic-test.js b/test/javascripts/acceptance/events-topic-test.js index 14048d2e..3c624dae 100644 --- a/test/javascripts/acceptance/events-topic-test.js +++ b/test/javascripts/acceptance/events-topic-test.js @@ -72,7 +72,10 @@ acceptance("Events | topic with an event that is a deadline", function (needs) { this.siteSettings.events_support_deadlines = true; await visit("/t/280"); - assert.ok(exists(".event-label.deadline.past-due"), "the event-label is visible"); + assert.ok( + exists(".event-label.deadline.past-due"), + "the event-label is visible" + ); assert.strictEqual( query(".event-label .deadline").innerText.trim().split(":")[0], I18n.t("event_label.deadline.past_due"), diff --git a/test/javascripts/components/event-form-test.js b/test/javascripts/components/event-form-test.js index 4917a2ad..cedea568 100644 --- a/test/javascripts/components/event-form-test.js +++ b/test/javascripts/components/event-form-test.js @@ -14,9 +14,9 @@ module("Poll | Component | event-form", function (hooks) { this.site.event_timezones = Timezones["event_timezones"]; this.setProperties({ event: { - deadline: true + deadline: true, }, - updateEvent: () => {} + updateEvent: () => {}, }); await render(hbs` Date: Wed, 18 Sep 2024 08:47:54 +0100 Subject: [PATCH 14/30] more test linting --- test/javascripts/acceptance/events-topic-test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/javascripts/acceptance/events-topic-test.js b/test/javascripts/acceptance/events-topic-test.js index 3c624dae..700d3202 100644 --- a/test/javascripts/acceptance/events-topic-test.js +++ b/test/javascripts/acceptance/events-topic-test.js @@ -6,6 +6,7 @@ import { exists, query, } from "discourse/tests/helpers/qunit-helpers"; +import I18n from "discourse-i18n"; import { cloneJSON } from "discourse-common/lib/object"; import { default as Timezones } from "../fixtures/timezone-fixtures"; From 496dd1b6c88740715f5f8f4969749a2444517c5b Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 09:00:16 +0100 Subject: [PATCH 15/30] linting --- test/javascripts/acceptance/events-topic-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/javascripts/acceptance/events-topic-test.js b/test/javascripts/acceptance/events-topic-test.js index 700d3202..139f4db3 100644 --- a/test/javascripts/acceptance/events-topic-test.js +++ b/test/javascripts/acceptance/events-topic-test.js @@ -6,8 +6,8 @@ import { exists, query, } from "discourse/tests/helpers/qunit-helpers"; -import I18n from "discourse-i18n"; import { cloneJSON } from "discourse-common/lib/object"; +import I18n from "discourse-i18n"; import { default as Timezones } from "../fixtures/timezone-fixtures"; const setupServer = (needs, attrs = {}) => { From e4fa6750d9d834ff774df8bc327e9bd187213383 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 09:47:21 +0100 Subject: [PATCH 16/30] linting --- .../discourse/components/event-form.gjs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/assets/javascripts/discourse/components/event-form.gjs b/assets/javascripts/discourse/components/event-form.gjs index 0c0f2a12..6f202d1e 100644 --- a/assets/javascripts/discourse/components/event-form.gjs +++ b/assets/javascripts/discourse/components/event-form.gjs @@ -1,5 +1,6 @@ import Component from "@glimmer/component"; import { tracked } from "@glimmer/tracking"; +import { Input } from "@ember/component"; import { on } from "@ember/modifier"; import { action } from "@ember/object"; import { service } from "@ember/service"; @@ -162,18 +163,18 @@ export default class EventForm extends Component {
- {{i18n "add_event.end_enabled"}}
- {{i18n "add_event.all_day"}} @@ -181,10 +182,10 @@ export default class EventForm extends Component { {{#if this.showDeadlineToggle}}
- {{i18n @@ -287,7 +288,7 @@ export default class EventForm extends Component { {{#if this.siteSettings.events_rsvp}}
- {{input type="checkbox" checked=this.rsvpEnabled}} + {{i18n "add_event.rsvp_enabled"}}
@@ -295,7 +296,7 @@ export default class EventForm extends Component {
{{i18n "add_event.going_max"}} - {{input type="number" value=this.goingMax}} +
From 1e701086117003360b33ce3159fcc6665c65c483 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 09:52:18 +0100 Subject: [PATCH 17/30] linting --- assets/javascripts/discourse/components/event-form.gjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/javascripts/discourse/components/event-form.gjs b/assets/javascripts/discourse/components/event-form.gjs index 6f202d1e..1be2af36 100644 --- a/assets/javascripts/discourse/components/event-form.gjs +++ b/assets/javascripts/discourse/components/event-form.gjs @@ -185,7 +185,7 @@ export default class EventForm extends Component { {{i18n From e238b0163dec447b22f0dba10e5a742a79d41fbc Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 09:55:01 +0100 Subject: [PATCH 18/30] fix merge error --- lib/discourse_events/event_creator.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/discourse_events/event_creator.rb b/lib/discourse_events/event_creator.rb index 89b460dd..129660f1 100644 --- a/lib/discourse_events/event_creator.rb +++ b/lib/discourse_events/event_creator.rb @@ -35,6 +35,8 @@ def create ( event_params.is_a?(String) ? ::JSON.parse(event_params) : event_params ).with_indifferent_access + event_start = event["start"] ? event["start"].to_datetime : nil + event_end = event["end"] ? event["end"].to_datetime : nil topic.custom_fields["event_start"] = event_start.to_i if event_start topic.custom_fields["event_end"] = event_end.to_i if event_end From c60a659c382ea68c108e58fdd1370d7de2380ced Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 15:00:19 +0100 Subject: [PATCH 19/30] remove redundant decorator --- assets/javascripts/discourse/components/event-form.gjs | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/javascripts/discourse/components/event-form.gjs b/assets/javascripts/discourse/components/event-form.gjs index 1be2af36..ebbde409 100644 --- a/assets/javascripts/discourse/components/event-form.gjs +++ b/assets/javascripts/discourse/components/event-form.gjs @@ -40,7 +40,6 @@ export default class EventForm extends Component { this.setupProperties(); } - @action setupProperties() { const props = setupEventForm(this.args.event, { siteSettings: this.siteSettings, From f82eb8353770e7af0e168916c861172c5c38ec85 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 15:06:49 +0100 Subject: [PATCH 20/30] rename settings --- assets/javascripts/discourse/components/event-form.gjs | 4 ++-- assets/javascripts/discourse/lib/date-utilities.js.es6 | 6 +++--- config/locales/server.en.yml | 6 +++--- config/settings.yml | 6 +++--- test/javascripts/acceptance/events-topic-test.js | 2 +- test/javascripts/components/event-form-test.js | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/assets/javascripts/discourse/components/event-form.gjs b/assets/javascripts/discourse/components/event-form.gjs index ebbde409..68ff4f66 100644 --- a/assets/javascripts/discourse/components/event-form.gjs +++ b/assets/javascripts/discourse/components/event-form.gjs @@ -53,7 +53,7 @@ export default class EventForm extends Component { ) { this.endEnabled = true; } - if (this.siteSettings.events_support_deadlines) { + if (this.siteSettings.events_deadlines) { this.toggleDeadlineEnabled; } } @@ -68,7 +68,7 @@ export default class EventForm extends Component { } get showDeadlineToggle() { - return this.siteSettings.events_support_deadlines; + return this.siteSettings.events_deadlines; } @action diff --git a/assets/javascripts/discourse/lib/date-utilities.js.es6 b/assets/javascripts/discourse/lib/date-utilities.js.es6 index 12b6d2c4..91f61959 100644 --- a/assets/javascripts/discourse/lib/date-utilities.js.es6 +++ b/assets/javascripts/discourse/lib/date-utilities.js.es6 @@ -368,12 +368,12 @@ function eventLabel(event, args = {}) { pastDue = moment() > start; - if (siteSettings.events_support_deadlines && event.deadline) { + if (siteSettings.events_deadlines && event.deadline) { deadline = true; const countdownIconPending = - siteSettings.events_event_countdown_icon_pending || "hourglass-half"; + siteSettings.events_deadlines_countdown_icon_pending || "hourglass-half"; const countdownIconpastDue = - siteSettings.events_event_countdown_icon_passed_due || "hourglass-end"; + siteSettings.events_deadlines_countdown_icon_passed_due || "hourglass-end"; const countdownIcon = pastDue ? countdownIconpastDue : countdownIconPending; diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 1bce0dbc..c4da4a98 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -36,9 +36,9 @@ en: events_split_series_into_different_topics: "A seperate topic will be created for each event in a series." events_allow_moderator_management: "Allow moderators to administer events." events_add_default_end_time: "Add a default end time to events." - events_support_deadlines: "Offer option to create an event as a deadline, which will display a countdown in both Topic and Topic Lists" - events_event_countdown_icon_pending: "Icon for countdown" - events_event_countdown_icon_passed_due: "Icon for countdown once passed due" + events_deadlines: "Offer option to create an event as a deadline, which will display a countdown in both Topic and Topic Lists" + events_deadlines_countdown_icon_pending: "Icon for countdown" + events_deadlines_countdown_icon_passed_due: "Icon for countdown once passed due" rss_description: events: "Events" diff --git a/config/settings.yml b/config/settings.yml index 8bc41605..560d85ca 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -87,13 +87,13 @@ plugins: events_add_default_end_time: default: false client: true - events_support_deadlines: + events_deadlines: default: false client: true - events_event_countdown_icon_pending: + events_deadlines_countdown_icon_pending: default: "hourglass-half" client: true - events_event_countdown_icon_passed_due: + events_deadlines_countdown_icon_passed_due: default: "hourglass-end" client: true diff --git a/test/javascripts/acceptance/events-topic-test.js b/test/javascripts/acceptance/events-topic-test.js index 139f4db3..f0392c8e 100644 --- a/test/javascripts/acceptance/events-topic-test.js +++ b/test/javascripts/acceptance/events-topic-test.js @@ -70,7 +70,7 @@ acceptance("Events | topic with an event that is a deadline", function (needs) { test("shows event", async function (assert) { this.siteSettings.events_timezone_include_in_topic_list = true; this.siteSettings.events_timezone_display = "event"; - this.siteSettings.events_support_deadlines = true; + this.siteSettings.events_deadlines = true; await visit("/t/280"); assert.ok( diff --git a/test/javascripts/components/event-form-test.js b/test/javascripts/components/event-form-test.js index cedea568..57021443 100644 --- a/test/javascripts/components/event-form-test.js +++ b/test/javascripts/components/event-form-test.js @@ -10,7 +10,7 @@ module("Poll | Component | event-form", function (hooks) { setupRenderingTest(hooks); test("open the form without a defined event", async function (assert) { - this.siteSettings.events_support_deadlines = true; + this.siteSettings.events_deadlines = true; this.site.event_timezones = Timezones["event_timezones"]; this.setProperties({ event: { From 7b0049825ccb0de3fa42cec8dd2e87713da0b508 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 15:19:15 +0100 Subject: [PATCH 21/30] delete unnecessary migration --- ...0240905012439_add_deadline_to_discourse_events_events.rb | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb diff --git a/db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb b/db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb deleted file mode 100644 index 855b2965..00000000 --- a/db/migrate/20240905012439_add_deadline_to_discourse_events_events.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -class AddDeadlineToDiscourseEventsEvents < ActiveRecord::Migration[7.1] - def change - add_column :discourse_events_events, :deadline, :boolean, default: false - end -end From 9ba5e1749de5f293168c3497c3ca5cb39461aad1 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 15:27:21 +0100 Subject: [PATCH 22/30] linting --- assets/javascripts/discourse/lib/date-utilities.js.es6 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/assets/javascripts/discourse/lib/date-utilities.js.es6 b/assets/javascripts/discourse/lib/date-utilities.js.es6 index 91f61959..c9ad77a3 100644 --- a/assets/javascripts/discourse/lib/date-utilities.js.es6 +++ b/assets/javascripts/discourse/lib/date-utilities.js.es6 @@ -371,9 +371,11 @@ function eventLabel(event, args = {}) { if (siteSettings.events_deadlines && event.deadline) { deadline = true; const countdownIconPending = - siteSettings.events_deadlines_countdown_icon_pending || "hourglass-half"; + siteSettings.events_deadlines_countdown_icon_pending || + "hourglass-half"; const countdownIconpastDue = - siteSettings.events_deadlines_countdown_icon_passed_due || "hourglass-end"; + siteSettings.events_deadlines_countdown_icon_passed_due || + "hourglass-end"; const countdownIcon = pastDue ? countdownIconpastDue : countdownIconPending; From 10246da59b7448d236ed7a90806a28dccb37d16d Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 18 Sep 2024 19:01:40 +0200 Subject: [PATCH 23/30] Remove deadline from serializer --- app/serializers/discourse_events/basic_event_serializer.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/serializers/discourse_events/basic_event_serializer.rb b/app/serializers/discourse_events/basic_event_serializer.rb index ff1a103b..33617319 100644 --- a/app/serializers/discourse_events/basic_event_serializer.rb +++ b/app/serializers/discourse_events/basic_event_serializer.rb @@ -5,7 +5,6 @@ class BasicEventSerializer < ApplicationSerializer attributes :id, :start_time, :end_time, - :deadline, :name, :description, :status, From 46aaf3a70c173d037f36a2bce7d2d7b35eb51412 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 20:37:15 +0100 Subject: [PATCH 24/30] improve formatting of countdown: use core localisation, suppress zeros --- assets/javascripts/discourse/lib/date-utilities.js.es6 | 8 ++++---- config/locales/client.en.yml | 10 ---------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/assets/javascripts/discourse/lib/date-utilities.js.es6 b/assets/javascripts/discourse/lib/date-utilities.js.es6 index c9ad77a3..08017061 100644 --- a/assets/javascripts/discourse/lib/date-utilities.js.es6 +++ b/assets/javascripts/discourse/lib/date-utilities.js.es6 @@ -389,13 +389,13 @@ function eventLabel(event, args = {}) { ? `${I18n.t("event_label.deadline.past_due")}: ${moment(start) .locale(I18n.locale) .fromNow()}` - : `${d} ${I18n.t("event_label.deadline.units.day", { + : `${d > 0 ? I18n.t("dates.medium.x_days", { count: d, - })}, ${h} ${I18n.t("event_label.deadline.units.hour", { + }) : ""} ${h > 0 ? I18n.t("dates.medium.x_hours", { count: h, - })}, ${m} ${I18n.t("event_label.deadline.units.minute", { + }) : ""} ${m > 0 ? I18n.t("dates.medium.x_minutes", { count: m, - })}`; + }): ""}`; label += renderIcon("string", countdownIcon); label += `${timeLeft}`; diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 05081957..e62e3b1c 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -24,16 +24,6 @@ en: error: "Event end should be same as or after event start" event_label: deadline: - units: - day: - one: "day" - other: "days" - hour: - one: "hour" - other: "hours" - minute: - one: "minute" - other: "minutes" past_due: "Past Due" category: events_setting_heading: "Events" From 68c3674a2560dac5c42dd65ccf85f3eb1dbb120c Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 20:51:22 +0100 Subject: [PATCH 25/30] linting --- .../discourse/lib/date-utilities.js.es6 | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/assets/javascripts/discourse/lib/date-utilities.js.es6 b/assets/javascripts/discourse/lib/date-utilities.js.es6 index 08017061..ffa7a0b0 100644 --- a/assets/javascripts/discourse/lib/date-utilities.js.es6 +++ b/assets/javascripts/discourse/lib/date-utilities.js.es6 @@ -389,13 +389,25 @@ function eventLabel(event, args = {}) { ? `${I18n.t("event_label.deadline.past_due")}: ${moment(start) .locale(I18n.locale) .fromNow()}` - : `${d > 0 ? I18n.t("dates.medium.x_days", { - count: d, - }) : ""} ${h > 0 ? I18n.t("dates.medium.x_hours", { - count: h, - }) : ""} ${m > 0 ? I18n.t("dates.medium.x_minutes", { - count: m, - }): ""}`; + : `${ + d > 0 + ? I18n.t("dates.medium.x_days", { + count: d, + }) + : "" + } ${ + h > 0 + ? I18n.t("dates.medium.x_hours", { + count: h, + }) + : "" + } ${ + m > 0 + ? I18n.t("dates.medium.x_minutes", { + count: m, + }) + : "" + }`; label += renderIcon("string", countdownIcon); label += `${timeLeft}`; From 8771d31a55c2716902820b828884e6045f2bae79 Mon Sep 17 00:00:00 2001 From: merefield Date: Wed, 18 Sep 2024 21:09:40 +0100 Subject: [PATCH 26/30] add space before countdown icon --- assets/stylesheets/common/events.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/stylesheets/common/events.scss b/assets/stylesheets/common/events.scss index 028b3c3f..eab5f1cc 100644 --- a/assets/stylesheets/common/events.scss +++ b/assets/stylesheets/common/events.scss @@ -630,6 +630,10 @@ ul.events-calendar-events { .add-event-controls { display: flex; align-items: center; + + .add-event span svg:nth-of-type(2) { + padding-left: 5px; + } } .composer-controls-event { From d98a81c35aab61f2224ce4648db3e17844121ab6 Mon Sep 17 00:00:00 2001 From: merefield Date: Thu, 19 Sep 2024 08:54:16 +0100 Subject: [PATCH 27/30] fix timezone update --- assets/javascripts/discourse/components/event-form.gjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/assets/javascripts/discourse/components/event-form.gjs b/assets/javascripts/discourse/components/event-form.gjs index 68ff4f66..efd74852 100644 --- a/assets/javascripts/discourse/components/event-form.gjs +++ b/assets/javascripts/discourse/components/event-form.gjs @@ -136,6 +136,12 @@ export default class EventForm extends Component { this.updateEvent(); } + @action + updateTimezone(newTimezone) { + this.timezone = newTimezone; + this.updateEvent(); + } + @action updateEvent() { const event = compileEvent({ @@ -199,7 +205,7 @@ export default class EventForm extends Component { @id="add-event-select-timezone" @value={{this.timezone}} @valueProperty="value" - @onChange={{mut this.timezone}} + @onChange={{this.updateTimezone}} @content={{this.timezones}} @options={{hash filterable=true none="add_event.no_timezone"}} /> From 00011b079e662c35c13e225a006ddeb5f1d0e837 Mon Sep 17 00:00:00 2001 From: merefield Date: Thu, 19 Sep 2024 09:30:55 +0100 Subject: [PATCH 28/30] tweak margins of hourglass icon --- assets/stylesheets/common/events.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/stylesheets/common/events.scss b/assets/stylesheets/common/events.scss index eab5f1cc..6f2e7d4e 100644 --- a/assets/stylesheets/common/events.scss +++ b/assets/stylesheets/common/events.scss @@ -632,7 +632,7 @@ ul.events-calendar-events { align-items: center; .add-event span svg:nth-of-type(2) { - padding-left: 5px; + margin-left: 0.6em; } } From 090f97a81038ae38d237665308bfe095de794e0d Mon Sep 17 00:00:00 2001 From: merefield Date: Thu, 19 Sep 2024 14:32:59 +0100 Subject: [PATCH 29/30] improve events calendar card presentation --- assets/stylesheets/common/events.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/assets/stylesheets/common/events.scss b/assets/stylesheets/common/events.scss index 6f2e7d4e..8d8c347d 100644 --- a/assets/stylesheets/common/events.scss +++ b/assets/stylesheets/common/events.scss @@ -462,6 +462,13 @@ ul.events-calendar-events { display: inline-block; cursor: pointer; color: var(--primary); + .deadline { + display: inline-block; + svg:nth-of-type(2) { + margin-right: 0.3em; + margin-left: 0.6em; + } + } } .topic-excerpt { From dca047c99ae9cb300c96e998dd89699bfb770a9e Mon Sep 17 00:00:00 2001 From: merefield Date: Thu, 19 Sep 2024 14:47:34 +0100 Subject: [PATCH 30/30] make icon margin consistent for deadline icons on event card --- assets/stylesheets/common/events.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/stylesheets/common/events.scss b/assets/stylesheets/common/events.scss index 8d8c347d..b0cda18a 100644 --- a/assets/stylesheets/common/events.scss +++ b/assets/stylesheets/common/events.scss @@ -464,8 +464,10 @@ ul.events-calendar-events { color: var(--primary); .deadline { display: inline-block; - svg:nth-of-type(2) { + svg { margin-right: 0.3em; + } + svg:nth-of-type(2) { margin-left: 0.6em; } }