Explorar el Código

Wait briefly for a broadcast message before attempting to connect (#943)

Lennart Grahl hace 5 años
padre
commit
dfdf4a6551
Se han modificado 2 ficheros con 58 adiciones y 18 borrados
  1. 54 15
      src/partials/welcome.ts
  2. 4 3
      tsconfig.json

+ 54 - 15
src/partials/welcome.ts

@@ -44,6 +44,7 @@ import GlobalConnectionState = threema.GlobalConnectionState;
 import DisconnectReason = threema.DisconnectReason;
 
 class WelcomeController {
+    private static BROADCAST_DELAY = 100;
     private static REDIRECT_DELAY = 500;
 
     // Angular services
@@ -262,9 +263,6 @@ class WelcomeController {
             resume: false,
         });
 
-        // Set up the broadcast channel that checks whether we're already connected in another tab
-        this.setupBroadcastChannel(this.webClientService.salty.keyStore.publicKeyHex);
-
         // Initialize QR code params
         this.$scope.$watch(() => this.password, () => {
             const payload = this.webClientService.buildQrCodePayload(this.password.length > 0);
@@ -272,8 +270,26 @@ class WelcomeController {
             this.passwordStrength = scorePassword(this.password);
         });
 
-        // Start webclient
-        this.start();
+        // Set up the broadcast channel that checks whether we're already connected in another tab
+        this.setupBroadcastChannel(this.webClientService.salty.keyStore.publicKeyHex, 0)
+            .then((result) => {
+                this.$scope.$apply(() => {
+                    switch (result) {
+                        case 'already_open':
+                            this.log.warn('Session already connected in another tab or window');
+                            break;
+                        case 'no_answer':
+                            this.start();
+                            break;
+                    }
+                });
+            })
+            .catch((error) => {
+                this.$scope.$apply(() => {
+                    this.log.warn('Unable to set up broadcast channel:', error);
+                    this.start();
+                });
+            });
     }
 
     /**
@@ -301,24 +317,43 @@ class WelcomeController {
         const keyStore = new saltyrtcClient.KeyStore(decrypted.ownSecretKey);
 
         // Set up the broadcast channel that checks whether we're already connected in another tab
-        this.setupBroadcastChannel(keyStore.publicKeyHex);
-
-        // Reconnect
-        this.reconnect(keyStore, decrypted);
+        this.setupBroadcastChannel(keyStore.publicKeyHex, WelcomeController.BROADCAST_DELAY)
+            .then((result) => {
+                this.$scope.$apply(() => {
+                    switch (result) {
+                        case 'already_open':
+                            this.log.warn('Session already connected in another tab or window');
+                            break;
+                        case 'no_answer':
+                            this.log.debug('No broadcast received indicating that a session is already open');
+                            this.reconnect(keyStore, decrypted);
+                            break;
+                    }
+                });
+            })
+            .catch((error) => {
+                this.$scope.$apply(() => {
+                    this.log.warn('Unable to set up broadcast channel:', error);
+                    this.reconnect(keyStore, decrypted);
+                });
+            });
     }
 
     /**
      * Set up a `BroadcastChannel` to check if there are other tabs running on
-     * the same session.
+     * the same session. Resolves when either an `already_connected` message has
+     * been received or a timeout of `delayMs` has been elapsed.
      *
      * The `publicKeyHex` parameter is the hex-encoded public key of the keystore
      * used to establish the SaltyRTC connection.
      */
-    private setupBroadcastChannel(publicKeyHex: string) {
+    private setupBroadcastChannel(publicKeyHex: string, delayMs: number): Future<'already_open' | 'no_answer'> {
+        const future: Future<'already_open' | 'no_answer'> = new Future();
+
+        // Check for broadcast support in the browser
         if (!('BroadcastChannel' in this.$window)) {
-            // No BroadcastChannel support in this browser
-            this.log.warn('BroadcastChannel not supported in this browser');
-            return;
+            future.reject('BroadcastChannel not supported in this browser');
+            return future;
         }
 
         // Config constants
@@ -351,7 +386,7 @@ class WelcomeController {
                     // Another tab notified us that the session we're trying to connect to
                     // is already active.
                     if (message.key === publicKeyHex && this.stateService.connectionBuildupState !== 'done') {
-                        this.log.error('Session already connected in another tab or window');
+                        future.resolve('already_open');
                         this.timeoutService.register(() => {
                             this.stateService.updateConnectionBuildupState('already_connected');
                             this.stateService.state = GlobalConnectionState.Error;
@@ -370,6 +405,10 @@ class WelcomeController {
             type: TYPE_PUBLIC_KEY,
             key: publicKeyHex,
         }));
+
+        // Resolve after the specified delay without an `already_connected` response
+        setTimeout(() => future.resolve('no_answer'), delayMs);
+        return future;
     }
 
     /**

+ 4 - 3
tsconfig.json

@@ -1,8 +1,9 @@
 {
     "compilerOptions": {
-        "target": "ES2017",
-        "module": "esNext",
-        "moduleResolution": "node",
+        "lib": ["DOM", "ESNext"],
+        "target": "ESNext",
+        "module": "ESNext",
+        "moduleResolution": "Node",
         "removeComments": true
     },
     "exclude": [