Support deserialising HR tags for editing (#7543)

pull/21833/head
Michael Telatynski 2022-01-14 13:24:51 +00:00 committed by GitHub
parent f4a6219c88
commit 2ef36507fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 26 additions and 13 deletions

View File

@ -399,7 +399,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
const room = this.getRoom(); const room = this.getRoom();
const partCreator = new CommandPartCreator(room, this.props.mxClient); const partCreator = new CommandPartCreator(room, this.props.mxClient);
let parts; let parts: Part[];
let isRestored = false; let isRestored = false;
if (editState.hasEditorState()) { if (editState.hasEditorState()) {
// if restoring state from a previous editor, // if restoring state from a previous editor,

View File

@ -24,9 +24,9 @@ import { Part, PartCreator, Type } from "./parts";
import SdkConfig from "../SdkConfig"; import SdkConfig from "../SdkConfig";
import { textToHtmlRainbow } from "../utils/colour"; import { textToHtmlRainbow } from "../utils/colour";
function parseAtRoomMentions(text: string, partCreator: PartCreator) { function parseAtRoomMentions(text: string, partCreator: PartCreator): Part[] {
const ATROOM = "@room"; const ATROOM = "@room";
const parts = []; const parts: Part[] = [];
text.split(ATROOM).forEach((textPart, i, arr) => { text.split(ATROOM).forEach((textPart, i, arr) => {
if (textPart.length) { if (textPart.length) {
parts.push(partCreator.plain(textPart)); parts.push(partCreator.plain(textPart));
@ -42,7 +42,7 @@ function parseAtRoomMentions(text: string, partCreator: PartCreator) {
return parts; return parts;
} }
function parseLink(a: HTMLAnchorElement, partCreator: PartCreator) { function parseLink(a: HTMLAnchorElement, partCreator: PartCreator): Part {
const { href } = a; const { href } = a;
const resourceId = getPrimaryPermalinkEntity(href); // The room/user ID const resourceId = getPrimaryPermalinkEntity(href); // The room/user ID
const prefix = resourceId ? resourceId[0] : undefined; // First character of ID const prefix = resourceId ? resourceId[0] : undefined; // First character of ID
@ -61,13 +61,13 @@ function parseLink(a: HTMLAnchorElement, partCreator: PartCreator) {
} }
} }
function parseImage(img: HTMLImageElement, partCreator: PartCreator) { function parseImage(img: HTMLImageElement, partCreator: PartCreator): Part {
const { src } = img; const { src } = img;
return partCreator.plain(`![${img.alt.replace(/[[\\\]]/g, c => "\\" + c)}](${src})`); return partCreator.plain(`![${img.alt.replace(/[[\\\]]/g, c => "\\" + c)}](${src})`);
} }
function parseCodeBlock(n: HTMLElement, partCreator: PartCreator) { function parseCodeBlock(n: HTMLElement, partCreator: PartCreator): Part[] {
const parts = []; const parts: Part[] = [];
let language = ""; let language = "";
if (n.firstChild && n.firstChild.nodeName === "CODE") { if (n.firstChild && n.firstChild.nodeName === "CODE") {
for (const className of (<HTMLElement>n.firstChild).classList) { for (const className of (<HTMLElement>n.firstChild).classList) {
@ -87,7 +87,7 @@ function parseCodeBlock(n: HTMLElement, partCreator: PartCreator) {
return parts; return parts;
} }
function parseHeader(el: HTMLElement, partCreator: PartCreator) { function parseHeader(el: HTMLElement, partCreator: PartCreator): Part {
const depth = parseInt(el.nodeName.substr(1), 10); const depth = parseInt(el.nodeName.substr(1), 10);
return partCreator.plain("#".repeat(depth) + " "); return partCreator.plain("#".repeat(depth) + " ");
} }
@ -97,7 +97,12 @@ interface IState {
listDepth?: number; listDepth?: number;
} }
function parseElement(n: HTMLElement, partCreator: PartCreator, lastNode: HTMLElement | undefined, state: IState) { function parseElement(
n: HTMLElement,
partCreator: PartCreator,
lastNode: Node | undefined,
state: IState,
): Part | Part[] {
switch (n.nodeName) { switch (n.nodeName) {
case "H1": case "H1":
case "H2": case "H2":
@ -112,6 +117,14 @@ function parseElement(n: HTMLElement, partCreator: PartCreator, lastNode: HTMLEl
return parseImage(<HTMLImageElement>n, partCreator); return parseImage(<HTMLImageElement>n, partCreator);
case "BR": case "BR":
return partCreator.newline(); return partCreator.newline();
case "HR":
// the newline arrangement here is quite specific otherwise it may be misconstrued as marking the previous
// text line as a header instead of acting as a horizontal rule.
return [
partCreator.newline(),
partCreator.plain("---"),
partCreator.newline(),
];
case "EM": case "EM":
return partCreator.plain(`_${n.textContent}_`); return partCreator.plain(`_${n.textContent}_`);
case "STRONG": case "STRONG":
@ -222,13 +235,13 @@ function parseHtmlMessage(html: string, partCreator: PartCreator, isQuotedMessag
// we're only taking text, so that is fine // we're only taking text, so that is fine
const rootNode = new DOMParser().parseFromString(html, "text/html").body; const rootNode = new DOMParser().parseFromString(html, "text/html").body;
const parts: Part[] = []; const parts: Part[] = [];
let lastNode; let lastNode: Node;
let inQuote = isQuotedMessage; let inQuote = isQuotedMessage;
const state: IState = { const state: IState = {
listIndex: [], listIndex: [],
}; };
function onNodeEnter(n) { function onNodeEnter(n: Node) {
if (checkIgnored(n)) { if (checkIgnored(n)) {
return false; return false;
} }
@ -256,7 +269,7 @@ function parseHtmlMessage(html: string, partCreator: PartCreator, isQuotedMessag
newParts.push(partCreator.newline()); newParts.push(partCreator.newline());
} }
} else if (n.nodeType === Node.ELEMENT_NODE) { } else if (n.nodeType === Node.ELEMENT_NODE) {
const parseResult = parseElement(n, partCreator, lastNode, state); const parseResult = parseElement(n as HTMLElement, partCreator, lastNode, state);
if (parseResult) { if (parseResult) {
if (Array.isArray(parseResult)) { if (Array.isArray(parseResult)) {
newParts.push(...parseResult); newParts.push(...parseResult);
@ -280,7 +293,7 @@ function parseHtmlMessage(html: string, partCreator: PartCreator, isQuotedMessag
return descend; return descend;
} }
function onNodeLeave(n) { function onNodeLeave(n: Node) {
if (checkIgnored(n)) { if (checkIgnored(n)) {
return; return;
} }