diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index 4c987a0095..abe570cdcd 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -1,6 +1,7 @@ /* Copyright 2017 Vector Creations Ltd Copyright 2018 New Vector Ltd +Copyright 2019 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. @@ -22,23 +23,21 @@ import classNames from 'classnames'; import { Room, RoomMember, MatrixClient } from 'matrix-js-sdk'; import PropTypes from 'prop-types'; import MatrixClientPeg from '../../../MatrixClientPeg'; -import { MATRIXTO_URL_PATTERN } from '../../../linkify-matrix'; import { getDisplayAliasForRoom } from '../../../Rooms'; import FlairStore from "../../../stores/FlairStore"; - -const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN); +import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/RoomPermalinkCreator"; // 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_MATRIXTO = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/; +const REGEX_LOCAL_PERMALINK = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/; const Pill = createReactClass({ statics: { isPillUrl: (url) => { - return !!REGEX_MATRIXTO.exec(url); + return !!getPrimaryPermalinkEntity(url); }, isMessagePillUrl: (url) => { - return !!REGEX_LOCAL_MATRIXTO.exec(url); + return !!REGEX_LOCAL_PERMALINK.exec(url); }, roomNotifPos: (text) => { return text.indexOf("@room"); @@ -95,22 +94,21 @@ const Pill = createReactClass({ }, async componentWillReceiveProps(nextProps) { - let regex = REGEX_MATRIXTO; - if (nextProps.inMessage) { - regex = REGEX_LOCAL_MATRIXTO; - } - - let matrixToMatch; let resourceId; let prefix; if (nextProps.url) { - // Default to the empty array if no match for simplicity - // resource and prefix will be undefined instead of throwing - matrixToMatch = regex.exec(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 + resourceId = matrixToMatch[1]; // The room/user ID + prefix = matrixToMatch[2]; // The first character of prefix + } else { + resourceId = getPrimaryPermalinkEntity(nextProps.url); + prefix = resourceId ? resourceId[0] : undefined; + } } const pillType = this.props.type || { diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 917465aaaa..6696a91483 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -48,13 +48,11 @@ import Markdown from '../../../Markdown'; import MessageComposerStore from '../../../stores/MessageComposerStore'; import ContentMessages from '../../../ContentMessages'; -import {MATRIXTO_URL_PATTERN} from '../../../linkify-matrix'; - import EMOJIBASE from 'emojibase-data/en/compact.json'; import EMOTICON_REGEX from 'emojibase-regex/emoticon'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; -import {makeUserPermalink} from "../../../utils/permalinks/RoomPermalinkCreator"; +import {getPrimaryPermalinkEntity, makeUserPermalink} from "../../../utils/permalinks/RoomPermalinkCreator"; import ReplyPreview from "./ReplyPreview"; import RoomViewStore from '../../../stores/RoomViewStore'; import ReplyThread from "../elements/ReplyThread"; @@ -224,18 +222,15 @@ export default class MessageComposerInput extends React.Component { // special case links if (tag === 'a') { const href = el.getAttribute('href'); - let m; - if (href) { - m = href.match(MATRIXTO_URL_PATTERN); - } - if (m) { + const permalinkEntity = getPrimaryPermalinkEntity(href); + if (permalinkEntity) { return { object: 'inline', type: 'pill', data: { href, completion: el.innerText, - completionId: m[1], + completionId: permalinkEntity, }, }; } else { diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/RoomPermalinkCreator.js index 489a6c8b82..666729bacd 100644 --- a/src/utils/permalinks/RoomPermalinkCreator.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -321,6 +321,32 @@ export function tryTransformPermalinkToLocalHref(permalink: string): string { return permalink; } +export function getPrimaryPermalinkEntity(permalink: string): string { + try { + let permalinkParts = parsePermalink(permalink); + + // If not a permalink, try the vector patterns. + if (!permalinkParts) { + let m = permalink.match(matrixLinkify.VECTOR_URL_PATTERN); + if (m) { + // A bit of a hack, but it gets the job done + const handler = new RiotPermalinkConstructor("http://localhost"); + const entityInfo = m[1].split('#').slice(1).join('#'); + permalinkParts = handler.parsePermalink(`http://localhost/#${entityInfo}`); + } + } + + if (!permalinkParts) return null; // not processable + if (permalinkParts.userId) return permalinkParts.userId; + if (permalinkParts.groupId) return permalinkParts.groupId; + if (permalinkParts.roomIdOrAlias) return permalinkParts.roomIdOrAlias; + } catch (e) { + // no entity - not a permalink + } + + return null; +} + function getPermalinkConstructor(): PermalinkConstructor { const riotPrefix = SdkConfig.get()['permalinkPrefix']; if (riotPrefix && riotPrefix !== matrixtoBaseUrl) {