diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 58cc6bb23..89091c807 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -961,6 +961,12 @@
"showPairingWordsTitle": {
"message": "Pairing Secret Words"
},
+ "confirmUnpairingTitle": {
+ "message": "Please confirm you want to unpair the following device:"
+ },
+ "unpairDevice": {
+ "message": "Unpair Device"
+ },
"clear": {
"message": "Clear"
},
diff --git a/background.html b/background.html
index ecfe74afa..19d7e6b65 100644
--- a/background.html
+++ b/background.html
@@ -253,7 +253,6 @@
{{ defaultTitle }}
@@ -292,6 +291,16 @@
+
+
+
+
{{ confirmUnpairViewTitle }}
+
+
+
+
+
+
diff --git a/js/views/device_pairing_dialog_view.js b/js/views/device_pairing_dialog_view.js
index b8fb7fba9..7dd49cbc1 100644
--- a/js/views/device_pairing_dialog_view.js
+++ b/js/views/device_pairing_dialog_view.js
@@ -1,4 +1,12 @@
-/* global Whisper, i18n, libloki, textsecure, ConversationController */
+/* global
+ Whisper,
+ i18n,
+ libloki,
+ textsecure,
+ ConversationController,
+ $,
+ lokiFileServerAPI
+*/
// eslint-disable-next-line func-names
(function() {
@@ -19,6 +27,7 @@
this.pubKey = null;
this.accepted = false;
this.isListening = false;
+ this.pubKeyToUnpair = null;
this.success = false;
},
events: {
@@ -28,6 +37,8 @@
'click .requestReceivedView .skip': 'skipDevice',
'click #allowPairing': 'allowDevice',
'click .requestAcceptedView .ok': 'stopReceivingRequests',
+ 'click .confirmUnpairView .cancel': 'stopReceivingRequests',
+ 'click .confirmUnpairView .unpairDevice': 'confirmUnpairDevice',
},
render_attributes() {
return {
@@ -37,10 +48,12 @@
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() {
@@ -103,31 +116,64 @@
// FIFO: pop at the back of the array using pop()
this.pubKey = this.pubKeyRequests.pop();
},
+ async confirmUnpairDevice() {
+ await libloki.storage.removePairingAuthorisationForSecondaryPubKey(
+ this.pubKeyToUnpair
+ );
+ await lokiFileServerAPI.updateOurDeviceMapping();
+ 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: ${secretWords})`;
+ },
async showView() {
const defaultView = this.$('.defaultView');
const waitingForRequestView = this.$('.waitingForRequestView');
const requestReceivedView = this.$('.requestReceivedView');
const requestAcceptedView = this.$('.requestAcceptedView');
- if (!this.isListening) {
- const ourPubKey = textsecure.storage.user.getNumber();
- defaultView.show();
+ 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.$('#pairedPubKeys').empty();
pubKeys.forEach(x => {
- let deviceAlias = 'Paired Device';
- const secretWords = window.mnemonic.pubkey_to_secret_words(x);
- const conv = ConversationController.get(x);
- if (conv) {
- deviceAlias = conv.getNickname();
+ const name = this.getPubkeyName(x);
+ const li = $('').html(name);
+ if (window.lokiFeatureFlags.multiDeviceUnpairing) {
+ const link = $('')
+ .text('Unpair')
+ .attr('href', '#');
+ link.on('click', () => this.requestUnpairDevice(x));
+ li.append(' - ');
+ li.append(link);
}
- this.$('#pairedPubKeys').append(
- `${deviceAlias} (${secretWords})`
- );
+ this.$('#pairedPubKeys').append(li);
});
+ } else {
+ this.$('#pairedPubKeys').append('No paired devices');
}
} else if (this.accepted) {
defaultView.hide();
diff --git a/preload.js b/preload.js
index c414f02c1..3c586fdda 100644
--- a/preload.js
+++ b/preload.js
@@ -457,3 +457,7 @@ if (config.environment === 'test') {
window.shortenPubkey = pubkey => `(...${pubkey.substring(pubkey.length - 6)})`;
window.pubkeyPattern = /@[a-fA-F0-9]{64,66}\b/g;
+
+window.lokiFeatureFlags = {
+ multiDeviceUnpairing: false,
+};