import React, { ChangeEvent } from 'react'; import { QRCode } from 'react-qr-svg'; import { SessionModal } from './session/SessionModal'; import { SessionButton } from './session/SessionButton'; import { SessionSpinner } from './session/SessionSpinner'; interface Props { onClose: any; } interface State { currentPubKey: string | undefined; accepted: boolean; pubKeyRequests: Array; currentView: 'filterRequestView' | 'qrcodeView'; 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.state = { currentPubKey: undefined, accepted: false, pubKeyRequests: Array(), currentView: 'qrcodeView', loading: false, errors: undefined, deviceAlias: 'Unnamed Device', }; } public componentWillMount() { this.startReceivingRequests(); } public componentWillUnmount() { this.closeDialog(); } /* dialog.on('deviceUnpairingRequested', pubKey => Whisper.events.trigger('deviceUnpairingRequested', pubKey) );*/ public renderFilterRequestsView() { const { currentPubKey, accepted, deviceAlias } = this.state; const secretWords = window.mnemonic.pubkey_to_secret_words(currentPubKey); if (accepted) { return ( null} onClose={this.closeDialog} >
); } return ( null} onClose={this.closeDialog} >
{secretWords}
); } public renderQrCodeView() { const theme = window.Events.getThemeSetting(); const requestReceived = this.hasReceivedRequests(); const title = window.i18n('pairingDevice'); // Foreground equivalent to .session-modal background color const bgColor = 'rgba(0, 0, 0, 0)'; const fgColor = theme === 'dark' ? '#FFFFFF' : '#1B1B1B'; return ( null} onClose={this.closeDialog}>

{window.i18n('waitingForDeviceToRegister')}

{window.i18n('pairNewDevicePrompt')}
{!requestReceived ? ( ) : (
)}
); } public render() { const { currentView } = this.state; const renderQrCodeView = currentView === 'qrcodeView'; const renderFilterRequestView = currentView === 'filterRequestView'; return ( <> {renderQrCodeView && this.renderQrCodeView()} {renderFilterRequestView && this.renderFilterRequestsView()} ); } 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); window.pushToast({ title: window.i18n('gotPairingRequest'), description: `${window.shortenPubkey( secondaryDevicePubKey )} ${window.i18n( 'showPairingWordsTitle' )}: ${window.mnemonic.pubkey_to_secret_words(secondaryDevicePubKey)}`, }); if (!this.state.currentPubKey) { this.nextPubKey(); } } private allowDevice() { this.setState({ accepted: true, }); } private transmissionCB(errors: any) { if (!errors) { this.setState({ errors: null, }); this.closeDialog(); window.pushToast({ title: window.i18n('devicePairedSuccessfully'), }); const conv = window.ConversationController.get(this.state.currentPubKey); if (conv) { conv.setNickname(this.state.deviceAlias); } // FIXME display error somewhere // FIXME display list of linked device // FIXME do not show linked device in list of contacts return; } /* this.$('.transmissionStatus').text(errors); this.$('.requestAcceptedView .ok').show();*/ this.setState({ errors: errors, }); } private skipDevice() { window.Whisper.events.trigger( 'devicePairingRequestRejected', this.state.currentPubKey ); const hasNext = this.state.pubKeyRequests.length > 0; this.nextPubKey(); if (!hasNext) { this.startReceivingRequests(); } this.setState({ currentView: hasNext ? 'filterRequestView' : 'qrcodeView', }); } 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); 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 }); } } }