Files
cinny/src/app/organisms/emoji-verification/EmojiVerification.jsx
T

201 lines
5.8 KiB
React
Raw Normal View History

2022-05-01 13:22:55 +05:30
/* eslint-disable react/prop-types */
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import './EmojiVerification.scss';
import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation';
2022-05-01 20:56:30 +05:30
import { hasPrivateKey } from '../../../client/state/secretStorageKeys';
import { getDefaultSSKey, isCrossVerified } from '../../../util/matrixUtil';
2022-05-01 13:22:55 +05:30
import Text from '../../atoms/text/Text';
import IconButton from '../../atoms/button/IconButton';
import Button from '../../atoms/button/Button';
import Spinner from '../../atoms/spinner/Spinner';
import Dialog from '../../molecules/dialog/Dialog';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import { useStore } from '../../hooks/useStore';
2022-05-01 20:56:30 +05:30
import { accessSecretStorage } from '../settings/SecretStorageAccess';
2022-05-01 13:22:55 +05:30
2022-05-03 12:52:26 +05:30
function EmojiVerificationContent({ data, requestClose }) {
2022-05-01 13:22:55 +05:30
const [sas, setSas] = useState(null);
const [process, setProcess] = useState(false);
2022-05-03 12:52:26 +05:30
const { request, targetDevice } = data;
2022-05-01 20:56:30 +05:30
const mx = initMatrix.matrixClient;
2022-05-01 13:22:55 +05:30
const mountStore = useStore();
const beginStore = useStore();
2022-05-01 13:22:55 +05:30
2022-05-03 12:52:26 +05:30
const beginVerification = async () => {
if (
isCrossVerified(mx.deviceId)
&& (mx.getCrossSigningId() === null || await mx.crypto.crossSigningInfo.isStoredInKeyCache('self_signing') === false)
) {
if (!hasPrivateKey(getDefaultSSKey())) {
const keyData = await accessSecretStorage('Emoji verification');
if (!keyData) {
request.cancel();
return;
}
2022-05-01 20:56:30 +05:30
}
await mx.checkOwnCrossSigningTrust();
2022-05-01 20:56:30 +05:30
}
2022-05-01 13:22:55 +05:30
setProcess(true);
await request.accept();
2022-05-01 20:56:30 +05:30
const verifier = request.beginKeyVerification('m.sas.v1', targetDevice);
2022-05-03 12:52:26 +05:30
const handleVerifier = (sasData) => {
2022-05-01 20:56:30 +05:30
verifier.off('show_sas', handleVerifier);
2022-05-01 13:22:55 +05:30
if (!mountStore.getItem()) return;
2022-05-03 12:52:26 +05:30
setSas(sasData);
2022-05-01 13:22:55 +05:30
setProcess(false);
2022-05-01 20:56:30 +05:30
};
verifier.on('show_sas', handleVerifier);
2022-05-01 13:22:55 +05:30
await verifier.verify();
};
const sasMismatch = () => {
sas.mismatch();
setProcess(true);
};
const sasConfirm = () => {
sas.confirm();
setProcess(true);
};
2022-05-03 12:52:26 +05:30
useEffect(() => {
mountStore.setItem(true);
const handleChange = () => {
if (request.done || request.cancelled) {
requestClose();
return;
}
if (targetDevice && !beginStore.getItem()) {
beginStore.setItem(true);
2022-05-03 12:52:26 +05:30
beginVerification();
}
};
2024-01-21 23:50:56 +11:00
if (request === null) return undefined;
2022-05-03 12:52:26 +05:30
const req = request;
req.on('change', handleChange);
return () => {
req.off('change', handleChange);
if (req.cancelled === false && req.done === false) {
2022-05-03 12:52:26 +05:30
req.cancel();
}
};
}, [request]);
2022-05-01 13:22:55 +05:30
const renderWait = () => (
<>
<Spinner size="small" />
<Text>Waiting for response from other device...</Text>
</>
);
if (sas !== null) {
return (
<div className="emoji-verification__content">
<Text>Confirm the emoji below are displayed on both devices, in the same order:</Text>
<div className="emoji-verification__emojis">
2022-05-01 20:56:30 +05:30
{sas.sas.emoji.map((emoji, i) => (
// eslint-disable-next-line react/no-array-index-key
<div className="emoji-verification__emoji-block" key={`${emoji[1]}-${i}`}>
2022-05-01 13:22:55 +05:30
<Text variant="h1">{twemojify(emoji[0])}</Text>
<Text>{emoji[1]}</Text>
</div>
))}
</div>
<div className="emoji-verification__buttons">
{process ? renderWait() : (
<>
<Button variant="primary" onClick={sasConfirm}>They match</Button>
<Button onClick={sasMismatch}>{'They don\'t match'}</Button>
</>
)}
</div>
</div>
);
}
2022-05-03 12:52:26 +05:30
if (targetDevice) {
return (
<div className="emoji-verification__content">
<Text>Please accept the request from other device.</Text>
<div className="emoji-verification__buttons">
{renderWait()}
</div>
</div>
);
}
2022-05-01 13:22:55 +05:30
return (
<div className="emoji-verification__content">
2022-05-03 16:01:50 +05:30
<Text>Click accept to start the verification process.</Text>
2022-05-01 13:22:55 +05:30
<div className="emoji-verification__buttons">
{
process
? renderWait()
2022-05-03 12:52:26 +05:30
: <Button variant="primary" onClick={beginVerification}>Accept</Button>
2022-05-01 13:22:55 +05:30
}
</div>
</div>
);
}
EmojiVerificationContent.propTypes = {
2022-05-03 12:52:26 +05:30
data: PropTypes.shape({}).isRequired,
2022-05-01 13:22:55 +05:30
requestClose: PropTypes.func.isRequired,
};
function useVisibilityToggle() {
2022-05-03 12:52:26 +05:30
const [data, setData] = useState(null);
2022-05-01 13:22:55 +05:30
const mx = initMatrix.matrixClient;
useEffect(() => {
2022-05-03 12:52:26 +05:30
const handleOpen = (request, targetDevice) => {
setData({ request, targetDevice });
};
2022-05-01 13:22:55 +05:30
navigation.on(cons.events.navigation.EMOJI_VERIFICATION_OPENED, handleOpen);
mx.on('crypto.verification.request', handleOpen);
return () => {
navigation.removeListener(cons.events.navigation.EMOJI_VERIFICATION_OPENED, handleOpen);
mx.removeListener('crypto.verification.request', handleOpen);
};
}, []);
2022-05-03 12:52:26 +05:30
const requestClose = () => setData(null);
2022-05-01 13:22:55 +05:30
2022-05-03 12:52:26 +05:30
return [data, requestClose];
2022-05-01 13:22:55 +05:30
}
function EmojiVerification() {
2022-05-03 12:52:26 +05:30
const [data, requestClose] = useVisibilityToggle();
2022-05-01 13:22:55 +05:30
return (
<Dialog
2022-05-03 12:52:26 +05:30
isOpen={data !== null}
2022-05-01 13:22:55 +05:30
className="emoji-verification"
title={(
<Text variant="s1" weight="medium" primary>
Emoji verification
</Text>
)}
contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip="Close" />}
onRequestClose={requestClose}
>
{
2022-05-03 12:52:26 +05:30
data !== null
? <EmojiVerificationContent data={data} requestClose={requestClose} />
2022-05-01 13:22:55 +05:30
: <div />
}
</Dialog>
);
}
export default EmojiVerification;