add unpairing of device from settings

pull/725/head
Audric Ackermann 6 years ago
parent 34458406fd
commit 411b343e6a

@ -994,6 +994,9 @@
"unpairDevice": { "unpairDevice": {
"message": "Unpair Device" "message": "Unpair Device"
}, },
"deviceUnpaired": {
"message": "Device Unpaired"
},
"clear": { "clear": {
"message": "Clear" "message": "Clear"
}, },

@ -1198,9 +1198,9 @@
} }
}); });
Whisper.events.on('showDevicePairingDialog', async () => { Whisper.events.on('showDevicePairingDialog', async (options = {}) => {
if (appView) { if (appView) {
appView.showDevicePairingDialog(); appView.showDevicePairingDialog(options);
} }
}); });
@ -1269,6 +1269,7 @@
await window.lokiFileServerAPI.updateOurDeviceMapping(); await window.lokiFileServerAPI.updateOurDeviceMapping();
// TODO: we should ensure the message was sent and retry automatically if not // TODO: we should ensure the message was sent and retry automatically if not
await libloki.api.sendUnpairingMessageToSecondary(pubKey); await libloki.api.sendUnpairingMessageToSecondary(pubKey);
Whisper.events.trigger('refreshLinkedDeviceList');
}); });
} }

@ -213,8 +213,8 @@
}); });
this.el.append(dialog.el); this.el.append(dialog.el);
}, },
showDevicePairingDialog() { showDevicePairingDialog(options) {
const dialog = new Whisper.DevicePairingDialogView(); const dialog = new Whisper.DevicePairingDialogView(options);
this.el.append(dialog.el); this.el.append(dialog.el);
}, },

@ -8,8 +8,9 @@
Whisper.DevicePairingDialogView = Whisper.View.extend({ Whisper.DevicePairingDialogView = Whisper.View.extend({
className: 'loki-dialog device-pairing-dialog modal', className: 'loki-dialog device-pairing-dialog modal',
initialize() { initialize(options) {
this.close = this.close.bind(this); this.close = this.close.bind(this);
this.pubKeyToUnpair = options.pubKeyToUnpair;
this.render(); this.render();
}, },
@ -20,6 +21,7 @@
props: { props: {
i18n, i18n,
onClose: this.close, onClose: this.close,
pubKeyToUnpair: this.pubKeyToUnpair,
}, },
}); });

@ -2,18 +2,19 @@ import React, { ChangeEvent } from 'react';
import { QRCode } from 'react-qr-svg'; import { QRCode } from 'react-qr-svg';
import { SessionModal } from './session/SessionModal'; import { SessionModal } from './session/SessionModal';
import { SessionButton } from './session/SessionButton'; import { SessionButton, SessionButtonColor } from './session/SessionButton';
import { SessionSpinner } from './session/SessionSpinner'; import { SessionSpinner } from './session/SessionSpinner';
interface Props { interface Props {
onClose: any; onClose: any;
pubKeyToUnpair: string | undefined;
} }
interface State { interface State {
currentPubKey: string | undefined; currentPubKey: string | undefined;
accepted: boolean; accepted: boolean;
pubKeyRequests: Array<any>; pubKeyRequests: Array<any>;
currentView: 'filterRequestView' | 'qrcodeView'; currentView: 'filterRequestView' | 'qrcodeView' | 'unpairDeviceView';
errors: any; errors: any;
loading: boolean; loading: boolean;
deviceAlias: string | undefined; deviceAlias: string | undefined;
@ -31,12 +32,13 @@ export class DevicePairingDialog extends React.Component<Props, State> {
this.allowDevice = this.allowDevice.bind(this); this.allowDevice = this.allowDevice.bind(this);
this.validateSecondaryDevice = this.validateSecondaryDevice.bind(this); this.validateSecondaryDevice = this.validateSecondaryDevice.bind(this);
this.handleUpdateDeviceAlias = this.handleUpdateDeviceAlias.bind(this); this.handleUpdateDeviceAlias = this.handleUpdateDeviceAlias.bind(this);
this.triggerUnpairDevice = this.triggerUnpairDevice.bind(this);
this.state = { this.state = {
currentPubKey: undefined, currentPubKey: undefined,
accepted: false, accepted: false,
pubKeyRequests: Array(), pubKeyRequests: Array(),
currentView: 'qrcodeView', currentView: props.pubKeyToUnpair ? 'unpairDeviceView' : 'qrcodeView',
loading: false, loading: false,
errors: undefined, errors: undefined,
deviceAlias: 'Unnamed Device', deviceAlias: 'Unnamed Device',
@ -44,21 +46,21 @@ export class DevicePairingDialog extends React.Component<Props, State> {
} }
public componentWillMount() { public componentWillMount() {
this.startReceivingRequests(); if (this.state.currentView === 'qrcodeView') {
this.startReceivingRequests();
}
} }
public componentWillUnmount() { public componentWillUnmount() {
this.closeDialog(); this.closeDialog();
} }
/*
dialog.on('deviceUnpairingRequested', pubKey =>
Whisper.events.trigger('deviceUnpairingRequested', pubKey)
);*/
public renderFilterRequestsView() { public renderFilterRequestsView() {
const { currentPubKey, accepted, deviceAlias } = this.state; const { currentPubKey, accepted, deviceAlias } = this.state;
const secretWords = window.mnemonic.pubkey_to_secret_words(currentPubKey); let secretWords: undefined;
if (currentPubKey) {
secretWords = window.mnemonic.pubkey_to_secret_words(currentPubKey);
}
if (accepted) { if (accepted) {
return ( return (
@ -68,7 +70,12 @@ export class DevicePairingDialog extends React.Component<Props, State> {
onClose={this.closeDialog} onClose={this.closeDialog}
> >
<div className="session-modal__centered"> <div className="session-modal__centered">
<input type="text" onChange={this.handleUpdateDeviceAlias} value={deviceAlias} id={currentPubKey}/> <input
type="text"
onChange={this.handleUpdateDeviceAlias}
value={deviceAlias}
id={currentPubKey}
/>
<div className="session-modal__button-group"> <div className="session-modal__button-group">
<SessionButton <SessionButton
text={window.i18n('ok')} text={window.i18n('ok')}
@ -154,15 +161,60 @@ export class DevicePairingDialog extends React.Component<Props, State> {
); );
} }
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 (
<SessionModal
title={window.i18n('unpairDevice')}
onOk={() => null}
onClose={this.closeDialog}
>
<div className="session-modal__centered">
<p className="session-modal__description">
{window.i18n('confirmUnpairingTitle')}
<br />
<span className="text-subtle">{description}</span>
</p>
<div className="spacer-xs" />
<div className="session-modal__button-group">
<SessionButton
text={window.i18n('cancel')}
onClick={this.closeDialog}
/>
<SessionButton
text={window.i18n('unpairDevice')}
onClick={this.triggerUnpairDevice}
buttonColor={SessionButtonColor.Danger}
/>
</div>
</div>
</SessionModal>
);
}
public render() { public render() {
const { currentView } = this.state; const { currentView } = this.state;
const renderQrCodeView = currentView === 'qrcodeView'; const renderQrCodeView = currentView === 'qrcodeView';
const renderFilterRequestView = currentView === 'filterRequestView'; const renderFilterRequestView = currentView === 'filterRequestView';
const renderUnpairDeviceView = currentView === 'unpairDeviceView';
return ( return (
<> <>
{renderQrCodeView && this.renderQrCodeView()} {renderQrCodeView && this.renderQrCodeView()}
{renderFilterRequestView && this.renderFilterRequestsView()} {renderFilterRequestView && this.renderFilterRequestsView()}
{renderUnpairDeviceView && this.renderUnpairDeviceView()}
</> </>
); );
} }
@ -232,6 +284,7 @@ export class DevicePairingDialog extends React.Component<Props, State> {
// FIXME display error somewhere // FIXME display error somewhere
// FIXME display list of linked device // FIXME display list of linked device
// FIXME do not show linked device in list of contacts // FIXME do not show linked device in list of contacts
console.log('FIXME');
return; return;
} }
@ -283,6 +336,7 @@ export class DevicePairingDialog extends React.Component<Props, State> {
this.state.currentPubKey, this.state.currentPubKey,
(errors: any) => { (errors: any) => {
this.transmissionCB(errors); this.transmissionCB(errors);
window.Whisper.events.trigger('refreshLinkedDeviceList');
return true; return true;
} }
@ -314,4 +368,15 @@ export class DevicePairingDialog extends React.Component<Props, State> {
this.setState({ deviceAlias: undefined }); this.setState({ deviceAlias: undefined });
} }
} }
private triggerUnpairDevice() {
window.Whisper.events.trigger(
'deviceUnpairingRequested',
this.props.pubKeyToUnpair
);
window.pushToast({
title: window.i18n('deviceUnpaired'),
});
this.closeDialog();
}
} }

@ -31,8 +31,6 @@ export class LeftPaneSettingSection extends React.Component<any, State> {
this.setCategory = this.setCategory.bind(this); this.setCategory = this.setCategory.bind(this);
this.renderRows = this.renderRows.bind(this); this.renderRows = this.renderRows.bind(this);
//this.updateSearchBound = this.updateSearch.bind(this);
} }
public render(): JSX.Element { public render(): JSX.Element {

@ -40,10 +40,9 @@ export class SessionSettingListItem extends React.Component<Props, State> {
public render(): JSX.Element { public render(): JSX.Element {
const { title, description, type, value, content } = this.props; const { title, description, type, value, content } = this.props;
const inline = !!type && ![ const inline =
SessionSettingType.Options, !!type &&
SessionSettingType.Slider, ![SessionSettingType.Options, SessionSettingType.Slider].includes(type);
].includes(type);
const currentSliderValue = const currentSliderValue =
type === SessionSettingType.Slider && (this.state.sliderValue || value); type === SessionSettingType.Slider && (this.state.sliderValue || value);

@ -57,19 +57,20 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
this.onPasswordUpdated = this.onPasswordUpdated.bind(this); this.onPasswordUpdated = this.onPasswordUpdated.bind(this);
this.hasPassword(); this.hasPassword();
this.refreshLinkedDevice = this.refreshLinkedDevice.bind(this);
} }
public componentWillMount() { public componentDidMount() {
const { category } = this.props; window.Whisper.events.on('refreshLinkedDeviceList', async () => {
if (category === SessionSettingCategory.Devices) { setTimeout(() => {
const ourPubKey = window.textsecure.storage.user.getNumber(); this.refreshLinkedDevice();
}, 1000);
});
this.refreshLinkedDevice();
}
window.libloki.storage.getSecondaryDevicesFor(ourPubKey).then((pubKeys: any) => { public componentWillUnmount() {
this.setState({ window.Whisper.events.off('refreshLinkedDeviceList');
linkedPubKeys: pubKeys,
});
});
}
} }
/* tslint:disable-next-line:max-func-body-length */ /* tslint:disable-next-line:max-func-body-length */
@ -93,7 +94,6 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
<> <>
{this.state.hasPassword !== null && {this.state.hasPassword !== null &&
settings.map(setting => { settings.map(setting => {
const { category } = this.props;
const content = setting.content || undefined; const content = setting.content || undefined;
const shouldRenderSettings = setting.category === category; const shouldRenderSettings = setting.category === category;
const description = setting.description || ''; const description = setting.description || '';
@ -137,7 +137,6 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
); );
} }
public render() { public render() {
const { category } = this.props; const { category } = this.props;
@ -152,7 +151,6 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
} }
public setOptionsSetting(settingID: string) { public setOptionsSetting(settingID: string) {
const selectedValue = $(`#${settingID} .session-radio input:checked`).val(); const selectedValue = $(`#${settingID} .session-radio input:checked`).val();
window.setSettingValue(settingID, selectedValue); window.setSettingValue(settingID, selectedValue);
@ -210,7 +208,7 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
} }
// tslint:disable-next-line: max-func-body-length // tslint:disable-next-line: max-func-body-length
private getLocalSettings() : Array<LocalSettingType> { private getLocalSettings(): Array<LocalSettingType> {
const { Settings } = window.Signal.Types; const { Settings } = window.Signal.Types;
return [ return [
@ -385,7 +383,7 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
]; ];
} }
private getLinkedDeviceSettings() : Array<LocalSettingType> { private getLinkedDeviceSettings(): Array<LocalSettingType> {
const { linkedPubKeys } = this.state; const { linkedPubKeys } = this.state;
if (linkedPubKeys && linkedPubKeys.length > 0) { if (linkedPubKeys && linkedPubKeys.length > 0) {
@ -393,69 +391,69 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
const { deviceAlias, secretWords } = this.getPubkeyName(pubkey); const { deviceAlias, secretWords } = this.getPubkeyName(pubkey);
const description = `${secretWords} ${window.shortenPubkey(pubkey)}`; const description = `${secretWords} ${window.shortenPubkey(pubkey)}`;
return { if (window.lokiFeatureFlags.multiDeviceUnpairing) {
id: pubkey, return {
title: deviceAlias, id: pubkey,
description: description, title: deviceAlias,
type: SessionSettingType.Button, description: description,
type: SessionSettingType.Button,
category: SessionSettingCategory.Devices,
content: {
buttonColor: SessionButtonColor.Danger,
buttonText: window.i18n('unpairDevice'),
},
comparisonValue: undefined,
setFn: () => {
window.Whisper.events.trigger('showDevicePairingDialog', {
pubKeyToUnpair: pubkey,
});
},
hidden: undefined,
onClick: undefined,
};
} else {
return {
id: pubkey,
title: deviceAlias,
description: description,
type: undefined,
category: SessionSettingCategory.Devices,
content: {},
comparisonValue: undefined,
setFn: undefined,
hidden: undefined,
onClick: undefined,
};
}
});
} else {
return [
{
id: 'no-linked-device',
title: window.i18n('noPairedDevices'),
type: undefined,
description: '',
category: SessionSettingCategory.Devices, category: SessionSettingCategory.Devices,
content: { content: {},
buttonColor: SessionButtonColor.Danger,
buttonText: window.i18n('unpairDevice'),
},
comparisonValue: undefined, comparisonValue: undefined,
onClick: undefined, onClick: undefined,
setFn: undefined, setFn: undefined,
hidden: undefined, hidden: undefined,
}; },
}); ];
} else {
return [{
id: 'no-linked-device',
title: window.i18n('noPairedDevices'),
type: undefined,
description: '',
category: SessionSettingCategory.Devices,
content: {},
comparisonValue: undefined,
setFn: undefined,
hidden: undefined,
onClick: undefined,
}];
} }
} }
}
// private refreshLinkedDevice() {
const ourPubKey = window.textsecure.storage.user.getNumber();
// /*const li = $('<li>').html(name);
// if (window.lokiFeatureFlags.multiDeviceUnpairing) { window.libloki.storage
// const link = $('<a>') .getSecondaryDevicesFor(ourPubKey)
// .text('Unpair') .then((pubKeys: any) => {
// .attr('href', '#'); this.setState({
// link.on('click', () => this.requestUnpairDevice(x)); linkedPubKeys: pubKeys,
// li.append(' - '); });
// li.append(link); });
// }*/ }
}
// if (linkedPubKeys && linkedPubKeys.length > 0) { // //this.$('#startPairing').removeAttr('disabled');
// //this.$('#startPairing').attr('disabled', true);
// const items = linkedPubKeys.map((pubkey: any) => {
// const { deviceAlias, secretWords } = this.getPubkeyName(pubkey);
// const description = `${secretWords} ${window.shortenPubkey(pubkey)}`;
// return (
// <SessionLinkedDeviceListItem onClick={() => {}} title={deviceAlias} key={pubkey} description={description} />
// );
// });
// return (
// <div>
// {items}
// </div>);
// } else {
// //this.$('#startPairing').removeAttr('disabled');
// //this.$('#pairedPubKeys').append('<li>No paired devices</li>');
// return (<li>No paired devices</li>);
// }

1
ts/global.d.ts vendored

@ -41,6 +41,7 @@ interface Window {
getSettingValue: any; getSettingValue: any;
setSettingValue: any; setSettingValue: any;
lokiFeatureFlags: any;
} }
interface Promise<T> { interface Promise<T> {

Loading…
Cancel
Save