diff --git a/package-lock.json b/package-lock.json
index 5c2db9d38e..2b013b359c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
"@fontsource/roboto": "^4.5.8",
"@khanacademy/simple-markdown": "^0.8.3",
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.12.tgz",
+ "@tauri-apps/api": "^1.0.2",
"@tippyjs/react": "^4.2.6",
"babel-polyfill": "^6.26.0",
"blurhash": "^1.1.5",
@@ -2496,6 +2497,20 @@
"resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-3.0.1.tgz",
"integrity": "sha512-XjDVbs3ZU16CO1h5Q3Ew2RPJqmZBDE/EVf1LYp6ePEffs3V/MX9ZbL5bJr8qiK5SbGmUMuDoaFgyKacYz8prRA=="
},
+ "node_modules/@tauri-apps/api": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.0.2.tgz",
+ "integrity": "sha512-yuNW0oeJ1/ZA7wNF1KgxhHrSu5viPVzY/UgUczzN5ptLM8dH15Juy5rEGkoHfeXGju90Y/l22hi3BtIrp/za+w==",
+ "engines": {
+ "node": ">= 12.22.0",
+ "npm": ">= 6.6.0",
+ "yarn": ">= 1.19.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/tauri"
+ }
+ },
"node_modules/@tippyjs/react": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz",
@@ -3025,8 +3040,6 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"dev": true,
- "optional": true,
- "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -3042,9 +3055,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "dev": true,
- "optional": true,
- "peer": true
+ "dev": true
},
"node_modules/ajv-keywords": {
"version": "3.5.2",
@@ -15347,6 +15358,11 @@
"resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-3.0.1.tgz",
"integrity": "sha512-XjDVbs3ZU16CO1h5Q3Ew2RPJqmZBDE/EVf1LYp6ePEffs3V/MX9ZbL5bJr8qiK5SbGmUMuDoaFgyKacYz8prRA=="
},
+ "@tauri-apps/api": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.0.2.tgz",
+ "integrity": "sha512-yuNW0oeJ1/ZA7wNF1KgxhHrSu5viPVzY/UgUczzN5ptLM8dH15Juy5rEGkoHfeXGju90Y/l22hi3BtIrp/za+w=="
+ },
"@tippyjs/react": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz",
@@ -15823,14 +15839,15 @@
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"dev": true,
- "requires": {},
+ "requires": {
+ "ajv": "^8.0.0"
+ },
"dependencies": {
"ajv": {
- "version": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"dev": true,
- "optional": true,
- "peer": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -15842,9 +15859,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "dev": true,
- "optional": true,
- "peer": true
+ "dev": true
}
}
},
diff --git a/package.json b/package.json
index 63e46a64b6..8d3269a44d 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"@fontsource/roboto": "^4.5.8",
"@khanacademy/simple-markdown": "^0.8.3",
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.12.tgz",
+ "@tauri-apps/api": "^1.0.2",
"@tippyjs/react": "^4.2.6",
"babel-polyfill": "^6.26.0",
"blurhash": "^1.1.5",
diff --git a/src/util/linkHandlers.js b/src/util/linkHandlers.js
new file mode 100644
index 0000000000..256875d0f1
--- /dev/null
+++ b/src/util/linkHandlers.js
@@ -0,0 +1,27 @@
+import { invoke } from '@tauri-apps/api/tauri';
+import handleMatrix from './linkHandlers/matrix.to';
+
+const handlers = {
+ 'matrix.to': (url) => handleMatrix(url),
+};
+
+export default function handleLink(e) {
+ const url = new URL(e.target.href);
+ const handler = handlers[url.hostname];
+
+ if (handler) {
+ handler(url);
+ e.preventDefault();
+ return;
+ }
+
+ // if running inside tauri, let the backend handle the opening of the url
+ // useful so we can open urls in the user's browser specified app or a specified app
+ if (window.__TAURI__) {
+ e.preventDefault();
+ invoke('open_link', { url: e.target.href, bypassHandlers: false });
+ } else {
+ window.open(e.target.href);
+ e.preventDefault();
+ }
+}
diff --git a/src/util/linkHandlers/matrix.to.js b/src/util/linkHandlers/matrix.to.js
new file mode 100644
index 0000000000..a6f9009c21
--- /dev/null
+++ b/src/util/linkHandlers/matrix.to.js
@@ -0,0 +1,30 @@
+import initMatrix from '../../client/initMatrix';
+import { selectRoom, selectTab, openJoinAlias } from '../../client/action/navigation';
+
+function handleMatrix(url) {
+ const mx = initMatrix.matrixClient;
+ const rooms = mx.getRooms();
+
+ const roomName = url.hash.slice(2);
+
+ let joinedRoom;
+
+ rooms.forEach((room) => {
+ if (room.roomId === roomName
+ || room.getCanonicalAlias() === roomName
+ || roomName in room.getAltAliases()) {
+ joinedRoom = room;
+ }
+ });
+
+ if (joinedRoom) {
+ if (joinedRoom.isSpaceRoom()) selectTab(joinedRoom.roomId);
+ else selectRoom(joinedRoom.roomId);
+
+ return;
+ }
+
+ openJoinAlias(roomName);
+}
+
+export default handleMatrix;
diff --git a/src/util/twemojify.jsx b/src/util/twemojify.jsx
index 0a4fede732..1e4d1777e6 100644
--- a/src/util/twemojify.jsx
+++ b/src/util/twemojify.jsx
@@ -5,27 +5,48 @@ import linkifyHtml from 'linkify-html';
import parse from 'html-react-parser';
import twemoji from 'twemoji';
import { sanitizeText } from './sanitize';
+import handleLink from './linkHandlers';
const Math = lazy(() => import('../app/atoms/math/Math'));
-const mathOptions = {
- replace: (node) => {
- const maths = node.attribs?.['data-mx-maths'];
- if (maths) {
- return (
- {maths}}>
-
-
- );
- }
- return null;
- },
-};
+function replaceMath(node) {
+ const maths = node.attribs?.['data-mx-maths'];
+
+ return (
+ {maths}}>
+
+
+ );
+}
+
+function replaceLink(node) {
+ if (node.children?.[0]?.type !== 'text') { return null; }
+
+ return (
+ handleLink(e)}>
+ { node.children[0].data }
+
+ );
+}
+
+function parseOptions(maths = false) {
+ return {
+ replace: (node) => {
+ if (maths) {
+ if (node.attribs?.['data-mx-maths']) { return replaceMath(node); }
+ }
+
+ if (node.name === 'a') { return replaceLink(node); }
+
+ return null;
+ },
+ };
+}
/**
* @param {string} text - text to twemojify
@@ -49,5 +70,8 @@ export function twemojify(text, opts, linkify = false, sanitize = true, maths =
rel: 'noreferrer noopener',
});
}
- return parse(content, maths ? mathOptions : null);
+
+ const elements = parse(content, parseOptions(maths));
+
+ return elements;
}