From 4f169988a30c2ca933ede0143542b7fc96c5fe31 Mon Sep 17 00:00:00 2001 From: Vlad Piersec Date: Fri, 19 Jun 2020 10:03:26 +0300 Subject: [PATCH] fix(prejoin): Store prejoin tracks in 'features/base/tracks' --- conference.js | 29 +-- css/_settings-button.scss | 4 + react/features/base/media/functions.js | 11 ++ react/features/base/settings/functions.web.js | 20 +- react/features/base/tracks/actions.js | 98 ++++++---- react/features/base/tracks/functions.js | 12 ++ react/features/base/tracks/middleware.js | 14 +- react/features/prejoin/actionTypes.js | 25 --- react/features/prejoin/actions.js | 179 ++---------------- react/features/prejoin/components/Prejoin.js | 10 +- react/features/prejoin/functions.js | 139 +------------- react/features/prejoin/middleware.js | 53 +----- react/features/prejoin/reducer.js | 64 +------ .../toolbox/components/AudioMuteButton.js | 19 +- .../toolbox/components/VideoMuteButton.js | 17 +- .../components/web/VideoSettingsButton.js | 23 ++- react/features/toolbox/functions.web.js | 20 +- 17 files changed, 178 insertions(+), 559 deletions(-) diff --git a/conference.js b/conference.js index 4e266bdc3..cfcd0479f 100644 --- a/conference.js +++ b/conference.js @@ -118,10 +118,7 @@ import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay import { suspendDetected } from './react/features/power-monitor'; import { initPrejoin, - isPrejoinPageEnabled, - isPrejoinPageVisible, - replacePrejoinAudioTrack, - replacePrejoinVideoTrack + isPrejoinPageEnabled } from './react/features/prejoin'; import { createRnnoiseProcessorPromise } from './react/features/rnnoise'; import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture'; @@ -1409,18 +1406,6 @@ export default { useVideoStream(newStream) { return new Promise((resolve, reject) => { _replaceLocalVideoTrackQueue.enqueue(onFinish => { - /** - * When the prejoin page is visible there is no conference object - * created. The prejoin tracks are managed separately, - * so this updates the prejoin video track. - */ - if (isPrejoinPageVisible(APP.store.getState())) { - return APP.store.dispatch(replacePrejoinVideoTrack(newStream)) - .then(resolve) - .catch(reject) - .then(onFinish); - } - APP.store.dispatch( replaceLocalTrack(this.localVideo, newStream, room)) .then(() => { @@ -1474,18 +1459,6 @@ export default { useAudioStream(newStream) { return new Promise((resolve, reject) => { _replaceLocalAudioTrackQueue.enqueue(onFinish => { - /** - * When the prejoin page is visible there is no conference object - * created. The prejoin tracks are managed separately, - * so this updates the prejoin audio stream. - */ - if (isPrejoinPageVisible(APP.store.getState())) { - return APP.store.dispatch(replacePrejoinAudioTrack(newStream)) - .then(resolve) - .catch(reject) - .then(onFinish); - } - APP.store.dispatch( replaceLocalTrack(this.localAudio, newStream, room)) .then(() => { diff --git a/css/_settings-button.scss b/css/_settings-button.scss index 46b776a99..082b2ca8e 100644 --- a/css/_settings-button.scss +++ b/css/_settings-button.scss @@ -35,6 +35,10 @@ cursor: initial; color: #fff; background-color: #a4b8d1; + + &:hover { + background-color: #a4b8d1; + } } svg { diff --git a/react/features/base/media/functions.js b/react/features/base/media/functions.js index e1e684768..ceeb39735 100644 --- a/react/features/base/media/functions.js +++ b/react/features/base/media/functions.js @@ -4,6 +4,17 @@ import { toState } from '../redux'; import { VIDEO_MUTISM_AUTHORITY } from './constants'; +/** + * Determines whether audio is currently muted. + * + * @param {Function|Object} stateful - The redux store, state, or + * {@code getState} function. + * @returns {boolean} + */ +export function isAudioMuted(stateful: Function | Object) { + return Boolean(toState(stateful)['features/base/media'].audio.muted); +} + /** * Determines whether video is currently muted by the audio-only authority. * diff --git a/react/features/base/settings/functions.web.js b/react/features/base/settings/functions.web.js index 0ca12e127..da8668a72 100644 --- a/react/features/base/settings/functions.web.js +++ b/react/features/base/settings/functions.web.js @@ -9,7 +9,7 @@ export * from './functions.any'; * @returns {void} */ export function getCurrentCameraDeviceId(state: Object) { - return state['features/base/settings'].cameraDeviceId; + return getDeviceIdByType(state, 'isVideoTrack'); } /** @@ -19,7 +19,7 @@ export function getCurrentCameraDeviceId(state: Object) { * @returns {void} */ export function getCurrentMicDeviceId(state: Object) { - return state['features/base/settings'].micDeviceId; + return getDeviceIdByType(state, 'isAudioTrack'); } /** @@ -32,6 +32,22 @@ export function getCurrentOutputDeviceId(state: Object) { return state['features/base/settings'].audioOutputDeviceId; } +/** + * Returns the deviceId for the corresponding local track type. + * + * @param {Object} state - The state of the application. + * @param {string} isType - Can be 'isVideoTrack' | 'isAudioTrack'. + * @returns {string} + */ +function getDeviceIdByType(state: Object, isType: string) { + const [ deviceId ] = state['features/base/tracks'] + .map(t => t.jitsiTrack) + .filter(t => t && t.isLocal() && t[isType]()) + .map(t => t.getDeviceId()); + + return deviceId || ''; +} + /** * Returns the saved display name. * diff --git a/react/features/base/tracks/actions.js b/react/features/base/tracks/actions.js index 044003cdb..9e8a8a788 100644 --- a/react/features/base/tracks/actions.js +++ b/react/features/base/tracks/actions.js @@ -267,56 +267,70 @@ export function toggleScreensharing() { * @returns {Function} */ export function replaceLocalTrack(oldTrack, newTrack, conference) { - return (dispatch, getState) => { + return async (dispatch, getState) => { conference // eslint-disable-next-line no-param-reassign || (conference = getState()['features/base/conference'].conference); - return conference.replaceTrack(oldTrack, newTrack) + if (conference) { + await conference.replaceTrack(oldTrack, newTrack); + } + + return dispatch(replaceStoredTracks(oldTrack, newTrack)); + }; +} + +/** + * Replaces a stored track with another. + * + * @param {JitsiLocalTrack|null} oldTrack - The track to dispose. + * @param {JitsiLocalTrack|null} newTrack - The track to use instead. + * @returns {Function} + */ +function replaceStoredTracks(oldTrack, newTrack) { + return dispatch => { + // We call dispose after doing the replace because dispose will + // try and do a new o/a after the track removes itself. Doing it + // after means the JitsiLocalTrack.conference is already + // cleared, so it won't try and do the o/a. + const disposePromise + = oldTrack + ? dispatch(_disposeAndRemoveTracks([ oldTrack ])) + : Promise.resolve(); + + return disposePromise .then(() => { - // We call dispose after doing the replace because dispose will - // try and do a new o/a after the track removes itself. Doing it - // after means the JitsiLocalTrack.conference is already - // cleared, so it won't try and do the o/a. - const disposePromise - = oldTrack - ? dispatch(_disposeAndRemoveTracks([ oldTrack ])) - : Promise.resolve(); + if (newTrack) { + // The mute state of the new track should be + // reflected in the app's mute state. For example, + // if the app is currently muted and changing to a + // new track that is not muted, the app's mute + // state should be falsey. As such, emit a mute + // event here to set up the app to reflect the + // track's mute state. If this is not done, the + // current mute state of the app will be reflected + // on the track, not vice-versa. + const setMuted + = newTrack.isVideoTrack() + ? setVideoMuted + : setAudioMuted; + const isMuted = newTrack.isMuted(); - return disposePromise - .then(() => { - if (newTrack) { - // The mute state of the new track should be - // reflected in the app's mute state. For example, - // if the app is currently muted and changing to a - // new track that is not muted, the app's mute - // state should be falsey. As such, emit a mute - // event here to set up the app to reflect the - // track's mute state. If this is not done, the - // current mute state of the app will be reflected - // on the track, not vice-versa. - const setMuted - = newTrack.isVideoTrack() - ? setVideoMuted - : setAudioMuted; - const isMuted = newTrack.isMuted(); + sendAnalytics(createTrackMutedEvent( + newTrack.getType(), + 'track.replaced', + isMuted)); + logger.log(`Replace ${newTrack.getType()} track - ${ + isMuted ? 'muted' : 'unmuted'}`); - sendAnalytics(createTrackMutedEvent( - newTrack.getType(), - 'track.replaced', - isMuted)); - logger.log(`Replace ${newTrack.getType()} track - ${ - isMuted ? 'muted' : 'unmuted'}`); - - return dispatch(setMuted(isMuted)); - } - }) - .then(() => { - if (newTrack) { - return dispatch(_addTracks([ newTrack ])); - } - }); + return dispatch(setMuted(isMuted)); + } + }) + .then(() => { + if (newTrack) { + return dispatch(_addTracks([ newTrack ])); + } }); }; } diff --git a/react/features/base/tracks/functions.js b/react/features/base/tracks/functions.js index 0810da23c..b7219169c 100644 --- a/react/features/base/tracks/functions.js +++ b/react/features/base/tracks/functions.js @@ -200,6 +200,18 @@ export function getLocalVideoType(tracks) { return presenterTrack ? MEDIA_TYPE.PRESENTER : MEDIA_TYPE.VIDEO; } +/** + * Returns the stored local video track. + * + * @param {Object} state - The redux state. + * @returns {Object} + */ +export function getLocalJitsiVideoTrack(state) { + const track = getLocalVideoTrack(state['features/base/tracks']); + + return track?.jitsiTrack; +} + /** * Returns track of specified media type for specified participant id. * diff --git a/react/features/base/tracks/middleware.js b/react/features/base/tracks/middleware.js index e3a64949a..65a975f8d 100644 --- a/react/features/base/tracks/middleware.js +++ b/react/features/base/tracks/middleware.js @@ -2,6 +2,8 @@ import UIEvents from '../../../../service/UI/UIEvents'; import { hideNotification } from '../../notifications'; +import { isPrejoinPageVisible } from '../../prejoin/functions'; +import { getAvailableDevices } from '../devices/actions'; import { CAMERA_FACING_MODE, MEDIA_TYPE, @@ -15,6 +17,7 @@ import { import { MiddlewareRegistry } from '../redux'; import { + TRACK_ADDED, TOGGLE_SCREENSHARING, TRACK_NO_DATA_FROM_SOURCE, TRACK_REMOVED, @@ -44,6 +47,15 @@ declare var APP: Object; */ MiddlewareRegistry.register(store => next => action => { switch (action.type) { + case TRACK_ADDED: { + // The devices list needs to be refreshed when no initial video permissions + // were granted and a local video track is added by umuting the video. + if (action.track.local) { + store.dispatch(getAvailableDevices()); + } + + break; + } case TRACK_NO_DATA_FROM_SOURCE: { const result = next(action); @@ -281,7 +293,7 @@ function _setMuted(store, { ensureTrack, authority, muted }, mediaType: MEDIA_TY // anymore, unless it is muted by audioOnly. jitsiTrack && (jitsiTrack.videoType !== 'desktop' || isAudioOnly) && setTrackMuted(jitsiTrack, muted); - } else if (!muted && ensureTrack && typeof APP === 'undefined') { + } else if (!muted && ensureTrack && (typeof APP === 'undefined' || isPrejoinPageVisible(store.getState()))) { // FIXME: This only runs on mobile now because web has its own way of // creating local tracks. Adjust the check once they are unified. store.dispatch(createLocalTracksA({ devices: [ mediaType ] })); diff --git a/react/features/prejoin/actionTypes.js b/react/features/prejoin/actionTypes.js index b98cb5501..8ab5f4632 100644 --- a/react/features/prejoin/actionTypes.js +++ b/react/features/prejoin/actionTypes.js @@ -1,18 +1,3 @@ -/** - * Action type to add a video track to the store. - */ -export const ADD_PREJOIN_VIDEO_TRACK = 'ADD_PREJOIN_VIDEO_TRACK'; - -/** - * Action type to add an audio track to the store. - */ -export const ADD_PREJOIN_AUDIO_TRACK = 'ADD_PREJOIN_AUDIO_TRACK'; - -/** - * Action type to add a content sharing track to the store. - */ -export const ADD_PREJOIN_CONTENT_SHARING_TRACK - = 'ADD_PREJOIN_CONTENT_SHARING_TRACK'; /** * Action type to signal the start of the conference. @@ -68,13 +53,3 @@ export const SET_PREJOIN_DEVICE_ERRORS = 'SET_PREJOIN_DEVICE_ERRORS'; * Action type to set the visibility of the prejoin page. */ export const SET_PREJOIN_PAGE_VISIBILITY = 'SET_PREJOIN_PAGE_VISIBILITY'; - -/** - * Action type to mute/unmute the video while on prejoin page. - */ -export const SET_PREJOIN_VIDEO_DISABLED = 'SET_PREJOIN_VIDEO_DISABLED'; - -/** - * Action type to mute/unmute the video while on prejoin page. - */ -export const SET_PREJOIN_VIDEO_MUTED = 'SET_PREJOIN_VIDEO_MUTED'; diff --git a/react/features/prejoin/actions.js b/react/features/prejoin/actions.js index ff790dbc9..2c0129f84 100644 --- a/react/features/prejoin/actions.js +++ b/react/features/prejoin/actions.js @@ -5,14 +5,17 @@ import uuid from 'uuid'; import { getRoomName } from '../base/conference'; import { getDialOutStatusUrl, getDialOutUrl } from '../base/config/functions'; import { createLocalTrack } from '../base/lib-jitsi-meet'; +import { + getLocalAudioTrack, + getLocalVideoTrack, + trackAdded, + replaceLocalTrack +} from '../base/tracks'; import { openURLInBrowser } from '../base/util'; import { executeDialOutRequest, executeDialOutStatusRequest, getDialInfoPageURL } from '../invite/functions'; import { showErrorNotification } from '../notifications'; import { - ADD_PREJOIN_AUDIO_TRACK, - ADD_PREJOIN_CONTENT_SHARING_TRACK, - ADD_PREJOIN_VIDEO_TRACK, PREJOIN_START_CONFERENCE, SET_DEVICE_STATUS, SET_DIALOUT_COUNTRY, @@ -20,19 +23,13 @@ import { SET_DIALOUT_STATUS, SET_SKIP_PREJOIN, SET_JOIN_BY_PHONE_DIALOG_VISIBLITY, - SET_PREJOIN_AUDIO_DISABLED, - SET_PREJOIN_AUDIO_MUTED, SET_PREJOIN_DEVICE_ERRORS, - SET_PREJOIN_PAGE_VISIBILITY, - SET_PREJOIN_VIDEO_DISABLED, - SET_PREJOIN_VIDEO_MUTED + SET_PREJOIN_PAGE_VISIBILITY } from './actionTypes'; import { getFullDialOutNumber, - getAudioTrack, getDialOutConferenceUrl, getDialOutCountry, - getVideoTrack, isJoinByPhoneDialogVisible } from './functions'; import logger from './logger'; @@ -60,45 +57,6 @@ const STATUS_REQ_FREQUENCY = 2000; */ const STATUS_REQ_CAP = 45; -/** - * Action used to add an audio track to the store. - * - * @param {Object} value - The track to be added. - * @returns {Object} - */ -export function addPrejoinAudioTrack(value: Object) { - return { - type: ADD_PREJOIN_AUDIO_TRACK, - value - }; -} - -/** - * Action used to add a video track to the store. - * - * @param {Object} value - The track to be added. - * @returns {Object} - */ -export function addPrejoinVideoTrack(value: Object) { - return { - type: ADD_PREJOIN_VIDEO_TRACK, - value - }; -} - -/** - * Action used to add a content sharing track to the store. - * - * @param {Object} value - The track to be added. - * @returns {Object} - */ -export function addPrejoinContentSharingTrack(value: Object) { - return { - type: ADD_PREJOIN_CONTENT_SHARING_TRACK, - value - }; -} - /** * Polls for status change after dial out. * Changes dialog message based on response, closes the dialog if there is an error, @@ -232,27 +190,10 @@ export function dialOut(onSuccess: Function, onFail: Function) { */ export function initPrejoin(tracks: Object[], errors: Object) { return async function(dispatch: Function) { - const audioTrack = tracks.find(t => t.isAudioTrack()); - const videoTrack = tracks.find(t => t.isVideoTrack()); - dispatch(setPrejoinDeviceErrors(errors)); - if (audioTrack) { - dispatch(addPrejoinAudioTrack(audioTrack)); - } else { - dispatch(setAudioDisabled()); - } - if (videoTrack) { - if (videoTrack.videoType === 'desktop') { - dispatch(addPrejoinContentSharingTrack(videoTrack)); - dispatch(setPrejoinVideoDisabled(true)); - } else { - dispatch(addPrejoinVideoTrack(videoTrack)); - } - } else { - dispatch(setPrejoinVideoDisabled(true)); - } + tracks.forEach(track => dispatch(trackAdded(track))); }; } @@ -275,12 +216,12 @@ export function joinConference() { */ export function joinConferenceWithoutAudio() { return async function(dispatch: Function, getState: Function) { - const audioTrack = getAudioTrack(getState()); + const tracks = getState()['features/base/tracks']; + const audioTrack = getLocalAudioTrack(tracks)?.jitsiTrack; if (audioTrack) { - await dispatch(replacePrejoinAudioTrack(null)); + await dispatch(replaceLocalTrack(audioTrack, null)); } - dispatch(setAudioDisabled()); dispatch(joinConference()); }; } @@ -301,21 +242,6 @@ export function openDialInPage() { }; } -/** - * Replaces the existing audio track with a new one. - * - * @param {Object} track - The new track. - * @returns {Function} - */ -export function replacePrejoinAudioTrack(track: Object) { - return async (dispatch: Function, getState: Function) => { - const oldTrack = getAudioTrack(getState()); - - oldTrack && await oldTrack.dispose(); - dispatch(addPrejoinAudioTrack(track)); - }; -} - /** * Creates a new audio track based on a device id and replaces the current one. * @@ -323,11 +249,13 @@ export function replacePrejoinAudioTrack(track: Object) { * @returns {Function} */ export function replaceAudioTrackById(deviceId: string) { - return async (dispatch: Function) => { + return async (dispatch: Function, getState: Function) => { try { - const track = await createLocalTrack('audio', deviceId); + const tracks = getState()['features/base/tracks']; + const newTrack = await createLocalTrack('audio', deviceId); + const oldTrack = getLocalAudioTrack(tracks)?.jitsiTrack; - dispatch(replacePrejoinAudioTrack(track)); + dispatch(replaceLocalTrack(oldTrack, newTrack)); } catch (err) { dispatch(setDeviceStatusWarning('prejoin.audioTrackError')); logger.log('Error replacing audio track', err); @@ -335,21 +263,6 @@ export function replaceAudioTrackById(deviceId: string) { }; } -/** - * Replaces the existing video track with a new one. - * - * @param {Object} track - The new track. - * @returns {Function} - */ -export function replacePrejoinVideoTrack(track: Object) { - return async (dispatch: Function, getState: Function) => { - const oldTrack = getVideoTrack(getState()); - - oldTrack && await oldTrack.dispose(); - dispatch(addPrejoinVideoTrack(track)); - }; -} - /** * Creates a new video track based on a device id and replaces the current one. * @@ -357,11 +270,13 @@ export function replacePrejoinVideoTrack(track: Object) { * @returns {Function} */ export function replaceVideoTrackById(deviceId: Object) { - return async (dispatch: Function) => { + return async (dispatch: Function, getState: Function) => { try { - const track = await createLocalTrack('video', deviceId); + const tracks = getState()['features/base/tracks']; + const newTrack = await createLocalTrack('video', deviceId); + const oldTrack = getLocalVideoTrack(tracks)?.jitsiTrack; - dispatch(replacePrejoinVideoTrack(track)); + dispatch(replaceLocalTrack(oldTrack, newTrack)); } catch (err) { dispatch(setDeviceStatusWarning('prejoin.videoTrackError')); logger.log('Error replacing video track', err); @@ -369,58 +284,6 @@ export function replaceVideoTrackById(deviceId: Object) { }; } - -/** - * Action used to mark audio muted. - * - * @param {boolean} value - True for muted. - * @returns {Object} - */ -export function setPrejoinAudioMuted(value: boolean) { - return { - type: SET_PREJOIN_AUDIO_MUTED, - value - }; -} - -/** - * Action used to mark video disabled. - * - * @param {boolean} value - True for muted. - * @returns {Object} - */ -export function setPrejoinVideoDisabled(value: boolean) { - return { - type: SET_PREJOIN_VIDEO_DISABLED, - value - }; -} - - -/** - * Action used to mark video muted. - * - * @param {boolean} value - True for muted. - * @returns {Object} - */ -export function setPrejoinVideoMuted(value: boolean) { - return { - type: SET_PREJOIN_VIDEO_MUTED, - value - }; -} - -/** - * Action used to mark audio as disabled. - * - * @returns {Object} - */ -export function setAudioDisabled() { - return { - type: SET_PREJOIN_AUDIO_DISABLED - }; -} - /** * Sets the device status as OK with the corresponding text. * diff --git a/react/features/prejoin/components/Prejoin.js b/react/features/prejoin/components/Prejoin.js index a2cd7767e..b0fefe6fa 100644 --- a/react/features/prejoin/components/Prejoin.js +++ b/react/features/prejoin/components/Prejoin.js @@ -6,9 +6,11 @@ import React, { Component } from 'react'; import { getRoomName } from '../../base/conference'; import { translate } from '../../base/i18n'; import { Icon, IconPhone, IconVolumeOff } from '../../base/icons'; +import { isVideoMutedByUser } from '../../base/media'; import { ActionButton, InputField, PreMeetingScreen } from '../../base/premeeting'; import { connect } from '../../base/redux'; import { getDisplayName, updateSettings } from '../../base/settings'; +import { getLocalJitsiVideoTrack } from '../../base/tracks'; import { joinConference as joinConferenceAction, joinConferenceWithoutAudio as joinConferenceWithoutAudioAction, @@ -16,11 +18,9 @@ import { setJoinByPhoneDialogVisiblity as setJoinByPhoneDialogVisiblityAction } from '../actions'; import { - getActiveVideoTrack, isJoinByPhoneButtonVisible, isDeviceStatusVisible, - isJoinByPhoneDialogVisible, - isPrejoinVideoMuted + isJoinByPhoneDialogVisible } from '../functions'; import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog'; @@ -315,8 +315,8 @@ function mapStateToProps(state): Object { roomName: getRoomName(state), showDialog: isJoinByPhoneDialogVisible(state), hasJoinByPhoneButton: isJoinByPhoneButtonVisible(state), - showCameraPreview: !isPrejoinVideoMuted(state), - videoTrack: getActiveVideoTrack(state) + showCameraPreview: !isVideoMutedByUser(state), + videoTrack: getLocalJitsiVideoTrack(state) }; } diff --git a/react/features/prejoin/functions.js b/react/features/prejoin/functions.js index f2a8c9bcf..75863939c 100644 --- a/react/features/prejoin/functions.js +++ b/react/features/prejoin/functions.js @@ -2,25 +2,7 @@ import { getRoomName } from '../base/conference'; import { getDialOutStatusUrl, getDialOutUrl } from '../base/config/functions'; - -/** - * Mutes or unmutes a track. - * - * @param {Object} track - The track to be configured. - * @param {boolean} shouldMute - If it should mute or not. - * @returns {Promise} - */ -function applyMuteOptionsToTrack(track, shouldMute) { - if (track.isMuted() === shouldMute) { - return; - } - - if (shouldMute) { - return track.mute(); - } - - return track.unmute(); -} +import { isAudioMuted, isVideoMutedByUser } from '../base/media'; /** * Selector for the visibility of the 'join by phone' button. @@ -39,73 +21,8 @@ export function isJoinByPhoneButtonVisible(state: Object): boolean { * @returns {boolean} */ export function isDeviceStatusVisible(state: Object): boolean { - return !((isAudioDisabled(state) && isPrejoinVideoDisabled(state)) - || (isPrejoinAudioMuted(state) && isPrejoinVideoMuted(state))); -} - -/** - * Selector for getting the active video/content sharing track. - * - * @param {Object} state - The state of the app. - * @returns {boolean} - */ -export function getActiveVideoTrack(state: Object): Object { - const track = getVideoTrack(state) || getContentSharingTrack(state); - - if (track && track.isActive()) { - return track; - } - - return null; -} - -/** - * Returns a list with all the prejoin tracks configured according to - * user's preferences. - * - * @param {Object} state - The state of the app. - * @returns {Promise} - */ -export async function getAllPrejoinConfiguredTracks(state: Object): Promise { - const tracks = []; - const audioTrack = getAudioTrack(state); - const videoTrack = getVideoTrack(state); - const csTrack = getContentSharingTrack(state); - - if (csTrack) { - tracks.push(csTrack); - } else if (videoTrack) { - await applyMuteOptionsToTrack(videoTrack, isPrejoinVideoMuted(state)); - tracks.push(videoTrack); - } - - if (audioTrack) { - await applyMuteOptionsToTrack(audioTrack, isPrejoinAudioMuted(state)); - isPrejoinAudioMuted(state) && audioTrack.mute(); - tracks.push(audioTrack); - } - - return tracks; -} - -/** - * Selector for getting the prejoin audio track. - * - * @param {Object} state - The state of the app. - * @returns {Object} - */ -export function getAudioTrack(state: Object): Object { - return state['features/prejoin']?.audioTrack; -} - -/** - * Selector for getting the prejoin content sharing track. - * - * @param {Object} state - The state of the app. - * @returns {Object} - */ -export function getContentSharingTrack(state: Object): Object { - return state['features/prejoin']?.contentSharingTrack; + return !(isAudioMuted(state) && isVideoMutedByUser(state)) + && !state['features/base/config'].startSilent; } /** @@ -181,36 +98,6 @@ export function getFullDialOutNumber(state: Object): string { return `+${country.dialCode}${dialOutNumber}`; } -/** - * Selector for getting the prejoin video track. - * - * @param {Object} state - The state of the app. - * @returns {Object} - */ -export function getVideoTrack(state: Object): Object { - return state['features/prejoin']?.videoTrack; -} - -/** - * Selector for getting the mute status of the prejoin audio. - * - * @param {Object} state - The state of the app. - * @returns {boolean} - */ -export function isPrejoinAudioMuted(state: Object): boolean { - return state['features/prejoin']?.audioMuted; -} - -/** - * Selector for getting the mute status of the prejoin video. - * - * @param {Object} state - The state of the app. - * @returns {boolean} - */ -export function isPrejoinVideoMuted(state: Object): boolean { - return state['features/prejoin']?.videoMuted; -} - /** * Selector for getting the error if any while creating streams. * @@ -221,26 +108,6 @@ export function getRawError(state: Object): string { return state['features/prejoin']?.rawError; } -/** - * Selector for getting state of the prejoin audio. - * - * @param {Object} state - The state of the app. - * @returns {boolean} - */ -export function isAudioDisabled(state: Object): Object { - return state['features/prejoin']?.audioDisabled; -} - -/** - * Selector for getting state of the prejoin video. - * - * @param {Object} state - The state of the app. - * @returns {boolean} - */ -export function isPrejoinVideoDisabled(state: Object): Object { - return state['features/prejoin']?.videoDisabled; -} - /** * Selector for getting the visiblity state for the 'JoinByPhoneDialog'. * diff --git a/react/features/prejoin/middleware.js b/react/features/prejoin/middleware.js index 96daede05..2b983b15e 100644 --- a/react/features/prejoin/middleware.js +++ b/react/features/prejoin/middleware.js @@ -1,16 +1,9 @@ // @flow -import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../base/media'; import { MiddlewareRegistry } from '../base/redux'; import { updateSettings } from '../base/settings'; -import { - ADD_PREJOIN_AUDIO_TRACK, - ADD_PREJOIN_VIDEO_TRACK, - PREJOIN_START_CONFERENCE -} from './actionTypes'; -import { setPrejoinAudioMuted, setPrejoinVideoMuted } from './actions'; -import { getAllPrejoinConfiguredTracks } from './functions'; +import { PREJOIN_START_CONFERENCE } from './actionTypes'; declare var APP: Object; @@ -22,62 +15,22 @@ declare var APP: Object; */ MiddlewareRegistry.register(store => next => async action => { switch (action.type) { - case ADD_PREJOIN_AUDIO_TRACK: { - const { value: audioTrack } = action; - - if (audioTrack) { - store.dispatch( - updateSettings({ - micDeviceId: audioTrack.getDeviceId() - }), - ); - } - - break; - } - - case ADD_PREJOIN_VIDEO_TRACK: { - const { value: videoTrack } = action; - - if (videoTrack) { - store.dispatch( - updateSettings({ - cameraDeviceId: videoTrack.getDeviceId() - }), - ); - } - - break; - } - case PREJOIN_START_CONFERENCE: { const { getState, dispatch } = store; const state = getState(); const { userSelectedSkipPrejoin } = state['features/prejoin']; + const tracks = state['features/base/tracks']; userSelectedSkipPrejoin && dispatch(updateSettings({ userSelectedSkipPrejoin })); - - const tracks = await getAllPrejoinConfiguredTracks(state); - - APP.conference.prejoinStart(tracks); + APP.conference.prejoinStart(tracks.map(t => t.jitsiTrack)); break; } - - case SET_AUDIO_MUTED: { - store.dispatch(setPrejoinAudioMuted(Boolean(action.muted))); - break; } - case SET_VIDEO_MUTED: { - store.dispatch(setPrejoinVideoMuted(Boolean(action.muted))); - break; - } - - } return next(action); }); diff --git a/react/features/prejoin/reducer.js b/react/features/prejoin/reducer.js index d574d130d..f00860657 100644 --- a/react/features/prejoin/reducer.js +++ b/react/features/prejoin/reducer.js @@ -1,28 +1,17 @@ import { ReducerRegistry } from '../base/redux'; import { - ADD_PREJOIN_AUDIO_TRACK, - ADD_PREJOIN_CONTENT_SHARING_TRACK, - ADD_PREJOIN_VIDEO_TRACK, SET_DEVICE_STATUS, SET_DIALOUT_NUMBER, SET_DIALOUT_COUNTRY, SET_DIALOUT_STATUS, SET_JOIN_BY_PHONE_DIALOG_VISIBLITY, SET_SKIP_PREJOIN, - SET_PREJOIN_AUDIO_DISABLED, - SET_PREJOIN_AUDIO_MUTED, SET_PREJOIN_DEVICE_ERRORS, - SET_PREJOIN_PAGE_VISIBILITY, - SET_PREJOIN_VIDEO_DISABLED, - SET_PREJOIN_VIDEO_MUTED + SET_PREJOIN_PAGE_VISIBILITY } from './actionTypes'; const DEFAULT_STATE = { - audioDisabled: false, - audioMuted: false, - audioTrack: null, - contentSharingTrack: null, country: '', deviceStatusText: 'prejoin.configuringDevices', deviceStatusType: 'ok', @@ -37,10 +26,7 @@ const DEFAULT_STATE = { rawError: '', showPrejoin: true, showJoinByPhoneDialog: false, - userSelectedSkipPrejoin: false, - videoTrack: null, - videoDisabled: false, - videoMuted: false + userSelectedSkipPrejoin: false }; /** @@ -49,26 +35,6 @@ const DEFAULT_STATE = { ReducerRegistry.register( 'features/prejoin', (state = DEFAULT_STATE, action) => { switch (action.type) { - case ADD_PREJOIN_AUDIO_TRACK: { - return { - ...state, - audioTrack: action.value - }; - } - - case ADD_PREJOIN_CONTENT_SHARING_TRACK: { - return { - ...state, - contentSharingTrack: action.value - }; - } - - case ADD_PREJOIN_VIDEO_TRACK: { - return { - ...state, - videoTrack: action.value - }; - } case SET_SKIP_PREJOIN: { return { @@ -83,25 +49,6 @@ ReducerRegistry.register( showPrejoin: action.value }; - case SET_PREJOIN_VIDEO_DISABLED: { - return { - ...state, - videoDisabled: action.value - }; - } - - case SET_PREJOIN_VIDEO_MUTED: - return { - ...state, - videoMuted: action.value - }; - - case SET_PREJOIN_AUDIO_MUTED: - return { - ...state, - audioMuted: action.value - }; - case SET_PREJOIN_DEVICE_ERRORS: { const status = getStatusFromErrors(action.value); @@ -119,13 +66,6 @@ ReducerRegistry.register( }; } - case SET_PREJOIN_AUDIO_DISABLED: { - return { - ...state, - audioDisabled: true - }; - } - case SET_DIALOUT_NUMBER: { return { ...state, diff --git a/react/features/toolbox/components/AudioMuteButton.js b/react/features/toolbox/components/AudioMuteButton.js index fb7a10bac..58ec049e5 100644 --- a/react/features/toolbox/components/AudioMuteButton.js +++ b/react/features/toolbox/components/AudioMuteButton.js @@ -12,11 +12,6 @@ import { connect } from '../../base/redux'; import { AbstractAudioMuteButton } from '../../base/toolbox'; import type { AbstractButtonProps } from '../../base/toolbox'; import { isLocalTrackMuted } from '../../base/tracks'; -import { - isPrejoinAudioMuted, - isAudioDisabled, - isPrejoinPageVisible -} from '../../prejoin/functions'; import { muteLocal } from '../../remote-video-menu/actions'; declare var APP: Object; @@ -154,18 +149,8 @@ class AudioMuteButton extends AbstractAudioMuteButton { * }} */ function _mapStateToProps(state): Object { - let _audioMuted; - let _disabled; - - if (isPrejoinPageVisible(state)) { - _audioMuted = isPrejoinAudioMuted(state); - _disabled = state['features/base/config'].startSilent; - } else { - const tracks = state['features/base/tracks']; - - _audioMuted = isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO); - _disabled = state['features/base/config'].startSilent || isAudioDisabled(state); - } + const _audioMuted = isLocalTrackMuted(state['features/base/tracks'], MEDIA_TYPE.AUDIO); + const _disabled = state['features/base/config'].startSilent; return { _audioMuted, diff --git a/react/features/toolbox/components/VideoMuteButton.js b/react/features/toolbox/components/VideoMuteButton.js index e6b33fa09..1f119531a 100644 --- a/react/features/toolbox/components/VideoMuteButton.js +++ b/react/features/toolbox/components/VideoMuteButton.js @@ -9,6 +9,7 @@ import { sendAnalytics } from '../../analytics'; import { setAudioOnly } from '../../base/audio-only'; +import { hasAvailableDevices } from '../../base/devices'; import { translate } from '../../base/i18n'; import { VIDEO_MUTISM_AUTHORITY, @@ -18,11 +19,6 @@ import { connect } from '../../base/redux'; import { AbstractVideoMuteButton } from '../../base/toolbox'; import type { AbstractButtonProps } from '../../base/toolbox'; import { getLocalVideoType, isLocalVideoTrackMuted } from '../../base/tracks'; -import { - isPrejoinPageVisible, - isPrejoinVideoDisabled, - isPrejoinVideoMuted -} from '../../prejoin/functions'; declare var APP: Object; @@ -191,19 +187,12 @@ class VideoMuteButton extends AbstractVideoMuteButton { function _mapStateToProps(state): Object { const { enabled: audioOnly } = state['features/base/audio-only']; const tracks = state['features/base/tracks']; - let _videoMuted = isLocalVideoTrackMuted(tracks); - let _videoDisabled = false; - - if (isPrejoinPageVisible(state)) { - _videoMuted = isPrejoinVideoMuted(state); - _videoDisabled = isPrejoinVideoDisabled(state); - } return { _audioOnly: Boolean(audioOnly), - _videoDisabled, + _videoDisabled: !hasAvailableDevices(state, 'videoInput'), _videoMediaType: getLocalVideoType(tracks), - _videoMuted + _videoMuted: isLocalVideoTrackMuted(tracks) }; } diff --git a/react/features/toolbox/components/web/VideoSettingsButton.js b/react/features/toolbox/components/web/VideoSettingsButton.js index bedd21223..8e3a98f0f 100644 --- a/react/features/toolbox/components/web/VideoSettingsButton.js +++ b/react/features/toolbox/components/web/VideoSettingsButton.js @@ -6,6 +6,7 @@ import { IconArrowDown } from '../../../base/icons'; import JitsiMeetJS from '../../../base/lib-jitsi-meet/_'; import { connect } from '../../../base/redux'; import { ToolboxButtonWithIcon } from '../../../base/toolbox'; +import { getLocalJitsiVideoTrack } from '../../../base/tracks'; import { getMediaPermissionPromptVisibility } from '../../../overlay'; import { toggleVideoSettings, VideoSettingsPopup } from '../../../settings'; import { isVideoSettingsButtonDisabled } from '../../functions'; @@ -24,6 +25,11 @@ type Props = { */ permissionPromptVisibility: boolean, + /** + * Whether there is a video track or not. + */ + hasVideoTrack: boolean, + /** * If the button should be disabled */ @@ -66,6 +72,17 @@ class VideoSettingsButton extends Component { }; } + /** + * Returns true if the settings icon is disabled. + * + * @returns {boolean} + */ + _isIconDisabled() { + const { hasVideoTrack, isDisabled } = this.props; + + return (!this.state.hasPermissions || isDisabled) && !hasVideoTrack; + } + /** * Updates device permissions. * @@ -116,14 +133,13 @@ class VideoSettingsButton extends Component { * @inheritdoc */ render() { - const { isDisabled, onVideoOptionsClick, visible } = this.props; - const iconDisabled = !this.state.hasPermissions || isDisabled; + const { onVideoOptionsClick, visible } = this.props; return visible ? ( @@ -140,6 +156,7 @@ class VideoSettingsButton extends Component { */ function mapStateToProps(state) { return { + hasVideoTrack: Boolean(getLocalJitsiVideoTrack(state)), isDisabled: isVideoSettingsButtonDisabled(state), permissionPromptVisibility: getMediaPermissionPromptVisibility(state) }; diff --git a/react/features/toolbox/functions.web.js b/react/features/toolbox/functions.web.js index 62cf32b02..624f82ae3 100644 --- a/react/features/toolbox/functions.web.js +++ b/react/features/toolbox/functions.web.js @@ -1,11 +1,6 @@ // @flow import { hasAvailableDevices } from '../base/devices'; -import { - isAudioDisabled, - isPrejoinPageVisible, - isPrejoinVideoDisabled -} from '../prejoin'; declare var interfaceConfig: Object; @@ -60,12 +55,9 @@ export function isToolboxVisible(state: Object) { * @returns {boolean} */ export function isAudioSettingsButtonDisabled(state: Object) { - const devicesMissing = !hasAvailableDevices(state, 'audioInput') - && !hasAvailableDevices(state, 'audioOutput'); - - return isPrejoinPageVisible(state) - ? devicesMissing || isAudioDisabled(state) - : devicesMissing; + return (!hasAvailableDevices(state, 'audioInput') + && !hasAvailableDevices(state, 'audioOutput')) + || state['features/base/config'].startSilent; } /** @@ -75,9 +67,5 @@ export function isAudioSettingsButtonDisabled(state: Object) { * @returns {boolean} */ export function isVideoSettingsButtonDisabled(state: Object) { - const devicesMissing = !hasAvailableDevices(state, 'videoInput'); - - return isPrejoinPageVisible(state) - ? devicesMissing || isPrejoinVideoDisabled(state) - : devicesMissing; + return !hasAvailableDevices(state, 'videoInput'); }