fix(prejoin): Prevent double joining conference.
This commit is contained in:
parent
0214138863
commit
2a725d2165
|
@ -129,7 +129,8 @@ import {
|
||||||
initPrejoin,
|
initPrejoin,
|
||||||
isPrejoinPageEnabled,
|
isPrejoinPageEnabled,
|
||||||
isPrejoinPageVisible,
|
isPrejoinPageVisible,
|
||||||
makePrecallTest
|
makePrecallTest,
|
||||||
|
setJoiningInProgress
|
||||||
} from './react/features/prejoin';
|
} from './react/features/prejoin';
|
||||||
import { disableReceiver, stopReceiver } from './react/features/remote-control';
|
import { disableReceiver, stopReceiver } from './react/features/remote-control';
|
||||||
import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
|
import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
|
||||||
|
@ -861,9 +862,15 @@ export default {
|
||||||
_onConnectionPromiseCreated = undefined;
|
_onConnectionPromiseCreated = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const con = await _connectionPromise;
|
let con;
|
||||||
|
|
||||||
this.startConference(con, tracks);
|
try {
|
||||||
|
con = await _connectionPromise;
|
||||||
|
this.startConference(con, tracks);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`An error occurred while trying to join a meeting from the prejoin screen: ${error}`);
|
||||||
|
APP.store.dispatch(setJoiningInProgress(false));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action type to signal the start of the conference.
|
* Action type to signal that joining is in progress.
|
||||||
*/
|
*/
|
||||||
export const PREJOIN_START_CONFERENCE = 'PREJOIN_START_CONFERENCE';
|
export const PREJOIN_JOINING_IN_PROGRESS = 'PREJOIN_JOINING_IN_PROGRESS';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action type to signal that prejoin page was initialized.
|
* Action type to signal that prejoin page was initialized.
|
||||||
|
|
|
@ -1,27 +1,30 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
declare var JitsiMeetJS: Object;
|
declare var JitsiMeetJS: Object;
|
||||||
|
declare var APP: Object;
|
||||||
|
|
||||||
import uuid from 'uuid';
|
import uuid from 'uuid';
|
||||||
|
|
||||||
import { getDialOutStatusUrl, getDialOutUrl } from '../base/config/functions';
|
import { getDialOutStatusUrl, getDialOutUrl, updateConfig } from '../base/config';
|
||||||
|
import { isIosMobileBrowser } from '../base/environment/utils';
|
||||||
import { createLocalTrack } from '../base/lib-jitsi-meet';
|
import { createLocalTrack } from '../base/lib-jitsi-meet';
|
||||||
import { isVideoMutedByUser } from '../base/media';
|
import { isVideoMutedByUser, MEDIA_TYPE } from '../base/media';
|
||||||
|
import { updateSettings } from '../base/settings';
|
||||||
import {
|
import {
|
||||||
|
createLocalTracksF,
|
||||||
getLocalAudioTrack,
|
getLocalAudioTrack,
|
||||||
|
getLocalTracks,
|
||||||
getLocalVideoTrack,
|
getLocalVideoTrack,
|
||||||
trackAdded,
|
trackAdded,
|
||||||
replaceLocalTrack
|
replaceLocalTrack
|
||||||
} from '../base/tracks';
|
} from '../base/tracks';
|
||||||
import { createLocalTracksF } from '../base/tracks/functions';
|
|
||||||
import { openURLInBrowser } from '../base/util';
|
import { openURLInBrowser } from '../base/util';
|
||||||
import { executeDialOutRequest, executeDialOutStatusRequest, getDialInfoPageURL } from '../invite/functions';
|
import { executeDialOutRequest, executeDialOutStatusRequest, getDialInfoPageURL } from '../invite/functions';
|
||||||
import { showErrorNotification } from '../notifications';
|
import { showErrorNotification } from '../notifications';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
PREJOIN_JOINING_IN_PROGRESS,
|
||||||
PREJOIN_INITIALIZED,
|
PREJOIN_INITIALIZED,
|
||||||
PREJOIN_START_CONFERENCE,
|
|
||||||
SET_DEVICE_STATUS,
|
|
||||||
SET_DIALOUT_COUNTRY,
|
SET_DIALOUT_COUNTRY,
|
||||||
SET_DIALOUT_NUMBER,
|
SET_DIALOUT_NUMBER,
|
||||||
SET_DIALOUT_STATUS,
|
SET_DIALOUT_STATUS,
|
||||||
|
@ -31,9 +34,10 @@ import {
|
||||||
SET_JOIN_BY_PHONE_DIALOG_VISIBLITY,
|
SET_JOIN_BY_PHONE_DIALOG_VISIBLITY,
|
||||||
SET_PRECALL_TEST_RESULTS,
|
SET_PRECALL_TEST_RESULTS,
|
||||||
SET_PREJOIN_DEVICE_ERRORS,
|
SET_PREJOIN_DEVICE_ERRORS,
|
||||||
SET_PREJOIN_PAGE_VISIBILITY
|
SET_PREJOIN_PAGE_VISIBILITY,
|
||||||
|
SET_DEVICE_STATUS
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
import { type PREJOIN_SCREEN_STATE } from './constants';
|
import { type PREJOIN_SCREEN_STATE, PREJOIN_SCREEN_STATES } from './constants';
|
||||||
import {
|
import {
|
||||||
getFullDialOutNumber,
|
getFullDialOutNumber,
|
||||||
getDialOutConferenceUrl,
|
getDialOutConferenceUrl,
|
||||||
|
@ -209,15 +213,74 @@ export function initPrejoin(tracks: Object[], errors: Object) {
|
||||||
* Action used to start the conference.
|
* Action used to start the conference.
|
||||||
*
|
*
|
||||||
* @param {Object} options - The config options that override the default ones (if any).
|
* @param {Object} options - The config options that override the default ones (if any).
|
||||||
|
* @param {boolean} ignoreJoiningInProgress - If true we won't check the joiningInProgress flag.
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
export function joinConference(options?: Object) {
|
export function joinConference(options?: Object, ignoreJoiningInProgress: boolean = false) {
|
||||||
return {
|
return async function(dispatch: Function, getState: Function) {
|
||||||
type: PREJOIN_START_CONFERENCE,
|
if (!ignoreJoiningInProgress) {
|
||||||
options
|
const state = getState();
|
||||||
|
const { joiningInProgress } = state['features/prejoin'];
|
||||||
|
|
||||||
|
if (joiningInProgress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(setJoiningInProgress(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = getState();
|
||||||
|
const { userSelectedSkipPrejoin } = state['features/prejoin'];
|
||||||
|
let localTracks = getLocalTracks(state['features/base/tracks']);
|
||||||
|
|
||||||
|
options && dispatch(updateConfig(options));
|
||||||
|
|
||||||
|
userSelectedSkipPrejoin && dispatch(updateSettings({
|
||||||
|
userSelectedSkipPrejoin
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Do not signal audio/video tracks if the user joins muted.
|
||||||
|
for (const track of localTracks) {
|
||||||
|
// Always add the audio track on mobile Safari because of a known issue where audio playout doesn't happen
|
||||||
|
// if the user joins audio and video muted.
|
||||||
|
if (track.muted
|
||||||
|
&& !(isIosMobileBrowser() && track.jitsiTrack && track.jitsiTrack.getType() === MEDIA_TYPE.AUDIO)) {
|
||||||
|
try {
|
||||||
|
await dispatch(replaceLocalTrack(track.jitsiTrack, null));
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to replace local track (${track.jitsiTrack}) with null: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-fetch the local tracks after muted tracks have been removed above.
|
||||||
|
// This is needed, because the tracks are effectively disposed by the replaceLocalTrack and should not be used
|
||||||
|
// anymore.
|
||||||
|
localTracks = getLocalTracks(getState()['features/base/tracks']);
|
||||||
|
|
||||||
|
const jitsiTracks = localTracks.map(t => t.jitsiTrack);
|
||||||
|
|
||||||
|
dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.LOADING));
|
||||||
|
|
||||||
|
APP.conference.prejoinStart(jitsiTracks);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action used to set the flag for joining operation in progress.
|
||||||
|
*
|
||||||
|
* @param {boolean} value - The config options that override the default ones (if any).
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export function setJoiningInProgress(value: boolean) {
|
||||||
|
return {
|
||||||
|
type: PREJOIN_JOINING_IN_PROGRESS,
|
||||||
|
value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Joins the conference without audio.
|
* Joins the conference without audio.
|
||||||
*
|
*
|
||||||
|
@ -225,16 +288,28 @@ export function joinConference(options?: Object) {
|
||||||
*/
|
*/
|
||||||
export function joinConferenceWithoutAudio() {
|
export function joinConferenceWithoutAudio() {
|
||||||
return async function(dispatch: Function, getState: Function) {
|
return async function(dispatch: Function, getState: Function) {
|
||||||
const tracks = getState()['features/base/tracks'];
|
const state = getState();
|
||||||
|
const { joiningInProgress } = state['features/prejoin'];
|
||||||
|
|
||||||
|
if (joiningInProgress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(setJoiningInProgress(true));
|
||||||
|
const tracks = state['features/base/tracks'];
|
||||||
const audioTrack = getLocalAudioTrack(tracks)?.jitsiTrack;
|
const audioTrack = getLocalAudioTrack(tracks)?.jitsiTrack;
|
||||||
|
|
||||||
if (audioTrack) {
|
if (audioTrack) {
|
||||||
await dispatch(replaceLocalTrack(audioTrack, null));
|
try {
|
||||||
|
await dispatch(replaceLocalTrack(audioTrack, null));
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to replace local audio with null: ${error}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(joinConference({
|
dispatch(joinConference({
|
||||||
startSilent: true
|
startSilent: true
|
||||||
}));
|
}, true));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { CONFERENCE_JOINED } from '../base/conference';
|
import { CONFERENCE_FAILED, CONFERENCE_JOINED } from '../base/conference';
|
||||||
import { updateConfig } from '../base/config';
|
import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../base/media';
|
||||||
import { isIosMobileBrowser } from '../base/environment/utils';
|
|
||||||
import { MEDIA_TYPE, SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../base/media';
|
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
import { MiddlewareRegistry } from '../base/redux';
|
||||||
import { updateSettings } from '../base/settings';
|
import { updateSettings } from '../base/settings';
|
||||||
import {
|
import {
|
||||||
getLocalTracks,
|
|
||||||
replaceLocalTrack,
|
|
||||||
TRACK_ADDED,
|
TRACK_ADDED,
|
||||||
TRACK_NO_DATA_FROM_SOURCE
|
TRACK_NO_DATA_FROM_SOURCE
|
||||||
} from '../base/tracks';
|
} from '../base/tracks';
|
||||||
|
|
||||||
import { PREJOIN_START_CONFERENCE } from './actionTypes';
|
|
||||||
import {
|
import {
|
||||||
setDeviceStatusOk,
|
setDeviceStatusOk,
|
||||||
setDeviceStatusWarning,
|
setDeviceStatusWarning,
|
||||||
|
setJoiningInProgress,
|
||||||
setPrejoinPageVisibility
|
setPrejoinPageVisibility
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import { PREJOIN_SCREEN_STATES } from './constants';
|
import { PREJOIN_SCREEN_STATES } from './constants';
|
||||||
|
@ -32,43 +28,6 @@ declare var APP: Object;
|
||||||
*/
|
*/
|
||||||
MiddlewareRegistry.register(store => next => async action => {
|
MiddlewareRegistry.register(store => next => async action => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case PREJOIN_START_CONFERENCE: {
|
|
||||||
const { getState, dispatch } = store;
|
|
||||||
const state = getState();
|
|
||||||
const { userSelectedSkipPrejoin } = state['features/prejoin'];
|
|
||||||
let localTracks = getLocalTracks(state['features/base/tracks']);
|
|
||||||
const { options } = action;
|
|
||||||
|
|
||||||
options && store.dispatch(updateConfig(options));
|
|
||||||
|
|
||||||
userSelectedSkipPrejoin && dispatch(updateSettings({
|
|
||||||
userSelectedSkipPrejoin
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Do not signal audio/video tracks if the user joins muted.
|
|
||||||
for (const track of localTracks) {
|
|
||||||
// Always add the audio track on mobile Safari because of a known issue where audio playout doesn't happen
|
|
||||||
// if the user joins audio and video muted.
|
|
||||||
if (track.muted
|
|
||||||
&& !(isIosMobileBrowser() && track.jitsiTrack && track.jitsiTrack.getType() === MEDIA_TYPE.AUDIO)) {
|
|
||||||
await dispatch(replaceLocalTrack(track.jitsiTrack, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-fetch the local tracks after muted tracks have been removed above.
|
|
||||||
// This is needed, because the tracks are effectively disposed by the replaceLocalTrack and should not be used
|
|
||||||
// anymore.
|
|
||||||
localTracks = getLocalTracks(getState()['features/base/tracks']);
|
|
||||||
|
|
||||||
const jitsiTracks = localTracks.map(t => t.jitsiTrack);
|
|
||||||
|
|
||||||
dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.LOADING));
|
|
||||||
|
|
||||||
APP.conference.prejoinStart(jitsiTracks);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SET_AUDIO_MUTED: {
|
case SET_AUDIO_MUTED: {
|
||||||
if (isPrejoinPageVisible(store.getState())) {
|
if (isPrejoinPageVisible(store.getState())) {
|
||||||
store.dispatch(updateSettings({
|
store.dispatch(updateSettings({
|
||||||
|
@ -110,6 +69,9 @@ MiddlewareRegistry.register(store => next => async action => {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CONFERENCE_FAILED:
|
||||||
|
store.dispatch(setJoiningInProgress(false));
|
||||||
|
break;
|
||||||
case CONFERENCE_JOINED:
|
case CONFERENCE_JOINED:
|
||||||
return _conferenceJoined(store, next, action);
|
return _conferenceJoined(store, next, action);
|
||||||
}
|
}
|
||||||
|
@ -127,6 +89,7 @@ MiddlewareRegistry.register(store => next => async action => {
|
||||||
*/
|
*/
|
||||||
function _conferenceJoined({ dispatch }, next, action) {
|
function _conferenceJoined({ dispatch }, next, action) {
|
||||||
dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.HIDDEN));
|
dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.HIDDEN));
|
||||||
|
dispatch(setJoiningInProgress(false));
|
||||||
|
|
||||||
return next(action);
|
return next(action);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { PersistenceRegistry, ReducerRegistry } from '../base/redux';
|
import { PersistenceRegistry, ReducerRegistry } from '../base/redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
PREJOIN_JOINING_IN_PROGRESS,
|
||||||
SET_DEVICE_STATUS,
|
SET_DEVICE_STATUS,
|
||||||
SET_DIALOUT_COUNTRY,
|
SET_DIALOUT_COUNTRY,
|
||||||
SET_DIALOUT_NUMBER,
|
SET_DIALOUT_NUMBER,
|
||||||
|
@ -53,7 +54,11 @@ PersistenceRegistry.register(STORE_NAME, {
|
||||||
ReducerRegistry.register(
|
ReducerRegistry.register(
|
||||||
'features/prejoin', (state = DEFAULT_STATE, action) => {
|
'features/prejoin', (state = DEFAULT_STATE, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case PREJOIN_JOINING_IN_PROGRESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
joiningInProgress: action.value
|
||||||
|
};
|
||||||
case SET_SKIP_PREJOIN: {
|
case SET_SKIP_PREJOIN: {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|
Loading…
Reference in New Issue