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.
This commit is contained in:
parent
ccc5e19e3c
commit
774c5ecd18
|
@ -5,6 +5,9 @@ import {
|
||||||
sendAnalytics
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { getName } from '../../app';
|
import { getName } from '../../app';
|
||||||
|
import { endpointMessageReceived } from '../../subtitles';
|
||||||
|
|
||||||
|
import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection';
|
||||||
import { JitsiConferenceEvents } from '../lib-jitsi-meet';
|
import { JitsiConferenceEvents } from '../lib-jitsi-meet';
|
||||||
import { setAudioMuted, setVideoMuted } from '../media';
|
import { setAudioMuted, setVideoMuted } from '../media';
|
||||||
import {
|
import {
|
||||||
|
@ -15,7 +18,6 @@ import {
|
||||||
participantRoleChanged,
|
participantRoleChanged,
|
||||||
participantUpdated
|
participantUpdated
|
||||||
} from '../participants';
|
} from '../participants';
|
||||||
import { endpointMessageReceived } from '../../subtitles';
|
|
||||||
import { getLocalTracks, trackAdded, trackRemoved } from '../tracks';
|
import { getLocalTracks, trackAdded, trackRemoved } from '../tracks';
|
||||||
import { getJitsiMeetGlobalNS } from '../util';
|
import { getJitsiMeetGlobalNS } from '../util';
|
||||||
|
|
||||||
|
@ -378,7 +380,10 @@ export function createConference() {
|
||||||
getWiFiStatsMethod: getJitsiMeetGlobalNS().getWiFiStats
|
getWiFiStatsMethod: getJitsiMeetGlobalNS().getWiFiStats
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connection[JITSI_CONNECTION_CONFERENCE_KEY] = conference;
|
||||||
|
|
||||||
conference[JITSI_CONFERENCE_URL_KEY] = locationURL;
|
conference[JITSI_CONFERENCE_URL_KEY] = locationURL;
|
||||||
|
|
||||||
dispatch(_conferenceWillJoin(conference));
|
dispatch(_conferenceWillJoin(conference));
|
||||||
|
|
||||||
_addConferenceListeners(conference, dispatch);
|
_addConferenceListeners(conference, dispatch);
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
CONNECTION_WILL_CONNECT,
|
CONNECTION_WILL_CONNECT,
|
||||||
SET_LOCATION_URL
|
SET_LOCATION_URL
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
|
import { JITSI_CONNECTION_URL_KEY } from './constants';
|
||||||
|
|
||||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@ export function connect(id: ?string, password: ?string) {
|
||||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const options = _constructOptions(state);
|
const options = _constructOptions(state);
|
||||||
|
const { locationURL } = state['features/base/connection'];
|
||||||
const { issuer, jwt } = state['features/base/jwt'];
|
const { issuer, jwt } = state['features/base/jwt'];
|
||||||
const connection
|
const connection
|
||||||
= new JitsiMeetJS.JitsiConnection(
|
= new JitsiMeetJS.JitsiConnection(
|
||||||
|
@ -86,6 +88,8 @@ export function connect(id: ?string, password: ?string) {
|
||||||
jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
|
jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
|
||||||
options);
|
options);
|
||||||
|
|
||||||
|
connection[JITSI_CONNECTION_URL_KEY] = locationURL;
|
||||||
|
|
||||||
dispatch(_connectionWillConnect(connection));
|
dispatch(_connectionWillConnect(connection));
|
||||||
|
|
||||||
connection.addEventListener(
|
connection.addEventListener(
|
||||||
|
@ -284,14 +288,13 @@ function _constructOptions(state) {
|
||||||
let { bosh } = options;
|
let { bosh } = options;
|
||||||
|
|
||||||
if (bosh) {
|
if (bosh) {
|
||||||
|
const { locationURL } = state['features/base/connection'];
|
||||||
|
|
||||||
if (bosh.startsWith('//')) {
|
if (bosh.startsWith('//')) {
|
||||||
// By default our config.js doesn't include the protocol.
|
// By default our config.js doesn't include the protocol.
|
||||||
const { locationURL } = state['features/base/connection'];
|
|
||||||
|
|
||||||
bosh = `${locationURL.protocol}${bosh}`;
|
bosh = `${locationURL.protocol}${bosh}`;
|
||||||
} else if (bosh.startsWith('/')) {
|
} else if (bosh.startsWith('/')) {
|
||||||
// Handle relative URLs, which won't work on mobile.
|
// Handle relative URLs, which won't work on mobile.
|
||||||
const { locationURL } = state['features/base/connection'];
|
|
||||||
const {
|
const {
|
||||||
protocol,
|
protocol,
|
||||||
hostname,
|
hostname,
|
||||||
|
@ -366,6 +369,11 @@ export function disconnect() {
|
||||||
|
|
||||||
if (connection_) {
|
if (connection_) {
|
||||||
promise = promise.then(() => connection_.disconnect());
|
promise = promise.then(() => connection_.disconnect());
|
||||||
|
} else {
|
||||||
|
// FIXME: We have no connection! Fake a disconnect. Because of how the current disconnec is implemented
|
||||||
|
// (by doing the diconnect() in the Conference component unmount) we have lost the location URL already.
|
||||||
|
// Oh well, at least send the event.
|
||||||
|
promise.then(() => dispatch(_connectionDisconnected({}, '')));
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the {@code JitsiConnection} property which identifies the {@code JitsiConference} currently associated
|
||||||
|
* with it.
|
||||||
|
*
|
||||||
|
* FIXME: This is a hack. It was introduced to solve the following case: if a user presses hangup quickly, they may
|
||||||
|
* "leave the conference" before the actual conference was ever created. While we might have a connection in place,
|
||||||
|
* there is no conference which can be left, thus no CONFERENCE_LEFT action will ever be fired.
|
||||||
|
*
|
||||||
|
* This is problematic because the external API module used to send events to the native SDK won't know what to send.
|
||||||
|
* So, in order to detect this situation we are attaching the conference object to the connection which runs it.
|
||||||
|
*/
|
||||||
|
export const JITSI_CONNECTION_CONFERENCE_KEY = Symbol('conference');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the {@code JitsiConnection} property which identifies the location URL where the connection will be made.
|
||||||
|
*/
|
||||||
|
export const JITSI_CONNECTION_URL_KEY = Symbol('url');
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
export * from './actions';
|
export * from './actions';
|
||||||
export * from './actionTypes';
|
export * from './actionTypes';
|
||||||
|
export * from './constants';
|
||||||
export * from './functions';
|
export * from './functions';
|
||||||
|
|
||||||
import './reducer';
|
import './reducer';
|
||||||
|
|
|
@ -199,7 +199,7 @@ class Conference extends AbstractConference<Props, *> {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// If the location URL changes we need to reconnect.
|
// If the location URL changes we need to reconnect.
|
||||||
oldLocationURL !== newLocationURL && this.props._onDisconnect();
|
oldLocationURL !== newLocationURL && newRoom && this.props._onDisconnect();
|
||||||
|
|
||||||
// Start the connection process when there is a (valid) room.
|
// Start the connection process when there is a (valid) room.
|
||||||
oldRoom !== newRoom && newRoom && this.props._onConnect();
|
oldRoom !== newRoom && newRoom && this.props._onConnect();
|
||||||
|
|
|
@ -11,7 +11,12 @@ import {
|
||||||
isRoomValid
|
isRoomValid
|
||||||
} from '../../base/conference';
|
} from '../../base/conference';
|
||||||
import { LOAD_CONFIG_ERROR } from '../../base/config';
|
import { LOAD_CONFIG_ERROR } from '../../base/config';
|
||||||
import { CONNECTION_FAILED } from '../../base/connection';
|
import {
|
||||||
|
CONNECTION_DISCONNECTED,
|
||||||
|
CONNECTION_FAILED,
|
||||||
|
JITSI_CONNECTION_CONFERENCE_KEY,
|
||||||
|
JITSI_CONNECTION_URL_KEY
|
||||||
|
} from '../../base/connection';
|
||||||
import { MiddlewareRegistry } from '../../base/redux';
|
import { MiddlewareRegistry } from '../../base/redux';
|
||||||
import { toURLString } from '../../base/util';
|
import { toURLString } from '../../base/util';
|
||||||
import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture';
|
import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture';
|
||||||
|
@ -63,6 +68,27 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
_sendConferenceEvent(store, action);
|
_sendConferenceEvent(store, action);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CONNECTION_DISCONNECTED: {
|
||||||
|
// FIXME: This is a hack. See the description in the JITSI_CONNECTION_CONFERENCE_KEY constant definition.
|
||||||
|
// Check if this connection was attached to any conference. If it wasn't, fake a CONFERENCE_TERMINATED event.
|
||||||
|
const { connection } = action;
|
||||||
|
const conference = connection[JITSI_CONNECTION_CONFERENCE_KEY];
|
||||||
|
|
||||||
|
if (!conference) {
|
||||||
|
// This action will arrive late, so the locationURL stored on the state is no longer valid.
|
||||||
|
const locationURL = connection[JITSI_CONNECTION_URL_KEY];
|
||||||
|
|
||||||
|
sendEvent(
|
||||||
|
store,
|
||||||
|
CONFERENCE_TERMINATED,
|
||||||
|
/* data */ {
|
||||||
|
url: toURLString(locationURL)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case CONNECTION_FAILED:
|
case CONNECTION_FAILED:
|
||||||
!action.error.recoverable
|
!action.error.recoverable
|
||||||
&& _sendConferenceFailedOnConnectionError(store, action);
|
&& _sendConferenceFailedOnConnectionError(store, action);
|
||||||
|
|
Loading…
Reference in New Issue