You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			203 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			JavaScript
		
	
			
		
		
	
	
			203 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			JavaScript
		
	
/* global Whisper, i18n, getAccountManager, $, textsecure, QRCode */
 | 
						|
 | 
						|
/* eslint-disable more/no-then */
 | 
						|
 | 
						|
// eslint-disable-next-line func-names
 | 
						|
(function() {
 | 
						|
  'use strict';
 | 
						|
 | 
						|
  window.Whisper = window.Whisper || {};
 | 
						|
 | 
						|
  const Steps = {
 | 
						|
    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;
 | 
						|
 | 
						|
  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 = {}) {
 | 
						|
      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 =
 | 
						|
        Whisper.Registration.everDone() || options.hasExistingData;
 | 
						|
    },
 | 
						|
    render_attributes() {
 | 
						|
      let errorMessage;
 | 
						|
 | 
						|
      if (this.error) {
 | 
						|
        if (
 | 
						|
          this.error.name === 'HTTPError' &&
 | 
						|
          this.error.code === TOO_MANY_DEVICES
 | 
						|
        ) {
 | 
						|
          errorMessage = i18n('installTooManyDevices');
 | 
						|
        } else if (
 | 
						|
          this.error.name === 'HTTPError' &&
 | 
						|
          this.error.code === CONNECTION_ERROR
 | 
						|
        ) {
 | 
						|
          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) {
 | 
						|
      this.step = step;
 | 
						|
      this.render();
 | 
						|
    },
 | 
						|
    connect() {
 | 
						|
      this.error = null;
 | 
						|
      this.selectStep(Steps.SCAN_QR_CODE);
 | 
						|
      this.clearQR();
 | 
						|
      if (this.timeout) {
 | 
						|
        clearTimeout(this.timeout);
 | 
						|
        this.timeout = null;
 | 
						|
      }
 | 
						|
 | 
						|
      const accountManager = getAccountManager();
 | 
						|
 | 
						|
      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
 | 
						|
      );
 | 
						|
 | 
						|
      this.error = error;
 | 
						|
      this.render();
 | 
						|
 | 
						|
      if (error.message === 'websocket closed') {
 | 
						|
        this.trigger('disconnected');
 | 
						|
      } else if (
 | 
						|
        error.name !== 'HTTPError' ||
 | 
						|
        (error.code !== CONNECTION_ERROR && error.code !== TOO_MANY_DEVICES)
 | 
						|
      ) {
 | 
						|
        throw error;
 | 
						|
      }
 | 
						|
    },
 | 
						|
    reconnect() {
 | 
						|
      if (this.timeout) {
 | 
						|
        clearTimeout(this.timeout);
 | 
						|
        this.timeout = null;
 | 
						|
      }
 | 
						|
      this.timeout = setTimeout(this.connect.bind(this), 10000);
 | 
						|
    },
 | 
						|
    clearQR() {
 | 
						|
      this.$('#qr img').remove();
 | 
						|
      this.$('#qr canvas').remove();
 | 
						|
      this.$('#qr .container').show();
 | 
						|
      this.$('#qr').removeClass('ready');
 | 
						|
    },
 | 
						|
    setProvisioningUrl(url) {
 | 
						|
      if ($('#qr').length === 0) {
 | 
						|
        window.log.error('Did not find #qr element in the DOM!');
 | 
						|
        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();
 | 
						|
 | 
						|
      this.$(DEVICE_NAME_SELECTOR).val(deviceName || window.getHostName());
 | 
						|
      this.$(DEVICE_NAME_SELECTOR).focus();
 | 
						|
    },
 | 
						|
    finishLinking() {
 | 
						|
      // We use a form so we get submit-on-enter behavior
 | 
						|
      this.$('#link-phone').submit();
 | 
						|
    },
 | 
						|
    confirmNumber() {
 | 
						|
      const tsp = textsecure.storage.protocol;
 | 
						|
 | 
						|
      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 = () => 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
 | 
						|
            );
 | 
						|
            finish();
 | 
						|
          });
 | 
						|
        });
 | 
						|
      });
 | 
						|
    },
 | 
						|
  });
 | 
						|
})();
 |