Created Link handlers for matrix.to
This commit is contained in:
parent
33949dbdb1
commit
c2f6a80244
5 changed files with 157 additions and 31 deletions
39
package-lock.json
generated
39
package-lock.json
generated
|
@ -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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -18,6 +18,9 @@ import Dialog from '../../molecules/dialog/Dialog';
|
|||
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
|
||||
|
||||
import { useStore } from '../../hooks/useStore';
|
||||
import RoomTile from '../../molecules/room-tile/RoomTile';
|
||||
|
||||
import { Debounce } from '../../../util/common';
|
||||
|
||||
const ALIAS_OR_ID_REG = /^[#|!].+:.+\..+$/;
|
||||
|
||||
|
@ -25,9 +28,13 @@ function JoinAliasContent({ term, requestClose }) {
|
|||
const [process, setProcess] = useState(false);
|
||||
const [error, setError] = useState(undefined);
|
||||
const [lastJoinId, setLastJoinId] = useState(undefined);
|
||||
const [preview, setPreview] = useState(undefined);
|
||||
const [loadingPreview, setLoadingPreview] = useState(undefined)
|
||||
const [debounce] = useState(new Debounce());
|
||||
|
||||
const mx = initMatrix.matrixClient;
|
||||
const mountStore = useStore();
|
||||
let roomData = null;
|
||||
|
||||
const openRoom = (roomId) => {
|
||||
const room = mx.getRoom(roomId);
|
||||
|
@ -48,6 +55,33 @@ function JoinAliasContent({ term, requestClose }) {
|
|||
};
|
||||
}, [lastJoinId]);
|
||||
|
||||
const loadPreview = async (text) => {
|
||||
setLoadingPreview(true);
|
||||
setPreview(undefined);
|
||||
debounce._(async () => {
|
||||
try {
|
||||
let room_id = text;
|
||||
|
||||
if(text.startsWith('#')){
|
||||
const room = await mx.resolveRoomAlias(text);
|
||||
room_id = room.room_id;
|
||||
}
|
||||
|
||||
roomData = await mx.getRoom(room_id)
|
||||
setLoadingPreview(false)
|
||||
setPreview(roomData)
|
||||
}
|
||||
catch{
|
||||
setLoadingPreview(false)
|
||||
setPreview(undefined)
|
||||
}
|
||||
|
||||
}, 500)();
|
||||
}
|
||||
|
||||
if(loadingPreview === undefined && term)
|
||||
loadPreview(term)
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
mountStore.setItem(true);
|
||||
|
@ -85,6 +119,9 @@ function JoinAliasContent({ term, requestClose }) {
|
|||
}
|
||||
};
|
||||
|
||||
if(term && loadingPreview === undefined)
|
||||
loadPreview(term);
|
||||
|
||||
return (
|
||||
<form className="join-alias" onSubmit={handleSubmit}>
|
||||
<Input
|
||||
|
@ -92,8 +129,17 @@ function JoinAliasContent({ term, requestClose }) {
|
|||
value={term}
|
||||
name="alias"
|
||||
required
|
||||
onChange={(e) => (loadPreview(e.target.value))}
|
||||
/>
|
||||
{error && <Text className="join-alias__error" variant="b3">{error}</Text>}
|
||||
{ loadingPreview ? <Spinner></Spinner> : <></> }
|
||||
{ preview ? <RoomTile
|
||||
key={preview.roomId}
|
||||
name={preview.name}
|
||||
avatarSrc={initMatrix.matrixClient.getRoom(preview.roomId).getAvatarUrl(initMatrix.matrixClient.baseUrl, 42, 42, 'crop')}
|
||||
id={preview.roomId}
|
||||
/> : <></>
|
||||
}
|
||||
<div className="join-alias__btn">
|
||||
{
|
||||
process
|
||||
|
@ -137,6 +183,7 @@ function useWindowToggle() {
|
|||
|
||||
function JoinAlias() {
|
||||
const [data, requestClose] = useWindowToggle();
|
||||
const [loaded, setLoaded] = useState(null);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
|
39
src/util/linkHandlers.jsx
Normal file
39
src/util/linkHandlers.jsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
import React, { useState } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/tauri';
|
||||
import { openReusableDialog } from '../client/action/navigation';
|
||||
import Text from '../app/atoms/text/Text';
|
||||
import RoomTile from '../app/molecules/room-tile/RoomTile';
|
||||
import initMatrix from '../client/initMatrix';
|
||||
import PopupWindow from '../app/molecules/popup-window/PopupWindow';
|
||||
import Spinner from '../app/atoms/spinner/Spinner';
|
||||
import { openJoinAlias } from '../client/action/navigation';
|
||||
|
||||
const handlers = {
|
||||
'matrix.to': (url) => {
|
||||
openJoinAlias(url.hash.slice(2));
|
||||
},
|
||||
};
|
||||
|
||||
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, check if the tauri backend has a custom handler for this link
|
||||
// useful so we can open urls in a specified app
|
||||
if (window.__TAURI__) {
|
||||
invoke('open_link', { url: e.target.href })
|
||||
.then(() => e.preventDefault())
|
||||
.catch((error) => console.error(error));
|
||||
}
|
||||
else {
|
||||
//open in new tab
|
||||
window.open(e.target.href);
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
|
@ -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 (
|
||||
<Suspense fallback={<code>{maths}</code>}>
|
||||
<Math
|
||||
content={maths}
|
||||
throwOnError={false}
|
||||
errorColor="var(--tc-danger-normal)"
|
||||
displayMode={node.name === 'div'}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
};
|
||||
function replaceMath(node) {
|
||||
const maths = node.attribs?.['data-mx-maths'];
|
||||
|
||||
return (
|
||||
<Suspense fallback={<code>{maths}</code>}>
|
||||
<Math
|
||||
content={maths}
|
||||
throwOnError={false}
|
||||
errorColor="var(--tc-danger-normal)"
|
||||
displayMode={node.name === 'div'}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
function replaceLink(node) {
|
||||
if (node.children?.[0]?.type !== 'text') { return null; }
|
||||
|
||||
return (
|
||||
<a href={node.attribs?.href} onClick={(e) => handleLink(e)}>
|
||||
{ node.children[0].data }
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue