fix(prejoin): Prevent double joining conference.

This commit is contained in:
Hristo Terezov 2021-09-28 17:50:57 -05:00
parent 0214138863
commit 2a725d2165
5 changed files with 114 additions and 64 deletions

View File

@ -129,7 +129,8 @@ import {
initPrejoin,
isPrejoinPageEnabled,
isPrejoinPageVisible,
makePrecallTest
makePrecallTest,
setJoiningInProgress
} from './react/features/prejoin';
import { disableReceiver, stopReceiver } from './react/features/remote-control';
import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
@ -861,9 +862,15 @@ export default {
_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));
}
},
/**

View File

@ -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.

View File

@ -1,27 +1,30 @@
// @flow
declare var JitsiMeetJS: Object;
declare var APP: Object;
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 { isVideoMutedByUser } from '../base/media';
import { isVideoMutedByUser, MEDIA_TYPE } from '../base/media';
import { updateSettings } from '../base/settings';
import {
createLocalTracksF,
getLocalAudioTrack,
getLocalTracks,
getLocalVideoTrack,
trackAdded,
replaceLocalTrack
} from '../base/tracks';
import { createLocalTracksF } from '../base/tracks/functions';
import { openURLInBrowser } from '../base/util';
import { executeDialOutRequest, executeDialOutStatusRequest, getDialInfoPageURL } from '../invite/functions';
import { showErrorNotification } from '../notifications';
import {
PREJOIN_JOINING_IN_PROGRESS,
PREJOIN_INITIALIZED,
PREJOIN_START_CONFERENCE,
SET_DEVICE_STATUS,
SET_DIALOUT_COUNTRY,
SET_DIALOUT_NUMBER,
SET_DIALOUT_STATUS,
@ -31,9 +34,10 @@ import {
SET_JOIN_BY_PHONE_DIALOG_VISIBLITY,
SET_PRECALL_TEST_RESULTS,
SET_PREJOIN_DEVICE_ERRORS,
SET_PREJOIN_PAGE_VISIBILITY
SET_PREJOIN_PAGE_VISIBILITY,
SET_DEVICE_STATUS
} from './actionTypes';
import { type PREJOIN_SCREEN_STATE } from './constants';
import { type PREJOIN_SCREEN_STATE, PREJOIN_SCREEN_STATES } from './constants';
import {
getFullDialOutNumber,
getDialOutConferenceUrl,
@ -209,15 +213,74 @@ export function initPrejoin(tracks: Object[], errors: Object) {
* Action used to start the conference.
*
* @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}
*/
export function joinConference(options?: Object) {
return {
type: PREJOIN_START_CONFERENCE,
options
export function joinConference(options?: Object, ignoreJoiningInProgress: boolean = false) {
return async function(dispatch: Function, getState: Function) {
if (!ignoreJoiningInProgress) {
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.
*
@ -225,16 +288,28 @@ export function joinConference(options?: Object) {
*/
export function joinConferenceWithoutAudio() {
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;
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({
startSilent: true
}));
}, true));
};
}

View File

@ -1,22 +1,18 @@
// @flow
import { CONFERENCE_JOINED } from '../base/conference';
import { updateConfig } from '../base/config';
import { isIosMobileBrowser } from '../base/environment/utils';
import { MEDIA_TYPE, SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../base/media';
import { CONFERENCE_FAILED, CONFERENCE_JOINED } from '../base/conference';
import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../base/media';
import { MiddlewareRegistry } from '../base/redux';
import { updateSettings } from '../base/settings';
import {
getLocalTracks,
replaceLocalTrack,
TRACK_ADDED,
TRACK_NO_DATA_FROM_SOURCE
} from '../base/tracks';
import { PREJOIN_START_CONFERENCE } from './actionTypes';
import {
setDeviceStatusOk,
setDeviceStatusWarning,
setJoiningInProgress,
setPrejoinPageVisibility
} from './actions';
import { PREJOIN_SCREEN_STATES } from './constants';
@ -32,43 +28,6 @@ declare var APP: Object;
*/
MiddlewareRegistry.register(store => next => async action => {
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: {
if (isPrejoinPageVisible(store.getState())) {
store.dispatch(updateSettings({
@ -110,6 +69,9 @@ MiddlewareRegistry.register(store => next => async action => {
}
break;
}
case CONFERENCE_FAILED:
store.dispatch(setJoiningInProgress(false));
break;
case CONFERENCE_JOINED:
return _conferenceJoined(store, next, action);
}
@ -127,6 +89,7 @@ MiddlewareRegistry.register(store => next => async action => {
*/
function _conferenceJoined({ dispatch }, next, action) {
dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.HIDDEN));
dispatch(setJoiningInProgress(false));
return next(action);
}

View File

@ -1,6 +1,7 @@
import { PersistenceRegistry, ReducerRegistry } from '../base/redux';
import {
PREJOIN_JOINING_IN_PROGRESS,
SET_DEVICE_STATUS,
SET_DIALOUT_COUNTRY,
SET_DIALOUT_NUMBER,
@ -53,7 +54,11 @@ PersistenceRegistry.register(STORE_NAME, {
ReducerRegistry.register(
'features/prejoin', (state = DEFAULT_STATE, action) => {
switch (action.type) {
case PREJOIN_JOINING_IN_PROGRESS:
return {
...state,
joiningInProgress: action.value
};
case SET_SKIP_PREJOIN: {
return {
...state,