Browse Source

rn: ensure the conference terminated event is always sent

Dear reader, I'm not proud at all of what you are about to read, but sometimes
life just gives you lemons, so enjoy some lemonade!

Joining a conference implies first creating the XMPP connection and then joining
the MUC. It's very possible the XMPP connection was made but there was no chance
for the conference to be created.

This patch fixes this case by artificially genrating a conference terminated
event in such case. In order to have all the necessary knowledge for this event
to be sent the connection now keeps track of the conference that runs it.

In addition, there is an even more obscure corner case: it's not impossible to
try to disconnect when there is not even a connection. This was fixed by
creating a fake disconnect event. Alas the location URL is lost at this point,
but it's better than nothing I guess.
master
Saúl Ibarra Corretgé 6 years ago
parent
commit
774c5ecd18

+ 6
- 1
react/features/base/conference/actions.js View File

5
     sendAnalytics
5
     sendAnalytics
6
 } from '../../analytics';
6
 } from '../../analytics';
7
 import { getName } from '../../app';
7
 import { getName } from '../../app';
8
+import { endpointMessageReceived } from '../../subtitles';
9
+
10
+import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection';
8
 import { JitsiConferenceEvents } from '../lib-jitsi-meet';
11
 import { JitsiConferenceEvents } from '../lib-jitsi-meet';
9
 import { setAudioMuted, setVideoMuted } from '../media';
12
 import { setAudioMuted, setVideoMuted } from '../media';
10
 import {
13
 import {
15
     participantRoleChanged,
18
     participantRoleChanged,
16
     participantUpdated
19
     participantUpdated
17
 } from '../participants';
20
 } from '../participants';
18
-import { endpointMessageReceived } from '../../subtitles';
19
 import { getLocalTracks, trackAdded, trackRemoved } from '../tracks';
21
 import { getLocalTracks, trackAdded, trackRemoved } from '../tracks';
20
 import { getJitsiMeetGlobalNS } from '../util';
22
 import { getJitsiMeetGlobalNS } from '../util';
21
 
23
 
378
                     getWiFiStatsMethod: getJitsiMeetGlobalNS().getWiFiStats
380
                     getWiFiStatsMethod: getJitsiMeetGlobalNS().getWiFiStats
379
                 });
381
                 });
380
 
382
 
383
+        connection[JITSI_CONNECTION_CONFERENCE_KEY] = conference;
384
+
381
         conference[JITSI_CONFERENCE_URL_KEY] = locationURL;
385
         conference[JITSI_CONFERENCE_URL_KEY] = locationURL;
386
+
382
         dispatch(_conferenceWillJoin(conference));
387
         dispatch(_conferenceWillJoin(conference));
383
 
388
 
384
         _addConferenceListeners(conference, dispatch);
389
         _addConferenceListeners(conference, dispatch);

+ 11
- 3
react/features/base/connection/actions.native.js View File

18
     CONNECTION_WILL_CONNECT,
18
     CONNECTION_WILL_CONNECT,
19
     SET_LOCATION_URL
19
     SET_LOCATION_URL
20
 } from './actionTypes';
20
 } from './actionTypes';
21
+import { JITSI_CONNECTION_URL_KEY } from './constants';
21
 
22
 
22
 const logger = require('jitsi-meet-logger').getLogger(__filename);
23
 const logger = require('jitsi-meet-logger').getLogger(__filename);
23
 
24
 
79
     return (dispatch: Dispatch<any>, getState: Function) => {
80
     return (dispatch: Dispatch<any>, getState: Function) => {
80
         const state = getState();
81
         const state = getState();
81
         const options = _constructOptions(state);
82
         const options = _constructOptions(state);
83
+        const { locationURL } = state['features/base/connection'];
82
         const { issuer, jwt } = state['features/base/jwt'];
84
         const { issuer, jwt } = state['features/base/jwt'];
83
         const connection
85
         const connection
84
             = new JitsiMeetJS.JitsiConnection(
86
             = new JitsiMeetJS.JitsiConnection(
86
                 jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
88
                 jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
87
                 options);
89
                 options);
88
 
90
 
91
+        connection[JITSI_CONNECTION_URL_KEY] = locationURL;
92
+
89
         dispatch(_connectionWillConnect(connection));
93
         dispatch(_connectionWillConnect(connection));
90
 
94
 
91
         connection.addEventListener(
95
         connection.addEventListener(
284
     let { bosh } = options;
288
     let { bosh } = options;
285
 
289
 
286
     if (bosh) {
290
     if (bosh) {
291
+        const { locationURL } = state['features/base/connection'];
292
+
287
         if (bosh.startsWith('//')) {
293
         if (bosh.startsWith('//')) {
288
             // By default our config.js doesn't include the protocol.
294
             // By default our config.js doesn't include the protocol.
289
-            const { locationURL } = state['features/base/connection'];
290
-
291
             bosh = `${locationURL.protocol}${bosh}`;
295
             bosh = `${locationURL.protocol}${bosh}`;
292
         } else if (bosh.startsWith('/')) {
296
         } else if (bosh.startsWith('/')) {
293
             // Handle relative URLs, which won't work on mobile.
297
             // Handle relative URLs, which won't work on mobile.
294
-            const { locationURL } = state['features/base/connection'];
295
             const {
298
             const {
296
                 protocol,
299
                 protocol,
297
                 hostname,
300
                 hostname,
366
 
369
 
367
         if (connection_) {
370
         if (connection_) {
368
             promise = promise.then(() => connection_.disconnect());
371
             promise = promise.then(() => connection_.disconnect());
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({}, '')));
369
         }
377
         }
370
 
378
 
371
         return promise;
379
         return promise;

+ 19
- 0
react/features/base/connection/constants.js View File

1
+// @flow
2
+
3
+/**
4
+ * The name of the {@code JitsiConnection} property which identifies the {@code JitsiConference} currently associated
5
+ * with it.
6
+ *
7
+ * FIXME: This is a hack. It was introduced to solve the following case: if a user presses hangup quickly, they may
8
+ * "leave the conference" before the actual conference was ever created. While we might have a connection in place,
9
+ * there is no conference which can be left, thus no CONFERENCE_LEFT action will ever be fired.
10
+ *
11
+ * This is problematic because the external API module used to send events to the native SDK won't know what to send.
12
+ * So, in order to detect this situation we are attaching the conference object to the connection which runs it.
13
+ */
14
+export const JITSI_CONNECTION_CONFERENCE_KEY = Symbol('conference');
15
+
16
+/**
17
+ * The name of the {@code JitsiConnection} property which identifies the location URL where the connection will be made.
18
+ */
19
+export const JITSI_CONNECTION_URL_KEY = Symbol('url');

+ 1
- 0
react/features/base/connection/index.js View File

2
 
2
 
3
 export * from './actions';
3
 export * from './actions';
4
 export * from './actionTypes';
4
 export * from './actionTypes';
5
+export * from './constants';
5
 export * from './functions';
6
 export * from './functions';
6
 
7
 
7
 import './reducer';
8
 import './reducer';

+ 1
- 1
react/features/conference/components/native/Conference.js View File

199
         } = this.props;
199
         } = this.props;
200
 
200
 
201
         // If the location URL changes we need to reconnect.
201
         // If the location URL changes we need to reconnect.
202
-        oldLocationURL !== newLocationURL && this.props._onDisconnect();
202
+        oldLocationURL !== newLocationURL && newRoom && this.props._onDisconnect();
203
 
203
 
204
         // Start the connection process when there is a (valid) room.
204
         // Start the connection process when there is a (valid) room.
205
         oldRoom !== newRoom && newRoom && this.props._onConnect();
205
         oldRoom !== newRoom && newRoom && this.props._onConnect();

+ 27
- 1
react/features/mobile/external-api/middleware.js View File

11
     isRoomValid
11
     isRoomValid
12
 } from '../../base/conference';
12
 } from '../../base/conference';
13
 import { LOAD_CONFIG_ERROR } from '../../base/config';
13
 import { LOAD_CONFIG_ERROR } from '../../base/config';
14
-import { CONNECTION_FAILED } from '../../base/connection';
14
+import {
15
+    CONNECTION_DISCONNECTED,
16
+    CONNECTION_FAILED,
17
+    JITSI_CONNECTION_CONFERENCE_KEY,
18
+    JITSI_CONNECTION_URL_KEY
19
+} from '../../base/connection';
15
 import { MiddlewareRegistry } from '../../base/redux';
20
 import { MiddlewareRegistry } from '../../base/redux';
16
 import { toURLString } from '../../base/util';
21
 import { toURLString } from '../../base/util';
17
 import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture';
22
 import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture';
63
         _sendConferenceEvent(store, action);
68
         _sendConferenceEvent(store, action);
64
         break;
69
         break;
65
 
70
 
71
+    case CONNECTION_DISCONNECTED: {
72
+        // FIXME: This is a hack. See the description in the JITSI_CONNECTION_CONFERENCE_KEY constant definition.
73
+        // Check if this connection was attached to any conference. If it wasn't, fake a CONFERENCE_TERMINATED event.
74
+        const { connection } = action;
75
+        const conference = connection[JITSI_CONNECTION_CONFERENCE_KEY];
76
+
77
+        if (!conference) {
78
+            // This action will arrive late, so the locationURL stored on the state is no longer valid.
79
+            const locationURL = connection[JITSI_CONNECTION_URL_KEY];
80
+
81
+            sendEvent(
82
+                store,
83
+                CONFERENCE_TERMINATED,
84
+                /* data */ {
85
+                    url: toURLString(locationURL)
86
+                });
87
+        }
88
+
89
+        break;
90
+    }
91
+
66
     case CONNECTION_FAILED:
92
     case CONNECTION_FAILED:
67
         !action.error.recoverable
93
         !action.error.recoverable
68
             && _sendConferenceFailedOnConnectionError(store, action);
94
             && _sendConferenceFailedOnConnectionError(store, action);

Loading…
Cancel
Save