diff --git a/src/editor/autocomplete.js b/src/editor/autocomplete.js index ceaf18c444..92c4db415e 100644 --- a/src/editor/autocomplete.js +++ b/src/editor/autocomplete.js @@ -18,12 +18,13 @@ limitations under the License. import {UserPillPart, RoomPillPart, PlainPart} from "./parts"; export default class AutocompleteWrapperModel { - constructor(updateCallback, getAutocompleterComponent, updateQuery, room) { + constructor(updateCallback, getAutocompleterComponent, updateQuery, room, client) { this._updateCallback = updateCallback; this._getAutocompleterComponent = getAutocompleterComponent; this._updateQuery = updateQuery; this._query = null; this._room = room; + this._client = client; } onEscape(e) { @@ -106,7 +107,7 @@ export default class AutocompleteWrapperModel { } case "#": { const displayAlias = completion.completionId; - return new RoomPillPart(displayAlias); + return new RoomPillPart(displayAlias, this._client); } // also used for emoji completion default: diff --git a/src/editor/deserialize.js b/src/editor/deserialize.js index 64b219c2a9..48625cba5f 100644 --- a/src/editor/deserialize.js +++ b/src/editor/deserialize.js @@ -21,7 +21,7 @@ import { walkDOMDepthFirst } from "./dom"; const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN); -function parseLink(a, room) { +function parseLink(a, room, client) { const {href} = a; const pillMatch = REGEX_MATRIXTO.exec(href) || []; const resourceId = pillMatch[1]; // The room/user ID @@ -34,7 +34,7 @@ function parseLink(a, room) { room.getMember(resourceId), ); case "#": - return new RoomPillPart(resourceId); + return new RoomPillPart(resourceId, client); default: { if (href === a.textContent) { return new PlainPart(a.textContent); @@ -57,10 +57,10 @@ function parseCodeBlock(n) { return parts; } -function parseElement(n, room) { +function parseElement(n, room, client) { switch (n.nodeName) { case "A": - return parseLink(n, room); + return parseLink(n, room, client); case "BR": return new NewlinePart("\n"); case "EM": @@ -140,7 +140,7 @@ function prefixQuoteLines(isFirstNode, parts) { } } -function parseHtmlMessage(html, room) { +function parseHtmlMessage(html, room, client) { // no nodes from parsing here should be inserted in the document, // as scripts in event handlers, etc would be executed then. // we're only taking text, so that is fine @@ -165,7 +165,7 @@ function parseHtmlMessage(html, room) { if (n.nodeType === Node.TEXT_NODE) { newParts.push(new PlainPart(n.nodeValue)); } else if (n.nodeType === Node.ELEMENT_NODE) { - const parseResult = parseElement(n, room); + const parseResult = parseElement(n, room, client); if (parseResult) { if (Array.isArray(parseResult)) { newParts.push(...parseResult); @@ -205,10 +205,10 @@ function parseHtmlMessage(html, room) { return parts; } -export function parseEvent(event, room) { +export function parseEvent(event, room, client) { const content = event.getContent(); if (content.format === "org.matrix.custom.html") { - return parseHtmlMessage(content.formatted_body || "", room); + return parseHtmlMessage(content.formatted_body || "", room, client); } else { const body = content.body || ""; const lines = body.split("\n"); diff --git a/src/editor/model.js b/src/editor/model.js index fb6b417530..a5d2f25f95 100644 --- a/src/editor/model.js +++ b/src/editor/model.js @@ -73,7 +73,7 @@ export default class EditorModel { } serializeParts() { - return this._parts.map(({type, text}) => {return {type, text};}); + return this._parts.map(p => p.serialize()); } _diff(newValue, inputType, caret) { diff --git a/src/editor/parts.js b/src/editor/parts.js index be3080db12..193b35a5ea 100644 --- a/src/editor/parts.js +++ b/src/editor/parts.js @@ -102,6 +102,10 @@ class BasePart { toString() { return `${this.type}(${this.text})`; } + + serialize() { + return {type: this.type, text: this.text}; + } } export class PlainPart extends BasePart { @@ -233,13 +237,12 @@ export class NewlinePart extends BasePart { } export class RoomPillPart extends PillPart { - constructor(displayAlias) { + constructor(displayAlias, client) { super(displayAlias, displayAlias); - this._room = this._findRoomByAlias(displayAlias); + this._room = this._findRoomByAlias(displayAlias, client); } - _findRoomByAlias(alias) { - const client = MatrixClientPeg.get(); + _findRoomByAlias(alias, client) { if (alias[0] === '#') { return client.getRooms().find((r) => { return r.getAliases().includes(alias); @@ -300,6 +303,12 @@ export class UserPillPart extends PillPart { get className() { return "mx_UserPill mx_Pill"; } + + serialize() { + const obj = super.serialize(); + obj.userId = this.resourceId; + return obj; + } } @@ -335,13 +344,16 @@ export class PillCandidatePart extends PlainPart { } export class PartCreator { - constructor(getAutocompleterComponent, updateQuery, room) { + constructor(getAutocompleterComponent, updateQuery, room, client) { + this._room = room; + this._client = client; this._autoCompleteCreator = (updateCallback) => { return new AutocompleteWrapperModel( updateCallback, getAutocompleterComponent, updateQuery, room, + client, ); }; } @@ -362,5 +374,22 @@ export class PartCreator { createDefaultPart(text) { return new PlainPart(text); } + + deserializePart(part) { + switch (part.type) { + case "plain": + return new PlainPart(part.text); + case "newline": + return new NewlinePart(part.text); + case "pill-candidate": + return new PillCandidatePart(part.text, this._autoCompleteCreator); + case "room-pill": + return new RoomPillPart(part.text, this._client); + case "user-pill": { + const member = this._room.getMember(part.userId); + return new UserPillPart(part.userId, part.text, member); + } + } + } }