Merge pull request #3108 from matrix-org/bwindels/edit-room-notif-pill
Support @room pills while editing
This commit is contained in:
commit
63fba611c0
6 changed files with 136 additions and 93 deletions
|
@ -233,7 +233,7 @@ export default class MessageEditor extends React.Component {
|
||||||
parts = editState.getSerializedParts().map(p => partCreator.deserializePart(p));
|
parts = editState.getSerializedParts().map(p => partCreator.deserializePart(p));
|
||||||
} else {
|
} else {
|
||||||
// otherwise, parse the body of the event
|
// otherwise, parse the body of the event
|
||||||
parts = parseEvent(editState.getEvent(), room, this.context.matrixClient);
|
parts = parseEvent(editState.getEvent(), partCreator);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new EditorModel(
|
return new EditorModel(
|
||||||
|
|
|
@ -214,7 +214,13 @@ module.exports = React.createClass({
|
||||||
// update the current node with one that's now taken its place
|
// update the current node with one that's now taken its place
|
||||||
node = pillContainer;
|
node = pillContainer;
|
||||||
}
|
}
|
||||||
} else if (node.nodeType === Node.TEXT_NODE) {
|
} else if (
|
||||||
|
node.nodeType === Node.TEXT_NODE &&
|
||||||
|
// as applying pills happens outside of react, make sure we're not doubly
|
||||||
|
// applying @room pills here, as a rerender with the same content won't touch the DOM
|
||||||
|
// to clear the pills from the last run of pillifyLinks
|
||||||
|
!node.parentElement.classList.contains("mx_AtRoomPill")
|
||||||
|
) {
|
||||||
const Pill = sdk.getComponent('elements.Pill');
|
const Pill = sdk.getComponent('elements.Pill');
|
||||||
|
|
||||||
let currentTextNode = node;
|
let currentTextNode = node;
|
||||||
|
|
|
@ -15,22 +15,19 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {UserPillPart, RoomPillPart, PlainPart} from "./parts";
|
|
||||||
|
|
||||||
export default class AutocompleteWrapperModel {
|
export default class AutocompleteWrapperModel {
|
||||||
constructor(updateCallback, getAutocompleterComponent, updateQuery, room, client) {
|
constructor(updateCallback, getAutocompleterComponent, updateQuery, partCreator) {
|
||||||
this._updateCallback = updateCallback;
|
this._updateCallback = updateCallback;
|
||||||
this._getAutocompleterComponent = getAutocompleterComponent;
|
this._getAutocompleterComponent = getAutocompleterComponent;
|
||||||
this._updateQuery = updateQuery;
|
this._updateQuery = updateQuery;
|
||||||
|
this._partCreator = partCreator;
|
||||||
this._query = null;
|
this._query = null;
|
||||||
this._room = room;
|
|
||||||
this._client = client;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onEscape(e) {
|
onEscape(e) {
|
||||||
this._getAutocompleterComponent().onEscape(e);
|
this._getAutocompleterComponent().onEscape(e);
|
||||||
this._updateCallback({
|
this._updateCallback({
|
||||||
replacePart: new PlainPart(this._queryPart.text),
|
replacePart: this._partCreator.plain(this._queryPart.text),
|
||||||
caretOffset: this._queryOffset,
|
caretOffset: this._queryOffset,
|
||||||
close: true,
|
close: true,
|
||||||
});
|
});
|
||||||
|
@ -93,21 +90,22 @@ export default class AutocompleteWrapperModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
_partForCompletion(completion) {
|
_partForCompletion(completion) {
|
||||||
const firstChr = completion.completionId && completion.completionId[0];
|
const {completionId} = completion;
|
||||||
|
const text = completion.completion;
|
||||||
|
const firstChr = completionId && completionId[0];
|
||||||
switch (firstChr) {
|
switch (firstChr) {
|
||||||
case "@": {
|
case "@": {
|
||||||
const displayName = completion.completion;
|
if (completionId === "@room") {
|
||||||
const userId = completion.completionId;
|
return this._partCreator.atRoomPill(completionId);
|
||||||
const member = this._room.getMember(userId);
|
} else {
|
||||||
return new UserPillPart(userId, displayName, member);
|
return this._partCreator.userPill(text, completionId);
|
||||||
}
|
}
|
||||||
case "#": {
|
|
||||||
const displayAlias = completion.completionId;
|
|
||||||
return new RoomPillPart(displayAlias, this._client);
|
|
||||||
}
|
}
|
||||||
|
case "#":
|
||||||
|
return this._partCreator.roomPill(completionId);
|
||||||
// also used for emoji completion
|
// also used for emoji completion
|
||||||
default:
|
default:
|
||||||
return new PlainPart(completion.completion);
|
return this._partCreator.plain(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,73 +16,86 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MATRIXTO_URL_PATTERN } from '../linkify-matrix';
|
import { MATRIXTO_URL_PATTERN } from '../linkify-matrix';
|
||||||
import { PlainPart, UserPillPart, RoomPillPart, NewlinePart } from "./parts";
|
|
||||||
import { walkDOMDepthFirst } from "./dom";
|
import { walkDOMDepthFirst } from "./dom";
|
||||||
|
|
||||||
const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
|
const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
|
||||||
|
|
||||||
function parseLink(a, room, client) {
|
function parseAtRoomMentions(text, partCreator) {
|
||||||
|
const ATROOM = "@room";
|
||||||
|
const parts = [];
|
||||||
|
text.split(ATROOM).forEach((textPart, i, arr) => {
|
||||||
|
if (textPart.length) {
|
||||||
|
parts.push(partCreator.plain(textPart));
|
||||||
|
}
|
||||||
|
// it's safe to never append @room after the last textPart
|
||||||
|
// as split will report an empty string at the end if
|
||||||
|
// `text` ended in @room.
|
||||||
|
const isLast = i === arr.length - 1;
|
||||||
|
if (!isLast) {
|
||||||
|
parts.push(partCreator.atRoomPill(ATROOM));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseLink(a, partCreator) {
|
||||||
const {href} = a;
|
const {href} = a;
|
||||||
const pillMatch = REGEX_MATRIXTO.exec(href) || [];
|
const pillMatch = REGEX_MATRIXTO.exec(href) || [];
|
||||||
const resourceId = pillMatch[1]; // The room/user ID
|
const resourceId = pillMatch[1]; // The room/user ID
|
||||||
const prefix = pillMatch[2]; // The first character of prefix
|
const prefix = pillMatch[2]; // The first character of prefix
|
||||||
switch (prefix) {
|
switch (prefix) {
|
||||||
case "@":
|
case "@":
|
||||||
return new UserPillPart(
|
return partCreator.userPill(a.textContent, resourceId);
|
||||||
resourceId,
|
|
||||||
a.textContent,
|
|
||||||
room.getMember(resourceId),
|
|
||||||
);
|
|
||||||
case "#":
|
case "#":
|
||||||
return new RoomPillPart(resourceId, client);
|
return partCreator.roomPill(resourceId);
|
||||||
default: {
|
default: {
|
||||||
if (href === a.textContent) {
|
if (href === a.textContent) {
|
||||||
return new PlainPart(a.textContent);
|
return partCreator.plain(a.textContent);
|
||||||
} else {
|
} else {
|
||||||
return new PlainPart(`[${a.textContent}](${href})`);
|
return partCreator.plain(`[${a.textContent}](${href})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCodeBlock(n) {
|
function parseCodeBlock(n, partCreator) {
|
||||||
const parts = [];
|
const parts = [];
|
||||||
const preLines = ("```\n" + n.textContent + "```").split("\n");
|
const preLines = ("```\n" + n.textContent + "```").split("\n");
|
||||||
preLines.forEach((l, i) => {
|
preLines.forEach((l, i) => {
|
||||||
parts.push(new PlainPart(l));
|
parts.push(partCreator.plain(l));
|
||||||
if (i < preLines.length - 1) {
|
if (i < preLines.length - 1) {
|
||||||
parts.push(new NewlinePart("\n"));
|
parts.push(partCreator.newline());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseElement(n, room, client) {
|
function parseElement(n, partCreator) {
|
||||||
switch (n.nodeName) {
|
switch (n.nodeName) {
|
||||||
case "A":
|
case "A":
|
||||||
return parseLink(n, room, client);
|
return parseLink(n, partCreator);
|
||||||
case "BR":
|
case "BR":
|
||||||
return new NewlinePart("\n");
|
return partCreator.newline();
|
||||||
case "EM":
|
case "EM":
|
||||||
return new PlainPart(`*${n.textContent}*`);
|
return partCreator.plain(`*${n.textContent}*`);
|
||||||
case "STRONG":
|
case "STRONG":
|
||||||
return new PlainPart(`**${n.textContent}**`);
|
return partCreator.plain(`**${n.textContent}**`);
|
||||||
case "PRE":
|
case "PRE":
|
||||||
return parseCodeBlock(n);
|
return parseCodeBlock(n, partCreator);
|
||||||
case "CODE":
|
case "CODE":
|
||||||
return new PlainPart(`\`${n.textContent}\``);
|
return partCreator.plain(`\`${n.textContent}\``);
|
||||||
case "DEL":
|
case "DEL":
|
||||||
return new PlainPart(`<del>${n.textContent}</del>`);
|
return partCreator.plain(`<del>${n.textContent}</del>`);
|
||||||
case "LI":
|
case "LI":
|
||||||
if (n.parentElement.nodeName === "OL") {
|
if (n.parentElement.nodeName === "OL") {
|
||||||
return new PlainPart(` 1. `);
|
return partCreator.plain(` 1. `);
|
||||||
} else {
|
} else {
|
||||||
return new PlainPart(` - `);
|
return partCreator.plain(` - `);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// don't textify block nodes we'll decend into
|
// don't textify block nodes we'll decend into
|
||||||
if (!checkDecendInto(n)) {
|
if (!checkDecendInto(n)) {
|
||||||
return new PlainPart(n.textContent);
|
return partCreator.plain(n.textContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,22 +138,22 @@ function checkIgnored(n) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function prefixQuoteLines(isFirstNode, parts) {
|
function prefixQuoteLines(isFirstNode, parts, partCreator) {
|
||||||
const PREFIX = "> ";
|
const PREFIX = "> ";
|
||||||
// a newline (to append a > to) wouldn't be added to parts for the first line
|
// a newline (to append a > to) wouldn't be added to parts for the first line
|
||||||
// if there was no content before the BLOCKQUOTE, so handle that
|
// if there was no content before the BLOCKQUOTE, so handle that
|
||||||
if (isFirstNode) {
|
if (isFirstNode) {
|
||||||
parts.splice(0, 0, new PlainPart(PREFIX));
|
parts.splice(0, 0, partCreator.plain(PREFIX));
|
||||||
}
|
}
|
||||||
for (let i = 0; i < parts.length; i += 1) {
|
for (let i = 0; i < parts.length; i += 1) {
|
||||||
if (parts[i].type === "newline") {
|
if (parts[i].type === "newline") {
|
||||||
parts.splice(i + 1, 0, new PlainPart(PREFIX));
|
parts.splice(i + 1, 0, partCreator.plain(PREFIX));
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseHtmlMessage(html, room, client) {
|
function parseHtmlMessage(html, partCreator) {
|
||||||
// no nodes from parsing here should be inserted in the document,
|
// no nodes from parsing here should be inserted in the document,
|
||||||
// as scripts in event handlers, etc would be executed then.
|
// as scripts in event handlers, etc would be executed then.
|
||||||
// we're only taking text, so that is fine
|
// we're only taking text, so that is fine
|
||||||
|
@ -159,13 +172,13 @@ function parseHtmlMessage(html, room, client) {
|
||||||
|
|
||||||
const newParts = [];
|
const newParts = [];
|
||||||
if (lastNode && (checkBlockNode(lastNode) || checkBlockNode(n))) {
|
if (lastNode && (checkBlockNode(lastNode) || checkBlockNode(n))) {
|
||||||
newParts.push(new NewlinePart("\n"));
|
newParts.push(partCreator.newline());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n.nodeType === Node.TEXT_NODE) {
|
if (n.nodeType === Node.TEXT_NODE) {
|
||||||
newParts.push(new PlainPart(n.nodeValue));
|
newParts.push(...parseAtRoomMentions(n.nodeValue, partCreator));
|
||||||
} else if (n.nodeType === Node.ELEMENT_NODE) {
|
} else if (n.nodeType === Node.ELEMENT_NODE) {
|
||||||
const parseResult = parseElement(n, room, client);
|
const parseResult = parseElement(n, partCreator);
|
||||||
if (parseResult) {
|
if (parseResult) {
|
||||||
if (Array.isArray(parseResult)) {
|
if (Array.isArray(parseResult)) {
|
||||||
newParts.push(...parseResult);
|
newParts.push(...parseResult);
|
||||||
|
@ -177,14 +190,14 @@ function parseHtmlMessage(html, room, client) {
|
||||||
|
|
||||||
if (newParts.length && inQuote) {
|
if (newParts.length && inQuote) {
|
||||||
const isFirstPart = parts.length === 0;
|
const isFirstPart = parts.length === 0;
|
||||||
prefixQuoteLines(isFirstPart, newParts);
|
prefixQuoteLines(isFirstPart, newParts, partCreator);
|
||||||
}
|
}
|
||||||
|
|
||||||
parts.push(...newParts);
|
parts.push(...newParts);
|
||||||
|
|
||||||
// extra newline after quote, only if there something behind it...
|
// extra newline after quote, only if there something behind it...
|
||||||
if (lastNode && lastNode.nodeName === "BLOCKQUOTE") {
|
if (lastNode && lastNode.nodeName === "BLOCKQUOTE") {
|
||||||
parts.push(new NewlinePart("\n"));
|
parts.push(partCreator.newline());
|
||||||
}
|
}
|
||||||
lastNode = null;
|
lastNode = null;
|
||||||
return checkDecendInto(n);
|
return checkDecendInto(n);
|
||||||
|
@ -205,27 +218,25 @@ function parseHtmlMessage(html, room, client) {
|
||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseEvent(event, room, client) {
|
export function parseEvent(event, partCreator) {
|
||||||
const content = event.getContent();
|
const content = event.getContent();
|
||||||
let parts;
|
let parts;
|
||||||
if (content.format === "org.matrix.custom.html") {
|
if (content.format === "org.matrix.custom.html") {
|
||||||
parts = parseHtmlMessage(content.formatted_body || "", room, client);
|
parts = parseHtmlMessage(content.formatted_body || "", partCreator);
|
||||||
} else {
|
} else {
|
||||||
const body = content.body || "";
|
const body = content.body || "";
|
||||||
const lines = body.split("\n");
|
const lines = body.split("\n");
|
||||||
parts = lines.reduce((parts, line, i) => {
|
parts = lines.reduce((parts, line, i) => {
|
||||||
const isLast = i === lines.length - 1;
|
const isLast = i === lines.length - 1;
|
||||||
const text = new PlainPart(line);
|
const newParts = parseAtRoomMentions(line, partCreator);
|
||||||
const newLine = !isLast && new NewlinePart("\n");
|
if (!isLast) {
|
||||||
if (newLine) {
|
newParts.push(partCreator.newline());
|
||||||
return parts.concat(text, newLine);
|
|
||||||
} else {
|
|
||||||
return parts.concat(text);
|
|
||||||
}
|
}
|
||||||
|
return parts.concat(newParts);
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
if (content.msgtype === "m.emote") {
|
if (content.msgtype === "m.emote") {
|
||||||
parts.unshift(new PlainPart("/me "));
|
parts.unshift(partCreator.plain("/me "));
|
||||||
}
|
}
|
||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,7 @@ class BasePart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PlainPart extends BasePart {
|
class PlainPart extends BasePart {
|
||||||
acceptsInsertion(chr) {
|
acceptsInsertion(chr) {
|
||||||
return chr !== "@" && chr !== "#" && chr !== ":" && chr !== "\n";
|
return chr !== "@" && chr !== "#" && chr !== ":" && chr !== "\n";
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ class PillPart extends BasePart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NewlinePart extends BasePart {
|
class NewlinePart extends BasePart {
|
||||||
acceptsInsertion(chr, i) {
|
acceptsInsertion(chr, i) {
|
||||||
return (this.text.length + i) === 0 && chr === "\n";
|
return (this.text.length + i) === 0 && chr === "\n";
|
||||||
}
|
}
|
||||||
|
@ -235,20 +235,10 @@ export class NewlinePart extends BasePart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RoomPillPart extends PillPart {
|
class RoomPillPart extends PillPart {
|
||||||
constructor(displayAlias, client) {
|
constructor(displayAlias, room) {
|
||||||
super(displayAlias, displayAlias);
|
super(displayAlias, displayAlias);
|
||||||
this._room = this._findRoomByAlias(displayAlias, client);
|
this._room = room;
|
||||||
}
|
|
||||||
|
|
||||||
_findRoomByAlias(alias, client) {
|
|
||||||
if (alias[0] === '#') {
|
|
||||||
return client.getRooms().find((r) => {
|
|
||||||
return r.getAliases().includes(alias);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return client.getRoom(alias);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setAvatar(node) {
|
setAvatar(node) {
|
||||||
|
@ -270,7 +260,13 @@ export class RoomPillPart extends PillPart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserPillPart extends PillPart {
|
class AtRoomPillPart extends RoomPillPart {
|
||||||
|
get type() {
|
||||||
|
return "at-room-pill";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserPillPart extends PillPart {
|
||||||
constructor(userId, displayName, member) {
|
constructor(userId, displayName, member) {
|
||||||
super(userId, displayName);
|
super(userId, displayName);
|
||||||
this._member = member;
|
this._member = member;
|
||||||
|
@ -311,7 +307,7 @@ export class UserPillPart extends PillPart {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class PillCandidatePart extends PlainPart {
|
class PillCandidatePart extends PlainPart {
|
||||||
constructor(text, autoCompleteCreator) {
|
constructor(text, autoCompleteCreator) {
|
||||||
super(text);
|
super(text);
|
||||||
this._autoCompleteCreator = autoCompleteCreator;
|
this._autoCompleteCreator = autoCompleteCreator;
|
||||||
|
@ -351,8 +347,7 @@ export class PartCreator {
|
||||||
updateCallback,
|
updateCallback,
|
||||||
getAutocompleterComponent,
|
getAutocompleterComponent,
|
||||||
updateQuery,
|
updateQuery,
|
||||||
room,
|
this,
|
||||||
client,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -362,7 +357,7 @@ export class PartCreator {
|
||||||
case "#":
|
case "#":
|
||||||
case "@":
|
case "@":
|
||||||
case ":":
|
case ":":
|
||||||
return new PillCandidatePart("", this._autoCompleteCreator);
|
return this.pillCandidate("");
|
||||||
case "\n":
|
case "\n":
|
||||||
return new NewlinePart();
|
return new NewlinePart();
|
||||||
default:
|
default:
|
||||||
|
@ -371,24 +366,57 @@ export class PartCreator {
|
||||||
}
|
}
|
||||||
|
|
||||||
createDefaultPart(text) {
|
createDefaultPart(text) {
|
||||||
return new PlainPart(text);
|
return this.plain(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
deserializePart(part) {
|
deserializePart(part) {
|
||||||
switch (part.type) {
|
switch (part.type) {
|
||||||
case "plain":
|
case "plain":
|
||||||
return new PlainPart(part.text);
|
return this.plain(part.text);
|
||||||
case "newline":
|
case "newline":
|
||||||
return new NewlinePart(part.text);
|
return this.newline();
|
||||||
|
case "at-room-pill":
|
||||||
|
return this.atRoomPill(part.text);
|
||||||
case "pill-candidate":
|
case "pill-candidate":
|
||||||
return new PillCandidatePart(part.text, this._autoCompleteCreator);
|
return this.pillCandidate(part.text);
|
||||||
case "room-pill":
|
case "room-pill":
|
||||||
return new RoomPillPart(part.text, this._client);
|
return this.roomPill(part.text);
|
||||||
case "user-pill": {
|
case "user-pill":
|
||||||
const member = this._room.getMember(part.userId);
|
return this.userPill(part.text, part.userId);
|
||||||
return new UserPillPart(part.userId, part.text, member);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plain(text) {
|
||||||
|
return new PlainPart(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
newline() {
|
||||||
|
return new NewlinePart("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
pillCandidate(text) {
|
||||||
|
return new PillCandidatePart(text, this._autoCompleteCreator);
|
||||||
|
}
|
||||||
|
|
||||||
|
roomPill(alias) {
|
||||||
|
let room;
|
||||||
|
if (alias[0] === '#') {
|
||||||
|
room = this._client.getRooms().find((r) => {
|
||||||
|
return r.getAliases().includes(alias);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
room = this._client.getRoom(alias);
|
||||||
|
}
|
||||||
|
return new RoomPillPart(alias, room);
|
||||||
|
}
|
||||||
|
|
||||||
|
atRoomPill(text) {
|
||||||
|
return new AtRoomPillPart(text, this._room);
|
||||||
|
}
|
||||||
|
|
||||||
|
userPill(displayName, userId) {
|
||||||
|
const member = this._room.getMember(userId);
|
||||||
|
return new UserPillPart(userId, displayName, member);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ export function mdSerialize(model) {
|
||||||
return html + "\n";
|
return html + "\n";
|
||||||
case "plain":
|
case "plain":
|
||||||
case "pill-candidate":
|
case "pill-candidate":
|
||||||
|
case "at-room-pill":
|
||||||
return html + part.text;
|
return html + part.text;
|
||||||
case "room-pill":
|
case "room-pill":
|
||||||
case "user-pill":
|
case "user-pill":
|
||||||
|
@ -47,6 +48,7 @@ export function textSerialize(model) {
|
||||||
return text + "\n";
|
return text + "\n";
|
||||||
case "plain":
|
case "plain":
|
||||||
case "pill-candidate":
|
case "pill-candidate":
|
||||||
|
case "at-room-pill":
|
||||||
return text + part.text;
|
return text + part.text;
|
||||||
case "room-pill":
|
case "room-pill":
|
||||||
case "user-pill":
|
case "user-pill":
|
||||||
|
@ -58,13 +60,11 @@ export function textSerialize(model) {
|
||||||
export function requiresHtml(model) {
|
export function requiresHtml(model) {
|
||||||
return model.parts.some(part => {
|
return model.parts.some(part => {
|
||||||
switch (part.type) {
|
switch (part.type) {
|
||||||
case "newline":
|
|
||||||
case "plain":
|
|
||||||
case "pill-candidate":
|
|
||||||
return false;
|
|
||||||
case "room-pill":
|
case "room-pill":
|
||||||
case "user-pill":
|
case "user-pill":
|
||||||
return true;
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue