FIX: Quoting local dates bbcode regeneration (#17141)

This commit allows quoting of discourse-local-date elements
and converts the quoted tags back into bbcode so that the
rendered quote will also render the discourse-local-date HTML.
This works on single dates as well as date ranges, and supports
all of the options used by discourse-local-date.

This also necessitated adding addTextDecorateCallback to the
to-markdown core lib (similar to addBlockDecorateCallback and
addTagDecorateCallback) to transform the text nodes between
date ranges to remove the -> in the final quote.

c.f. https://meta.discourse.org/t/quotes-that-contain-date-time/101999
This commit is contained in:
Martin Brennan
2022-06-21 10:07:21 +10:00
committed by GitHub
parent fd294a60cf
commit 54a518b21d
7 changed files with 331 additions and 44 deletions

View File

@ -14,6 +14,7 @@ import { propertyNotEqual } from "discourse/lib/computed";
import { schedule } from "@ember/runloop";
import { getOwner } from "discourse-common/lib/get-owner";
import { applyLocalDates } from "discourse/lib/local-dates";
import generateDateMarkup from "discourse/plugins/discourse-local-dates/lib/local-date-markup-generator";
export default Component.extend({
timeFormat: "HH:mm:ss",
@ -262,43 +263,7 @@ export default Component.extend({
},
_generateDateMarkup(fromDateTime, options, isRange, toDateTime) {
let text = ``;
if (isRange) {
let from = [fromDateTime.date, fromDateTime.time]
.filter((element) => !isEmpty(element))
.join("T");
let to = [toDateTime.date, toDateTime.time]
.filter((element) => !isEmpty(element))
.join("T");
text += `[date-range from=${from} to=${to}`;
} else {
text += `[date=${fromDateTime.date}`;
}
if (fromDateTime.time && !isRange) {
text += ` time=${fromDateTime.time}`;
}
if (fromDateTime.format && fromDateTime.format.length) {
text += ` format="${fromDateTime.format}"`;
}
if (options.timezone) {
text += ` timezone="${options.timezone}"`;
}
if (options.timezones && options.timezones.length) {
text += ` timezones="${options.timezones.join("|")}"`;
}
if (options.recurring && !isRange) {
text += ` recurring="${options.recurring}"`;
}
text += `]`;
return text;
return generateDateMarkup(fromDateTime, options, isRange, toDateTime);
},
@computed("advancedMode")

View File

@ -7,6 +7,11 @@ import { downloadCalendar } from "discourse/lib/download-calendar";
import { renderIcon } from "discourse-common/lib/icon-library";
import I18n from "I18n";
import { hidePopover, showPopover } from "discourse/lib/d-popover";
import {
addTagDecorateCallback,
addTextDecorateCallback,
} from "discourse/lib/to-markdown";
import generateDateMarkup from "discourse/plugins/discourse-local-dates/lib/local-date-markup-generator";
// Import applyLocalDates from discourse/lib/local-dates instead
export function applyLocalDates(dates, siteSettings) {
@ -66,6 +71,24 @@ function buildOptionsFromElement(element, siteSettings) {
return opts;
}
function buildOptionsFromMarkdownTag(element) {
const opts = {};
// siteSettings defaults as used by buildOptionsFromElement are purposefully
// ommitted to reproduce exactly what was on the original element
opts.time = element.attributes["data-time"];
opts.date = element.attributes["data-date"];
opts.recurring = element.attributes["data-recurring"];
opts.timezones = element.attributes["data-timezones"];
opts.timezone = element.attributes["data-timezone"];
opts.calendar = (element.attributes["data-calendar"] || "on") === "on";
opts.displayedTimezone = element.attributes["data-displayed-timezone"];
opts.format = element.attributes["data-format"];
opts.countdown = element.attributes["data-countdown"];
return opts;
}
function _rangeElements(element) {
if (!element.parentElement) {
return [];
@ -128,6 +151,60 @@ function initializeDiscourseLocalDates(api) {
},
},
});
addTextDecorateCallback(function (
text,
nextElement,
_previousElement,
metadata
) {
if (
metadata.discourseLocalDateStartRangeOpts &&
nextElement?.attributes.class?.includes("discourse-local-date") &&
text === "→"
) {
return "";
}
});
addTagDecorateCallback(function () {
if (this.element.attributes.class?.includes("discourse-local-date")) {
if (this.metadata.discourseLocalDateStartRangeOpts) {
const startRangeOpts = this.metadata.discourseLocalDateStartRangeOpts;
const endRangeOpts = buildOptionsFromMarkdownTag(this.element);
const markup = generateDateMarkup(
{
date: startRangeOpts.date,
time: startRangeOpts.time,
format: startRangeOpts.format,
},
endRangeOpts,
true,
{
date: endRangeOpts.date,
time: endRangeOpts.time,
format: endRangeOpts.format,
}
);
this.prefix = markup;
this.metadata.discourseLocalDateStartRangeOpts = null;
return "";
}
if (this.element.attributes["data-range"] === "true") {
this.metadata.discourseLocalDateStartRangeOpts = buildOptionsFromMarkdownTag(
this.element
);
return "";
}
const opts = buildOptionsFromMarkdownTag(this.element, siteSettings);
const markup = generateDateMarkup(
{ date: opts.date, time: opts.time, format: opts.format },
opts,
false
);
this.prefix = markup;
return "";
}
});
}
function buildHtmlPreview(element, siteSettings) {

View File

@ -0,0 +1,58 @@
import { isEmpty } from "@ember/utils";
export default function generateDateMarkup(
fromDateTime,
options,
isRange,
toDateTime
) {
let text = ``;
if (isRange) {
let from = [fromDateTime.date, fromDateTime.time]
.filter((element) => !isEmpty(element))
.join("T");
let to = [toDateTime.date, toDateTime.time]
.filter((element) => !isEmpty(element))
.join("T");
text += `[date-range from=${from} to=${to}`;
} else {
text += `[date=${fromDateTime.date}`;
}
if (fromDateTime.time && !isRange) {
text += ` time=${fromDateTime.time}`;
}
if (fromDateTime.format && fromDateTime.format.length) {
text += ` format="${fromDateTime.format}"`;
}
if (options.timezone) {
text += ` timezone="${options.timezone}"`;
}
if (options.countdown) {
text += ` countdown="${options.countdown}"`;
}
if (options.displayedTimezone) {
text += ` displayedTimezone="${options.displayedTimezone}"`;
}
if (options.timezones && options.timezones.length) {
if (Array.isArray(options.timezones)) {
text += ` timezones="${options.timezones.join("|")}"`;
} else {
text += ` timezones="${options.timezones}"`;
}
}
if (options.recurring && !isRange) {
text += ` recurring="${options.recurring}"`;
}
text += `]`;
return text;
}