/* 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'; import { hasPrivateKey } from '../../../client/state/secretStorageKeys'; import { getDefaultSSKey, isCrossVerified } from '../../../util/matrixUtil'; 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'; import { accessSecretStorage } from '../settings/SecretStorageAccess'; function EmojiVerificationContent({ data, requestClose }) { const [sas, setSas] = useState(null); const [process, setProcess] = useState(false); const { request, targetDevice } = data; const mx = initMatrix.matrixClient; const mountStore = useStore(); 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; } } await mx.checkOwnCrossSigningTrust(); } setProcess(true); await request.accept(); const verifier = request.beginKeyVerification('m.sas.v1', targetDevice); const handleVerifier = (sasData) => { verifier.off('show_sas', handleVerifier); if (!mountStore.getItem()) return; setSas(sasData); setProcess(false); }; verifier.on('show_sas', handleVerifier); await verifier.verify(); }; const sasMismatch = () => { sas.mismatch(); setProcess(true); }; const sasConfirm = () => { sas.confirm(); setProcess(true); }; useEffect(() => { mountStore.setItem(true); const handleChange = () => { if (request.done || request.cancelled) { requestClose(); return; } if (targetDevice && request.started) { beginVerification(); } }; if (request === null) return null; const req = request; req.on('change', handleChange); return () => { req.off('change', handleChange); if (req.cancelled === false && req.done === false) { req.cancel(); } }; }, [request]); const renderWait = () => ( <> Waiting for response from other device... ); if (sas !== null) { return (
Confirm the emoji below are displayed on both devices, in the same order:
{sas.sas.emoji.map((emoji, i) => ( // eslint-disable-next-line react/no-array-index-key
{twemojify(emoji[0])} {emoji[1]}
))}
{process ? renderWait() : ( <> )}
); } if (targetDevice) { return (
Please accept the request from other device.
{renderWait()}
); } return (
Click accept to start the verification process.
{ process ? renderWait() : }
); } EmojiVerificationContent.propTypes = { data: PropTypes.shape({}).isRequired, requestClose: PropTypes.func.isRequired, }; function useVisibilityToggle() { const [data, setData] = useState(null); const mx = initMatrix.matrixClient; useEffect(() => { const handleOpen = (request, targetDevice) => { setData({ request, targetDevice }); }; 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); }; }, []); const requestClose = () => setData(null); return [data, requestClose]; } function EmojiVerification() { const [data, requestClose] = useVisibilityToggle(); return ( Emoji verification )} contentOptions={} onRequestClose={requestClose} > { data !== null ? :
}
); } export default EmojiVerification;