Преглед изворни кода

rn: fix losing audio if call is hangup too quickly

This PR changes the logic for connecting / disconnecting conferences. Instead of
doing it in mount / unmount events from the Conference component, it moves the
logic to the appNavigatee action.

This fixes a regression introduced in 774c5ecd when trying to make sure the
conference terminated event is always sent.

By moving the logic to appNavigate we no longer depend on side-effects for
connecting / disconnecting, and the code should be more maintainable moving
forward.

An improvement to this is the concept of sessions, which, while not tackled
here, was taken into consideration.
master
Saúl Ibarra Corretgé пре 6 година
родитељ
комит
a4cf79c161

+ 21
- 6
react/features/app/actions.js Прегледај датотеку

10
     setConfig,
10
     setConfig,
11
     storeConfig
11
     storeConfig
12
 } from '../base/config';
12
 } from '../base/config';
13
-import { setLocationURL } from '../base/connection';
13
+import { connect, disconnect, setLocationURL } from '../base/connection';
14
 import { loadConfig } from '../base/lib-jitsi-meet';
14
 import { loadConfig } from '../base/lib-jitsi-meet';
15
+import { createDesiredLocalTracks } from '../base/tracks';
15
 import { parseURIString, toURLString } from '../base/util';
16
 import { parseURIString, toURLString } from '../base/util';
16
 import { setFatalError } from '../overlay';
17
 import { setFatalError } from '../overlay';
17
 
18
 
58
         const { contextRoot, host, room } = location;
59
         const { contextRoot, host, room } = location;
59
         const locationURL = new URL(location.toString());
60
         const locationURL = new URL(location.toString());
60
 
61
 
62
+        // Disconnect from any current conference.
63
+        // FIXME: unify with web.
64
+        if (navigator.product === 'ReactNative') {
65
+            dispatch(disconnect());
66
+        }
67
+
61
         dispatch(configWillLoad(locationURL, room));
68
         dispatch(configWillLoad(locationURL, room));
62
 
69
 
63
         let protocol = location.protocol.toLowerCase();
70
         let protocol = location.protocol.toLowerCase();
87
             }
94
             }
88
         }
95
         }
89
 
96
 
90
-        if (getState()['features/base/config'].locationURL === locationURL) {
91
-            dispatch(setLocationURL(locationURL));
92
-            dispatch(setConfig(config));
93
-            dispatch(setRoom(room));
94
-        } else {
97
+        if (getState()['features/base/config'].locationURL !== locationURL) {
95
             dispatch(loadConfigError(new Error('Config no longer needed!'), locationURL));
98
             dispatch(loadConfigError(new Error('Config no longer needed!'), locationURL));
99
+
100
+            return;
101
+        }
102
+
103
+        dispatch(setLocationURL(locationURL));
104
+        dispatch(setConfig(config));
105
+        dispatch(setRoom(room));
106
+
107
+        // FIXME: unify with web, currently the connection and track creation happens in conference.js.
108
+        if (room && navigator.product === 'ReactNative') {
109
+            dispatch(createDesiredLocalTracks());
110
+            dispatch(connect());
96
         }
111
         }
97
     };
112
     };
98
 }
113
 }

+ 1
- 51
react/features/base/conference/middleware.js Прегледај датотеку

24
 
24
 
25
 import {
25
 import {
26
     conferenceFailed,
26
     conferenceFailed,
27
-    conferenceLeft,
28
     conferenceWillLeave,
27
     conferenceWillLeave,
29
     createConference,
28
     createConference,
30
     setLastN,
29
     setLastN,
37
     CONFERENCE_WILL_LEAVE,
36
     CONFERENCE_WILL_LEAVE,
38
     DATA_CHANNEL_OPENED,
37
     DATA_CHANNEL_OPENED,
39
     SET_AUDIO_ONLY,
38
     SET_AUDIO_ONLY,
40
-    SET_LASTN,
41
-    SET_ROOM
39
+    SET_LASTN
42
 } from './actionTypes';
40
 } from './actionTypes';
43
 import {
41
 import {
44
     _addLocalTracksToConference,
42
     _addLocalTracksToConference,
99
     case SET_LASTN:
97
     case SET_LASTN:
100
         return _setLastN(store, next, action);
98
         return _setLastN(store, next, action);
101
 
99
 
102
-    case SET_ROOM:
103
-        return _setRoom(store, next, action);
104
-
105
     case TRACK_ADDED:
100
     case TRACK_ADDED:
106
     case TRACK_REMOVED:
101
     case TRACK_REMOVED:
107
         return _trackAddedOrRemoved(store, next, action);
102
         return _trackAddedOrRemoved(store, next, action);
566
     }
561
     }
567
 }
562
 }
568
 
563
 
569
-/**
570
- * Notifies the feature {@code base/conference} that the redix action
571
- * {@link SET_ROOM} is being dispatched within a specific redux store.
572
- *
573
- * @param {Store} store - The redux store in which the specified {@code action}
574
- * is being dispatched.
575
- * @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
576
- * specified {@code action} to the specified {@code store}.
577
- * @param {Action} action - The redux action {@code SET_ROOM} which is being
578
- * dispatched in the specified {@code store}.
579
- * @private
580
- * @returns {Object} The value returned by {@code next(action)}.
581
- */
582
-function _setRoom({ dispatch, getState }, next, action) {
583
-    const result = next(action);
584
-
585
-    // By the time SET_ROOM is dispatched, base/connection's locationURL and
586
-    // base/conference's leaving should be the only conference-related sources
587
-    // of truth.
588
-    const state = getState();
589
-    const { leaving } = state['features/base/conference'];
590
-    const { locationURL } = state['features/base/connection'];
591
-    const dispatchConferenceLeft = new Set();
592
-
593
-    // Figure out which of the JitsiConferences referenced by base/conference
594
-    // have not dispatched or are not likely to dispatch CONFERENCE_FAILED and
595
-    // CONFERENCE_LEFT.
596
-    forEachConference(state, (conference, url) => {
597
-        if (conference !== leaving && url && url !== locationURL) {
598
-            dispatchConferenceLeft.add(conference);
599
-        }
600
-
601
-        return true; // All JitsiConference instances are to be examined.
602
-    });
603
-
604
-    // Dispatch CONFERENCE_LEFT for the JitsiConferences referenced by
605
-    // base/conference which have not dispatched or are not likely to dispatch
606
-    // CONFERENCE_FAILED or CONFERENCE_LEFT.
607
-    for (const conference of dispatchConferenceLeft) {
608
-        dispatch(conferenceLeft(conference));
609
-    }
610
-
611
-    return result;
612
-}
613
-
614
 /**
564
 /**
615
  * Synchronizes local tracks from state with local tracks in JitsiConference
565
  * Synchronizes local tracks from state with local tracks in JitsiConference
616
  * instance.
566
  * instance.

+ 1
- 4
react/features/base/connection/actions.native.js Прегледај датотеку

370
         if (connection_) {
370
         if (connection_) {
371
             promise = promise.then(() => connection_.disconnect());
371
             promise = promise.then(() => connection_.disconnect());
372
         } else {
372
         } else {
373
-            // FIXME: We have no connection! Fake a disconnect. Because of how the current disconnec is implemented
374
-            // (by doing the diconnect() in the Conference component unmount) we have lost the location URL already.
375
-            // Oh well, at least send the event.
376
-            promise.then(() => dispatch(_connectionDisconnected({}, '')));
373
+            logger.info('No connection found while disconnecting.');
377
         }
374
         }
378
 
375
 
379
         return promise;
376
         return promise;

+ 4
- 76
react/features/conference/components/native/Conference.js Прегледај датотеку

5
 import { BackHandler, SafeAreaView, StatusBar, View } from 'react-native';
5
 import { BackHandler, SafeAreaView, StatusBar, View } from 'react-native';
6
 
6
 
7
 import { appNavigate } from '../../../app';
7
 import { appNavigate } from '../../../app';
8
-import { connect, disconnect } from '../../../base/connection';
9
 import { getParticipantCount } from '../../../base/participants';
8
 import { getParticipantCount } from '../../../base/participants';
10
 import { Container, LoadingIndicator, TintedView } from '../../../base/react';
9
 import { Container, LoadingIndicator, TintedView } from '../../../base/react';
11
 import { connect as reactReduxConnect } from '../../../base/redux';
10
 import { connect as reactReduxConnect } from '../../../base/redux';
14
     makeAspectRatioAware
13
     makeAspectRatioAware
15
 } from '../../../base/responsive-ui';
14
 } from '../../../base/responsive-ui';
16
 import { TestConnectionInfo } from '../../../base/testing';
15
 import { TestConnectionInfo } from '../../../base/testing';
17
-import { createDesiredLocalTracks } from '../../../base/tracks';
18
 import { ConferenceNotification } from '../../../calendar-sync';
16
 import { ConferenceNotification } from '../../../calendar-sync';
19
 import { Chat } from '../../../chat';
17
 import { Chat } from '../../../chat';
20
 import { DisplayNameLabel } from '../../../display-name';
18
 import { DisplayNameLabel } from '../../../display-name';
66
      */
64
      */
67
     _largeVideoParticipantId: string,
65
     _largeVideoParticipantId: string,
68
 
66
 
69
-    /**
70
-     * Current conference's full URL.
71
-     *
72
-     * @private
73
-     */
74
-    _locationURL: URL,
75
-
76
-    /**
77
-     * The handler which dispatches the (redux) action connect.
78
-     *
79
-     * @private
80
-     * @returns {void}
81
-     */
82
-    _onConnect: Function,
83
-
84
-    /**
85
-     * The handler which dispatches the (redux) action disconnect.
86
-     *
87
-     * @private
88
-     * @returns {void}
89
-     */
90
-    _onDisconnect: Function,
91
-
92
     /**
67
     /**
93
      * Handles a hardware button press for back navigation. Leaves the
68
      * Handles a hardware button press for back navigation. Leaves the
94
      * associated {@code Conference}.
69
      * associated {@code Conference}.
166
      * @returns {void}
141
      * @returns {void}
167
      */
142
      */
168
     componentDidMount() {
143
     componentDidMount() {
169
-        this.props._onConnect();
170
-
171
         BackHandler.addEventListener(
144
         BackHandler.addEventListener(
172
             'hardwareBackPress',
145
             'hardwareBackPress',
173
             this.props._onHardwareBackPress);
146
             this.props._onHardwareBackPress);
184
      *
157
      *
185
      * @inheritdoc
158
      * @inheritdoc
186
      */
159
      */
187
-    componentDidUpdate(pevProps: Props) {
160
+    componentDidUpdate(prevProps: Props) {
188
         const {
161
         const {
189
-            _locationURL: oldLocationURL,
190
-            _participantCount: oldParticipantCount,
191
-            _room: oldRoom
192
-        } = pevProps;
162
+            _participantCount: oldParticipantCount
163
+        } = prevProps;
193
         const {
164
         const {
194
-            _locationURL: newLocationURL,
195
             _participantCount: newParticipantCount,
165
             _participantCount: newParticipantCount,
196
-            _room: newRoom,
197
             _setToolboxVisible,
166
             _setToolboxVisible,
198
             _toolboxVisible
167
             _toolboxVisible
199
         } = this.props;
168
         } = this.props;
200
 
169
 
201
-        // If the location URL changes we need to reconnect.
202
-        oldLocationURL !== newLocationURL && newRoom && this.props._onDisconnect();
203
-
204
-        // Start the connection process when there is a (valid) room.
205
-        oldRoom !== newRoom && newRoom && this.props._onConnect();
206
-
207
         if (oldParticipantCount === 1
170
         if (oldParticipantCount === 1
208
                 && newParticipantCount > 1
171
                 && newParticipantCount > 1
209
                 && _toolboxVisible) {
172
                 && _toolboxVisible) {
228
         BackHandler.removeEventListener(
191
         BackHandler.removeEventListener(
229
             'hardwareBackPress',
192
             'hardwareBackPress',
230
             this.props._onHardwareBackPress);
193
             this.props._onHardwareBackPress);
231
-
232
-        this.props._onDisconnect();
233
     }
194
     }
234
 
195
 
235
     /**
196
     /**
396
  * @param {Function} dispatch - Redux action dispatcher.
357
  * @param {Function} dispatch - Redux action dispatcher.
397
  * @private
358
  * @private
398
  * @returns {{
359
  * @returns {{
399
- *     _onConnect: Function,
400
- *     _onDisconnect: Function,
401
  *     _onHardwareBackPress: Function,
360
  *     _onHardwareBackPress: Function,
402
  *     _setToolboxVisible: Function
361
  *     _setToolboxVisible: Function
403
  * }}
362
  * }}
404
  */
363
  */
405
 function _mapDispatchToProps(dispatch) {
364
 function _mapDispatchToProps(dispatch) {
406
     return {
365
     return {
407
-        /**
408
-         * Dispatches actions to create the desired local tracks and for
409
-         * connecting to the conference.
410
-         *
411
-         * @private
412
-         * @returns {void}
413
-         */
414
-        _onConnect() {
415
-            dispatch(createDesiredLocalTracks());
416
-            dispatch(connect());
417
-        },
418
-
419
-        /**
420
-         * Dispatches an action disconnecting from the conference.
421
-         *
422
-         * @private
423
-         * @returns {void}
424
-         */
425
-        _onDisconnect() {
426
-            dispatch(disconnect());
427
-        },
428
-
429
         /**
366
         /**
430
          * Handles a hardware button press for back navigation. Leaves the
367
          * Handles a hardware button press for back navigation. Leaves the
431
          * associated {@code Conference}.
368
          * associated {@code Conference}.
462
  * @returns {Props}
399
  * @returns {Props}
463
  */
400
  */
464
 function _mapStateToProps(state) {
401
 function _mapStateToProps(state) {
465
-    const { connecting, connection, locationURL }
466
-        = state['features/base/connection'];
402
+    const { connecting, connection } = state['features/base/connection'];
467
     const {
403
     const {
468
         conference,
404
         conference,
469
         joining,
405
         joining,
508
          */
444
          */
509
         _largeVideoParticipantId: state['features/large-video'].participantId,
445
         _largeVideoParticipantId: state['features/large-video'].participantId,
510
 
446
 
511
-        /**
512
-         * Current conference's full URL.
513
-         *
514
-         * @private
515
-         * @type {URL}
516
-         */
517
-        _locationURL: locationURL,
518
-
519
         /**
447
         /**
520
          * The number of participants in the conference.
448
          * The number of participants in the conference.
521
          *
449
          *

+ 25
- 8
react/features/toolbox/components/HangupButton.js Прегледај датотеку

1
 // @flow
1
 // @flow
2
 
2
 
3
+import _ from 'lodash';
3
 
4
 
4
 import { createToolbarEvent, sendAnalytics } from '../../analytics';
5
 import { createToolbarEvent, sendAnalytics } from '../../analytics';
5
 import { appNavigate } from '../../app';
6
 import { appNavigate } from '../../app';
26
  * @extends AbstractHangupButton
27
  * @extends AbstractHangupButton
27
  */
28
  */
28
 class HangupButton extends AbstractHangupButton<Props, *> {
29
 class HangupButton extends AbstractHangupButton<Props, *> {
30
+    _hangup: Function;
31
+
29
     accessibilityLabel = 'toolbar.accessibilityLabel.hangup';
32
     accessibilityLabel = 'toolbar.accessibilityLabel.hangup';
30
     label = 'toolbar.hangup';
33
     label = 'toolbar.hangup';
31
     tooltip = 'toolbar.hangup';
34
     tooltip = 'toolbar.hangup';
32
 
35
 
36
+    /**
37
+     * Initializes a new HangupButton instance.
38
+     *
39
+     * @param {Props} props - The read-only properties with which the new
40
+     * instance is to be initialized.
41
+     */
42
+    constructor(props: Props) {
43
+        super(props);
44
+
45
+        this._hangup = _.once(() => {
46
+            sendAnalytics(createToolbarEvent('hangup'));
47
+
48
+            // FIXME: these should be unified.
49
+            if (navigator.product === 'ReactNative') {
50
+                this.props.dispatch(appNavigate(undefined));
51
+            } else {
52
+                this.props.dispatch(disconnect(true));
53
+            }
54
+        });
55
+    }
56
+
33
     /**
57
     /**
34
      * Helper function to perform the actual hangup action.
58
      * Helper function to perform the actual hangup action.
35
      *
59
      *
38
      * @returns {void}
62
      * @returns {void}
39
      */
63
      */
40
     _doHangup() {
64
     _doHangup() {
41
-        sendAnalytics(createToolbarEvent('hangup'));
42
-
43
-        // FIXME: these should be unified.
44
-        if (navigator.product === 'ReactNative') {
45
-            this.props.dispatch(appNavigate(undefined));
46
-        } else {
47
-            this.props.dispatch(disconnect(true));
48
-        }
65
+        this._hangup();
49
     }
66
     }
50
 }
67
 }
51
 
68
 

Loading…
Откажи
Сачувај