Преглед на файлове

feat(conference): Enable forced reload of client on bridge failure.

* feat(conference): Enable forced reload of client on bridge failure.
Force the client to reload when the bridge that is handling the media goes down.
This mitigates issues seen on the bridge because of a client re-joining the call with the same endpointId, BWE issues, etc.
This behavior is configurable through 'enableForcedReload' setting in config.js.
The client skips the pre-join page when the page reloads.

* squash: refactor the restart logic.

* squash: fix description

* squash: dispatch conferenceWillLeave action before reload.
master
Jaya Allamsetty преди 4 години
родител
ревизия
0138f23755
No account linked to committer's email address

+ 6
- 0
config.js Целия файл

@@ -325,6 +325,11 @@ var config = {
325 325
     // TCC sequence numbers starting from 0.
326 326
     // enableIceRestart: false,
327 327
 
328
+    // Enables forced reload of the client when the call is migrated as a result of
329
+    // the bridge going down. Currently enabled by default as call migration through
330
+    // session-terminate is causing siganling issues when Octo is enabled.
331
+    // enableForcedReload: true,
332
+
328 333
     // Use TURN/UDP servers for the jitsi-videobridge connection (by default
329 334
     // we filter out TURN/UDP because it is usually not needed since the
330 335
     // bridge itself is reachable via UDP)
@@ -732,6 +737,7 @@ var config = {
732 737
     //     'dialog.reservationError',
733 738
     //     'dialog.serviceUnavailable', // shown when server is not reachable
734 739
     //     'dialog.sessTerminated', // shown when there is a failed conference session
740
+    //     'dialog.sessionRestarted', // show when a client reload is initiated because of bridge migration
735 741
     //     'dialog.tokenAuthFailed', // show when an invalid jwt is used
736 742
     //     'dialog.transcribing', // transcribing notifications (pending, off)
737 743
     //     'dialOut.statusMessage', // shown when dial out status is updated.

+ 1
- 0
lang/main.json Целия файл

@@ -280,6 +280,7 @@
280 280
         "sendPrivateMessageTitle": "Send privately?",
281 281
         "serviceUnavailable": "Service unavailable",
282 282
         "sessTerminated": "Call terminated",
283
+        "sessionRestarted": "Call restarted by the bridge",
283 284
         "Share": "Share",
284 285
         "shareVideoLinkError": "Please provide a correct youtube link.",
285 286
         "shareVideoTitle": "Share a video",

+ 10
- 10
react/features/base/conference/actionTypes.js Целия файл

@@ -42,16 +42,6 @@ export const CONFERENCE_JOINED = 'CONFERENCE_JOINED';
42 42
  */
43 43
 export const CONFERENCE_LEFT = 'CONFERENCE_LEFT';
44 44
 
45
-/**
46
- * The type of (redux) action which signals that an uuid for a conference has been set.
47
- *
48
- * {
49
- *     type: CONFERENCE_UNIQUE_ID_SET,
50
- *     conference: JitsiConference
51
- * }
52
- */
53
-export const CONFERENCE_UNIQUE_ID_SET = 'CONFERENCE_UNIQUE_ID_SET';
54
-
55 45
 /**
56 46
  * The type of (redux) action, which indicates conference subject changes.
57 47
  *
@@ -72,6 +62,16 @@ export const CONFERENCE_SUBJECT_CHANGED = 'CONFERENCE_SUBJECT_CHANGED';
72 62
 */
73 63
 export const CONFERENCE_TIMESTAMP_CHANGED = 'CONFERENCE_TIMESTAMP_CHANGED';
74 64
 
65
+/**
66
+ * The type of (redux) action which signals that an uuid for a conference has been set.
67
+ *
68
+ * {
69
+ *     type: CONFERENCE_UNIQUE_ID_SET,
70
+ *     conference: JitsiConference
71
+ * }
72
+ */
73
+export const CONFERENCE_UNIQUE_ID_SET = 'CONFERENCE_UNIQUE_ID_SET';
74
+
75 75
 /**
76 76
  * The type of (redux) action which signals that a specific conference will be
77 77
  * joined.

+ 16
- 16
react/features/base/conference/actions.js Целия файл

@@ -296,22 +296,6 @@ export function conferenceLeft(conference: Object) {
296 296
     };
297 297
 }
298 298
 
299
-/**
300
- * Signals that the unique identifier for conference has been set.
301
- *
302
- * @param {JitsiConference} conference - The JitsiConference instance, where the uuid has been set.
303
- * @returns {{
304
-    *   type: CONFERENCE_UNIQUE_ID_SET,
305
-    *   conference: JitsiConference,
306
-    * }}
307
-    */
308
-export function conferenceUniqueIdSet(conference: Object) {
309
-    return {
310
-        type: CONFERENCE_UNIQUE_ID_SET,
311
-        conference
312
-    };
313
-}
314
-
315 299
 /**
316 300
  * Signals that the conference subject has been changed.
317 301
  *
@@ -344,6 +328,22 @@ export function conferenceTimestampChanged(conferenceTimestamp: number) {
344 328
     };
345 329
 }
346 330
 
331
+/**
332
+* Signals that the unique identifier for conference has been set.
333
+*
334
+* @param {JitsiConference} conference - The JitsiConference instance, where the uuid has been set.
335
+* @returns {{
336
+*   type: CONFERENCE_UNIQUE_ID_SET,
337
+*   conference: JitsiConference,
338
+* }}
339
+*/
340
+export function conferenceUniqueIdSet(conference: Object) {
341
+    return {
342
+        type: CONFERENCE_UNIQUE_ID_SET,
343
+        conference
344
+    };
345
+}
346
+
347 347
 /**
348 348
  * Adds any existing local tracks to a specific conference before the conference
349 349
  * is joined. Then signals the intention of the application to have the local

+ 26
- 14
react/features/base/conference/middleware.any.js Целия файл

@@ -7,6 +7,7 @@ import {
7 7
     createPinnedEvent,
8 8
     sendAnalytics
9 9
 } from '../../analytics';
10
+import { reloadNow } from '../../app/actions';
10 11
 import { openDisplayNamePrompt } from '../../display-name';
11 12
 import { showErrorNotification } from '../../notifications';
12 13
 import { CONNECTION_ESTABLISHED, CONNECTION_FAILED, connectionDisconnected } from '../connection';
@@ -117,6 +118,7 @@ MiddlewareRegistry.register(store => next => action => {
117 118
 function _conferenceFailed({ dispatch, getState }, next, action) {
118 119
     const result = next(action);
119 120
     const { conference, error } = action;
121
+    const { enableForcedReload } = getState()['features/base/config'];
120 122
 
121 123
     // Handle specific failure reasons.
122 124
     switch (error.name) {
@@ -130,6 +132,16 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
130 132
 
131 133
         break;
132 134
     }
135
+    case JitsiConferenceErrors.CONFERENCE_RESTARTED: {
136
+        if (enableForcedReload) {
137
+            dispatch(showErrorNotification({
138
+                description: 'Restart initiated because of a bridge failure',
139
+                titleKey: 'dialog.sessionRestarted'
140
+            }));
141
+        }
142
+
143
+        break;
144
+    }
133 145
     case JitsiConferenceErrors.CONNECTION_ERROR: {
134 146
         const [ msg ] = error.params;
135 147
 
@@ -147,26 +159,26 @@ function _conferenceFailed({ dispatch, getState }, next, action) {
147 159
         break;
148 160
     }
149 161
 
150
-    // FIXME: Workaround for the web version. Currently, the creation of the
151
-    // conference is handled by /conference.js and appropriate failure handlers
152
-    // are set there.
153
-    if (typeof APP !== 'undefined') {
154
-        if (typeof beforeUnloadHandler !== 'undefined') {
155
-            window.removeEventListener('beforeunload', beforeUnloadHandler);
156
-            beforeUnloadHandler = undefined;
157
-        }
158
-
159
-        return result;
160
-    }
161
-
162
-    // XXX After next(action), it is clear whether the error is recoverable.
163
-    !error.recoverable
162
+    if (typeof APP === 'undefined') {
163
+        !error.recoverable
164 164
         && conference
165 165
         && conference.leave().catch(reason => {
166 166
             // Even though we don't care too much about the failure, it may be
167 167
             // good to know that it happen, so log it (on the info level).
168 168
             logger.info('JitsiConference.leave() rejected with:', reason);
169 169
         });
170
+    } else if (typeof beforeUnloadHandler !== 'undefined') {
171
+        // FIXME: Workaround for the web version. Currently, the creation of the
172
+        // conference is handled by /conference.js and appropriate failure handlers
173
+        // are set there.
174
+        window.removeEventListener('beforeunload', beforeUnloadHandler);
175
+        beforeUnloadHandler = undefined;
176
+    }
177
+
178
+    if (enableForcedReload && error?.name === JitsiConferenceErrors.CONFERENCE_RESTARTED) {
179
+        dispatch(conferenceWillLeave(conference));
180
+        dispatch(reloadNow());
181
+    }
170 182
 
171 183
     return result;
172 184
 }

+ 21
- 1
react/features/base/conference/middleware.web.js Целия файл

@@ -1,15 +1,35 @@
1 1
 // @flow
2 2
 
3 3
 import UIEvents from '../../../../service/UI/UIEvents';
4
+import { setPrejoinPageVisibility, setSkipPrejoinOnReload } from '../../prejoin';
5
+import { JitsiConferenceErrors } from '../lib-jitsi-meet';
4 6
 import { MiddlewareRegistry } from '../redux';
5 7
 import { TOGGLE_SCREENSHARING } from '../tracks/actionTypes';
6 8
 
9
+import { CONFERENCE_FAILED, CONFERENCE_JOINED } from './actionTypes';
7 10
 import './middleware.any';
8 11
 
9 12
 declare var APP: Object;
10 13
 
11
-MiddlewareRegistry.register((/* store */) => next => action => {
14
+MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
15
+    const { enableForcedReload } = getState()['features/base/config'];
16
+
12 17
     switch (action.type) {
18
+    case CONFERENCE_JOINED: {
19
+        if (enableForcedReload) {
20
+            dispatch(setPrejoinPageVisibility(false));
21
+            dispatch(setSkipPrejoinOnReload(false));
22
+        }
23
+
24
+        break;
25
+    }
26
+    case CONFERENCE_FAILED: {
27
+        enableForcedReload
28
+            && action.error?.name === JitsiConferenceErrors.CONFERENCE_RESTARTED
29
+            && dispatch(setSkipPrejoinOnReload(true));
30
+
31
+        break;
32
+    }
13 33
     case TOGGLE_SCREENSHARING: {
14 34
         if (typeof APP === 'object') {
15 35
             APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);

+ 5
- 0
react/features/prejoin/actionTypes.js Целия файл

@@ -19,6 +19,11 @@ export const SET_DEVICE_STATUS = 'SET_DEVICE_STATUS';
19 19
  */
20 20
 export const SET_SKIP_PREJOIN = 'SET_SKIP_PREJOIN';
21 21
 
22
+/**
23
+ * Action type to set the visiblity of the prejoin page when client is forcefully reloaded.
24
+ */
25
+export const SET_SKIP_PREJOIN_RELOAD = 'SET_SKIP_PREJOIN_RELOAD';
26
+
22 27
 /**
23 28
  * Action type used to set the mandatory stance of the prejoin display name.
24 29
  */

+ 15
- 0
react/features/prejoin/actions.js Целия файл

@@ -26,6 +26,7 @@ import {
26 26
     SET_DIALOUT_STATUS,
27 27
     SET_PREJOIN_DISPLAY_NAME_REQUIRED,
28 28
     SET_SKIP_PREJOIN,
29
+    SET_SKIP_PREJOIN_RELOAD,
29 30
     SET_JOIN_BY_PHONE_DIALOG_VISIBLITY,
30 31
     SET_PRECALL_TEST_RESULTS,
31 32
     SET_PREJOIN_DEVICE_ERRORS,
@@ -418,6 +419,20 @@ export function setSkipPrejoin(value: boolean) {
418 419
     };
419 420
 }
420 421
 
422
+/**
423
+ * Sets the visibility of the prejoin page when a client reload
424
+ * is triggered as a result of call migration initiated by Jicofo.
425
+ *
426
+ * @param {boolean} value - The visibility value.
427
+ * @returns {Object}
428
+ */
429
+export function setSkipPrejoinOnReload(value: boolean) {
430
+    return {
431
+        type: SET_SKIP_PREJOIN_RELOAD,
432
+        value
433
+    };
434
+}
435
+
421 436
 /**
422 437
  * Action used to set the visiblitiy of the 'JoinByPhoneDialog'.
423 438
  *

+ 2
- 1
react/features/prejoin/functions.js Целия файл

@@ -149,7 +149,8 @@ export function isJoinByPhoneDialogVisible(state: Object): boolean {
149 149
 export function isPrejoinPageEnabled(state: Object): boolean {
150 150
     return navigator.product !== 'ReactNative'
151 151
         && state['features/base/config'].prejoinPageEnabled
152
-        && !state['features/base/settings'].userSelectedSkipPrejoin;
152
+        && !state['features/base/settings'].userSelectedSkipPrejoin
153
+        && !(state['features/base/config'].enableForcedReload && state['features/prejoin'].skipPrejoinOnReload);
153 154
 }
154 155
 
155 156
 /**

+ 24
- 2
react/features/prejoin/reducer.js Целия файл

@@ -1,4 +1,4 @@
1
-import { ReducerRegistry } from '../base/redux';
1
+import { PersistenceRegistry, ReducerRegistry } from '../base/redux';
2 2
 
3 3
 import {
4 4
     SET_DEVICE_STATUS,
@@ -10,7 +10,8 @@ import {
10 10
     SET_PREJOIN_DEVICE_ERRORS,
11 11
     SET_PREJOIN_DISPLAY_NAME_REQUIRED,
12 12
     SET_PREJOIN_PAGE_VISIBILITY,
13
-    SET_SKIP_PREJOIN
13
+    SET_SKIP_PREJOIN,
14
+    SET_SKIP_PREJOIN_RELOAD
14 15
 } from './actionTypes';
15 16
 
16 17
 const DEFAULT_STATE = {
@@ -28,10 +29,24 @@ const DEFAULT_STATE = {
28 29
     name: '',
29 30
     rawError: '',
30 31
     showPrejoin: true,
32
+    skipPrejoinOnReload: false,
31 33
     showJoinByPhoneDialog: false,
32 34
     userSelectedSkipPrejoin: false
33 35
 };
34 36
 
37
+/**
38
+ * The name of the redux store/state property which is the root of the redux
39
+ * state of the feature {@code prejoin}.
40
+ */
41
+const STORE_NAME = 'features/prejoin';
42
+
43
+/**
44
+ * Sets up the persistence of the feature {@code prejoin}.
45
+ */
46
+PersistenceRegistry.register(STORE_NAME, {
47
+    skipPrejoinOnReload: true
48
+}, DEFAULT_STATE);
49
+
35 50
 /**
36 51
  * Listen for actions that mutate the prejoin state
37 52
  */
@@ -46,6 +61,13 @@ ReducerRegistry.register(
46 61
             };
47 62
         }
48 63
 
64
+        case SET_SKIP_PREJOIN_RELOAD: {
65
+            return {
66
+                ...state,
67
+                skipPrejoinOnReload: action.value
68
+            };
69
+        }
70
+
49 71
         case SET_PRECALL_TEST_RESULTS:
50 72
             return {
51 73
                 ...state,

Loading…
Отказ
Запис