Created Link handlers for matrix.to

This commit is contained in:
Dylan 2022-08-28 19:38:57 +09:30
parent 33949dbdb1
commit c2f6a80244
5 changed files with 157 additions and 31 deletions

39
package-lock.json generated
View file

@ -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
}
}
},

View file

@ -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",

View file

@ -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
View 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();
}
}

View file

@ -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;
}