import React, { ChangeEvent } from 'react'; import { QRCode } from 'react-qr-svg'; import { SessionModal } from './session/SessionModal'; import { SessionButton, SessionButtonColor } from './session/SessionButton'; import { SessionSpinner } from './session/SessionSpinner'; interface Props { onClose: any; pubKeyToUnpair: string | undefined; } interface State { currentPubKey: string | undefined; accepted: boolean; pubKeyRequests: Array; currentView: 'filterRequestView' | 'qrcodeView' | 'unpairDeviceView'; errors: any; loading: boolean; deviceAlias: string | undefined; } export class DevicePairingDialog extends React.Component { constructor(props: any) { super(props); this.closeDialog = this.closeDialog.bind(this); this.onKeyUp = this.onKeyUp.bind(this); this.stopReceivingRequests = this.stopReceivingRequests.bind(this); this.startReceivingRequests = this.startReceivingRequests.bind(this); this.skipDevice = this.skipDevice.bind(this); this.allowDevice = this.allowDevice.bind(this); this.validateSecondaryDevice = this.validateSecondaryDevice.bind(this); this.handleUpdateDeviceAlias = this.handleUpdateDeviceAlias.bind(this); this.triggerUnpairDevice = this.triggerUnpairDevice.bind(this); this.state = { currentPubKey: undefined, accepted: false, pubKeyRequests: Array(), currentView: props.pubKeyToUnpair ? 'unpairDeviceView' : 'qrcodeView', loading: false, errors: undefined, deviceAlias: 'Unnamed Device', }; } public componentWillMount() { if (this.state.currentView === 'qrcodeView') { this.startReceivingRequests(); } } public componentWillUnmount() { this.closeDialog(); } public renderErrors() { const { errors } = this.state; return ( <> {errors && ( <>
{errors}
)} ); } public renderFilterRequestsView() { const { currentPubKey, accepted, deviceAlias } = this.state; let secretWords: undefined; if (currentPubKey) { secretWords = window.mnemonic.pubkey_to_secret_words(currentPubKey); } if (accepted) { return ( null} onClose={this.closeDialog} >
{this.renderErrors()}
); } return ( null} onClose={this.closeDialog} >

{window.i18n('allowPairingWithDevice')}

{this.renderErrors()}
{secretWords}
); } public renderQrCodeView() { const requestReceived = this.hasReceivedRequests(); const title = window.i18n('pairingDevice'); return ( null} onClose={this.closeDialog}>
{this.renderErrors()}

{window.i18n('waitingForDeviceToRegister')}

{window.i18n('pairNewDevicePrompt')}
{!requestReceived ? ( ) : null}
); } public renderUnpairDeviceView() { const { pubKeyToUnpair } = this.props; const secretWords = window.mnemonic.pubkey_to_secret_words(pubKeyToUnpair); const conv = window.ConversationController.get(pubKeyToUnpair); let description; if (conv && conv.getNickname()) { description = `${conv.getNickname()}: ${window.shortenPubkey( pubKeyToUnpair )} ${secretWords}`; } else { description = `${window.shortenPubkey(pubKeyToUnpair)} ${secretWords}`; } return ( null} onClose={this.closeDialog} >
{this.renderErrors()}

{window.i18n('confirmUnpairingTitle')}
{description}

); } public render() { const { currentView } = this.state; const renderQrCodeView = currentView === 'qrcodeView'; const renderFilterRequestView = currentView === 'filterRequestView'; const renderUnpairDeviceView = currentView === 'unpairDeviceView'; return ( <> {renderQrCodeView && this.renderQrCodeView()} {renderFilterRequestView && this.renderFilterRequestsView()} {renderUnpairDeviceView && this.renderUnpairDeviceView()} ); } private reset() { this.setState({ currentPubKey: undefined, accepted: false, pubKeyRequests: Array(), currentView: 'filterRequestView', deviceAlias: 'Unnamed Device', }); } private startReceivingRequests() { this.reset(); window.Whisper.events.on( 'devicePairingRequestReceived', (pubKey: string) => { this.requestReceived(pubKey); } ); this.setState({ currentView: 'qrcodeView' }); } private stopReceivingRequests() { this.setState({ currentView: 'filterRequestView' }); window.Whisper.events.off('devicePairingRequestReceived'); } private requestReceived(secondaryDevicePubKey: string | EventHandlerNonNull) { // FIFO: push at the front of the array with unshift() this.state.pubKeyRequests.unshift(secondaryDevicePubKey); if (!this.state.currentPubKey) { this.nextPubKey(); this.stopReceivingRequests(); } } private allowDevice() { this.setState({ accepted: true, }); } private transmissionCB(errors: any) { if (!errors) { this.setState({ errors: null, }); this.closeDialog(); window.pushToast({ title: window.i18n('devicePairedSuccessfully'), type: 'success', }); const conv = window.ConversationController.get(this.state.currentPubKey); if (conv) { conv.setNickname(this.state.deviceAlias); } return; } this.setState({ errors: errors, }); } private skipDevice() { window.Whisper.events.trigger( 'devicePairingRequestRejected', this.state.currentPubKey ); this.closeDialog(); } private nextPubKey() { // FIFO: pop at the back of the array using pop() this.setState({ currentPubKey: this.state.pubKeyRequests.pop(), }); } private onKeyUp(event: any) { switch (event.key) { case 'Esc': case 'Escape': this.closeDialog(); break; default: } } private validateSecondaryDevice() { this.setState({ loading: true }); window.Whisper.events.trigger( 'devicePairingRequestAccepted', this.state.currentPubKey, (errors: any) => { this.transmissionCB(errors); window.Whisper.events.trigger('refreshLinkedDeviceList'); return true; } ); } private hasReceivedRequests() { return this.state.currentPubKey || this.state.pubKeyRequests.length > 0; } private closeDialog() { window.removeEventListener('keyup', this.onKeyUp); this.stopReceivingRequests(); window.Whisper.events.off('devicePairingRequestReceived'); if (this.state.currentPubKey && !this.state.accepted) { window.Whisper.events.trigger( 'devicePairingRequestRejected', this.state.currentPubKey ); } this.props.onClose(); } private handleUpdateDeviceAlias(value: ChangeEvent) { const trimmed = value.target.value.trim(); if (!!trimmed) { this.setState({ deviceAlias: trimmed }); } else { this.setState({ deviceAlias: undefined }); } } private triggerUnpairDevice() { const deviceUnpaired = () => { window.pushToast({ title: window.i18n('deviceUnpaired'), }); this.closeDialog(); this.setState({ loading: false }); }; this.setState({ loading: true }); window.Whisper.events.trigger( 'deviceUnpairingRequested', this.props.pubKeyToUnpair, deviceUnpaired ); } }