signal-desktop/js/views/install_view.js

209 lines
6 KiB
JavaScript
Raw Normal View History

/* global Whisper, i18n, getAccountManager, $, textsecure, QRCode */
/* eslint-disable more/no-then */
// eslint-disable-next-line func-names
2018-04-27 21:25:04 +00:00
(function() {
'use strict';
2018-04-27 21:25:04 +00:00
window.Whisper = window.Whisper || {};
const Steps = {
2018-04-27 21:25:04 +00:00
INSTALL_SIGNAL: 2,
SCAN_QR_CODE: 3,
ENTER_NAME: 4,
PROGRESS_BAR: 5,
TOO_MANY_DEVICES: 'TooManyDevices',
NETWORK_ERROR: 'NetworkError',
};
const DEVICE_NAME_SELECTOR = 'input.device-name';
const CONNECTION_ERROR = -1;
const TOO_MANY_DEVICES = 411;
2018-04-27 21:25:04 +00:00
Whisper.InstallView = Whisper.View.extend({
templateName: 'link-flow-template',
className: 'main full-screen-flow',
events: {
'click .try-again': 'connect',
'click .finish': 'finishLinking',
// the actual next step happens in confirmNumber() on submit form #link-phone
},
initialize(options = {}) {
window.readyForUpdates();
this.didLink = false;
2018-04-27 21:25:04 +00:00
this.selectStep(Steps.SCAN_QR_CODE);
this.connect();
this.on('disconnected', this.reconnect);
// Keep data around if it's a re-link, or the middle of a light import
this.shouldRetainData =
window.Signal.Util.Registration.everDone() || options.hasExistingData;
2018-04-27 21:25:04 +00:00
},
render_attributes() {
let errorMessage;
2018-04-27 21:25:04 +00:00
if (this.error) {
if (
this.error.name === 'HTTPError' &&
this.error.code === TOO_MANY_DEVICES
2018-04-27 21:25:04 +00:00
) {
errorMessage = i18n('installTooManyDevices');
} else if (
this.error.name === 'HTTPError' &&
this.error.code === CONNECTION_ERROR
2018-04-27 21:25:04 +00:00
) {
errorMessage = i18n('installConnectionFailed');
} else if (this.error.message === 'websocket closed') {
// AccountManager.registerSecondDevice uses this specific
// 'websocket closed' error message
errorMessage = i18n('installConnectionFailed');
}
return {
isError: true,
errorHeader: 'Something went wrong!',
errorMessage,
errorButton: 'Try again',
};
}
return {
isStep3: this.step === Steps.SCAN_QR_CODE,
linkYourPhone: i18n('linkYourPhone'),
signalSettings: i18n('signalSettings'),
linkedDevices: i18n('linkedDevices'),
androidFinalStep: i18n('plusButton'),
appleFinalStep: i18n('linkNewDevice'),
isStep4: this.step === Steps.ENTER_NAME,
chooseName: i18n('chooseDeviceName'),
finishLinkingPhoneButton: i18n('finishLinkingPhone'),
isStep5: this.step === Steps.PROGRESS_BAR,
syncing: i18n('initialSync'),
};
},
selectStep(step) {
2018-04-27 21:25:04 +00:00
this.step = step;
this.render();
},
connect() {
2018-04-27 21:25:04 +00:00
this.error = null;
this.selectStep(Steps.SCAN_QR_CODE);
this.clearQR();
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
const accountManager = getAccountManager();
2018-04-27 21:25:04 +00:00
accountManager
.registerSecondDevice(
this.setProvisioningUrl.bind(this),
this.confirmNumber.bind(this)
)
.catch(this.handleDisconnect.bind(this));
},
handleDisconnect(error) {
window.log.error(
'provisioning failed',
error && error.stack ? error.stack : error
);
2018-04-27 21:25:04 +00:00
this.error = error;
2018-04-27 21:25:04 +00:00
this.render();
if (error.message === 'websocket closed') {
2018-04-27 21:25:04 +00:00
this.trigger('disconnected');
} else if (
error.name !== 'HTTPError' ||
(error.code !== CONNECTION_ERROR && error.code !== TOO_MANY_DEVICES)
2018-04-27 21:25:04 +00:00
) {
throw error;
2018-04-27 21:25:04 +00:00
}
},
reconnect() {
2018-04-27 21:25:04 +00:00
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
this.timeout = setTimeout(this.connect.bind(this), 10000);
},
clearQR() {
2018-04-27 21:25:04 +00:00
this.$('#qr img').remove();
this.$('#qr canvas').remove();
this.$('#qr .container').show();
this.$('#qr').removeClass('ready');
},
setProvisioningUrl(url) {
2018-04-27 21:25:04 +00:00
if ($('#qr').length === 0) {
window.log.error('Did not find #qr element in the DOM!');
2018-04-27 21:25:04 +00:00
return;
}
this.$('#qr .container').hide();
this.qr = new QRCode(this.$('#qr')[0]).makeCode(url);
this.$('#qr').removeAttr('title');
this.$('#qr').addClass('ready');
},
setDeviceNameDefault() {
const deviceName = textsecure.storage.user.getDeviceName();
2018-04-27 21:25:04 +00:00
this.$(DEVICE_NAME_SELECTOR).val(deviceName || window.getHostName());
2018-04-27 21:25:04 +00:00
this.$(DEVICE_NAME_SELECTOR).focus();
},
finishLinking() {
2018-04-27 21:25:04 +00:00
// We use a form so we get submit-on-enter behavior
this.$('#link-phone').submit();
},
confirmNumber() {
const tsp = textsecure.storage.protocol;
2018-04-27 21:25:04 +00:00
window.removeSetupMenuItems();
this.selectStep(Steps.ENTER_NAME);
this.setDeviceNameDefault();
return new Promise(resolve => {
this.$('#link-phone').submit(e => {
e.stopPropagation();
e.preventDefault();
let name = this.$(DEVICE_NAME_SELECTOR).val();
name = name.replace(/\0/g, ''); // strip unicode null
if (name.trim().length === 0) {
this.$(DEVICE_NAME_SELECTOR).focus();
return null;
}
this.selectStep(Steps.PROGRESS_BAR);
const finish = () => {
this.didLink = true;
return resolve(name);
};
// Delete all data from database unless we're in the middle
// of a re-link, or we are finishing a light import. Without this,
// app restarts at certain times can cause weird things to happen,
// like data from a previous incomplete light import showing up
// after a new install.
if (this.shouldRetainData) {
return finish();
}
return tsp.removeAllData().then(finish, error => {
window.log.error(
'confirmNumber: error clearing database',
error && error.stack ? error.stack : error
);
return finish();
});
});
});
2018-04-27 21:25:04 +00:00
},
});
})();