From 501c73ae4b8a77459931d81eb245090ef34dcb46 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 26 Jan 2021 14:08:39 +0000 Subject: [PATCH 1/3] Remove outdated comment --- src/components/views/elements/Pill.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index 3094f17fb7..5cd5b8bbf1 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -28,7 +28,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {Action} from "../../../dispatcher/actions"; // For URLs of matrix.to links in the timeline which have been reformatted by -// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`) +// HttpUtils transformTags to relative links. const REGEX_LOCAL_PERMALINK = /^#\/(?:user|room|group)\/(([#!@+]).*?)(?=\/|\?|$)/; class Pill extends React.Component { From 71921ad705559265f8616dcf2cf82c828c4c9ac9 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 26 Jan 2021 14:11:09 +0000 Subject: [PATCH 2/3] Remove unused isPillUrl --- src/components/views/elements/Pill.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index 5cd5b8bbf1..dc4011fb65 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -32,10 +32,6 @@ import {Action} from "../../../dispatcher/actions"; const REGEX_LOCAL_PERMALINK = /^#\/(?:user|room|group)\/(([#!@+]).*?)(?=\/|\?|$)/; class Pill extends React.Component { - static isPillUrl(url) { - return !!getPrimaryPermalinkEntity(url); - } - static isMessagePillUrl(url) { return !!REGEX_LOCAL_PERMALINK.exec(url); } @@ -56,7 +52,7 @@ class Pill extends React.Component { static propTypes = { // The Type of this Pill. If url is given, this is auto-detected. type: PropTypes.string, - // The URL to pillify (no validation is done, see isPillUrl and isMessagePillUrl) + // The URL to pillify (no validation is done) url: PropTypes.string, // Whether the pill is in a message inMessage: PropTypes.bool, From fa3ace847386c06cabc74053f533a4348db9c65a Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 27 Jan 2021 11:46:20 +0000 Subject: [PATCH 3/3] Rework message pilling to ignore event permalinks This removes pills from event permalinks since they hide the text associated with the link, which can cause nonsensical messages since words have been removed. In addition, this moves away from fragile regexs to more straightforward code to parse links and adds a test for this case. Regressed by https://github.com/matrix-org/matrix-react-sdk/pull/5188 Fixes https://github.com/vector-im/element-web/issues/15159 --- src/components/views/elements/Pill.js | 21 ++++---------- .../permalinks/ElementPermalinkConstructor.js | 6 +++- src/utils/permalinks/PermalinkConstructor.js | 10 ++++++- src/utils/permalinks/Permalinks.js | 19 +++++++++++- src/utils/pillify.js | 13 ++++----- .../views/messages/TextualBody-test.js | 29 +++++++++++++++++++ 6 files changed, 72 insertions(+), 26 deletions(-) diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index dc4011fb65..daa4cb70e2 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -1,7 +1,7 @@ /* Copyright 2017 Vector Creations Ltd Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,19 +23,11 @@ import { Room, RoomMember } from 'matrix-js-sdk'; import PropTypes from 'prop-types'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import FlairStore from "../../../stores/FlairStore"; -import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks"; +import {getPrimaryPermalinkEntity, parseAppLocalLink} from "../../../utils/permalinks/Permalinks"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {Action} from "../../../dispatcher/actions"; -// For URLs of matrix.to links in the timeline which have been reformatted by -// HttpUtils transformTags to relative links. -const REGEX_LOCAL_PERMALINK = /^#\/(?:user|room|group)\/(([#!@+]).*?)(?=\/|\?|$)/; - class Pill extends React.Component { - static isMessagePillUrl(url) { - return !!REGEX_LOCAL_PERMALINK.exec(url); - } - static roomNotifPos(text) { return text.indexOf("@room"); } @@ -86,12 +78,9 @@ class Pill extends React.Component { if (nextProps.url) { if (nextProps.inMessage) { - // Default to the empty array if no match for simplicity - // resource and prefix will be undefined instead of throwing - const matrixToMatch = REGEX_LOCAL_PERMALINK.exec(nextProps.url) || []; - - resourceId = matrixToMatch[1]; // The room/user ID - prefix = matrixToMatch[2]; // The first character of prefix + const parts = parseAppLocalLink(nextProps.url); + resourceId = parts.primaryEntityId; // The room/user ID + prefix = parts.sigil; // The first character of prefix } else { resourceId = getPrimaryPermalinkEntity(nextProps.url); prefix = resourceId ? resourceId[0] : undefined; diff --git a/src/utils/permalinks/ElementPermalinkConstructor.js b/src/utils/permalinks/ElementPermalinkConstructor.js index 5ef0598ba1..29f2602304 100644 --- a/src/utils/permalinks/ElementPermalinkConstructor.js +++ b/src/utils/permalinks/ElementPermalinkConstructor.js @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -76,6 +76,10 @@ export default class ElementPermalinkConstructor extends PermalinkConstructor { } const parts = fullUrl.substring(`${this._elementUrl}/#/`.length).split("/"); + return ElementPermalinkConstructor.parseLinkParts(parts); + } + + static parseLinkParts(parts: string[]): PermalinkParts { if (parts.length < 2) { // we're expecting an entity and an ID of some kind at least throw new Error("URL is missing parts"); } diff --git a/src/utils/permalinks/PermalinkConstructor.js b/src/utils/permalinks/PermalinkConstructor.js index f74c432bf0..25855eb024 100644 --- a/src/utils/permalinks/PermalinkConstructor.js +++ b/src/utils/permalinks/PermalinkConstructor.js @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -80,4 +80,12 @@ export class PermalinkParts { static forEvent(roomId: string, eventId: string, viaServers: string[]): PermalinkParts { return new PermalinkParts(roomId, eventId, null, null, viaServers || []); } + + get primaryEntityId(): string { + return this.roomIdOrAlias || this.userId || this.groupId; + } + + get sigil(): string { + return this.primaryEntityId[0]; + } } diff --git a/src/utils/permalinks/Permalinks.js b/src/utils/permalinks/Permalinks.js index e157ecc55e..9851bc3cb3 100644 --- a/src/utils/permalinks/Permalinks.js +++ b/src/utils/permalinks/Permalinks.js @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019, 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -405,6 +405,23 @@ export function parsePermalink(fullUrl: string): PermalinkParts { return null; // not a permalink we can handle } +/** + * Parses an app local link (`#/(user|room|group)/identifer`) to a Matrix entity + * (room, user, group). Such links are produced by `HtmlUtils` when encountering + * links, which calls `tryTransformPermalinkToLocalHref` in this module. + * @param {string} localLink The app local link + * @returns {PermalinkParts} + */ +export function parseAppLocalLink(localLink: string): PermalinkParts { + try { + const segments = localLink.replace("#/", "").split("/"); + return ElementPermalinkConstructor.parseLinkParts(segments); + } catch (e) { + // Ignore failures + } + return null; +} + function getServerName(userId) { return userId.split(":").splice(1).join(":"); } diff --git a/src/utils/pillify.js b/src/utils/pillify.js index 432771592a..72e0e5a4db 100644 --- a/src/utils/pillify.js +++ b/src/utils/pillify.js @@ -1,5 +1,5 @@ /* -Copyright 2019, 2020 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020, 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +19,8 @@ import ReactDOM from 'react-dom'; import {MatrixClientPeg} from '../MatrixClientPeg'; import SettingsStore from "../settings/SettingsStore"; import {PushProcessor} from 'matrix-js-sdk/src/pushprocessor'; -import * as sdk from '../index'; +import Pill from "../components/views/elements/Pill"; +import { parseAppLocalLink } from "./permalinks/Permalinks"; /** * Recurses depth-first through a DOM tree, converting matrix.to links @@ -43,10 +44,10 @@ export function pillifyLinks(nodes, mxEvent, pills) { if (node.tagName === "A" && node.getAttribute("href")) { const href = node.getAttribute("href"); - + const parts = parseAppLocalLink(href); // If the link is a (localised) matrix.to link, replace it with a pill - const Pill = sdk.getComponent('elements.Pill'); - if (Pill.isMessagePillUrl(href)) { + // We don't want to pill event permalinks, so those are ignored. + if (parts && !parts.eventId) { const pillContainer = document.createElement('span'); const pill = ", () => { 'style="width: 16px; height: 16px;" title="@member:domain.bla" alt="" aria-hidden="true">Member' + ''); }); + + it("pills do not appear for event permalinks", () => { + const ev = mkEvent({ + type: "m.room.message", + room: "room_id", + user: "sender", + content: { + body: + "An [event link](https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com/" + + "$16085560162aNpaH:example.com?via=example.com) with text", + msgtype: "m.text", + format: "org.matrix.custom.html", + formatted_body: + "An event link with text", + }, + event: true, + }); + + const wrapper = mount(); + expect(wrapper.text()).toBe("An event link with text"); + const content = wrapper.find(".mx_EventTile_body"); + expect(content.html()).toBe( + '' + + 'An event link with text', + ); + }); }); it("renders url previews correctly", () => {