QR code completion
parent
798eb402cb
commit
659d4412bd
@ -0,0 +1,209 @@
|
|||||||
|
/* global
|
||||||
|
Whisper,
|
||||||
|
i18n,
|
||||||
|
libloki,
|
||||||
|
textsecure,
|
||||||
|
ConversationController,
|
||||||
|
$,
|
||||||
|
QRCode,
|
||||||
|
*/
|
||||||
|
|
||||||
|
// eslint-disable-next-line func-names
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.Whisper = window.Whisper || {};
|
||||||
|
|
||||||
|
Whisper.DevicePairingDialogView = Whisper.View.extend({
|
||||||
|
className: 'loki-dialog device-pairing-dialog modal',
|
||||||
|
templateName: 'device-pairing-dialog',
|
||||||
|
initialize() {
|
||||||
|
this.pubKeyRequests = [];
|
||||||
|
this.reset();
|
||||||
|
this.render();
|
||||||
|
this.showView();
|
||||||
|
this.qr = new QRCode(this.$('#qr')[0], {
|
||||||
|
correctLevel: QRCode.CorrectLevel.L,
|
||||||
|
});
|
||||||
|
this.qr.makeCode(textsecure.storage.user.getNumber());
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.pubKey = null;
|
||||||
|
this.accepted = false;
|
||||||
|
this.isListening = false;
|
||||||
|
this.pubKeyToUnpair = null;
|
||||||
|
this.success = false;
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
'click #startPairing': 'startReceivingRequests',
|
||||||
|
'click #close': 'close',
|
||||||
|
'click .waitingForRequestView .cancel': 'stopReceivingRequests',
|
||||||
|
'click .requestReceivedView .skip': 'skipDevice',
|
||||||
|
'click #allowPairing': 'allowDevice',
|
||||||
|
'click .requestAcceptedView .ok': 'stopReceivingRequests',
|
||||||
|
'click .confirmUnpairView .cancel': 'stopReceivingRequests',
|
||||||
|
'click .confirmUnpairView .unpairDevice': 'confirmUnpairDevice',
|
||||||
|
},
|
||||||
|
render_attributes() {
|
||||||
|
return {
|
||||||
|
defaultTitle: i18n('pairedDevices'),
|
||||||
|
waitingForRequestTitle: i18n('waitingForDeviceToRegister'),
|
||||||
|
requestReceivedTitle: i18n('devicePairingReceived'),
|
||||||
|
requestAcceptedTitle: i18n('devicePairingAccepted'),
|
||||||
|
startPairingText: i18n('pairNewDevice'),
|
||||||
|
cancelText: i18n('cancel'),
|
||||||
|
unpairDevice: i18n('unpairDevice'),
|
||||||
|
closeText: i18n('close'),
|
||||||
|
skipText: i18n('skip'),
|
||||||
|
okText: i18n('ok'),
|
||||||
|
allowPairingText: i18n('allowPairing'),
|
||||||
|
confirmUnpairViewTitle: i18n('confirmUnpairingTitle'),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
startReceivingRequests() {
|
||||||
|
this.trigger('startReceivingRequests');
|
||||||
|
this.isListening = true;
|
||||||
|
this.showView();
|
||||||
|
},
|
||||||
|
stopReceivingRequests() {
|
||||||
|
if (this.success) {
|
||||||
|
const deviceAlias = this.$('#deviceAlias')[0].value.trim();
|
||||||
|
const conv = ConversationController.get(this.pubKey);
|
||||||
|
if (conv) {
|
||||||
|
conv.setNickname(deviceAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.trigger('stopReceivingRequests');
|
||||||
|
this.reset();
|
||||||
|
this.showView();
|
||||||
|
},
|
||||||
|
requestReceived(secondaryDevicePubKey) {
|
||||||
|
// FIFO: push at the front of the array with unshift()
|
||||||
|
this.pubKeyRequests.unshift(secondaryDevicePubKey);
|
||||||
|
if (!this.pubKey) {
|
||||||
|
this.nextPubKey();
|
||||||
|
this.showView('requestReceived');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
allowDevice() {
|
||||||
|
this.accepted = true;
|
||||||
|
this.trigger('devicePairingRequestAccepted', this.pubKey, errors =>
|
||||||
|
this.transmisssionCB(errors)
|
||||||
|
);
|
||||||
|
this.showView();
|
||||||
|
},
|
||||||
|
transmisssionCB(errors) {
|
||||||
|
if (!errors) {
|
||||||
|
this.$('.transmissionStatus').text(i18n('provideDeviceAlias'));
|
||||||
|
this.$('#deviceAliasView').show();
|
||||||
|
this.$('#deviceAlias').on('input', e => {
|
||||||
|
if (e.target.value.trim()) {
|
||||||
|
this.$('.requestAcceptedView .ok').removeAttr('disabled');
|
||||||
|
} else {
|
||||||
|
this.$('.requestAcceptedView .ok').attr('disabled', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.$('.requestAcceptedView .ok').show();
|
||||||
|
this.$('.requestAcceptedView .ok').attr('disabled', true);
|
||||||
|
this.success = true;
|
||||||
|
} else {
|
||||||
|
this.$('.transmissionStatus').text(errors);
|
||||||
|
this.$('.requestAcceptedView .ok').show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
skipDevice() {
|
||||||
|
this.trigger('devicePairingRequestRejected', this.pubKey);
|
||||||
|
this.nextPubKey();
|
||||||
|
this.showView();
|
||||||
|
},
|
||||||
|
nextPubKey() {
|
||||||
|
// FIFO: pop at the back of the array using pop()
|
||||||
|
this.pubKey = this.pubKeyRequests.pop();
|
||||||
|
},
|
||||||
|
async confirmUnpairDevice() {
|
||||||
|
this.trigger('deviceUnpairingRequested', this.pubKeyToUnpair);
|
||||||
|
this.reset();
|
||||||
|
this.showView();
|
||||||
|
},
|
||||||
|
requestUnpairDevice(pubKey) {
|
||||||
|
this.pubKeyToUnpair = pubKey;
|
||||||
|
this.showView();
|
||||||
|
},
|
||||||
|
getPubkeyName(pubKey) {
|
||||||
|
const secretWords = window.mnemonic.pubkey_to_secret_words(pubKey);
|
||||||
|
const conv = ConversationController.get(pubKey);
|
||||||
|
const deviceAlias = conv ? conv.getNickname() : 'Unnamed Device';
|
||||||
|
return `${deviceAlias} (pairing secret: <i>${secretWords}</i>)`;
|
||||||
|
},
|
||||||
|
async showView() {
|
||||||
|
const defaultView = this.$('.defaultView');
|
||||||
|
const waitingForRequestView = this.$('.waitingForRequestView');
|
||||||
|
const requestReceivedView = this.$('.requestReceivedView');
|
||||||
|
const requestAcceptedView = this.$('.requestAcceptedView');
|
||||||
|
const confirmUnpairView = this.$('.confirmUnpairView');
|
||||||
|
if (this.pubKeyToUnpair) {
|
||||||
|
defaultView.hide();
|
||||||
|
requestReceivedView.hide();
|
||||||
|
waitingForRequestView.hide();
|
||||||
|
requestAcceptedView.hide();
|
||||||
|
confirmUnpairView.show();
|
||||||
|
const name = this.getPubkeyName(this.pubKeyToUnpair);
|
||||||
|
this.$('.confirmUnpairView #pubkey').html(name);
|
||||||
|
} else if (!this.isListening) {
|
||||||
|
requestReceivedView.hide();
|
||||||
|
waitingForRequestView.hide();
|
||||||
|
requestAcceptedView.hide();
|
||||||
|
confirmUnpairView.hide();
|
||||||
|
|
||||||
|
const ourPubKey = textsecure.storage.user.getNumber();
|
||||||
|
defaultView.show();
|
||||||
|
const pubKeys = await libloki.storage.getSecondaryDevicesFor(ourPubKey);
|
||||||
|
this.$('#pairedPubKeys').empty();
|
||||||
|
if (pubKeys && pubKeys.length > 0) {
|
||||||
|
this.$('#startPairing').attr('disabled', true);
|
||||||
|
pubKeys.forEach(x => {
|
||||||
|
const name = this.getPubkeyName(x);
|
||||||
|
const li = $('<li>').html(name);
|
||||||
|
if (window.lokiFeatureFlags.multiDeviceUnpairing) {
|
||||||
|
const link = $('<a>')
|
||||||
|
.text('Unpair')
|
||||||
|
.attr('href', '#');
|
||||||
|
link.on('click', () => this.requestUnpairDevice(x));
|
||||||
|
li.append(' - ');
|
||||||
|
li.append(link);
|
||||||
|
}
|
||||||
|
this.$('#pairedPubKeys').append(li);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.$('#startPairing').removeAttr('disabled');
|
||||||
|
this.$('#pairedPubKeys').append('<li>No paired devices</li>');
|
||||||
|
}
|
||||||
|
} else if (this.accepted) {
|
||||||
|
defaultView.hide();
|
||||||
|
requestReceivedView.hide();
|
||||||
|
waitingForRequestView.hide();
|
||||||
|
requestAcceptedView.show();
|
||||||
|
} else if (this.pubKey) {
|
||||||
|
const secretWords = window.mnemonic.pubkey_to_secret_words(this.pubKey);
|
||||||
|
this.$('.secretWords').text(secretWords);
|
||||||
|
requestReceivedView.show();
|
||||||
|
waitingForRequestView.hide();
|
||||||
|
requestAcceptedView.hide();
|
||||||
|
defaultView.hide();
|
||||||
|
} else {
|
||||||
|
waitingForRequestView.show();
|
||||||
|
requestReceivedView.hide();
|
||||||
|
requestAcceptedView.hide();
|
||||||
|
defaultView.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.remove();
|
||||||
|
this.qr.clear();
|
||||||
|
if (this.pubKey && !this.accepted) {
|
||||||
|
this.trigger('devicePairingRequestRejected', this.pubKey);
|
||||||
|
}
|
||||||
|
this.trigger('close');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
@ -0,0 +1,165 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { QRCode } from 'react-qrcode'
|
||||||
|
|
||||||
|
import { SessionModal } from './session/SessionModal';
|
||||||
|
import { SessionButton } from './session/SessionButton';
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
i18n: any,
|
||||||
|
onClose: any,
|
||||||
|
pubKeyToUnpair: string | null;
|
||||||
|
pubKey: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
accepted: boolean;
|
||||||
|
isListening: boolean;
|
||||||
|
success: boolean;
|
||||||
|
loading: boolean;
|
||||||
|
data: Array<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DevicePairingDialog extends React.Component<Props, State> {
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.closeDialog = this.closeDialog.bind(this);
|
||||||
|
this.onKeyUp = this.onKeyUp.bind(this);
|
||||||
|
this.startReceivingRequests = this.startReceivingRequests.bind(this);
|
||||||
|
this.stopReceivingRequests = this.stopReceivingRequests.bind(this);
|
||||||
|
this.getPubkeyName = this.getPubkeyName.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
accepted: false,
|
||||||
|
isListening: false,
|
||||||
|
success: false,
|
||||||
|
loading: true,
|
||||||
|
data: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getSecondaryDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getSecondaryDevices(){
|
||||||
|
const secondaryDevices = await window.libloki.storage.getSecondaryDevicesFor(this.props.pubKey);
|
||||||
|
this.setState({
|
||||||
|
data: secondaryDevices,
|
||||||
|
loading: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {i18n, } = this.props;
|
||||||
|
|
||||||
|
|
||||||
|
const newData = ['053e18835c106a5f9f463a44a9d7ff9a26281d529285a047bd969cfc59d4ab8607'];
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setState({
|
||||||
|
data: newData,
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{ ! this.state.loading ? (
|
||||||
|
<SessionModal
|
||||||
|
title={i18n('pairedDevices')}
|
||||||
|
onOk={() => null}
|
||||||
|
onClose={this.closeDialog}
|
||||||
|
>
|
||||||
|
{ this.state.isListening ? (
|
||||||
|
<div>
|
||||||
|
{i18n('waitingForDeviceToRegister')}
|
||||||
|
<div className="spacer-lg"></div>
|
||||||
|
|
||||||
|
<div id="qr">
|
||||||
|
<QRCode value={window.textsecure.storage.user.getNumber()}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<>
|
||||||
|
{this.state.data.length == 0 ? (
|
||||||
|
<>
|
||||||
|
<div>{i18n('noPairedDevices')}</div>
|
||||||
|
<div className="spacer-lg"></div>
|
||||||
|
|
||||||
|
<div className="session-modal__button-group__center">
|
||||||
|
<SessionButton
|
||||||
|
text = {i18n('pairNewDevice')}
|
||||||
|
onClick = {this.startReceivingRequests}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
this.state.data.map((pubKey: any) => {
|
||||||
|
const pubKeyInfo = this.getPubkeyName(pubKey);
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
{ pubKeyInfo.deviceAlias }
|
||||||
|
<br/>
|
||||||
|
<span className="text-subtle">Pairing Secret:</span> { pubKeyInfo.secretWords }
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</SessionModal>
|
||||||
|
) : null }
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private startReceivingRequests() {
|
||||||
|
this.setState({
|
||||||
|
isListening: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPubkeyName(pubKey: string) {
|
||||||
|
const secretWords = window.mnemonic.pubkey_to_secret_words(pubKey);
|
||||||
|
const conv = window.ConversationController.get(this.props.pubKey);
|
||||||
|
const deviceAlias = conv ? conv.getNickname() : 'Unnamed Device';
|
||||||
|
return {deviceAlias, secretWords};
|
||||||
|
}
|
||||||
|
|
||||||
|
private stopReceivingRequests() {
|
||||||
|
if (this.state.success) {
|
||||||
|
const conv = window.ConversationController.get(this.props.pubKey);
|
||||||
|
//if (conv) {
|
||||||
|
// conv.setNickname(this.props.deviceAlias);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private onKeyUp(event: any) {
|
||||||
|
switch (event.key) {
|
||||||
|
case 'Esc':
|
||||||
|
case 'Escape':
|
||||||
|
this.closeDialog();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private closeDialog() {
|
||||||
|
window.removeEventListener('keyup', this.onKeyUp);
|
||||||
|
this.props.onClose();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue