Parse matrix-schemed URIs (#7453)

Co-authored-by: J. Ryan Stinnett <jryans@gmail.com>
Co-authored-by: Dariusz Niemczyk <dariuszn@element.io>
Co-authored-by: Timo K <toger5@hotmail.de>

With this pr all href use matrix matrix.to links. As a consequence right-click copy link will always return get you a sharable matrix.to link.
This commit is contained in:
Travis Ralston 2022-01-20 10:18:47 -07:00 committed by GitHub
parent f59ea6d7ad
commit 6712a5b1c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 254 additions and 100 deletions

View file

@ -15,13 +15,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import * as linkifyjs from 'linkifyjs';
import linkifyElement from 'linkify-element';
import linkifyString from 'linkify-string';
import * as linkifyjs from '@matrix-org/linkifyjs';
import linkifyElement from '@matrix-org/linkify-element';
import linkifyString from '@matrix-org/linkify-string';
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
import { registerPlugin } from 'linkifyjs';
import { registerCustomProtocol, registerPlugin } from '@matrix-org/linkifyjs';
import { baseUrl } from "./utils/permalinks/SpecPermalinkConstructor";
//linkifyjs/src/core/fsm
import { baseUrl } from "./utils/permalinks/MatrixToPermalinkConstructor";
import {
parsePermalink,
tryTransformEntityToPermalink,
@ -31,7 +32,7 @@ import dis from './dispatcher/dispatcher';
import { Action } from './dispatcher/actions';
import { ViewUserPayload } from './dispatcher/payloads/ViewUserPayload';
enum Type {
export enum Type {
URL = "url",
UserId = "userid",
RoomAlias = "roomalias",
@ -53,41 +54,29 @@ function matrixOpaqueIdLinkifyParser({
name: Type;
}) {
const {
DOMAIN,
DOT,
// A generic catchall text token
TEXT,
// IPV4 necessity
NUM,
TLD,
COLON,
SYM,
HYPHEN,
UNDERSCORE,
// because 'localhost' is tokenised to the localhost token,
// usernames @localhost:foo.com are otherwise not matched!
LOCALHOST,
domain,
} = scanner.tokens;
const S_START = parser.start;
const matrixSymbol = utils.createTokenClass(name, { isLink: true });
const localpartTokens = [
DOMAIN,
// IPV4 necessity
NUM,
TLD,
// because 'localhost' is tokenised to the localhost token,
// usernames @localhost:foo.com are otherwise not matched!
LOCALHOST,
SYM,
UNDERSCORE,
TEXT,
];
const domainpartTokens = [DOMAIN, NUM, TLD, LOCALHOST];
const localpartTokens = [domain, TLD, LOCALHOST, SYM, UNDERSCORE, HYPHEN];
const domainpartTokens = [domain, TLD, LOCALHOST, HYPHEN];
const INITIAL_STATE = S_START.tt(token);
const LOCALPART_STATE = INITIAL_STATE.tt(DOMAIN);
const LOCALPART_STATE = INITIAL_STATE.tt(domain);
for (const token of localpartTokens) {
INITIAL_STATE.tt(token, LOCALPART_STATE);
LOCALPART_STATE.tt(token, LOCALPART_STATE);
@ -98,7 +87,7 @@ function matrixOpaqueIdLinkifyParser({
}
const DOMAINPART_STATE_DOT = LOCALPART_STATE.tt(COLON);
const DOMAINPART_STATE = DOMAINPART_STATE_DOT.tt(DOMAIN);
const DOMAINPART_STATE = DOMAINPART_STATE_DOT.tt(domain);
DOMAINPART_STATE.tt(DOT, DOMAINPART_STATE_DOT);
for (const token of domainpartTokens) {
DOMAINPART_STATE.tt(token, DOMAINPART_STATE);
@ -117,6 +106,7 @@ function matrixOpaqueIdLinkifyParser({
}
function onUserClick(event: MouseEvent, userId: string) {
event.preventDefault();
const member = new RoomMember(null, userId);
if (!member) { return; }
dis.dispatch<ViewUserPayload>({
@ -124,6 +114,7 @@ function onUserClick(event: MouseEvent, userId: string) {
member: member,
});
}
function onAliasClick(event: MouseEvent, roomAlias: string) {
event.preventDefault();
dis.dispatch({
@ -131,6 +122,7 @@ function onAliasClick(event: MouseEvent, roomAlias: string) {
room_alias: roomAlias,
});
}
function onGroupClick(event: MouseEvent, groupId: string) {
event.preventDefault();
dis.dispatch({ action: 'view_group', group_id: groupId });
@ -168,6 +160,19 @@ export const options = {
onUserClick(e, permalink.userId);
},
};
} else {
// for events, rooms etc. (anything other then users)
const localHref = tryTransformPermalinkToLocalHref(href);
if (localHref !== href) {
// it could be converted to a localHref -> therefore handle locally
return {
// @ts-ignore see https://linkify.js.org/docs/options.html
click: function(e) {
e.preventDefault();
window.location.hash = localHref;
},
};
}
}
} catch (e) {
// OK fine, it's not actually a permalink
@ -178,21 +183,24 @@ export const options = {
return {
// @ts-ignore see https://linkify.js.org/docs/options.html
click: function(e) {
onUserClick(e, href);
const userId = parsePermalink(href).userId;
onUserClick(e, userId);
},
};
case Type.RoomAlias:
return {
// @ts-ignore see https://linkify.js.org/docs/options.html
click: function(e) {
onAliasClick(e, href);
const alias = parsePermalink(href).roomIdOrAlias;
onAliasClick(e, alias);
},
};
case Type.GroupId:
return {
// @ts-ignore see https://linkify.js.org/docs/options.html
click: function(e) {
onGroupClick(e, href);
const groupId = parsePermalink(href).groupId;
onGroupClick(e, groupId);
},
};
}
@ -219,7 +227,9 @@ export const options = {
if (type === Type.URL) {
try {
const transformed = tryTransformPermalinkToLocalHref(href);
if (transformed !== href || decodeURIComponent(href).match(ELEMENT_URL_PATTERN)) {
if (transformed !== href || // if it could be converted to handle locally for matrix symbols e.g. @user:server.tdl and matrix.to
decodeURIComponent(href).match(ELEMENT_URL_PATTERN) // for https:vector|riot...
) {
return null;
} else {
return '_blank';
@ -266,6 +276,8 @@ registerPlugin(Type.UserId, ({ scanner, parser, utils }) => {
});
});
registerCustomProtocol("matrix", true);
export const linkify = linkifyjs;
export const _linkifyElement = linkifyElement;
export const _linkifyString = linkifyString;