瀏覽代碼

fix(prejoin): Prevent double joining conference.

master
Hristo Terezov 3 年之前
父節點
當前提交
2a725d2165

+ 10
- 3
conference.js 查看文件

129
     initPrejoin,
129
     initPrejoin,
130
     isPrejoinPageEnabled,
130
     isPrejoinPageEnabled,
131
     isPrejoinPageVisible,
131
     isPrejoinPageVisible,
132
-    makePrecallTest
132
+    makePrecallTest,
133
+    setJoiningInProgress
133
 } from './react/features/prejoin';
134
 } from './react/features/prejoin';
134
 import { disableReceiver, stopReceiver } from './react/features/remote-control';
135
 import { disableReceiver, stopReceiver } from './react/features/remote-control';
135
 import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
136
 import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
861
             _onConnectionPromiseCreated = undefined;
862
             _onConnectionPromiseCreated = undefined;
862
         }
863
         }
863
 
864
 
864
-        const con = await _connectionPromise;
865
+        let con;
865
 
866
 
866
-        this.startConference(con, tracks);
867
+        try {
868
+            con = await _connectionPromise;
869
+            this.startConference(con, tracks);
870
+        } catch (error) {
871
+            logger.error(`An error occurred while trying to join a meeting from the prejoin screen: ${error}`);
872
+            APP.store.dispatch(setJoiningInProgress(false));
873
+        }
867
     },
874
     },
868
 
875
 
869
     /**
876
     /**

+ 2
- 2
react/features/prejoin/actionTypes.js 查看文件

1
 
1
 
2
 /**
2
 /**
3
- * Action type to signal the start of the conference.
3
+ * Action type to signal that joining is in progress.
4
  */
4
  */
5
-export const PREJOIN_START_CONFERENCE = 'PREJOIN_START_CONFERENCE';
5
+export const PREJOIN_JOINING_IN_PROGRESS = 'PREJOIN_JOINING_IN_PROGRESS';
6
 
6
 
7
 /**
7
 /**
8
  * Action type to signal that prejoin page was initialized.
8
  * Action type to signal that prejoin page was initialized.

+ 88
- 13
react/features/prejoin/actions.js 查看文件

1
 // @flow
1
 // @flow
2
 
2
 
3
 declare var JitsiMeetJS: Object;
3
 declare var JitsiMeetJS: Object;
4
+declare var APP: Object;
4
 
5
 
5
 import uuid from 'uuid';
6
 import uuid from 'uuid';
6
 
7
 
7
-import { getDialOutStatusUrl, getDialOutUrl } from '../base/config/functions';
8
+import { getDialOutStatusUrl, getDialOutUrl, updateConfig } from '../base/config';
9
+import { isIosMobileBrowser } from '../base/environment/utils';
8
 import { createLocalTrack } from '../base/lib-jitsi-meet';
10
 import { createLocalTrack } from '../base/lib-jitsi-meet';
9
-import { isVideoMutedByUser } from '../base/media';
11
+import { isVideoMutedByUser, MEDIA_TYPE } from '../base/media';
12
+import { updateSettings } from '../base/settings';
10
 import {
13
 import {
14
+    createLocalTracksF,
11
     getLocalAudioTrack,
15
     getLocalAudioTrack,
16
+    getLocalTracks,
12
     getLocalVideoTrack,
17
     getLocalVideoTrack,
13
     trackAdded,
18
     trackAdded,
14
     replaceLocalTrack
19
     replaceLocalTrack
15
 } from '../base/tracks';
20
 } from '../base/tracks';
16
-import { createLocalTracksF } from '../base/tracks/functions';
17
 import { openURLInBrowser } from '../base/util';
21
 import { openURLInBrowser } from '../base/util';
18
 import { executeDialOutRequest, executeDialOutStatusRequest, getDialInfoPageURL } from '../invite/functions';
22
 import { executeDialOutRequest, executeDialOutStatusRequest, getDialInfoPageURL } from '../invite/functions';
19
 import { showErrorNotification } from '../notifications';
23
 import { showErrorNotification } from '../notifications';
20
 
24
 
21
 import {
25
 import {
26
+    PREJOIN_JOINING_IN_PROGRESS,
22
     PREJOIN_INITIALIZED,
27
     PREJOIN_INITIALIZED,
23
-    PREJOIN_START_CONFERENCE,
24
-    SET_DEVICE_STATUS,
25
     SET_DIALOUT_COUNTRY,
28
     SET_DIALOUT_COUNTRY,
26
     SET_DIALOUT_NUMBER,
29
     SET_DIALOUT_NUMBER,
27
     SET_DIALOUT_STATUS,
30
     SET_DIALOUT_STATUS,
31
     SET_JOIN_BY_PHONE_DIALOG_VISIBLITY,
34
     SET_JOIN_BY_PHONE_DIALOG_VISIBLITY,
32
     SET_PRECALL_TEST_RESULTS,
35
     SET_PRECALL_TEST_RESULTS,
33
     SET_PREJOIN_DEVICE_ERRORS,
36
     SET_PREJOIN_DEVICE_ERRORS,
34
-    SET_PREJOIN_PAGE_VISIBILITY
37
+    SET_PREJOIN_PAGE_VISIBILITY,
38
+    SET_DEVICE_STATUS
35
 } from './actionTypes';
39
 } from './actionTypes';
36
-import { type PREJOIN_SCREEN_STATE } from './constants';
40
+import { type PREJOIN_SCREEN_STATE, PREJOIN_SCREEN_STATES } from './constants';
37
 import {
41
 import {
38
     getFullDialOutNumber,
42
     getFullDialOutNumber,
39
     getDialOutConferenceUrl,
43
     getDialOutConferenceUrl,
209
  * Action used to start the conference.
213
  * Action used to start the conference.
210
  *
214
  *
211
  * @param {Object} options - The config options that override the default ones (if any).
215
  * @param {Object} options - The config options that override the default ones (if any).
216
+ * @param {boolean} ignoreJoiningInProgress - If true we won't check the joiningInProgress flag.
212
  * @returns {Function}
217
  * @returns {Function}
213
  */
218
  */
214
-export function joinConference(options?: Object) {
219
+export function joinConference(options?: Object, ignoreJoiningInProgress: boolean = false) {
220
+    return async function(dispatch: Function, getState: Function) {
221
+        if (!ignoreJoiningInProgress) {
222
+            const state = getState();
223
+            const { joiningInProgress } = state['features/prejoin'];
224
+
225
+            if (joiningInProgress) {
226
+                return;
227
+            }
228
+
229
+            dispatch(setJoiningInProgress(true));
230
+        }
231
+
232
+        const state = getState();
233
+        const { userSelectedSkipPrejoin } = state['features/prejoin'];
234
+        let localTracks = getLocalTracks(state['features/base/tracks']);
235
+
236
+        options && dispatch(updateConfig(options));
237
+
238
+        userSelectedSkipPrejoin && dispatch(updateSettings({
239
+            userSelectedSkipPrejoin
240
+        }));
241
+
242
+        // Do not signal audio/video tracks if the user joins muted.
243
+        for (const track of localTracks) {
244
+            // Always add the audio track on mobile Safari because of a known issue where audio playout doesn't happen
245
+            // if the user joins audio and video muted.
246
+            if (track.muted
247
+                && !(isIosMobileBrowser() && track.jitsiTrack && track.jitsiTrack.getType() === MEDIA_TYPE.AUDIO)) {
248
+                try {
249
+                    await dispatch(replaceLocalTrack(track.jitsiTrack, null));
250
+                } catch (error) {
251
+                    logger.error(`Failed to replace local track (${track.jitsiTrack}) with null: ${error}`);
252
+                }
253
+            }
254
+        }
255
+
256
+        // Re-fetch the local tracks after muted tracks have been removed above.
257
+        // This is needed, because the tracks are effectively disposed by the replaceLocalTrack and should not be used
258
+        // anymore.
259
+        localTracks = getLocalTracks(getState()['features/base/tracks']);
260
+
261
+        const jitsiTracks = localTracks.map(t => t.jitsiTrack);
262
+
263
+        dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.LOADING));
264
+
265
+        APP.conference.prejoinStart(jitsiTracks);
266
+    };
267
+}
268
+
269
+
270
+/**
271
+ * Action used to set the flag for joining operation in progress.
272
+ *
273
+ * @param {boolean} value - The config options that override the default ones (if any).
274
+ * @returns {Function}
275
+ */
276
+export function setJoiningInProgress(value: boolean) {
215
     return {
277
     return {
216
-        type: PREJOIN_START_CONFERENCE,
217
-        options
278
+        type: PREJOIN_JOINING_IN_PROGRESS,
279
+        value
218
     };
280
     };
219
 }
281
 }
220
 
282
 
283
+
221
 /**
284
 /**
222
  * Joins the conference without audio.
285
  * Joins the conference without audio.
223
  *
286
  *
225
  */
288
  */
226
 export function joinConferenceWithoutAudio() {
289
 export function joinConferenceWithoutAudio() {
227
     return async function(dispatch: Function, getState: Function) {
290
     return async function(dispatch: Function, getState: Function) {
228
-        const tracks = getState()['features/base/tracks'];
291
+        const state = getState();
292
+        const { joiningInProgress } = state['features/prejoin'];
293
+
294
+        if (joiningInProgress) {
295
+            return;
296
+        }
297
+
298
+        dispatch(setJoiningInProgress(true));
299
+        const tracks = state['features/base/tracks'];
229
         const audioTrack = getLocalAudioTrack(tracks)?.jitsiTrack;
300
         const audioTrack = getLocalAudioTrack(tracks)?.jitsiTrack;
230
 
301
 
231
         if (audioTrack) {
302
         if (audioTrack) {
232
-            await dispatch(replaceLocalTrack(audioTrack, null));
303
+            try {
304
+                await dispatch(replaceLocalTrack(audioTrack, null));
305
+            } catch (error) {
306
+                logger.error(`Failed to replace local audio with null: ${error}`);
307
+            }
233
         }
308
         }
234
 
309
 
235
         dispatch(joinConference({
310
         dispatch(joinConference({
236
             startSilent: true
311
             startSilent: true
237
-        }));
312
+        }, true));
238
     };
313
     };
239
 }
314
 }
240
 
315
 

+ 7
- 44
react/features/prejoin/middleware.js 查看文件

1
 // @flow
1
 // @flow
2
 
2
 
3
-import { CONFERENCE_JOINED } from '../base/conference';
4
-import { updateConfig } from '../base/config';
5
-import { isIosMobileBrowser } from '../base/environment/utils';
6
-import { MEDIA_TYPE, SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../base/media';
3
+import { CONFERENCE_FAILED, CONFERENCE_JOINED } from '../base/conference';
4
+import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../base/media';
7
 import { MiddlewareRegistry } from '../base/redux';
5
 import { MiddlewareRegistry } from '../base/redux';
8
 import { updateSettings } from '../base/settings';
6
 import { updateSettings } from '../base/settings';
9
 import {
7
 import {
10
-    getLocalTracks,
11
-    replaceLocalTrack,
12
     TRACK_ADDED,
8
     TRACK_ADDED,
13
     TRACK_NO_DATA_FROM_SOURCE
9
     TRACK_NO_DATA_FROM_SOURCE
14
 } from '../base/tracks';
10
 } from '../base/tracks';
15
 
11
 
16
-import { PREJOIN_START_CONFERENCE } from './actionTypes';
17
 import {
12
 import {
18
     setDeviceStatusOk,
13
     setDeviceStatusOk,
19
     setDeviceStatusWarning,
14
     setDeviceStatusWarning,
15
+    setJoiningInProgress,
20
     setPrejoinPageVisibility
16
     setPrejoinPageVisibility
21
 } from './actions';
17
 } from './actions';
22
 import { PREJOIN_SCREEN_STATES } from './constants';
18
 import { PREJOIN_SCREEN_STATES } from './constants';
32
  */
28
  */
33
 MiddlewareRegistry.register(store => next => async action => {
29
 MiddlewareRegistry.register(store => next => async action => {
34
     switch (action.type) {
30
     switch (action.type) {
35
-    case PREJOIN_START_CONFERENCE: {
36
-        const { getState, dispatch } = store;
37
-        const state = getState();
38
-        const { userSelectedSkipPrejoin } = state['features/prejoin'];
39
-        let localTracks = getLocalTracks(state['features/base/tracks']);
40
-        const { options } = action;
41
-
42
-        options && store.dispatch(updateConfig(options));
43
-
44
-        userSelectedSkipPrejoin && dispatch(updateSettings({
45
-            userSelectedSkipPrejoin
46
-        }));
47
-
48
-        // Do not signal audio/video tracks if the user joins muted.
49
-        for (const track of localTracks) {
50
-            // Always add the audio track on mobile Safari because of a known issue where audio playout doesn't happen
51
-            // if the user joins audio and video muted.
52
-            if (track.muted
53
-                && !(isIosMobileBrowser() && track.jitsiTrack && track.jitsiTrack.getType() === MEDIA_TYPE.AUDIO)) {
54
-                await dispatch(replaceLocalTrack(track.jitsiTrack, null));
55
-            }
56
-        }
57
-
58
-        // Re-fetch the local tracks after muted tracks have been removed above.
59
-        // This is needed, because the tracks are effectively disposed by the replaceLocalTrack and should not be used
60
-        // anymore.
61
-        localTracks = getLocalTracks(getState()['features/base/tracks']);
62
-
63
-        const jitsiTracks = localTracks.map(t => t.jitsiTrack);
64
-
65
-        dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.LOADING));
66
-
67
-        APP.conference.prejoinStart(jitsiTracks);
68
-
69
-        break;
70
-    }
71
-
72
     case SET_AUDIO_MUTED: {
31
     case SET_AUDIO_MUTED: {
73
         if (isPrejoinPageVisible(store.getState())) {
32
         if (isPrejoinPageVisible(store.getState())) {
74
             store.dispatch(updateSettings({
33
             store.dispatch(updateSettings({
110
         }
69
         }
111
         break;
70
         break;
112
     }
71
     }
72
+    case CONFERENCE_FAILED:
73
+        store.dispatch(setJoiningInProgress(false));
74
+        break;
113
     case CONFERENCE_JOINED:
75
     case CONFERENCE_JOINED:
114
         return _conferenceJoined(store, next, action);
76
         return _conferenceJoined(store, next, action);
115
     }
77
     }
127
  */
89
  */
128
 function _conferenceJoined({ dispatch }, next, action) {
90
 function _conferenceJoined({ dispatch }, next, action) {
129
     dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.HIDDEN));
91
     dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.HIDDEN));
92
+    dispatch(setJoiningInProgress(false));
130
 
93
 
131
     return next(action);
94
     return next(action);
132
 }
95
 }

+ 6
- 1
react/features/prejoin/reducer.js 查看文件

1
 import { PersistenceRegistry, ReducerRegistry } from '../base/redux';
1
 import { PersistenceRegistry, ReducerRegistry } from '../base/redux';
2
 
2
 
3
 import {
3
 import {
4
+    PREJOIN_JOINING_IN_PROGRESS,
4
     SET_DEVICE_STATUS,
5
     SET_DEVICE_STATUS,
5
     SET_DIALOUT_COUNTRY,
6
     SET_DIALOUT_COUNTRY,
6
     SET_DIALOUT_NUMBER,
7
     SET_DIALOUT_NUMBER,
53
 ReducerRegistry.register(
54
 ReducerRegistry.register(
54
     'features/prejoin', (state = DEFAULT_STATE, action) => {
55
     'features/prejoin', (state = DEFAULT_STATE, action) => {
55
         switch (action.type) {
56
         switch (action.type) {
56
-
57
+        case PREJOIN_JOINING_IN_PROGRESS:
58
+            return {
59
+                ...state,
60
+                joiningInProgress: action.value
61
+            };
57
         case SET_SKIP_PREJOIN: {
62
         case SET_SKIP_PREJOIN: {
58
             return {
63
             return {
59
                 ...state,
64
                 ...state,

Loading…
取消
儲存