Merge pull request #5575 from matrix-org/jryans/no-event-pills
Remove pills from event permalinks with textpull/21833/head
commit
89b835dd20
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2018 New Vector 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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -23,23 +23,11 @@ import { Room, RoomMember } from 'matrix-js-sdk';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import FlairStore from "../../../stores/FlairStore";
|
import FlairStore from "../../../stores/FlairStore";
|
||||||
import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks";
|
import {getPrimaryPermalinkEntity, parseAppLocalLink} from "../../../utils/permalinks/Permalinks";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import {Action} from "../../../dispatcher/actions";
|
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 `[^\/]*`)
|
|
||||||
const REGEX_LOCAL_PERMALINK = /^#\/(?:user|room|group)\/(([#!@+]).*?)(?=\/|\?|$)/;
|
|
||||||
|
|
||||||
class Pill extends React.Component {
|
class Pill extends React.Component {
|
||||||
static isPillUrl(url) {
|
|
||||||
return !!getPrimaryPermalinkEntity(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
static isMessagePillUrl(url) {
|
|
||||||
return !!REGEX_LOCAL_PERMALINK.exec(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
static roomNotifPos(text) {
|
static roomNotifPos(text) {
|
||||||
return text.indexOf("@room");
|
return text.indexOf("@room");
|
||||||
}
|
}
|
||||||
|
@ -56,7 +44,7 @@ class Pill extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
// The Type of this Pill. If url is given, this is auto-detected.
|
// The Type of this Pill. If url is given, this is auto-detected.
|
||||||
type: PropTypes.string,
|
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,
|
url: PropTypes.string,
|
||||||
// Whether the pill is in a message
|
// Whether the pill is in a message
|
||||||
inMessage: PropTypes.bool,
|
inMessage: PropTypes.bool,
|
||||||
|
@ -90,12 +78,9 @@ class Pill extends React.Component {
|
||||||
|
|
||||||
if (nextProps.url) {
|
if (nextProps.url) {
|
||||||
if (nextProps.inMessage) {
|
if (nextProps.inMessage) {
|
||||||
// Default to the empty array if no match for simplicity
|
const parts = parseAppLocalLink(nextProps.url);
|
||||||
// resource and prefix will be undefined instead of throwing
|
resourceId = parts.primaryEntityId; // The room/user ID
|
||||||
const matrixToMatch = REGEX_LOCAL_PERMALINK.exec(nextProps.url) || [];
|
prefix = parts.sigil; // The first character of prefix
|
||||||
|
|
||||||
resourceId = matrixToMatch[1]; // The room/user ID
|
|
||||||
prefix = matrixToMatch[2]; // The first character of prefix
|
|
||||||
} else {
|
} else {
|
||||||
resourceId = getPrimaryPermalinkEntity(nextProps.url);
|
resourceId = getPrimaryPermalinkEntity(nextProps.url);
|
||||||
prefix = resourceId ? resourceId[0] : undefined;
|
prefix = resourceId ? resourceId[0] : undefined;
|
||||||
|
|
|
@ -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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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("/");
|
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
|
if (parts.length < 2) { // we're expecting an entity and an ID of some kind at least
|
||||||
throw new Error("URL is missing parts");
|
throw new Error("URL is missing parts");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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 {
|
static forEvent(roomId: string, eventId: string, viaServers: string[]): PermalinkParts {
|
||||||
return new PermalinkParts(roomId, eventId, null, null, viaServers || []);
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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
|
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) {
|
function getServerName(userId) {
|
||||||
return userId.split(":").splice(1).join(":");
|
return userId.split(":").splice(1).join(":");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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 {MatrixClientPeg} from '../MatrixClientPeg';
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import {PushProcessor} from 'matrix-js-sdk/src/pushprocessor';
|
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
|
* 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")) {
|
if (node.tagName === "A" && node.getAttribute("href")) {
|
||||||
const href = 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
|
// If the link is a (localised) matrix.to link, replace it with a pill
|
||||||
const Pill = sdk.getComponent('elements.Pill');
|
// We don't want to pill event permalinks, so those are ignored.
|
||||||
if (Pill.isMessagePillUrl(href)) {
|
if (parts && !parts.eventId) {
|
||||||
const pillContainer = document.createElement('span');
|
const pillContainer = document.createElement('span');
|
||||||
|
|
||||||
const pill = <Pill
|
const pill = <Pill
|
||||||
|
@ -72,8 +73,6 @@ export function pillifyLinks(nodes, mxEvent, pills) {
|
||||||
// to clear the pills from the last run of pillifyLinks
|
// to clear the pills from the last run of pillifyLinks
|
||||||
!node.parentElement.classList.contains("mx_AtRoomPill")
|
!node.parentElement.classList.contains("mx_AtRoomPill")
|
||||||
) {
|
) {
|
||||||
const Pill = sdk.getComponent('elements.Pill');
|
|
||||||
|
|
||||||
let currentTextNode = node;
|
let currentTextNode = node;
|
||||||
const roomNotifTextNodes = [];
|
const roomNotifTextNodes = [];
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,35 @@ describe("<TextualBody />", () => {
|
||||||
'style="width: 16px; height: 16px;" title="@member:domain.bla" alt="" aria-hidden="true">Member</a>' +
|
'style="width: 16px; height: 16px;" title="@member:domain.bla" alt="" aria-hidden="true">Member</a>' +
|
||||||
'</span></span>');
|
'</span></span>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 <a href=\"https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com/" +
|
||||||
|
"$16085560162aNpaH:example.com?via=example.com\">event link</a> with text",
|
||||||
|
},
|
||||||
|
event: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = mount(<TextualBody mxEvent={ev} />);
|
||||||
|
expect(wrapper.text()).toBe("An event link with text");
|
||||||
|
const content = wrapper.find(".mx_EventTile_body");
|
||||||
|
expect(content.html()).toBe(
|
||||||
|
'<span class="mx_EventTile_body markdown-body" dir="auto">' +
|
||||||
|
'An <a href="#/room/!ZxbRYPQXDXKGmDnJNg:example.com/' +
|
||||||
|
'$16085560162aNpaH:example.com?via=example.com" ' +
|
||||||
|
'rel="noreferrer noopener">event link</a> with text</span>',
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders url previews correctly", () => {
|
it("renders url previews correctly", () => {
|
||||||
|
|
Loading…
Reference in New Issue