diff --git a/plugins/discourse-local-dates/assets/javascripts/initializers/discourse-local-dates.js.es6 b/plugins/discourse-local-dates/assets/javascripts/initializers/discourse-local-dates.js.es6 index 30c1b5c5103..50d543c971e 100644 --- a/plugins/discourse-local-dates/assets/javascripts/initializers/discourse-local-dates.js.es6 +++ b/plugins/discourse-local-dates/assets/javascripts/initializers/discourse-local-dates.js.es6 @@ -39,6 +39,11 @@ export function applyLocalDates(dates, siteSettings) { function buildOptionsFromElement(element, siteSettings) { const opts = {}; const dataset = element.dataset; + + if (_rangeElements(element).length === 2) { + opts.duration = _calculateDuration(element); + } + opts.time = dataset.time; opts.date = dataset.date; opts.recurring = dataset.recurring; @@ -57,6 +62,12 @@ function buildOptionsFromElement(element, siteSettings) { return opts; } +function _rangeElements(element) { + return Array.from(element.parentElement.children).filter( + (span) => span.dataset.date + ); +} + function initializeDiscourseLocalDates(api) { const siteSettings = api.container.lookup("site-settings:main"); @@ -114,7 +125,7 @@ function buildHtmlPreview(element, siteSettings) { const dateTimeNode = document.createElement("span"); dateTimeNode.classList.add("date-time"); - dateTimeNode.innerText = preview.formated; + dateTimeNode.innerHTML = preview.formated; previewNode.appendChild(dateTimeNode); return previewNode; @@ -127,6 +138,20 @@ function buildHtmlPreview(element, siteSettings) { return previewsNode.outerHTML; } +function _calculateDuration(element) { + const [startDataset, endDataset] = _rangeElements(element).map( + (dateElement) => dateElement.dataset + ); + const startDateTime = moment( + `${startDataset.date} ${startDataset.time || ""}` + ); + const endDateTime = moment(`${endDataset.date} ${endDataset.time || ""}`); + const duration = endDateTime.diff(startDateTime, "minutes"); + + // negative duration is used when we calculate difference for end date from range + return element.dataset === startDataset ? duration : -duration; +} + export default { name: "discourse-local-dates", @@ -138,9 +163,13 @@ export default { const siteSettings = owner.lookup("site-settings:main"); if (event?.target?.classList?.contains("discourse-local-date")) { - showPopover(event, { - htmlContent: buildHtmlPreview(event.target, siteSettings), - }); + if ($(document.getElementById("d-popover"))[0]) { + hidePopover(event); + } else { + showPopover(event, { + htmlContent: buildHtmlPreview(event.target, siteSettings), + }); + } } }, @@ -155,7 +184,6 @@ export default { router.on("routeWillChange", hidePopover); window.addEventListener("click", this.showDatePopover); - window.addEventListener("mouseover", this.showDatePopover); window.addEventListener("mouseout", this.hideDatePopover); const siteSettings = container.lookup("site-settings:main"); @@ -174,7 +202,6 @@ export default { teardown() { window.removeEventListener("click", this.showDatePopover); - window.removeEventListener("mouseover", this.showDatePopover); window.removeEventListener("mouseout", this.hideDatePopover); }, }; diff --git a/plugins/discourse-local-dates/assets/javascripts/lib/local-date-builder.js.es6 b/plugins/discourse-local-dates/assets/javascripts/lib/local-date-builder.js.es6 index 915d87ccf64..d2661343786 100644 --- a/plugins/discourse-local-dates/assets/javascripts/lib/local-date-builder.js.es6 +++ b/plugins/discourse-local-dates/assets/javascripts/lib/local-date-builder.js.es6 @@ -1,9 +1,15 @@ import DateWithZoneHelper from "./date-with-zone-helper"; import I18n from "I18n"; +import { renderIcon } from "discourse-common/lib/icon-library"; -const TIME_FORMAT = "LLL"; +const DATETIME_FORMAT = "LLL"; const DATE_FORMAT = "LL"; +const FULL_DATETIME_FORMAT = "LLLL"; +const TIME_FORMAT = "h:mm A"; +const DAY_OF_THE_WEEK_FORMAT = "dddd"; const RANGE_SEPARATOR = "→"; +const TIME_ICON = "clock"; +const SHORT_FORMAT_DAYS_PERIOD = 1; export default class LocalDateBuilder { constructor(params = {}, localTimezone) { @@ -17,8 +23,9 @@ export default class LocalDateBuilder { this.calendar = typeof params.calendar === "undefined" ? true : params.calendar; this.displayedTimezone = params.displayedTimezone; - this.format = params.format || (this.time ? TIME_FORMAT : DATE_FORMAT); + this.format = params.format || (this.time ? DATETIME_FORMAT : DATE_FORMAT); this.countdown = params.countdown; + this.duration = params.duration; this.localTimezone = localTimezone; } @@ -95,7 +102,8 @@ export default class LocalDateBuilder { localDate.timezone, this.localTimezone ), - this.time + this.time, + this.duration ), }); @@ -126,7 +134,8 @@ export default class LocalDateBuilder { localDate.timezone, timezone ), - this.time + this.time, + this.duration ), }); }); @@ -148,19 +157,56 @@ export default class LocalDateBuilder { ); } - _createDateTimeRange(startRange, time) { + _createDateTimeRange(startRange, time, duration) { + const [startDate, endDate] = this._calculateDatesForRange( + startRange, + time, + duration + ); + let formatElements = [ + startDate.format(`${DAY_OF_THE_WEEK_FORMAT}, ${DATE_FORMAT}`), + this._optionalTimeIcon(startDate, endDate), + startDate.format(TIME_FORMAT), + ]; + if (endDate) { + formatElements = formatElements.concat([ + RANGE_SEPARATOR, + endDate.format(this._endDateFormat(startDate, endDate)), + ]); + } + return formatElements.filter(Boolean).join(" "); + } + + _shortFormat(startDate, endDate) { + return ( + endDate.datetime.diff(startDate.datetime, "days") < + SHORT_FORMAT_DAYS_PERIOD + ); + } + + _optionalTimeIcon(startDate, endDate) { + if (!endDate || this._shortFormat(startDate, endDate)) { + return `
${renderIcon("string", TIME_ICON)}`; + } + } + + _endDateFormat(startDate, endDate) { + return this._shortFormat(startDate, endDate) + ? TIME_FORMAT + : FULL_DATETIME_FORMAT; + } + + _calculateDatesForRange(date, time, duration) { // if a time has been given we do not attempt to automatically create a range // instead we show only one date with a format showing the time - if (time) { - return startRange.format(TIME_FORMAT); - } else { - const endRange = startRange.add(24, "hours"); - return [ - startRange.format("LLLL"), - RANGE_SEPARATOR, - endRange.format("LLLL"), - ].join(" "); + if (time && !duration) { + return [date]; } + const dates = [ + date, + duration ? date.add(duration, "minutes") : date.add(24, "hours"), + ]; + return duration < 0 ? dates.reverse() : dates; } _applyFormatting(localDate, displayedTimezone) { diff --git a/plugins/discourse-local-dates/test/javascripts/lib/local-date-builder-test.js.es6 b/plugins/discourse-local-dates/test/javascripts/lib/local-date-builder-test.js.es6 index 9764dac4719..f01c289d1dd 100644 --- a/plugins/discourse-local-dates/test/javascripts/lib/local-date-builder-test.js.es6 +++ b/plugins/discourse-local-dates/test/javascripts/lib/local-date-builder-test.js.es6 @@ -446,6 +446,38 @@ test("previews", function (assert) { ); }); + freezeTime({ date: "2020-03-22", timezone: PARIS }, () => { + assert.buildsCorrectDate( + { duration: 90, timezone: PARIS, timezones: [PARIS] }, + { + previews: [ + { + current: true, + formated: + 'Sunday, March 22, 2020
12:00 AM → 1:30 AM', + timezone: "Paris", + }, + ], + } + ); + }); + + freezeTime({ date: "2020-03-22", timezone: PARIS }, () => { + assert.buildsCorrectDate( + { duration: 1440, timezone: PARIS, timezones: [PARIS] }, + { + previews: [ + { + current: true, + formated: + "Sunday, March 22, 2020 12:00 AM → Monday, March 23, 2020 12:00 AM", + timezone: "Paris", + }, + ], + } + ); + }); + freezeTime({ date: "2020-03-22", timezone: PARIS }, () => { assert.buildsCorrectDate( { time: "11:34", timezone: PARIS, timezones: [PARIS] }, @@ -453,7 +485,8 @@ test("previews", function (assert) { previews: [ { current: true, - formated: "March 22, 2020 11:34 AM", + formated: + 'Sunday, March 22, 2020
11:34 AM', timezone: "Paris", }, ], @@ -508,19 +541,23 @@ test("previews", function (assert) { previews: [ { current: true, - formated: "April 7, 2020 2:54 PM", + formated: + 'Tuesday, April 7, 2020
2:54 PM', timezone: "Paris", }, { - formated: "April 7, 2020 1:54 PM", + formated: + 'Tuesday, April 7, 2020
1:54 PM', timezone: "London", }, { - formated: "April 7, 2020 1:54 PM", + formated: + 'Tuesday, April 7, 2020
1:54 PM', timezone: "Lagos", }, { - formated: "April 7, 2020 10:54 PM", + formated: + 'Tuesday, April 7, 2020
10:54 PM', timezone: "Sydney", }, ], @@ -539,11 +576,13 @@ test("previews", function (assert) { previews: [ { current: true, - formated: "May 13, 2020 11:00 AM", + formated: + 'Wednesday, May 13, 2020
11:00 AM', timezone: "Los Angeles", }, { - formated: "May 13, 2020 6:00 PM", + formated: + 'Wednesday, May 13, 2020
6:00 PM', timezone: "UTC", }, ],