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:
parent
f59ea6d7ad
commit
6712a5b1c5
12 changed files with 254 additions and 100 deletions
|
@ -58,6 +58,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@matrix-org/linkify-element": "^4.0.0-rc.5",
|
||||||
|
"@matrix-org/linkify-string": "^4.0.0-rc.5",
|
||||||
|
"@matrix-org/linkifyjs": "^4.0.0-rc.5",
|
||||||
"@sentry/browser": "^6.11.0",
|
"@sentry/browser": "^6.11.0",
|
||||||
"@sentry/tracing": "^6.11.0",
|
"@sentry/tracing": "^6.11.0",
|
||||||
"@types/geojson": "^7946.0.8",
|
"@types/geojson": "^7946.0.8",
|
||||||
|
@ -85,9 +88,6 @@
|
||||||
"is-ip": "^3.1.0",
|
"is-ip": "^3.1.0",
|
||||||
"jszip": "^3.7.0",
|
"jszip": "^3.7.0",
|
||||||
"katex": "^0.12.0",
|
"katex": "^0.12.0",
|
||||||
"linkify-element": "^3.0.4",
|
|
||||||
"linkify-string": "^3.0.4",
|
|
||||||
"linkifyjs": "^3.0.5",
|
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"maplibre-gl": "^1.15.2",
|
"maplibre-gl": "^1.15.2",
|
||||||
"matrix-analytics-events": "https://github.com/matrix-org/matrix-analytics-events.git#1eab4356548c97722a183912fda1ceabbe8cc7c1",
|
"matrix-analytics-events": "https://github.com/matrix-org/matrix-analytics-events.git#1eab4356548c97722a183912fda1ceabbe8cc7c1",
|
||||||
|
@ -147,7 +147,6 @@
|
||||||
"@types/file-saver": "^2.0.3",
|
"@types/file-saver": "^2.0.3",
|
||||||
"@types/flux": "^3.1.9",
|
"@types/flux": "^3.1.9",
|
||||||
"@types/jest": "^26.0.20",
|
"@types/jest": "^26.0.20",
|
||||||
"@types/linkifyjs": "^2.1.3",
|
|
||||||
"@types/lodash": "^4.14.168",
|
"@types/lodash": "^4.14.168",
|
||||||
"@types/modernizr": "^3.5.3",
|
"@types/modernizr": "^3.5.3",
|
||||||
"@types/node": "^14.14.22",
|
"@types/node": "^14.14.22",
|
||||||
|
|
|
@ -175,9 +175,10 @@ const transformTags: IExtendedSanitizeOptions["transformTags"] = { // custom to
|
||||||
if (attribs.href) {
|
if (attribs.href) {
|
||||||
attribs.target = '_blank'; // by default
|
attribs.target = '_blank'; // by default
|
||||||
|
|
||||||
const transformed = tryTransformPermalinkToLocalHref(attribs.href);
|
const transformed = tryTransformPermalinkToLocalHref(attribs.href); // only used to check if it is a link that can be handled locally
|
||||||
if (transformed !== attribs.href || attribs.href.match(ELEMENT_URL_PATTERN)) {
|
if (transformed !== attribs.href || // it could be converted so handle locally symbols e.g. @user:server.tdl, matrix: and matrix.to
|
||||||
attribs.href = transformed;
|
attribs.href.match(ELEMENT_URL_PATTERN) // for https:vector|riot...
|
||||||
|
) {
|
||||||
delete attribs.target;
|
delete attribs.target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import * as sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||||
import FlairStore from "../../../stores/FlairStore";
|
import FlairStore from "../../../stores/FlairStore";
|
||||||
import { getPrimaryPermalinkEntity, parseAppLocalLink } from "../../../utils/permalinks/Permalinks";
|
import { getPrimaryPermalinkEntity, parsePermalink } from "../../../utils/permalinks/Permalinks";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import { Action } from "../../../dispatcher/actions";
|
import { Action } from "../../../dispatcher/actions";
|
||||||
import { mediaFromMxc } from "../../../customisations/Media";
|
import { mediaFromMxc } from "../../../customisations/Media";
|
||||||
|
@ -85,7 +85,7 @@ class Pill extends React.Component {
|
||||||
|
|
||||||
if (nextProps.url) {
|
if (nextProps.url) {
|
||||||
if (nextProps.inMessage) {
|
if (nextProps.inMessage) {
|
||||||
const parts = parseAppLocalLink(nextProps.url);
|
const parts = parsePermalink(nextProps.url);
|
||||||
resourceId = parts.primaryEntityId; // The room/user ID
|
resourceId = parts.primaryEntityId; // The room/user ID
|
||||||
prefix = parts.sigil; // The first character of prefix
|
prefix = parts.sigil; // The first character of prefix
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,13 +15,14 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as linkifyjs from 'linkifyjs';
|
import * as linkifyjs from '@matrix-org/linkifyjs';
|
||||||
import linkifyElement from 'linkify-element';
|
import linkifyElement from '@matrix-org/linkify-element';
|
||||||
import linkifyString from 'linkify-string';
|
import linkifyString from '@matrix-org/linkify-string';
|
||||||
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
|
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 {
|
import {
|
||||||
parsePermalink,
|
parsePermalink,
|
||||||
tryTransformEntityToPermalink,
|
tryTransformEntityToPermalink,
|
||||||
|
@ -31,7 +32,7 @@ import dis from './dispatcher/dispatcher';
|
||||||
import { Action } from './dispatcher/actions';
|
import { Action } from './dispatcher/actions';
|
||||||
import { ViewUserPayload } from './dispatcher/payloads/ViewUserPayload';
|
import { ViewUserPayload } from './dispatcher/payloads/ViewUserPayload';
|
||||||
|
|
||||||
enum Type {
|
export enum Type {
|
||||||
URL = "url",
|
URL = "url",
|
||||||
UserId = "userid",
|
UserId = "userid",
|
||||||
RoomAlias = "roomalias",
|
RoomAlias = "roomalias",
|
||||||
|
@ -53,41 +54,29 @@ function matrixOpaqueIdLinkifyParser({
|
||||||
name: Type;
|
name: Type;
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
DOMAIN,
|
|
||||||
DOT,
|
DOT,
|
||||||
// A generic catchall text token
|
// IPV4 necessity
|
||||||
TEXT,
|
|
||||||
NUM,
|
NUM,
|
||||||
TLD,
|
TLD,
|
||||||
COLON,
|
COLON,
|
||||||
SYM,
|
SYM,
|
||||||
|
HYPHEN,
|
||||||
UNDERSCORE,
|
UNDERSCORE,
|
||||||
// because 'localhost' is tokenised to the localhost token,
|
// because 'localhost' is tokenised to the localhost token,
|
||||||
// usernames @localhost:foo.com are otherwise not matched!
|
// usernames @localhost:foo.com are otherwise not matched!
|
||||||
LOCALHOST,
|
LOCALHOST,
|
||||||
|
domain,
|
||||||
} = scanner.tokens;
|
} = scanner.tokens;
|
||||||
|
|
||||||
const S_START = parser.start;
|
const S_START = parser.start;
|
||||||
const matrixSymbol = utils.createTokenClass(name, { isLink: true });
|
const matrixSymbol = utils.createTokenClass(name, { isLink: true });
|
||||||
|
|
||||||
const localpartTokens = [
|
const localpartTokens = [domain, TLD, LOCALHOST, SYM, UNDERSCORE, HYPHEN];
|
||||||
DOMAIN,
|
const domainpartTokens = [domain, TLD, LOCALHOST, HYPHEN];
|
||||||
// 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 INITIAL_STATE = S_START.tt(token);
|
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) {
|
for (const token of localpartTokens) {
|
||||||
INITIAL_STATE.tt(token, LOCALPART_STATE);
|
INITIAL_STATE.tt(token, LOCALPART_STATE);
|
||||||
LOCALPART_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_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);
|
DOMAINPART_STATE.tt(DOT, DOMAINPART_STATE_DOT);
|
||||||
for (const token of domainpartTokens) {
|
for (const token of domainpartTokens) {
|
||||||
DOMAINPART_STATE.tt(token, DOMAINPART_STATE);
|
DOMAINPART_STATE.tt(token, DOMAINPART_STATE);
|
||||||
|
@ -117,6 +106,7 @@ function matrixOpaqueIdLinkifyParser({
|
||||||
}
|
}
|
||||||
|
|
||||||
function onUserClick(event: MouseEvent, userId: string) {
|
function onUserClick(event: MouseEvent, userId: string) {
|
||||||
|
event.preventDefault();
|
||||||
const member = new RoomMember(null, userId);
|
const member = new RoomMember(null, userId);
|
||||||
if (!member) { return; }
|
if (!member) { return; }
|
||||||
dis.dispatch<ViewUserPayload>({
|
dis.dispatch<ViewUserPayload>({
|
||||||
|
@ -124,6 +114,7 @@ function onUserClick(event: MouseEvent, userId: string) {
|
||||||
member: member,
|
member: member,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAliasClick(event: MouseEvent, roomAlias: string) {
|
function onAliasClick(event: MouseEvent, roomAlias: string) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
|
@ -131,6 +122,7 @@ function onAliasClick(event: MouseEvent, roomAlias: string) {
|
||||||
room_alias: roomAlias,
|
room_alias: roomAlias,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onGroupClick(event: MouseEvent, groupId: string) {
|
function onGroupClick(event: MouseEvent, groupId: string) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
dis.dispatch({ action: 'view_group', group_id: groupId });
|
dis.dispatch({ action: 'view_group', group_id: groupId });
|
||||||
|
@ -168,6 +160,19 @@ export const options = {
|
||||||
onUserClick(e, permalink.userId);
|
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) {
|
} catch (e) {
|
||||||
// OK fine, it's not actually a permalink
|
// OK fine, it's not actually a permalink
|
||||||
|
@ -178,21 +183,24 @@ export const options = {
|
||||||
return {
|
return {
|
||||||
// @ts-ignore see https://linkify.js.org/docs/options.html
|
// @ts-ignore see https://linkify.js.org/docs/options.html
|
||||||
click: function(e) {
|
click: function(e) {
|
||||||
onUserClick(e, href);
|
const userId = parsePermalink(href).userId;
|
||||||
|
onUserClick(e, userId);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
case Type.RoomAlias:
|
case Type.RoomAlias:
|
||||||
return {
|
return {
|
||||||
// @ts-ignore see https://linkify.js.org/docs/options.html
|
// @ts-ignore see https://linkify.js.org/docs/options.html
|
||||||
click: function(e) {
|
click: function(e) {
|
||||||
onAliasClick(e, href);
|
const alias = parsePermalink(href).roomIdOrAlias;
|
||||||
|
onAliasClick(e, alias);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
case Type.GroupId:
|
case Type.GroupId:
|
||||||
return {
|
return {
|
||||||
// @ts-ignore see https://linkify.js.org/docs/options.html
|
// @ts-ignore see https://linkify.js.org/docs/options.html
|
||||||
click: function(e) {
|
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) {
|
if (type === Type.URL) {
|
||||||
try {
|
try {
|
||||||
const transformed = tryTransformPermalinkToLocalHref(href);
|
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;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return '_blank';
|
return '_blank';
|
||||||
|
@ -266,6 +276,8 @@ registerPlugin(Type.UserId, ({ scanner, parser, utils }) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registerCustomProtocol("matrix", true);
|
||||||
|
|
||||||
export const linkify = linkifyjs;
|
export const linkify = linkifyjs;
|
||||||
export const _linkifyElement = linkifyElement;
|
export const _linkifyElement = linkifyElement;
|
||||||
export const _linkifyString = linkifyString;
|
export const _linkifyString = linkifyString;
|
||||||
|
|
105
src/utils/permalinks/MatrixSchemePermalinkConstructor.ts
Normal file
105
src/utils/permalinks/MatrixSchemePermalinkConstructor.ts
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 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.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import PermalinkConstructor, { PermalinkParts } from "./PermalinkConstructor";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates matrix: scheme permalinks
|
||||||
|
*/
|
||||||
|
export default class MatrixSchemePermalinkConstructor extends PermalinkConstructor {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private encodeEntity(entity: string): string {
|
||||||
|
if (entity[0] === "!") {
|
||||||
|
return `roomid/${entity.slice(1)}`;
|
||||||
|
} else if (entity[0] === "#") {
|
||||||
|
return `r/${entity.slice(1)}`;
|
||||||
|
} else if (entity[0] === "@") {
|
||||||
|
return `u/${entity.slice(1)}`;
|
||||||
|
} else if (entity[0] === "$") {
|
||||||
|
return `e/${entity.slice(1)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Cannot encode entity: " + entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
forEvent(roomId: string, eventId: string, serverCandidates: string[]): string {
|
||||||
|
return `matrix:${this.encodeEntity(roomId)}` +
|
||||||
|
`/${this.encodeEntity(eventId)}${this.encodeServerCandidates(serverCandidates)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
forRoom(roomIdOrAlias: string, serverCandidates: string[]): string {
|
||||||
|
return `matrix:${this.encodeEntity(roomIdOrAlias)}${this.encodeServerCandidates(serverCandidates)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
forUser(userId: string): string {
|
||||||
|
return `matrix:${this.encodeEntity(userId)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
forGroup(groupId: string): string {
|
||||||
|
throw new Error("Deliberately not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
forEntity(entityId: string): string {
|
||||||
|
return `matrix:${this.encodeEntity(entityId)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPermalinkHost(testHost: string): boolean {
|
||||||
|
// TODO: Change API signature to accept the URL for checking
|
||||||
|
return testHost === "";
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeServerCandidates(candidates: string[]) {
|
||||||
|
if (!candidates || candidates.length === 0) return '';
|
||||||
|
return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
parsePermalink(fullUrl: string): PermalinkParts {
|
||||||
|
if (!fullUrl || !fullUrl.startsWith("matrix:")) {
|
||||||
|
throw new Error("Does not appear to be a permalink");
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = fullUrl.substring("matrix:".length).split('/');
|
||||||
|
|
||||||
|
const identifier = parts[0];
|
||||||
|
const entityNoSigil = parts[1];
|
||||||
|
if (identifier === 'u') {
|
||||||
|
// Probably a user, no further parsing needed.
|
||||||
|
return PermalinkParts.forUser(`@${entityNoSigil}`);
|
||||||
|
} else if (identifier === 'r' || identifier === 'roomid') {
|
||||||
|
const sigil = identifier === 'r' ? '#' : '!';
|
||||||
|
|
||||||
|
if (parts.length === 2) { // room without event permalink
|
||||||
|
const [roomId, query = ""] = entityNoSigil.split("?");
|
||||||
|
const via = query.split(/&?via=/g).filter(p => !!p);
|
||||||
|
return PermalinkParts.forRoom(`${sigil}${roomId}`, via);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts[2] === 'e') { // event permalink
|
||||||
|
const eventIdAndQuery = parts.length > 3 ? parts.slice(3).join('/') : "";
|
||||||
|
const [eventId, query = ""] = eventIdAndQuery.split("?");
|
||||||
|
const via = query.split(/&?via=/g).filter(p => !!p);
|
||||||
|
return PermalinkParts.forEvent(`${sigil}${entityNoSigil}`, `$${eventId}`, via);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Faulty room permalink");
|
||||||
|
} else {
|
||||||
|
throw new Error("Unknown entity type in permalink");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ export const baseUrl = `https://${host}`;
|
||||||
/**
|
/**
|
||||||
* Generates matrix.to permalinks
|
* Generates matrix.to permalinks
|
||||||
*/
|
*/
|
||||||
export default class SpecPermalinkConstructor extends PermalinkConstructor {
|
export default class MatrixToPermalinkConstructor extends PermalinkConstructor {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
|
@ -23,11 +23,12 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||||
import SpecPermalinkConstructor, { baseUrl as matrixtoBaseUrl } from "./SpecPermalinkConstructor";
|
import MatrixToPermalinkConstructor, { baseUrl as matrixtoBaseUrl } from "./MatrixToPermalinkConstructor";
|
||||||
import PermalinkConstructor, { PermalinkParts } from "./PermalinkConstructor";
|
import PermalinkConstructor, { PermalinkParts } from "./PermalinkConstructor";
|
||||||
import ElementPermalinkConstructor from "./ElementPermalinkConstructor";
|
import ElementPermalinkConstructor from "./ElementPermalinkConstructor";
|
||||||
import SdkConfig from "../../SdkConfig";
|
import SdkConfig from "../../SdkConfig";
|
||||||
import { ELEMENT_URL_PATTERN } from "../../linkify-matrix";
|
import { ELEMENT_URL_PATTERN } from "../../linkify-matrix";
|
||||||
|
import MatrixSchemePermalinkConstructor from "./MatrixSchemePermalinkConstructor";
|
||||||
|
|
||||||
// The maximum number of servers to pick when working out which servers
|
// The maximum number of servers to pick when working out which servers
|
||||||
// to add to permalinks. The servers are appended as ?via=example.org
|
// to add to permalinks. The servers are appended as ?via=example.org
|
||||||
|
@ -312,14 +313,14 @@ export function makeGroupPermalink(groupId: string): string {
|
||||||
export function isPermalinkHost(host: string): boolean {
|
export function isPermalinkHost(host: string): boolean {
|
||||||
// Always check if the permalink is a spec permalink (callers are likely to call
|
// Always check if the permalink is a spec permalink (callers are likely to call
|
||||||
// parsePermalink after this function).
|
// parsePermalink after this function).
|
||||||
if (new SpecPermalinkConstructor().isPermalinkHost(host)) return true;
|
if (new MatrixToPermalinkConstructor().isPermalinkHost(host)) return true;
|
||||||
return getPermalinkConstructor().isPermalinkHost(host);
|
return getPermalinkConstructor().isPermalinkHost(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms an entity (permalink, room alias, user ID, etc) into a local URL
|
* Transforms an entity (permalink, room alias, user ID, etc) into a local URL
|
||||||
* if possible. If the given entity is not found to be valid enough to be converted
|
* if possible. If it is already a permalink (matrix.to) it gets returned
|
||||||
* then a null value will be returned.
|
* unchanged.
|
||||||
* @param {string} entity The entity to transform.
|
* @param {string} entity The entity to transform.
|
||||||
* @returns {string|null} The transformed permalink or null if unable.
|
* @returns {string|null} The transformed permalink or null if unable.
|
||||||
*/
|
*/
|
||||||
|
@ -327,12 +328,31 @@ export function tryTransformEntityToPermalink(entity: string): string {
|
||||||
if (!entity) return null;
|
if (!entity) return null;
|
||||||
|
|
||||||
// Check to see if it is a bare entity for starters
|
// Check to see if it is a bare entity for starters
|
||||||
if (entity[0] === '#' || entity[0] === '!') return makeRoomPermalink(entity);
|
{if (entity[0] === '#' || entity[0] === '!') return makeRoomPermalink(entity);}
|
||||||
if (entity[0] === '@') return makeUserPermalink(entity);
|
if (entity[0] === '@') return makeUserPermalink(entity);
|
||||||
if (entity[0] === '+') return makeGroupPermalink(entity);
|
if (entity[0] === '+') return makeGroupPermalink(entity);
|
||||||
|
|
||||||
// Then try and merge it into a permalink
|
if (entity.slice(0, 7) === "matrix:") {
|
||||||
return tryTransformPermalinkToLocalHref(entity);
|
try {
|
||||||
|
const permalinkParts = parsePermalink(entity);
|
||||||
|
if (permalinkParts) {
|
||||||
|
if (permalinkParts.roomIdOrAlias) {
|
||||||
|
const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : '';
|
||||||
|
let pl = matrixtoBaseUrl+`/#/${permalinkParts.roomIdOrAlias}${eventIdPart}`;
|
||||||
|
if (permalinkParts.viaServers.length > 0) {
|
||||||
|
pl += new MatrixToPermalinkConstructor().encodeServerCandidates(permalinkParts.viaServers);
|
||||||
|
}
|
||||||
|
return pl;
|
||||||
|
} else if (permalinkParts.groupId) {
|
||||||
|
return matrixtoBaseUrl + `/#/${permalinkParts.groupId}`;
|
||||||
|
} else if (permalinkParts.userId) {
|
||||||
|
return matrixtoBaseUrl + `/#/${permalinkParts.userId}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -342,7 +362,7 @@ export function tryTransformEntityToPermalink(entity: string): string {
|
||||||
* @returns {string} The transformed permalink or original URL if unable.
|
* @returns {string} The transformed permalink or original URL if unable.
|
||||||
*/
|
*/
|
||||||
export function tryTransformPermalinkToLocalHref(permalink: string): string {
|
export function tryTransformPermalinkToLocalHref(permalink: string): string {
|
||||||
if (!permalink.startsWith("http:") && !permalink.startsWith("https:")) {
|
if (!permalink.startsWith("http:") && !permalink.startsWith("https:") && !permalink.startsWith("matrix:")) {
|
||||||
return permalink;
|
return permalink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,7 +384,7 @@ export function tryTransformPermalinkToLocalHref(permalink: string): string {
|
||||||
const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : '';
|
const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : '';
|
||||||
permalink = `#/room/${permalinkParts.roomIdOrAlias}${eventIdPart}`;
|
permalink = `#/room/${permalinkParts.roomIdOrAlias}${eventIdPart}`;
|
||||||
if (permalinkParts.viaServers.length > 0) {
|
if (permalinkParts.viaServers.length > 0) {
|
||||||
permalink += new SpecPermalinkConstructor().encodeServerCandidates(permalinkParts.viaServers);
|
permalink += new MatrixToPermalinkConstructor().encodeServerCandidates(permalinkParts.viaServers);
|
||||||
}
|
}
|
||||||
} else if (permalinkParts.groupId) {
|
} else if (permalinkParts.groupId) {
|
||||||
permalink = `#/group/${permalinkParts.groupId}`;
|
permalink = `#/group/${permalinkParts.groupId}`;
|
||||||
|
@ -411,13 +431,15 @@ function getPermalinkConstructor(): PermalinkConstructor {
|
||||||
return new ElementPermalinkConstructor(elementPrefix);
|
return new ElementPermalinkConstructor(elementPrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SpecPermalinkConstructor();
|
return new MatrixToPermalinkConstructor();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parsePermalink(fullUrl: string): PermalinkParts {
|
export function parsePermalink(fullUrl: string): PermalinkParts {
|
||||||
const elementPrefix = SdkConfig.get()['permalinkPrefix'];
|
const elementPrefix = SdkConfig.get()['permalinkPrefix'];
|
||||||
if (decodeURIComponent(fullUrl).startsWith(matrixtoBaseUrl)) {
|
if (decodeURIComponent(fullUrl).startsWith(matrixtoBaseUrl)) {
|
||||||
return new SpecPermalinkConstructor().parsePermalink(decodeURIComponent(fullUrl));
|
return new MatrixToPermalinkConstructor().parsePermalink(decodeURIComponent(fullUrl));
|
||||||
|
} else if (fullUrl.startsWith("matrix:")) {
|
||||||
|
return new MatrixSchemePermalinkConstructor().parsePermalink(fullUrl);
|
||||||
} else if (elementPrefix && fullUrl.startsWith(elementPrefix)) {
|
} else if (elementPrefix && fullUrl.startsWith(elementPrefix)) {
|
||||||
return new ElementPermalinkConstructor(elementPrefix).parsePermalink(fullUrl);
|
return new ElementPermalinkConstructor(elementPrefix).parsePermalink(fullUrl);
|
||||||
}
|
}
|
||||||
|
@ -425,23 +447,6 @@ 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("#/", "");
|
|
||||||
return ElementPermalinkConstructor.parseAppRoute(segments);
|
|
||||||
} catch (e) {
|
|
||||||
// Ignore failures
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getServerName(userId: string): string {
|
function getServerName(userId: string): string {
|
||||||
return userId.split(":").splice(1).join(":");
|
return userId.split(":").splice(1).join(":");
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { MatrixClientPeg } from '../MatrixClientPeg';
|
import { MatrixClientPeg } from '../MatrixClientPeg';
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import Pill from "../components/views/elements/Pill";
|
import Pill from "../components/views/elements/Pill";
|
||||||
import { parseAppLocalLink } from "./permalinks/Permalinks";
|
import { parsePermalink } 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
|
||||||
|
@ -46,7 +46,7 @@ export function pillifyLinks(nodes: ArrayLike<Element>, mxEvent: MatrixEvent, pi
|
||||||
|
|
||||||
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);
|
const parts = parsePermalink(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
|
||||||
// We don't want to pill event permalinks, so those are ignored.
|
// We don't want to pill event permalinks, so those are ignored.
|
||||||
if (parts && !parts.eventId) {
|
if (parts && !parts.eventId) {
|
||||||
|
|
|
@ -245,7 +245,7 @@ describe("<TextualBody />", () => {
|
||||||
const content = wrapper.find(".mx_EventTile_body");
|
const content = wrapper.find(".mx_EventTile_body");
|
||||||
expect(content.html()).toBe(
|
expect(content.html()).toBe(
|
||||||
'<span class="mx_EventTile_body markdown-body" dir="auto">' +
|
'<span class="mx_EventTile_body markdown-body" dir="auto">' +
|
||||||
'An <a href="#/room/!ZxbRYPQXDXKGmDnJNg:example.com/' +
|
'An <a href="https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com/' +
|
||||||
'$16085560162aNpaH:example.com?via=example.com" ' +
|
'$16085560162aNpaH:example.com?via=example.com" ' +
|
||||||
'rel="noreferrer noopener">event link</a> with text</span>',
|
'rel="noreferrer noopener">event link</a> with text</span>',
|
||||||
);
|
);
|
||||||
|
@ -274,7 +274,8 @@ describe("<TextualBody />", () => {
|
||||||
const content = wrapper.find(".mx_EventTile_body");
|
const content = wrapper.find(".mx_EventTile_body");
|
||||||
expect(content.html()).toBe(
|
expect(content.html()).toBe(
|
||||||
'<span class="mx_EventTile_body markdown-body" dir="auto">' +
|
'<span class="mx_EventTile_body markdown-body" dir="auto">' +
|
||||||
'A <span><a class="mx_Pill mx_RoomPill" href="#/room/!ZxbRYPQXDXKGmDnJNg:example.com' +
|
'A <span><a class="mx_Pill mx_RoomPill" ' +
|
||||||
|
'href="https://matrix.to/#/!ZxbRYPQXDXKGmDnJNg:example.com' +
|
||||||
'?via=example.com&via=bob.com"' +
|
'?via=example.com&via=bob.com"' +
|
||||||
'><img class="mx_BaseAvatar mx_BaseAvatar_image" ' +
|
'><img class="mx_BaseAvatar mx_BaseAvatar_image" ' +
|
||||||
'src="mxc://avatar.url/room.png" ' +
|
'src="mxc://avatar.url/room.png" ' +
|
||||||
|
|
|
@ -33,7 +33,7 @@ import { createPartCreator, createRenderer } from "../../../editor/mock";
|
||||||
import { createTestClient, mkEvent, mkStubRoom } from "../../../test-utils";
|
import { createTestClient, mkEvent, mkStubRoom } from "../../../test-utils";
|
||||||
import BasicMessageComposer from "../../../../src/components/views/rooms/BasicMessageComposer";
|
import BasicMessageComposer from "../../../../src/components/views/rooms/BasicMessageComposer";
|
||||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||||
import SpecPermalinkConstructor from "../../../../src/utils/permalinks/SpecPermalinkConstructor";
|
import MatrixToPermalinkConstructor from "../../../../src/utils/permalinks/MatrixToPermalinkConstructor";
|
||||||
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
||||||
import DocumentOffset from '../../../../src/editor/offset';
|
import DocumentOffset from '../../../../src/editor/offset';
|
||||||
import { Layout } from '../../../../src/settings/enums/Layout';
|
import { Layout } from '../../../../src/settings/enums/Layout';
|
||||||
|
@ -166,7 +166,7 @@ describe('<SendMessageComposer/>', () => {
|
||||||
<SendMessageComposer
|
<SendMessageComposer
|
||||||
room={mockRoom as any}
|
room={mockRoom as any}
|
||||||
placeholder="placeholder string"
|
placeholder="placeholder string"
|
||||||
permalinkCreator={new SpecPermalinkConstructor() as any}
|
permalinkCreator={new MatrixToPermalinkConstructor() as any}
|
||||||
/>
|
/>
|
||||||
</RoomContext.Provider>
|
</RoomContext.Provider>
|
||||||
</MatrixClientContext.Provider>);
|
</MatrixClientContext.Provider>);
|
||||||
|
@ -188,7 +188,7 @@ describe('<SendMessageComposer/>', () => {
|
||||||
<SendMessageComposer
|
<SendMessageComposer
|
||||||
room={mockRoom as any}
|
room={mockRoom as any}
|
||||||
placeholder=""
|
placeholder=""
|
||||||
permalinkCreator={new SpecPermalinkConstructor() as any}
|
permalinkCreator={new MatrixToPermalinkConstructor() as any}
|
||||||
replyToEvent={mockEvent}
|
replyToEvent={mockEvent}
|
||||||
/>
|
/>
|
||||||
</RoomContext.Provider>
|
</RoomContext.Provider>
|
||||||
|
@ -234,7 +234,7 @@ describe('<SendMessageComposer/>', () => {
|
||||||
<SendMessageComposer
|
<SendMessageComposer
|
||||||
room={mockRoom as any}
|
room={mockRoom as any}
|
||||||
placeholder=""
|
placeholder=""
|
||||||
permalinkCreator={new SpecPermalinkConstructor() as any}
|
permalinkCreator={new MatrixToPermalinkConstructor() as any}
|
||||||
/>
|
/>
|
||||||
</RoomContext.Provider>
|
</RoomContext.Provider>
|
||||||
</MatrixClientContext.Provider>);
|
</MatrixClientContext.Provider>);
|
||||||
|
@ -263,7 +263,7 @@ describe('<SendMessageComposer/>', () => {
|
||||||
<SendMessageComposer
|
<SendMessageComposer
|
||||||
room={mockRoom as any}
|
room={mockRoom as any}
|
||||||
placeholder="placeholder"
|
placeholder="placeholder"
|
||||||
permalinkCreator={new SpecPermalinkConstructor() as any}
|
permalinkCreator={new MatrixToPermalinkConstructor() as any}
|
||||||
replyToEvent={mockEvent}
|
replyToEvent={mockEvent}
|
||||||
/>
|
/>
|
||||||
</RoomContext.Provider>
|
</RoomContext.Provider>
|
||||||
|
@ -297,7 +297,7 @@ describe('<SendMessageComposer/>', () => {
|
||||||
<SendMessageComposer
|
<SendMessageComposer
|
||||||
room={mockRoom as any}
|
room={mockRoom as any}
|
||||||
placeholder=""
|
placeholder=""
|
||||||
permalinkCreator={new SpecPermalinkConstructor() as any}
|
permalinkCreator={new MatrixToPermalinkConstructor() as any}
|
||||||
relation={{
|
relation={{
|
||||||
rel_type: RelationType.Thread,
|
rel_type: RelationType.Thread,
|
||||||
event_id: "myFakeThreadId",
|
event_id: "myFakeThreadId",
|
||||||
|
|
|
@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { linkify } from '../src/linkify-matrix';
|
import { linkify, Type } from '../src/linkify-matrix';
|
||||||
|
|
||||||
describe('linkify-matrix', () => {
|
describe('linkify-matrix', () => {
|
||||||
const linkTypesByInitialCharacter = {
|
const linkTypesByInitialCharacter = {
|
||||||
|
@ -177,6 +177,18 @@ describe('linkify-matrix', () => {
|
||||||
isLink: true,
|
isLink: true,
|
||||||
}]));
|
}]));
|
||||||
});
|
});
|
||||||
|
it('accept hyphens in name ' + char + 'foo-bar:server.com', () => {
|
||||||
|
const test = '' + char + 'foo-bar:server.com';
|
||||||
|
const found = linkify.find(test);
|
||||||
|
expect(found).toEqual(([{
|
||||||
|
href: char + "foo-bar:server.com",
|
||||||
|
type,
|
||||||
|
value: char + "foo-bar:server.com",
|
||||||
|
start: 0,
|
||||||
|
end: test.length,
|
||||||
|
isLink: true,
|
||||||
|
}]));
|
||||||
|
});
|
||||||
it('ignores trailing `:`', () => {
|
it('ignores trailing `:`', () => {
|
||||||
const test = '' + char + 'foo:bar.com:';
|
const test = '' + char + 'foo:bar.com:';
|
||||||
const found = linkify.find(test);
|
const found = linkify.find(test);
|
||||||
|
@ -264,4 +276,30 @@ describe('linkify-matrix', () => {
|
||||||
describe('userid plugin', () => {
|
describe('userid plugin', () => {
|
||||||
genTests('@');
|
genTests('@');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('matrix uri', () => {
|
||||||
|
const AcceptedMatrixUris = [
|
||||||
|
'matrix:u/foo_bar:server.uk',
|
||||||
|
'matrix:r/foo-bar:server.uk',
|
||||||
|
'matrix:roomid/somewhere:example.org?via=elsewhere.ca',
|
||||||
|
'matrix:r/somewhere:example.org',
|
||||||
|
'matrix:r/somewhere:example.org/e/event',
|
||||||
|
'matrix:roomid/somewhere:example.org/e/event?via=elsewhere.ca',
|
||||||
|
'matrix:u/alice:example.org?action=chat',
|
||||||
|
];
|
||||||
|
for (const matrixUri of AcceptedMatrixUris) {
|
||||||
|
it('accepts ' + matrixUri, () => {
|
||||||
|
const test = matrixUri;
|
||||||
|
const found = linkify.find(test);
|
||||||
|
expect(found).toEqual(([{
|
||||||
|
href: matrixUri,
|
||||||
|
type: Type.URL,
|
||||||
|
value: matrixUri,
|
||||||
|
end: matrixUri.length,
|
||||||
|
start: 0,
|
||||||
|
isLink: true,
|
||||||
|
}]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
37
yarn.lock
37
yarn.lock
|
@ -1384,6 +1384,21 @@
|
||||||
resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe"
|
resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe"
|
||||||
integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==
|
integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==
|
||||||
|
|
||||||
|
"@matrix-org/linkify-element@^4.0.0-rc.5":
|
||||||
|
version "4.0.0-rc.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@matrix-org/linkify-element/-/linkify-element-4.0.0-rc.5.tgz#0c27e81272638674ba4162fec2fd131b2c36c163"
|
||||||
|
integrity sha512-6sdJ5x9EZpUNVZqc2Dig8Q4hySdL2fdE/ivehk4L3y3rV7tMD0fRs5rXQQ4wcgCMhGAAGbscbNiS8pyZIy9Hlg==
|
||||||
|
|
||||||
|
"@matrix-org/linkify-string@^4.0.0-rc.5":
|
||||||
|
version "4.0.0-rc.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@matrix-org/linkify-string/-/linkify-string-4.0.0-rc.5.tgz#139ba23c70a4f5b531656365a6109c122177b132"
|
||||||
|
integrity sha512-WFyu6+kVEPJsDwZwgCSrtUDeIMDdWIFzRRq5z+MLYHiO3J8G19jvRjRnNm4dwjDUqROWhvWS9b8JG7rbuwjkLQ==
|
||||||
|
|
||||||
|
"@matrix-org/linkifyjs@^4.0.0-rc.5":
|
||||||
|
version "4.0.0-rc.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@matrix-org/linkifyjs/-/linkifyjs-4.0.0-rc.5.tgz#3a2885754a8de51164a30e6e09909173e348d6bb"
|
||||||
|
integrity sha512-HGmEZuUzCOzdsUFM5dQK2R2KhBFnxRfye5CYJhM2EpRTO4t5aTcR6Ey09HuJ/DZevQ9GTFUjkuKAKurQhnAfOA==
|
||||||
|
|
||||||
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz":
|
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz":
|
||||||
version "3.2.8"
|
version "3.2.8"
|
||||||
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz#8d53636d045e1776e2a2ec6613e57330dd9ce856"
|
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz#8d53636d045e1776e2a2ec6613e57330dd9ce856"
|
||||||
|
@ -1815,13 +1830,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||||
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
|
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
|
||||||
|
|
||||||
"@types/linkifyjs@^2.1.3":
|
|
||||||
version "2.1.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/linkifyjs/-/linkifyjs-2.1.4.tgz#6b14e35d8d211f2666f602dcabcdc6859617516f"
|
|
||||||
integrity sha512-UuF0hyWNnLTT4xNJdrQx6OWYMNlPRBtt3fKCaROIx48boQyXkQ4YDDwTEQNi9mlsRX0Hpc6AnFKkDZ6IXkxD4g==
|
|
||||||
dependencies:
|
|
||||||
"@types/react" "*"
|
|
||||||
|
|
||||||
"@types/lodash@^4.14.168":
|
"@types/lodash@^4.14.168":
|
||||||
version "4.14.178"
|
version "4.14.178"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8"
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8"
|
||||||
|
@ -5968,21 +5976,6 @@ lines-and-columns@^1.1.6:
|
||||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
||||||
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
|
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
|
||||||
|
|
||||||
linkify-element@^3.0.4:
|
|
||||||
version "3.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/linkify-element/-/linkify-element-3.0.4.tgz#3566a3b48d4c211a684f42a23a9964bf53f3a31a"
|
|
||||||
integrity sha512-xrj2Upv4/XUxvvczoDwojEnzKnfJCHlorAxYmdFPSGNwLz2sXYkYyB7Lw1flkGS7L0yS0dq/HwOotG0Kpaiaxw==
|
|
||||||
|
|
||||||
linkify-string@^3.0.4:
|
|
||||||
version "3.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/linkify-string/-/linkify-string-3.0.4.tgz#6abf1a5e436e800c729274ae08f5703484647f84"
|
|
||||||
integrity sha512-OnNqqRjlYXaXipIAbBC8sDXsSumI1ftatzFg141Pw9HEXWjTVLFcMZoKbFupshqWRavtNJ6QHLa+u6AlxxgeRw==
|
|
||||||
|
|
||||||
linkifyjs@^3.0.5:
|
|
||||||
version "3.0.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-3.0.5.tgz#99e51a3a0c0e232fcb63ebb89eea3ff923378f34"
|
|
||||||
integrity sha512-1Y9XQH65eQKA9p2xtk+zxvnTeQBG7rdAXSkUG97DmuI/Xhji9uaUzaWxRj6rf9YC0v8KKHkxav7tnLX82Sz5Fg==
|
|
||||||
|
|
||||||
loader-utils@^2.0.0:
|
loader-utils@^2.0.0:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
|
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue