ref(TS) Convert base/tracks to TS (#12219)

This commit is contained in:
Robert Pintilii 2022-09-23 10:48:20 +03:00 committed by GitHub
parent fb2cfaa204
commit 3426960d5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 285 additions and 243 deletions

9
globals.d.ts vendored
View File

@ -1,6 +1,13 @@
import { IStore } from "./react/features/app/types";
export {}; export {};
declare global { declare global {
const APP: any; const APP: {
store: IStore;
UI: any;
API: any;
conference: any;
};
const interfaceConfig: any; const interfaceConfig: any;
} }

View File

@ -52,6 +52,7 @@ export interface IJitsiConference {
muteParticipant: Function; muteParticipant: Function;
on: Function; on: Function;
removeTrack: Function; removeTrack: Function;
replaceTrack: Function;
sendCommand: Function; sendCommand: Function;
sendEndpointMessage: Function; sendEndpointMessage: Function;
sessionId: string; sessionId: string;

View File

@ -54,7 +54,9 @@ export const VIDEO_MUTISM_AUTHORITY = {
* *
* @enum {string} * @enum {string}
*/ */
export const VIDEO_TYPE = { export const VIDEO_TYPE: { [key: string]: VideoType; } = {
CAMERA: 'camera', CAMERA: 'camera',
DESKTOP: 'desktop' DESKTOP: 'desktop'
}; };
export type VideoType = 'camera' | 'desktop';

View File

@ -1,25 +1,26 @@
/* global APP */ import { createTrackMutedEvent } from '../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../analytics/functions';
import { import { IStore } from '../../app/types';
createTrackMutedEvent, import { showErrorNotification, showNotification } from '../../notifications/actions';
sendAnalytics import { NOTIFICATION_TIMEOUT, NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
} from '../../analytics'; import { getCurrentConference } from '../conference/functions';
import { NOTIFICATION_TIMEOUT_TYPE, showErrorNotification, showNotification } from '../../notifications'; import { IJitsiConference } from '../conference/reducer';
import { getCurrentConference } from '../conference'; import { getMultipleVideoSendingSupportFeatureFlag } from '../config/functions.any';
import { getMultipleVideoSendingSupportFeatureFlag } from '../config';
import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet'; import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
// eslint-disable-next-line lines-around-comment
// @ts-ignore
import { createLocalTrack } from '../lib-jitsi-meet/functions'; import { createLocalTrack } from '../lib-jitsi-meet/functions';
import { setAudioMuted, setScreenshareMuted, setVideoMuted } from '../media/actions';
import { import {
CAMERA_FACING_MODE, CAMERA_FACING_MODE,
MediaType,
MEDIA_TYPE, MEDIA_TYPE,
setAudioMuted, VideoType,
setScreenshareMuted,
setVideoMuted,
VIDEO_MUTISM_AUTHORITY, VIDEO_MUTISM_AUTHORITY,
VIDEO_TYPE VIDEO_TYPE
} from '../media'; } from '../media/constants';
import { getLocalParticipant } from '../participants'; import { getLocalParticipant } from '../participants/functions';
import { updateSettings } from '../settings'; import { updateSettings } from '../settings/actions';
import { import {
SET_NO_SRC_DATA_NOTIFICATION_UID, SET_NO_SRC_DATA_NOTIFICATION_UID,
@ -43,6 +44,7 @@ import {
getTrackByJitsiTrack getTrackByJitsiTrack
} from './functions'; } from './functions';
import logger from './logger'; import logger from './logger';
import { TrackOptions } from './types';
/** /**
* Add a given local track to the conference. * Add a given local track to the conference.
@ -50,8 +52,8 @@ import logger from './logger';
* @param {JitsiLocalTrack} newTrack - The local track to be added to the conference. * @param {JitsiLocalTrack} newTrack - The local track to be added to the conference.
* @returns {Function} * @returns {Function}
*/ */
export function addLocalTrack(newTrack) { export function addLocalTrack(newTrack: any) {
return async (dispatch, getState) => { return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const conference = getCurrentConference(getState()); const conference = getCurrentConference(getState());
if (conference) { if (conference) {
@ -82,8 +84,8 @@ export function addLocalTrack(newTrack) {
* *
* @returns {Function} * @returns {Function}
*/ */
export function createDesiredLocalTracks(...desiredTypes) { export function createDesiredLocalTracks(...desiredTypes: any) {
return (dispatch, getState) => { return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState(); const state = getState();
dispatch(destroyLocalDesktopTrackIfExists()); dispatch(destroyLocalDesktopTrackIfExists());
@ -118,7 +120,7 @@ export function createDesiredLocalTracks(...desiredTypes) {
// We need to create the desired tracks which are not already available. // We need to create the desired tracks which are not already available.
const createTypes const createTypes
= desiredTypes.filter(type => availableTypes.indexOf(type) === -1); = desiredTypes.filter((type: MediaType) => availableTypes.indexOf(type) === -1);
createTypes.length createTypes.length
&& dispatch(createLocalTracksA({ devices: createTypes })); && dispatch(createLocalTracksA({ devices: createTypes }));
@ -132,8 +134,8 @@ export function createDesiredLocalTracks(...desiredTypes) {
* @param {Object} [options] - For info @see JitsiMeetJS.createLocalTracks. * @param {Object} [options] - For info @see JitsiMeetJS.createLocalTracks.
* @returns {Function} * @returns {Function}
*/ */
export function createLocalTracksA(options = {}) { export function createLocalTracksA(options: TrackOptions = {}) {
return (dispatch, getState) => { return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const devices const devices
= options.devices || [ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ]; = options.devices || [ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ];
const store = { const store = {
@ -154,7 +156,7 @@ export function createLocalTracksA(options = {}) {
for (const device of devices) { for (const device of devices) {
if (getLocalTrack( if (getLocalTrack(
getState()['features/base/tracks'], getState()['features/base/tracks'],
device, device as MediaType,
/* includePending */ true)) { /* includePending */ true)) {
throw new Error(`Local track for ${device} already exists`); throw new Error(`Local track for ${device} already exists`);
} }
@ -170,7 +172,7 @@ export function createLocalTracksA(options = {}) {
}, },
store) store)
.then( .then(
localTracks => { (localTracks: any[]) => {
// Because GUM is called for 1 device (which is actually // Because GUM is called for 1 device (which is actually
// a media type 'audio', 'video', 'screen', etc.) we // a media type 'audio', 'video', 'screen', etc.) we
// should not get more than one JitsiTrack. // should not get more than one JitsiTrack.
@ -184,15 +186,15 @@ export function createLocalTracksA(options = {}) {
if (gumProcess.canceled) { if (gumProcess.canceled) {
return _disposeTracks(localTracks) return _disposeTracks(localTracks)
.then(() => .then(() =>
dispatch(_trackCreateCanceled(device))); dispatch(_trackCreateCanceled(device as MediaType)));
} }
return dispatch(trackAdded(localTracks[0])); return dispatch(trackAdded(localTracks[0]));
}, },
reason => (reason: Error) =>
dispatch( dispatch(
gumProcess.canceled gumProcess.canceled
? _trackCreateCanceled(device) ? _trackCreateCanceled(device as MediaType)
: _onCreateLocalTracksRejected( : _onCreateLocalTracksRejected(
reason, reason,
device))); device)));
@ -230,12 +232,12 @@ export function createLocalTracksA(options = {}) {
*/ */
export function destroyLocalTracks(track = null) { export function destroyLocalTracks(track = null) {
if (track) { if (track) {
return dispatch => { return (dispatch: IStore['dispatch']) => {
dispatch(_disposeAndRemoveTracks([ track ])); dispatch(_disposeAndRemoveTracks([ track ]));
}; };
} }
return (dispatch, getState) => { return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
// First wait until any getUserMedia in progress is settled and then get // First wait until any getUserMedia in progress is settled and then get
// rid of all local tracks. // rid of all local tracks.
_cancelGUMProcesses(getState) _cancelGUMProcesses(getState)
@ -257,7 +259,7 @@ export function destroyLocalTracks(track = null) {
* track: Track * track: Track
* }} * }}
*/ */
export function noDataFromSource(track) { export function noDataFromSource(track: any) {
return { return {
type: TRACK_NO_DATA_FROM_SOURCE, type: TRACK_NO_DATA_FROM_SOURCE,
track track
@ -270,8 +272,8 @@ export function noDataFromSource(track) {
* @param {JitsiLocalTrack} jitsiTrack - The track. * @param {JitsiLocalTrack} jitsiTrack - The track.
* @returns {Function} * @returns {Function}
*/ */
export function showNoDataFromSourceVideoError(jitsiTrack) { export function showNoDataFromSourceVideoError(jitsiTrack: any) {
return async (dispatch, getState) => { return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
let notificationInfo; let notificationInfo;
const track = getTrackByJitsiTrack(getState()['features/base/tracks'], jitsiTrack); const track = getTrackByJitsiTrack(getState()['features/base/tracks'], jitsiTrack);
@ -289,7 +291,7 @@ export function showNoDataFromSourceVideoError(jitsiTrack) {
}, NOTIFICATION_TIMEOUT_TYPE.LONG)); }, NOTIFICATION_TIMEOUT_TYPE.LONG));
notificationInfo = { notificationInfo = {
uid: notificationAction.uid uid: notificationAction?.uid
}; };
} }
dispatch(trackNoDataFromSourceNotificationInfoChanged(jitsiTrack, notificationInfo)); dispatch(trackNoDataFromSourceNotificationInfoChanged(jitsiTrack, notificationInfo));
@ -311,7 +313,8 @@ export function showNoDataFromSourceVideoError(jitsiTrack) {
* shareOptions: Object * shareOptions: Object
* }} * }}
*/ */
export function toggleScreensharing(enabled, audioOnly = false, ignoreDidHaveVideo = false, shareOptions = {}) { export function toggleScreensharing(enabled: boolean, audioOnly = false,
ignoreDidHaveVideo = false, shareOptions = {}) {
return { return {
type: TOGGLE_SCREENSHARING, type: TOGGLE_SCREENSHARING,
enabled, enabled,
@ -333,8 +336,8 @@ export function toggleScreensharing(enabled, audioOnly = false, ignoreDidHaveVid
* will be used. * will be used.
* @returns {Function} * @returns {Function}
*/ */
export function replaceLocalTrack(oldTrack, newTrack, conference) { export function replaceLocalTrack(oldTrack: any, newTrack: any, conference?: IJitsiConference) {
return async (dispatch, getState) => { return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
conference conference
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
@ -355,8 +358,8 @@ export function replaceLocalTrack(oldTrack, newTrack, conference) {
* @param {JitsiLocalTrack|null} newTrack - The track to use instead. * @param {JitsiLocalTrack|null} newTrack - The track to use instead.
* @returns {Function} * @returns {Function}
*/ */
function replaceStoredTracks(oldTrack, newTrack) { function replaceStoredTracks(oldTrack: any, newTrack: any) {
return async (dispatch, getState) => { return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
// We call dispose after doing the replace because dispose will // We call dispose after doing the replace because dispose will
// try and do a new o/a after the track removes itself. Doing it // try and do a new o/a after the track removes itself. Doing it
// after means the JitsiLocalTrack.conference is already // after means the JitsiLocalTrack.conference is already
@ -395,14 +398,14 @@ function replaceStoredTracks(oldTrack, newTrack) {
* @param {(JitsiLocalTrack|JitsiRemoteTrack)} track - JitsiTrack instance. * @param {(JitsiLocalTrack|JitsiRemoteTrack)} track - JitsiTrack instance.
* @returns {{ type: TRACK_ADDED, track: Track }} * @returns {{ type: TRACK_ADDED, track: Track }}
*/ */
export function trackAdded(track) { export function trackAdded(track: any) {
return async (dispatch, getState) => { return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
track.on( track.on(
JitsiTrackEvents.TRACK_MUTE_CHANGED, JitsiTrackEvents.TRACK_MUTE_CHANGED,
() => dispatch(trackMutedChanged(track))); () => dispatch(trackMutedChanged(track)));
track.on( track.on(
JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED, JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED,
type => dispatch(trackVideoTypeChanged(track, type))); (type: VideoType) => dispatch(trackVideoTypeChanged(track, type)));
// participantId // participantId
const local = track.isLocal(); const local = track.isLocal();
@ -434,13 +437,13 @@ export function trackAdded(track) {
// Set the notification ID so that other parts of the application know that this was // Set the notification ID so that other parts of the application know that this was
// displayed in the context of the current device. // displayed in the context of the current device.
// I.E. The no-audio-signal notification shouldn't be displayed if this was already shown. // I.E. The no-audio-signal notification shouldn't be displayed if this was already shown.
dispatch(setNoSrcDataNotificationUid(notificationAction.uid)); dispatch(setNoSrcDataNotificationUid(notificationAction?.uid));
noDataFromSourceNotificationInfo = { uid: notificationAction.uid }; noDataFromSourceNotificationInfo = { uid: notificationAction?.uid };
} else { } else {
const timeout = setTimeout(() => dispatch( const timeout = setTimeout(() => dispatch(
showNoDataFromSourceVideoError(track)), showNoDataFromSourceVideoError(track)),
NOTIFICATION_TIMEOUT_TYPE.MEDIUM); NOTIFICATION_TIMEOUT.MEDIUM);
noDataFromSourceNotificationInfo = { timeout }; noDataFromSourceNotificationInfo = { timeout };
} }
@ -486,7 +489,7 @@ export function trackAdded(track) {
* track: Track * track: Track
* }} * }}
*/ */
export function trackMutedChanged(track) { export function trackMutedChanged(track: any) {
return { return {
type: TRACK_UPDATED, type: TRACK_UPDATED,
track: { track: {
@ -507,7 +510,7 @@ export function trackMutedChanged(track) {
* track: Track * track: Track
* }} * }}
*/ */
export function trackMuteUnmuteFailed(track, wasMuting) { export function trackMuteUnmuteFailed(track: any, wasMuting: boolean) {
return { return {
type: TRACK_MUTE_UNMUTE_FAILED, type: TRACK_MUTE_UNMUTE_FAILED,
track, track,
@ -525,7 +528,7 @@ export function trackMuteUnmuteFailed(track, wasMuting) {
* track: Track * track: Track
* }} * }}
*/ */
export function trackNoDataFromSourceNotificationInfoChanged(track, noDataFromSourceNotificationInfo) { export function trackNoDataFromSourceNotificationInfoChanged(track: any, noDataFromSourceNotificationInfo?: Object) {
return { return {
type: TRACK_UPDATED, type: TRACK_UPDATED,
track: { track: {
@ -545,7 +548,7 @@ export function trackNoDataFromSourceNotificationInfoChanged(track, noDataFromSo
* track: Track * track: Track
* }} * }}
*/ */
export function trackRemoved(track) { export function trackRemoved(track: any) {
track.removeAllListeners(JitsiTrackEvents.TRACK_MUTE_CHANGED); track.removeAllListeners(JitsiTrackEvents.TRACK_MUTE_CHANGED);
track.removeAllListeners(JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED); track.removeAllListeners(JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED);
track.removeAllListeners(JitsiTrackEvents.NO_DATA_FROM_SOURCE); track.removeAllListeners(JitsiTrackEvents.NO_DATA_FROM_SOURCE);
@ -567,7 +570,7 @@ export function trackRemoved(track) {
* track: Track * track: Track
* }} * }}
*/ */
export function trackVideoStarted(track) { export function trackVideoStarted(track: any) {
return { return {
type: TRACK_UPDATED, type: TRACK_UPDATED,
track: { track: {
@ -587,7 +590,7 @@ export function trackVideoStarted(track) {
* track: Track * track: Track
* }} * }}
*/ */
export function trackVideoTypeChanged(track, videoType) { export function trackVideoTypeChanged(track: any, videoType: VideoType) {
return { return {
type: TRACK_UPDATED, type: TRACK_UPDATED,
track: { track: {
@ -607,7 +610,7 @@ export function trackVideoTypeChanged(track, videoType) {
* track: Track * track: Track
* }} * }}
*/ */
export function trackStreamingStatusChanged(track, streamingStatus) { export function trackStreamingStatusChanged(track: any, streamingStatus: string) {
return { return {
type: TRACK_UPDATED, type: TRACK_UPDATED,
track: { track: {
@ -624,8 +627,8 @@ export function trackStreamingStatusChanged(track, streamingStatus) {
* @private * @private
* @returns {Function} * @returns {Function}
*/ */
function _addTracks(tracks) { function _addTracks(tracks: any[]) {
return dispatch => Promise.all(tracks.map(t => dispatch(trackAdded(t)))); return (dispatch: IStore['dispatch']) => Promise.all(tracks.map(t => dispatch(trackAdded(t))));
} }
/** /**
@ -640,16 +643,16 @@ function _addTracks(tracks) {
* about here is to be sure that the {@code getUserMedia} callbacks have * about here is to be sure that the {@code getUserMedia} callbacks have
* completed (i.e. Returned from the native side). * completed (i.e. Returned from the native side).
*/ */
function _cancelGUMProcesses(getState) { function _cancelGUMProcesses(getState: IStore['getState']) {
const logError const logError
= error => = (error: Error) =>
logger.error('gumProcess.cancel failed', JSON.stringify(error)); logger.error('gumProcess.cancel failed', JSON.stringify(error));
return Promise.all( return Promise.all(
getState()['features/base/tracks'] getState()['features/base/tracks']
.filter(t => t.local) .filter(t => t.local)
.map(({ gumProcess }) => .map(({ gumProcess }: any) =>
gumProcess && gumProcess.cancel().catch(logError))); gumProcess?.cancel().catch(logError)));
} }
/** /**
@ -659,8 +662,8 @@ function _cancelGUMProcesses(getState) {
* @protected * @protected
* @returns {Function} * @returns {Function}
*/ */
export function _disposeAndRemoveTracks(tracks) { export function _disposeAndRemoveTracks(tracks: any[]) {
return dispatch => return (dispatch: IStore['dispatch']) =>
_disposeTracks(tracks) _disposeTracks(tracks)
.then(() => .then(() =>
Promise.all(tracks.map(t => dispatch(trackRemoved(t))))); Promise.all(tracks.map(t => dispatch(trackRemoved(t)))));
@ -674,11 +677,11 @@ export function _disposeAndRemoveTracks(tracks) {
* @returns {Promise} - A Promise resolved once {@link JitsiTrack.dispose()} is * @returns {Promise} - A Promise resolved once {@link JitsiTrack.dispose()} is
* done for every track from the list. * done for every track from the list.
*/ */
function _disposeTracks(tracks) { function _disposeTracks(tracks: any) {
return Promise.all( return Promise.all(
tracks.map(t => tracks.map((t: any) =>
t.dispose() t.dispose()
.catch(err => { .catch((err: Error) => {
// Track might be already disposed so ignore such an error. // Track might be already disposed so ignore such an error.
// Of course, re-throw any other error(s). // Of course, re-throw any other error(s).
if (err.name !== JitsiTrackErrors.TRACK_IS_DISPOSED) { if (err.name !== JitsiTrackErrors.TRACK_IS_DISPOSED) {
@ -697,8 +700,8 @@ function _disposeTracks(tracks) {
* @private * @private
* @returns {Function} * @returns {Function}
*/ */
function _onCreateLocalTracksRejected(error, device) { function _onCreateLocalTracksRejected(error: Error, device: string) {
return dispatch => { return (dispatch: IStore['dispatch']) => {
// If permissions are not allowed, alert the user. // If permissions are not allowed, alert the user.
dispatch({ dispatch({
type: TRACK_CREATE_ERROR, type: TRACK_CREATE_ERROR,
@ -724,11 +727,10 @@ function _onCreateLocalTracksRejected(error, device) {
* @private * @private
* @returns {boolean} * @returns {boolean}
*/ */
function _shouldMirror(track) { function _shouldMirror(track: any) {
return ( return (
track track?.isLocal()
&& track.isLocal() && track?.isVideoTrack()
&& track.isVideoTrack()
// XXX The type of the return value of JitsiLocalTrack's // XXX The type of the return value of JitsiLocalTrack's
// getCameraFacingMode happens to be named CAMERA_FACING_MODE as // getCameraFacingMode happens to be named CAMERA_FACING_MODE as
@ -736,7 +738,7 @@ function _shouldMirror(track) {
// of the value on the right side of the equality check is defined // of the value on the right side of the equality check is defined
// by jitsi-meet. The type definitions are surely compatible today // by jitsi-meet. The type definitions are surely compatible today
// but that may not be the case tomorrow. // but that may not be the case tomorrow.
&& track.getCameraFacingMode() === CAMERA_FACING_MODE.USER); && track?.getCameraFacingMode() === CAMERA_FACING_MODE.USER);
} }
/** /**
@ -752,7 +754,7 @@ function _shouldMirror(track) {
* trackType: MEDIA_TYPE * trackType: MEDIA_TYPE
* }} * }}
*/ */
function _trackCreateCanceled(mediaType) { function _trackCreateCanceled(mediaType: MediaType) {
return { return {
type: TRACK_CREATE_CANCELED, type: TRACK_CREATE_CANCELED,
trackType: mediaType trackType: mediaType
@ -765,7 +767,7 @@ function _trackCreateCanceled(mediaType) {
* @returns {Function} * @returns {Function}
*/ */
export function destroyLocalDesktopTrackIfExists() { export function destroyLocalDesktopTrackIfExists() {
return (dispatch, getState) => { return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const videoTrack = getLocalVideoTrack(getState()['features/base/tracks']); const videoTrack = getLocalVideoTrack(getState()['features/base/tracks']);
const isDesktopTrack = videoTrack && videoTrack.videoType === VIDEO_TYPE.DESKTOP; const isDesktopTrack = videoTrack && videoTrack.videoType === VIDEO_TYPE.DESKTOP;
@ -782,10 +784,10 @@ export function destroyLocalDesktopTrackIfExists() {
* @param {number} uid - Notification UID. * @param {number} uid - Notification UID.
* @returns {{ * @returns {{
* type: SET_NO_AUDIO_SIGNAL_UID, * type: SET_NO_AUDIO_SIGNAL_UID,
* uid: number * uid: string
* }} * }}
*/ */
export function setNoSrcDataNotificationUid(uid) { export function setNoSrcDataNotificationUid(uid?: string) {
return { return {
type: SET_NO_SRC_DATA_NOTIFICATION_UID, type: SET_NO_SRC_DATA_NOTIFICATION_UID,
uid uid
@ -803,7 +805,7 @@ export function setNoSrcDataNotificationUid(uid) {
* name: string * name: string
* }} * }}
*/ */
export function updateLastTrackVideoMediaEvent(track, name) { export function updateLastTrackVideoMediaEvent(track: any, name: string) {
return { return {
type: TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT, type: TRACK_UPDATE_LAST_VIDEO_MEDIA_EVENT,
track, track,
@ -817,10 +819,10 @@ export function updateLastTrackVideoMediaEvent(track, name) {
* @returns {Function} * @returns {Function}
*/ */
export function toggleCamera() { export function toggleCamera() {
return async (dispatch, getState) => { return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState(); const state = getState();
const tracks = state['features/base/tracks']; const tracks = state['features/base/tracks'];
const localVideoTrack = getLocalVideoTrack(tracks).jitsiTrack; const localVideoTrack = getLocalVideoTrack(tracks)?.jitsiTrack;
const currentFacingMode = localVideoTrack.getCameraFacingMode(); const currentFacingMode = localVideoTrack.getCameraFacingMode();
/** /**

View File

@ -1,39 +1,44 @@
/* global APP */ import { IState, IStore } from '../../app/types';
import { IStateful } from '../app/types';
import { import {
getMultipleVideoSendingSupportFeatureFlag, getMultipleVideoSendingSupportFeatureFlag,
getMultipleVideoSupportFeatureFlag getMultipleVideoSupportFeatureFlag
} from '../config/functions.any'; } from '../config/functions.any';
import { isMobileBrowser } from '../environment/utils'; import { isMobileBrowser } from '../environment/utils';
import JitsiMeetJS, { JitsiTrackErrors, browser } from '../lib-jitsi-meet'; import JitsiMeetJS, { JitsiTrackErrors, browser } from '../lib-jitsi-meet';
import { MEDIA_TYPE, VIDEO_TYPE, setAudioMuted } from '../media'; import { setAudioMuted } from '../media/actions';
import { getParticipantByIdOrUndefined, getVirtualScreenshareParticipantOwnerId } from '../participants'; import { MediaType, MEDIA_TYPE, VIDEO_TYPE } from '../media/constants';
import { toState } from '../redux'; import { getParticipantByIdOrUndefined, getVirtualScreenshareParticipantOwnerId } from '../participants/functions';
import { Participant } from '../participants/types';
import { toState } from '../redux/functions';
import { import {
getUserSelectedCameraDeviceId, getUserSelectedCameraDeviceId,
getUserSelectedMicDeviceId getUserSelectedMicDeviceId
} from '../settings'; } from '../settings/functions.any';
// @ts-ignore
import loadEffects from './loadEffects'; import loadEffects from './loadEffects';
import logger from './logger'; import logger from './logger';
import { ITrack } from './reducer';
import { TrackOptions } from './types';
/** /**
* Returns root tracks state. * Returns root tracks state.
* *
* @param {Object} state - Global state. * @param {IState} state - Global state.
* @returns {Object} Tracks state. * @returns {Object} Tracks state.
*/ */
export const getTrackState = state => state['features/base/tracks']; export const getTrackState = (state: IState) => state['features/base/tracks'];
/** /**
* Checks if the passed media type is muted for the participant. * Checks if the passed media type is muted for the participant.
* *
* @param {Object} participant - Participant reference. * @param {Participant} participant - Participant reference.
* @param {MEDIA_TYPE} mediaType - Media type. * @param {MediaType} mediaType - Media type.
* @param {Object} state - Global state. * @param {IState} state - Global state.
* @returns {boolean} - Is the media type muted for the participant. * @returns {boolean} - Is the media type muted for the participant.
*/ */
export function isParticipantMediaMuted(participant, mediaType, state) { export function isParticipantMediaMuted(participant: Participant, mediaType: MediaType, state: IState) {
if (!participant) { if (!participant) {
return false; return false;
} }
@ -52,22 +57,22 @@ export function isParticipantMediaMuted(participant, mediaType, state) {
/** /**
* Checks if the participant is audio muted. * Checks if the participant is audio muted.
* *
* @param {Object} participant - Participant reference. * @param {Participant} participant - Participant reference.
* @param {Object} state - Global state. * @param {IState} state - Global state.
* @returns {boolean} - Is audio muted for the participant. * @returns {boolean} - Is audio muted for the participant.
*/ */
export function isParticipantAudioMuted(participant, state) { export function isParticipantAudioMuted(participant: Participant, state: IState) {
return isParticipantMediaMuted(participant, MEDIA_TYPE.AUDIO, state); return isParticipantMediaMuted(participant, MEDIA_TYPE.AUDIO, state);
} }
/** /**
* Checks if the participant is video muted. * Checks if the participant is video muted.
* *
* @param {Object} participant - Participant reference. * @param {Participant} participant - Participant reference.
* @param {Object} state - Global state. * @param {IState} state - Global state.
* @returns {boolean} - Is video muted for the participant. * @returns {boolean} - Is video muted for the participant.
*/ */
export function isParticipantVideoMuted(participant, state) { export function isParticipantVideoMuted(participant: Participant, state: IState) {
return isParticipantMediaMuted(participant, MEDIA_TYPE.VIDEO, state); return isParticipantMediaMuted(participant, MEDIA_TYPE.VIDEO, state);
} }
@ -83,7 +88,7 @@ export function isParticipantVideoMuted(participant, state) {
* shared. * shared.
* @returns {Promise<JitsiLocalTrack>} * @returns {Promise<JitsiLocalTrack>}
*/ */
export async function createLocalPresenterTrack(options, desktopHeight) { export async function createLocalPresenterTrack(options: TrackOptions, desktopHeight: number) {
const { cameraDeviceId } = options; const { cameraDeviceId } = options;
// compute the constraints of the camera track based on the resolution // compute the constraints of the camera track based on the resolution
@ -130,11 +135,11 @@ export async function createLocalPresenterTrack(options, desktopHeight) {
* @param {boolean} [options.fireSlowPromiseEvent] - Whether lib-jitsi-meet * @param {boolean} [options.fireSlowPromiseEvent] - Whether lib-jitsi-meet
* should check for a slow {@code getUserMedia} request and fire a * should check for a slow {@code getUserMedia} request and fire a
* corresponding event. * corresponding event.
* @param {Object} store - The redux store in the context of which the function * @param {IStore} store - The redux store in the context of which the function
* is to execute and from which state such as {@code config} is to be retrieved. * is to execute and from which state such as {@code config} is to be retrieved.
* @returns {Promise<JitsiLocalTrack[]>} * @returns {Promise<JitsiLocalTrack[]>}
*/ */
export function createLocalTracksF(options = {}, store) { export function createLocalTracksF(options: TrackOptions = {}, store?: IStore) {
let { cameraDeviceId, micDeviceId } = options; let { cameraDeviceId, micDeviceId } = options;
const { const {
desktopSharingSourceDevice, desktopSharingSourceDevice,
@ -147,7 +152,9 @@ export function createLocalTracksF(options = {}, store) {
if (typeof APP !== 'undefined') { if (typeof APP !== 'undefined') {
// TODO The app's settings should go in the redux store and then the // TODO The app's settings should go in the redux store and then the
// reliance on the global variable APP will go away. // reliance on the global variable APP will go away.
store || (store = APP.store); // eslint-disable-line no-param-reassign if (!store) {
store = APP.store; // eslint-disable-line no-param-reassign
}
const state = store.getState(); const state = store.getState();
@ -159,6 +166,7 @@ export function createLocalTracksF(options = {}, store) {
} }
} }
// @ts-ignore
const state = store.getState(); const state = store.getState();
const { const {
desktopSharingFrameRate, desktopSharingFrameRate,
@ -168,7 +176,7 @@ export function createLocalTracksF(options = {}, store) {
const constraints = options.constraints ?? state['features/base/config'].constraints; const constraints = options.constraints ?? state['features/base/config'].constraints;
return ( return (
loadEffects(store).then(effectsArray => { loadEffects(store).then((effectsArray: Object[]) => {
// Filter any undefined values returned by Promise.resolve(). // Filter any undefined values returned by Promise.resolve().
const effects = effectsArray.filter(effect => Boolean(effect)); const effects = effectsArray.filter(effect => Boolean(effect));
@ -181,7 +189,7 @@ export function createLocalTracksF(options = {}, store) {
desktopSharingSources, desktopSharingSources,
// Copy array to avoid mutations inside library. // Copy array to avoid mutations inside library.
devices: options.devices.slice(0), devices: options.devices?.slice(0),
effects, effects,
firefox_fake_device, // eslint-disable-line camelcase firefox_fake_device, // eslint-disable-line camelcase
firePermissionPromptIsShownEvent, firePermissionPromptIsShownEvent,
@ -190,7 +198,7 @@ export function createLocalTracksF(options = {}, store) {
resolution, resolution,
timeout timeout
}) })
.catch(err => { .catch((err: Error) => {
logger.error('Failed to create local tracks', options.devices, err); logger.error('Failed to create local tracks', options.devices, err);
return Promise.reject(err); return Promise.reject(err);
@ -207,7 +215,7 @@ export function createLocalTracksF(options = {}, store) {
* @todo Refactor to not use APP. * @todo Refactor to not use APP.
*/ */
export function createPrejoinTracks() { export function createPrejoinTracks() {
const errors = {}; const errors: any = {};
const initialDevices = [ 'audio' ]; const initialDevices = [ 'audio' ];
const requestedAudio = true; const requestedAudio = true;
let requestedVideo = false; let requestedVideo = false;
@ -235,8 +243,8 @@ export function createPrejoinTracks() {
tryCreateLocalTracks = createLocalTracksF({ tryCreateLocalTracks = createLocalTracksF({
devices: initialDevices, devices: initialDevices,
firePermissionPromptIsShownEvent: true firePermissionPromptIsShownEvent: true
}) }, APP.store)
.catch(err => { .catch((err: Error) => {
if (requestedAudio && requestedVideo) { if (requestedAudio && requestedVideo) {
// Try audio only... // Try audio only...
@ -258,7 +266,7 @@ export function createPrejoinTracks() {
} }
logger.error('Should never happen'); logger.error('Should never happen');
}) })
.catch(err => { .catch((err: Error) => {
// Log this just in case... // Log this just in case...
if (!requestedAudio) { if (!requestedAudio) {
logger.error('The impossible just happened', err); logger.error('The impossible just happened', err);
@ -273,7 +281,7 @@ export function createPrejoinTracks() {
}) })
: []; : [];
}) })
.catch(err => { .catch((err: Error) => {
// Log this just in case... // Log this just in case...
if (!requestedVideo) { if (!requestedVideo) {
logger.error('The impossible just happened', err); logger.error('The impossible just happened', err);
@ -293,10 +301,10 @@ export function createPrejoinTracks() {
/** /**
* Returns local audio track. * Returns local audio track.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @returns {(Track|undefined)} * @returns {(Track|undefined)}
*/ */
export function getLocalAudioTrack(tracks) { export function getLocalAudioTrack(tracks: ITrack[]) {
return getLocalTrack(tracks, MEDIA_TYPE.AUDIO); return getLocalTrack(tracks, MEDIA_TYPE.AUDIO);
} }
@ -309,7 +317,7 @@ export function getLocalAudioTrack(tracks) {
* {@code jitsiTrack} property is {@code undefined}. By default a pending local track is not returned. * {@code jitsiTrack} property is {@code undefined}. By default a pending local track is not returned.
* @returns {(Track|undefined)} * @returns {(Track|undefined)}
*/ */
export function getLocalDesktopTrack(tracks, includePending = false) { export function getLocalDesktopTrack(tracks: ITrack[], includePending = false) {
return ( return (
getLocalTracks(tracks, includePending) getLocalTracks(tracks, includePending)
.find(t => t.mediaType === MEDIA_TYPE.SCREENSHARE || t.videoType === VIDEO_TYPE.DESKTOP)); .find(t => t.mediaType === MEDIA_TYPE.SCREENSHARE || t.videoType === VIDEO_TYPE.DESKTOP));
@ -318,10 +326,10 @@ export function getLocalDesktopTrack(tracks, includePending = false) {
/** /**
* Returns the stored local desktop jitsiLocalTrack. * Returns the stored local desktop jitsiLocalTrack.
* *
* @param {Object} state - The redux state. * @param {IState} state - The redux state.
* @returns {JitsiLocalTrack|undefined} * @returns {JitsiLocalTrack|undefined}
*/ */
export function getLocalJitsiDesktopTrack(state) { export function getLocalJitsiDesktopTrack(state: IState) {
const track = getLocalDesktopTrack(getTrackState(state)); const track = getLocalDesktopTrack(getTrackState(state));
return track?.jitsiTrack; return track?.jitsiTrack;
@ -330,8 +338,8 @@ export function getLocalJitsiDesktopTrack(state) {
/** /**
* Returns local track by media type. * Returns local track by media type.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @param {MEDIA_TYPE} mediaType - Media type. * @param {MediaType} mediaType - Media type.
* @param {boolean} [includePending] - Indicates whether a local track is to be * @param {boolean} [includePending] - Indicates whether a local track is to be
* returned if it is still pending. A local track is pending if * returned if it is still pending. A local track is pending if
* {@code getUserMedia} is still executing to create it and, consequently, its * {@code getUserMedia} is still executing to create it and, consequently, its
@ -339,7 +347,7 @@ export function getLocalJitsiDesktopTrack(state) {
* track is not returned. * track is not returned.
* @returns {(Track|undefined)} * @returns {(Track|undefined)}
*/ */
export function getLocalTrack(tracks, mediaType, includePending = false) { export function getLocalTrack(tracks: ITrack[], mediaType: MediaType, includePending = false) {
return ( return (
getLocalTracks(tracks, includePending) getLocalTracks(tracks, includePending)
.find(t => t.mediaType === mediaType)); .find(t => t.mediaType === mediaType));
@ -349,7 +357,7 @@ export function getLocalTrack(tracks, mediaType, includePending = false) {
* Returns an array containing the local tracks with or without a (valid) * Returns an array containing the local tracks with or without a (valid)
* {@code JitsiTrack}. * {@code JitsiTrack}.
* *
* @param {Track[]} tracks - An array containing all local tracks. * @param {ITrack[]} tracks - An array containing all local tracks.
* @param {boolean} [includePending] - Indicates whether a local track is to be * @param {boolean} [includePending] - Indicates whether a local track is to be
* returned if it is still pending. A local track is pending if * returned if it is still pending. A local track is pending if
* {@code getUserMedia} is still executing to create it and, consequently, its * {@code getUserMedia} is still executing to create it and, consequently, its
@ -357,7 +365,7 @@ export function getLocalTrack(tracks, mediaType, includePending = false) {
* track is not returned. * track is not returned.
* @returns {Track[]} * @returns {Track[]}
*/ */
export function getLocalTracks(tracks, includePending = false) { export function getLocalTracks(tracks: ITrack[], includePending = false) {
// XXX A local track is considered ready only once it has its `jitsiTrack` // XXX A local track is considered ready only once it has its `jitsiTrack`
// property set by the `TRACK_ADDED` action. Until then there is a stub // property set by the `TRACK_ADDED` action. Until then there is a stub
// added just before the `getUserMedia` call with a cancellable // added just before the `getUserMedia` call with a cancellable
@ -371,20 +379,20 @@ export function getLocalTracks(tracks, includePending = false) {
/** /**
* Returns local video track. * Returns local video track.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @returns {(Track|undefined)} * @returns {(Track|undefined)}
*/ */
export function getLocalVideoTrack(tracks) { export function getLocalVideoTrack(tracks: ITrack[]) {
return getLocalTrack(tracks, MEDIA_TYPE.VIDEO); return getLocalTrack(tracks, MEDIA_TYPE.VIDEO);
} }
/** /**
* Returns the media type of the local video, presenter or video. * Returns the media type of the local video, presenter or video.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @returns {MEDIA_TYPE} * @returns {MEDIA_TYPE}
*/ */
export function getLocalVideoType(tracks) { export function getLocalVideoType(tracks: ITrack[]) {
const presenterTrack = getLocalTrack(tracks, MEDIA_TYPE.PRESENTER); const presenterTrack = getLocalTrack(tracks, MEDIA_TYPE.PRESENTER);
return presenterTrack ? MEDIA_TYPE.PRESENTER : MEDIA_TYPE.VIDEO; return presenterTrack ? MEDIA_TYPE.PRESENTER : MEDIA_TYPE.VIDEO;
@ -393,10 +401,10 @@ export function getLocalVideoType(tracks) {
/** /**
* Returns the stored local video track. * Returns the stored local video track.
* *
* @param {Object} state - The redux state. * @param {IState} state - The redux state.
* @returns {Object} * @returns {Object}
*/ */
export function getLocalJitsiVideoTrack(state) { export function getLocalJitsiVideoTrack(state: IState) {
const track = getLocalVideoTrack(getTrackState(state)); const track = getLocalVideoTrack(getTrackState(state));
return track?.jitsiTrack; return track?.jitsiTrack;
@ -405,10 +413,10 @@ export function getLocalJitsiVideoTrack(state) {
/** /**
* Returns the stored local audio track. * Returns the stored local audio track.
* *
* @param {Object} state - The redux state. * @param {IState} state - The redux state.
* @returns {Object} * @returns {Object}
*/ */
export function getLocalJitsiAudioTrack(state) { export function getLocalJitsiAudioTrack(state: IState) {
const track = getLocalAudioTrack(getTrackState(state)); const track = getLocalAudioTrack(getTrackState(state));
return track?.jitsiTrack; return track?.jitsiTrack;
@ -417,13 +425,13 @@ export function getLocalJitsiAudioTrack(state) {
/** /**
* Returns track of specified media type for specified participant. * Returns track of specified media type for specified participant.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @param {Object} participant - Participant Object. * @param {Participant} participant - Participant Object.
* @returns {(Track|undefined)} * @returns {(Track|undefined)}
*/ */
export function getVideoTrackByParticipant( export function getVideoTrackByParticipant(
tracks, tracks: ITrack[],
participant) { participant?: Participant) {
if (!participant) { if (!participant) {
return; return;
@ -439,11 +447,11 @@ export function getVideoTrackByParticipant(
/** /**
* Returns source name for specified participant id. * Returns source name for specified participant id.
* *
* @param {Object} state - The Redux state. * @param {IState} state - The Redux state.
* @param {string} participantId - Participant ID. * @param {string} participantId - Participant ID.
* @returns {string | undefined} * @returns {string | undefined}
*/ */
export function getSourceNameByParticipantId(state, participantId) { export function getSourceNameByParticipantId(state: IState, participantId: string) {
const participant = getParticipantByIdOrUndefined(state, participantId); const participant = getParticipantByIdOrUndefined(state, participantId);
const tracks = state['features/base/tracks']; const tracks = state['features/base/tracks'];
const track = getVideoTrackByParticipant(tracks, participant); const track = getVideoTrackByParticipant(tracks, participant);
@ -454,15 +462,15 @@ export function getSourceNameByParticipantId(state, participantId) {
/** /**
* Returns track of specified media type for specified participant id. * Returns track of specified media type for specified participant id.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @param {MEDIA_TYPE} mediaType - Media type. * @param {MediaType} mediaType - Media type.
* @param {string} participantId - Participant ID. * @param {string} participantId - Participant ID.
* @returns {(Track|undefined)} * @returns {(Track|undefined)}
*/ */
export function getTrackByMediaTypeAndParticipant( export function getTrackByMediaTypeAndParticipant(
tracks, tracks: ITrack[],
mediaType, mediaType: MediaType,
participantId) { participantId: string) {
return tracks.find( return tracks.find(
t => Boolean(t.jitsiTrack) && t.participantId === participantId && t.mediaType === mediaType t => Boolean(t.jitsiTrack) && t.participantId === participantId && t.mediaType === mediaType
); );
@ -471,11 +479,11 @@ export function getTrackByMediaTypeAndParticipant(
/** /**
* Returns screenshare track of given virtualScreenshareParticipantId. * Returns screenshare track of given virtualScreenshareParticipantId.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @param {string} virtualScreenshareParticipantId - Virtual Screenshare Participant ID. * @param {string} virtualScreenshareParticipantId - Virtual Screenshare Participant ID.
* @returns {(Track|undefined)} * @returns {(Track|undefined)}
*/ */
export function getVirtualScreenshareParticipantTrack(tracks, virtualScreenshareParticipantId) { export function getVirtualScreenshareParticipantTrack(tracks: ITrack[], virtualScreenshareParticipantId: string) {
const ownderId = getVirtualScreenshareParticipantOwnerId(virtualScreenshareParticipantId); const ownderId = getVirtualScreenshareParticipantOwnerId(virtualScreenshareParticipantId);
return getScreenShareTrack(tracks, ownderId); return getScreenShareTrack(tracks, ownderId);
@ -484,16 +492,16 @@ export function getVirtualScreenshareParticipantTrack(tracks, virtualScreenshare
/** /**
* Returns track source names of given screen share participant ids. * Returns track source names of given screen share participant ids.
* *
* @param {Object} state - The entire redux state. * @param {IState} state - The entire redux state.
* @param {string[]} screenShareParticipantIds - Participant ID. * @param {string[]} screenShareParticipantIds - Participant ID.
* @returns {(string[])} * @returns {(string[])}
*/ */
export function getRemoteScreenSharesSourceNames(state, screenShareParticipantIds = []) { export function getRemoteScreenSharesSourceNames(state: IState, screenShareParticipantIds = []) {
const tracks = state['features/base/tracks']; const tracks = state['features/base/tracks'];
return getMultipleVideoSupportFeatureFlag(state) return getMultipleVideoSupportFeatureFlag(state)
? screenShareParticipantIds ? screenShareParticipantIds
: screenShareParticipantIds.reduce((acc, id) => { : screenShareParticipantIds.reduce((acc: string[], id) => {
const sourceName = getScreenShareTrack(tracks, id)?.jitsiTrack.getSourceName(); const sourceName = getScreenShareTrack(tracks, id)?.jitsiTrack.getSourceName();
if (sourceName) { if (sourceName) {
@ -511,7 +519,7 @@ export function getRemoteScreenSharesSourceNames(state, screenShareParticipantId
* @param {string} ownerId - Screenshare track owner ID. * @param {string} ownerId - Screenshare track owner ID.
* @returns {(Track|undefined)} * @returns {(Track|undefined)}
*/ */
export function getScreenShareTrack(tracks, ownerId) { export function getScreenShareTrack(tracks: ITrack[], ownerId: string) {
return tracks.find( return tracks.find(
t => Boolean(t.jitsiTrack) t => Boolean(t.jitsiTrack)
&& t.participantId === ownerId && t.participantId === ownerId
@ -522,15 +530,15 @@ export function getScreenShareTrack(tracks, ownerId) {
/** /**
* Returns track source name of specified media type for specified participant id. * Returns track source name of specified media type for specified participant id.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @param {MEDIA_TYPE} mediaType - Media type. * @param {MediaType} mediaType - Media type.
* @param {string} participantId - Participant ID. * @param {string} participantId - Participant ID.
* @returns {(string|undefined)} * @returns {(string|undefined)}
*/ */
export function getTrackSourceNameByMediaTypeAndParticipant( export function getTrackSourceNameByMediaTypeAndParticipant(
tracks, tracks: ITrack[],
mediaType, mediaType: MediaType,
participantId) { participantId: string) {
const track = getTrackByMediaTypeAndParticipant( const track = getTrackByMediaTypeAndParticipant(
tracks, tracks,
mediaType, mediaType,
@ -543,32 +551,32 @@ export function getTrackSourceNameByMediaTypeAndParticipant(
* Returns the track if any which corresponds to a specific instance * Returns the track if any which corresponds to a specific instance
* of JitsiLocalTrack or JitsiRemoteTrack. * of JitsiLocalTrack or JitsiRemoteTrack.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @param {(JitsiLocalTrack|JitsiRemoteTrack)} jitsiTrack - JitsiTrack instance. * @param {(JitsiLocalTrack|JitsiRemoteTrack)} jitsiTrack - JitsiTrack instance.
* @returns {(Track|undefined)} * @returns {(Track|undefined)}
*/ */
export function getTrackByJitsiTrack(tracks, jitsiTrack) { export function getTrackByJitsiTrack(tracks: ITrack[], jitsiTrack: any) {
return tracks.find(t => t.jitsiTrack === jitsiTrack); return tracks.find(t => t.jitsiTrack === jitsiTrack);
} }
/** /**
* Returns tracks of specified media type. * Returns tracks of specified media type.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @param {MEDIA_TYPE} mediaType - Media type. * @param {MediaType} mediaType - Media type.
* @returns {Track[]} * @returns {Track[]}
*/ */
export function getTracksByMediaType(tracks, mediaType) { export function getTracksByMediaType(tracks: ITrack[], mediaType: MediaType) {
return tracks.filter(t => t.mediaType === mediaType); return tracks.filter(t => t.mediaType === mediaType);
} }
/** /**
* Checks if the local video camera track in the given set of tracks is muted. * Checks if the local video camera track in the given set of tracks is muted.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @returns {Track[]} * @returns {ITrack[]}
*/ */
export function isLocalCameraTrackMuted(tracks) { export function isLocalCameraTrackMuted(tracks: ITrack[]) {
const presenterTrack = getLocalTrack(tracks, MEDIA_TYPE.PRESENTER); const presenterTrack = getLocalTrack(tracks, MEDIA_TYPE.PRESENTER);
const videoTrack = getLocalTrack(tracks, MEDIA_TYPE.VIDEO); const videoTrack = getLocalTrack(tracks, MEDIA_TYPE.VIDEO);
@ -588,13 +596,13 @@ export function isLocalCameraTrackMuted(tracks) {
/** /**
* Checks if the first local track in the given tracks set is muted. * Checks if the first local track in the given tracks set is muted.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @param {MEDIA_TYPE} mediaType - The media type of tracks to be checked. * @param {MediaType} mediaType - The media type of tracks to be checked.
* @returns {boolean} True if local track is muted or false if the track is * @returns {boolean} True if local track is muted or false if the track is
* unmuted or if there are no local tracks of the given media type in the given * unmuted or if there are no local tracks of the given media type in the given
* set of tracks. * set of tracks.
*/ */
export function isLocalTrackMuted(tracks, mediaType) { export function isLocalTrackMuted(tracks: ITrack[], mediaType: MediaType) {
const track = getLocalTrack(tracks, mediaType); const track = getLocalTrack(tracks, mediaType);
return !track || track.muted; return !track || track.muted;
@ -603,10 +611,10 @@ export function isLocalTrackMuted(tracks, mediaType) {
/** /**
* Checks if the local video track is of type DESKtOP. * Checks if the local video track is of type DESKtOP.
* *
* @param {Object} state - The redux state. * @param {IState} state - The redux state.
* @returns {boolean} * @returns {boolean}
*/ */
export function isLocalVideoTrackDesktop(state) { export function isLocalVideoTrackDesktop(state: IState) {
const videoTrack = getLocalVideoTrack(getTrackState(state)); const videoTrack = getLocalVideoTrack(getTrackState(state));
return videoTrack && videoTrack.videoType === VIDEO_TYPE.DESKTOP; return videoTrack && videoTrack.videoType === VIDEO_TYPE.DESKTOP;
@ -617,12 +625,12 @@ export function isLocalVideoTrackDesktop(state) {
* Returns true if the remote track of the given media type and the given * Returns true if the remote track of the given media type and the given
* participant is muted, false otherwise. * participant is muted, false otherwise.
* *
* @param {Track[]} tracks - List of all tracks. * @param {ITrack[]} tracks - List of all tracks.
* @param {MEDIA_TYPE} mediaType - The media type of tracks to be checked. * @param {MediaType} mediaType - The media type of tracks to be checked.
* @param {*} participantId - Participant ID. * @param {string} participantId - Participant ID.
* @returns {boolean} * @returns {boolean}
*/ */
export function isRemoteTrackMuted(tracks, mediaType, participantId) { export function isRemoteTrackMuted(tracks: ITrack[], mediaType: MediaType, participantId: string) {
const track = getTrackByMediaTypeAndParticipant( const track = getTrackByMediaTypeAndParticipant(
tracks, mediaType, participantId); tracks, mediaType, participantId);
@ -633,10 +641,10 @@ export function isRemoteTrackMuted(tracks, mediaType, participantId) {
* Returns whether or not the current environment needs a user interaction with * Returns whether or not the current environment needs a user interaction with
* the page before any unmute can occur. * the page before any unmute can occur.
* *
* @param {Object} state - The redux state. * @param {IState} state - The redux state.
* @returns {boolean} * @returns {boolean}
*/ */
export function isUserInteractionRequiredForUnmute(state) { export function isUserInteractionRequiredForUnmute(state: IState) {
return browser.isUserInteractionRequiredForUnmute() return browser.isUserInteractionRequiredForUnmute()
&& window && window
&& window.self !== window.top && window.self !== window.top
@ -652,7 +660,7 @@ export function isUserInteractionRequiredForUnmute(state) {
* @param {Object} state - The redux state. * @param {Object} state - The redux state.
* @returns {Promise} * @returns {Promise}
*/ */
export function setTrackMuted(track, muted, state) { export function setTrackMuted(track: any, muted: boolean, state: IState) {
muted = Boolean(muted); // eslint-disable-line no-param-reassign muted = Boolean(muted); // eslint-disable-line no-param-reassign
// Ignore the check for desktop track muted operation. When the screenshare is terminated by clicking on the // Ignore the check for desktop track muted operation. When the screenshare is terminated by clicking on the
@ -665,7 +673,7 @@ export function setTrackMuted(track, muted, state) {
const f = muted ? 'mute' : 'unmute'; const f = muted ? 'mute' : 'unmute';
return track[f]().catch(error => { return track[f]().catch((error: Error) => {
// Track might be already disposed so ignore such an error. // Track might be already disposed so ignore such an error.
if (error.name !== JitsiTrackErrors.TRACK_IS_DISPOSED) { if (error.name !== JitsiTrackErrors.TRACK_IS_DISPOSED) {
logger.error(`set track ${f} failed`, error); logger.error(`set track ${f} failed`, error);
@ -681,9 +689,9 @@ export function setTrackMuted(track, muted, state) {
* @param {Function|Object} stateful - The redux store or {@code getState} function. * @param {Function|Object} stateful - The redux store or {@code getState} function.
* @returns {boolean} - Whether toggle camera should be enabled. * @returns {boolean} - Whether toggle camera should be enabled.
*/ */
export function isToggleCameraEnabled(stateful) { export function isToggleCameraEnabled(stateful: IStateful) {
const state = toState(stateful); const state = toState(stateful);
const { videoInput } = state['features/base/devices'].availableDevices; const { videoInput } = state['features/base/devices'].availableDevices;
return isMobileBrowser() && videoInput.length > 1; return isMobileBrowser() && Number(videoInput?.length) > 1;
} }

View File

@ -1,28 +1,30 @@
// @flow
import { batch } from 'react-redux'; import { batch } from 'react-redux';
import { IStore } from '../../app/types';
import { _RESET_BREAKOUT_ROOMS } from '../../breakout-rooms/actionTypes'; import { _RESET_BREAKOUT_ROOMS } from '../../breakout-rooms/actionTypes';
import { hideNotification } from '../../notifications'; import { hideNotification } from '../../notifications/actions';
import { isPrejoinPageVisible } from '../../prejoin/functions'; import { isPrejoinPageVisible } from '../../prejoin/functions';
import { getCurrentConference } from '../conference/functions'; import { getCurrentConference } from '../conference/functions';
import { getMultipleVideoSendingSupportFeatureFlag } from '../config'; import { getMultipleVideoSendingSupportFeatureFlag } from '../config/functions.any';
import { getAvailableDevices } from '../devices/actions'; import { getAvailableDevices } from '../devices/actions';
import { import {
CAMERA_FACING_MODE,
MEDIA_TYPE,
SET_AUDIO_MUTED, SET_AUDIO_MUTED,
SET_CAMERA_FACING_MODE, SET_CAMERA_FACING_MODE,
SET_VIDEO_MUTED, SET_VIDEO_MUTED,
VIDEO_MUTISM_AUTHORITY,
TOGGLE_CAMERA_FACING_MODE, TOGGLE_CAMERA_FACING_MODE,
toggleCameraFacingMode, SET_SCREENSHARE_MUTED
SET_SCREENSHARE_MUTED, } from '../media/actionTypes';
import { toggleCameraFacingMode, setScreenshareMuted } from '../media/actions';
import {
CAMERA_FACING_MODE,
MEDIA_TYPE,
VIDEO_MUTISM_AUTHORITY,
VIDEO_TYPE, VIDEO_TYPE,
setScreenshareMuted, SCREENSHARE_MUTISM_AUTHORITY,
SCREENSHARE_MUTISM_AUTHORITY MediaType
} from '../media'; } from '../media/constants';
import { MiddlewareRegistry, StateListenerRegistry } from '../redux'; import MiddlewareRegistry from '../redux/MiddlewareRegistry';
import StateListenerRegistry from '../redux/StateListenerRegistry';
import { import {
TRACK_ADDED, TRACK_ADDED,
@ -47,11 +49,10 @@ import {
isUserInteractionRequiredForUnmute, isUserInteractionRequiredForUnmute,
setTrackMuted setTrackMuted
} from './functions'; } from './functions';
import { ITrack } from './reducer';
import './subscriber'; import './subscriber';
declare var APP: Object;
/** /**
* Middleware that captures LIB_DID_DISPOSE and LIB_DID_INIT actions and, * Middleware that captures LIB_DID_DISPOSE and LIB_DID_INIT actions and,
* respectively, creates/destroys local media tracks. Also listens to * respectively, creates/destroys local media tracks. Also listens to
@ -267,7 +268,7 @@ StateListenerRegistry.register(
* @private * @private
* @returns {void} * @returns {void}
*/ */
function _handleNoDataFromSourceErrors(store, action) { function _handleNoDataFromSourceErrors(store: IStore, action: any) {
const { getState, dispatch } = store; const { getState, dispatch } = store;
const track = getTrackByJitsiTrack(getState()['features/base/tracks'], action.track.jitsiTrack); const track = getTrackByJitsiTrack(getState()['features/base/tracks'], action.track.jitsiTrack);
@ -323,9 +324,9 @@ function _handleNoDataFromSourceErrors(store, action) {
* {@code mediaType} in the specified {@code store}. * {@code mediaType} in the specified {@code store}.
*/ */
function _getLocalTrack( function _getLocalTrack(
{ getState }: { getState: Function }, { getState }: { getState: Function; },
mediaType: MEDIA_TYPE, mediaType: MediaType,
includePending: boolean = false) { includePending = false) {
return ( return (
getLocalTrack( getLocalTrack(
getState()['features/base/tracks'], getState()['features/base/tracks'],
@ -340,11 +341,11 @@ function _getLocalTrack(
* @param {Track} track - The redux action dispatched in the specified store. * @param {Track} track - The redux action dispatched in the specified store.
* @returns {void} * @returns {void}
*/ */
function _removeNoDataFromSourceNotification({ getState, dispatch }, track) { function _removeNoDataFromSourceNotification({ getState, dispatch }: IStore, track: ITrack) {
const t = getTrackByJitsiTrack(getState()['features/base/tracks'], track.jitsiTrack); const t = getTrackByJitsiTrack(getState()['features/base/tracks'], track.jitsiTrack);
const { jitsiTrack, noDataFromSourceNotificationInfo = {} } = t || {}; const { jitsiTrack, noDataFromSourceNotificationInfo = {} } = t || {};
if (noDataFromSourceNotificationInfo && noDataFromSourceNotificationInfo.uid) { if (noDataFromSourceNotificationInfo?.uid) {
dispatch(hideNotification(noDataFromSourceNotificationInfo.uid)); dispatch(hideNotification(noDataFromSourceNotificationInfo.uid));
dispatch(trackNoDataFromSourceNotificationInfoChanged(jitsiTrack, undefined)); dispatch(trackNoDataFromSourceNotificationInfoChanged(jitsiTrack, undefined));
} }
@ -361,7 +362,8 @@ function _removeNoDataFromSourceNotification({ getState, dispatch }, track) {
* @private * @private
* @returns {void} * @returns {void}
*/ */
async function _setMuted(store, { ensureTrack, authority, muted }, mediaType: MEDIA_TYPE) { async function _setMuted(store: IStore, { ensureTrack, authority, muted }: {
authority: number; ensureTrack: boolean; muted: boolean; }, mediaType: MediaType) {
const { dispatch, getState } = store; const { dispatch, getState } = store;
const localTrack = _getLocalTrack(store, mediaType, /* includePending */ true); const localTrack = _getLocalTrack(store, mediaType, /* includePending */ true);
const state = getState(); const state = getState();

View File

@ -1,3 +1,4 @@
import { MediaType } from '../media/constants';
import { PARTICIPANT_ID_CHANGED } from '../participants/actionTypes'; import { PARTICIPANT_ID_CHANGED } from '../participants/actionTypes';
import ReducerRegistry from '../redux/ReducerRegistry'; import ReducerRegistry from '../redux/ReducerRegistry';
import { set } from '../redux/functions'; import { set } from '../redux/functions';
@ -14,14 +15,18 @@ import {
TRACK_WILL_CREATE TRACK_WILL_CREATE
} from './actionTypes'; } from './actionTypes';
interface ITrack { export interface ITrack {
isReceivingData: boolean; isReceivingData: boolean;
jitsiTrack: any; jitsiTrack: any;
lastMediaEvent?: string; lastMediaEvent?: string;
local: boolean; local: boolean;
mediaType: string; mediaType: MediaType;
mirror: boolean; mirror: boolean;
muted: boolean; muted: boolean;
noDataFromSourceNotificationInfo?: {
timeout?: number;
uid?: string;
};
participantId: string; participantId: string;
streamingStatus?: string; streamingStatus?: string;
videoStarted: boolean; videoStarted: boolean;

View File

@ -1,13 +1,9 @@
// @flow
import _ from 'lodash'; import _ from 'lodash';
import { StateListenerRegistry } from '../../base/redux'; import StateListenerRegistry from '../redux/StateListenerRegistry';
import { isLocalCameraTrackMuted } from './functions'; import { isLocalCameraTrackMuted } from './functions';
declare var APP: Object;
/** /**
* Notifies when the list of currently sharing participants changes. * Notifies when the list of currently sharing participants changes.
*/ */

View File

@ -0,0 +1,20 @@
export interface TrackOptions {
cameraDeviceId?: string | null;
constraints?: {
video?: {
height?: {
ideal?: number;
max?: number;
min?: number;
};
};
};
desktopSharingSourceDevice?: string;
desktopSharingSources?: string[];
devices?: string[];
facingMode?: string;
firePermissionPromptIsShownEvent?: boolean;
fireSlowPromiseEvent?: boolean;
micDeviceId?: string | null;
timeout?: number;
}

View File

@ -64,7 +64,7 @@ const Prejoin: React.FC<PrejoinProps> = ({ navigation }: PrejoinProps) => {
(state: IState) => state['features/base/responsive-ui']?.aspectRatio (state: IState) => state['features/base/responsive-ui']?.aspectRatio
); );
const localParticipant = useSelector((state: IState) => getLocalParticipant(state)); const localParticipant = useSelector((state: IState) => getLocalParticipant(state));
const isDisplayNameMandatory = useSelector(state => isDisplayNameRequired(state)); const isDisplayNameMandatory = useSelector((state: IState) => isDisplayNameRequired(state));
const roomName = useSelector((state: IState) => getConferenceName(state)); const roomName = useSelector((state: IState) => getConferenceName(state));
const participantName = localParticipant?.name; const participantName = localParticipant?.name;
const [ displayName, setDisplayName ] const [ displayName, setDisplayName ]

View File

@ -1,26 +1,25 @@
// @flow import { IState } from '../app/types';
import { getRoomName } from '../base/conference/functions';
import { getRoomName } from '../base/conference'; import { getDialOutStatusUrl, getDialOutUrl } from '../base/config/functions.web';
import { getDialOutStatusUrl, getDialOutUrl } from '../base/config/functions'; import { isAudioMuted, isVideoMutedByUser } from '../base/media/functions';
import { isAudioMuted, isVideoMutedByUser } from '../base/media';
/** /**
* Selector for the visibility of the 'join by phone' button. * Selector for the visibility of the 'join by phone' button.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {boolean} * @returns {boolean}
*/ */
export function isJoinByPhoneButtonVisible(state: Object): boolean { export function isJoinByPhoneButtonVisible(state: IState): boolean {
return Boolean(getDialOutUrl(state) && getDialOutStatusUrl(state)); return Boolean(getDialOutUrl(state) && getDialOutStatusUrl(state));
} }
/** /**
* Selector for determining if the device status strip is visible or not. * Selector for determining if the device status strip is visible or not.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {boolean} * @returns {boolean}
*/ */
export function isDeviceStatusVisible(state: Object): boolean { export function isDeviceStatusVisible(state: IState): boolean {
return !(isAudioMuted(state) && isVideoMutedByUser(state)) return !(isAudioMuted(state) && isVideoMutedByUser(state))
&& !state['features/base/config'].startSilent; && !state['features/base/config'].startSilent;
} }
@ -28,91 +27,91 @@ export function isDeviceStatusVisible(state: Object): boolean {
/** /**
* Selector for determining if the display name is mandatory. * Selector for determining if the display name is mandatory.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {boolean} * @returns {boolean}
*/ */
export function isDisplayNameRequired(state: Object): boolean { export function isDisplayNameRequired(state: IState): boolean {
return state['features/prejoin']?.isDisplayNameRequired return Boolean(state['features/prejoin']?.isDisplayNameRequired
|| state['features/base/config']?.requireDisplayName; || state['features/base/config']?.requireDisplayName);
} }
/** /**
* Selector for determining if the prejoin display name field is visible. * Selector for determining if the prejoin display name field is visible.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {boolean} * @returns {boolean}
*/ */
export function isPrejoinDisplayNameVisible(state: Object): boolean { export function isPrejoinDisplayNameVisible(state: IState): boolean {
return !state['features/base/config'].prejoinConfig?.hideDisplayName; return !state['features/base/config'].prejoinConfig?.hideDisplayName;
} }
/** /**
* Returns the text for the prejoin status bar. * Returns the text for the prejoin status bar.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {string} * @returns {string}
*/ */
export function getDeviceStatusText(state: Object): string { export function getDeviceStatusText(state: IState): string {
return state['features/prejoin']?.deviceStatusText; return state['features/prejoin']?.deviceStatusText;
} }
/** /**
* Returns the type of the prejoin status bar: 'ok'|'warning'. * Returns the type of the prejoin status bar: 'ok'|'warning'.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {string} * @returns {string}
*/ */
export function getDeviceStatusType(state: Object): string { export function getDeviceStatusType(state: IState): string {
return state['features/prejoin']?.deviceStatusType; return state['features/prejoin']?.deviceStatusType;
} }
/** /**
* Returns the 'conferenceUrl' used for dialing out. * Returns the 'conferenceUrl' used for dialing out.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {string} * @returns {string}
*/ */
export function getDialOutConferenceUrl(state: Object): string { export function getDialOutConferenceUrl(state: IState): string {
return `${getRoomName(state)}@${state['features/base/config'].hosts.muc}`; return `${getRoomName(state)}@${state['features/base/config'].hosts?.muc}`;
} }
/** /**
* Selector for getting the dial out country. * Selector for getting the dial out country.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {Object} * @returns {Object}
*/ */
export function getDialOutCountry(state: Object): Object { export function getDialOutCountry(state: IState) {
return state['features/prejoin'].dialOutCountry; return state['features/prejoin'].dialOutCountry;
} }
/** /**
* Selector for getting the dial out number (without prefix). * Selector for getting the dial out number (without prefix).
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {string} * @returns {string}
*/ */
export function getDialOutNumber(state: Object): string { export function getDialOutNumber(state: IState): string {
return state['features/prejoin'].dialOutNumber; return state['features/prejoin'].dialOutNumber;
} }
/** /**
* Selector for getting the dial out status while calling. * Selector for getting the dial out status while calling.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {string} * @returns {string}
*/ */
export function getDialOutStatus(state: Object): string { export function getDialOutStatus(state: IState): string {
return state['features/prejoin'].dialOutStatus; return state['features/prejoin'].dialOutStatus;
} }
/** /**
* Returns the full dial out number (containing country code and +). * Returns the full dial out number (containing country code and +).
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {string} * @returns {string}
*/ */
export function getFullDialOutNumber(state: Object): string { export function getFullDialOutNumber(state: IState): string {
const dialOutNumber = getDialOutNumber(state); const dialOutNumber = getDialOutNumber(state);
const country = getDialOutCountry(state); const country = getDialOutCountry(state);
@ -122,20 +121,20 @@ export function getFullDialOutNumber(state: Object): string {
/** /**
* Selector for getting the error if any while creating streams. * Selector for getting the error if any while creating streams.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {string} * @returns {string}
*/ */
export function getRawError(state: Object): string { export function getRawError(state: IState): string {
return state['features/prejoin']?.rawError; return state['features/prejoin']?.rawError;
} }
/** /**
* Selector for getting the visibility state for the 'JoinByPhoneDialog'. * Selector for getting the visibility state for the 'JoinByPhoneDialog'.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {boolean} * @returns {boolean}
*/ */
export function isJoinByPhoneDialogVisible(state: Object): boolean { export function isJoinByPhoneDialogVisible(state: IState): boolean {
return state['features/prejoin']?.showJoinByPhoneDialog; return state['features/prejoin']?.showJoinByPhoneDialog;
} }
@ -143,28 +142,28 @@ export function isJoinByPhoneDialogVisible(state: Object): boolean {
* Returns true if the prejoin page is enabled and no flag * Returns true if the prejoin page is enabled and no flag
* to bypass showing the page is present. * to bypass showing the page is present.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {boolean} * @returns {boolean}
*/ */
export function isPrejoinPageVisible(state: Object): boolean { export function isPrejoinPageVisible(state: IState): boolean {
return navigator.product !== 'ReactNative' return Boolean(navigator.product !== 'ReactNative'
&& state['features/base/config'].prejoinConfig?.enabled && state['features/base/config'].prejoinConfig?.enabled
&& state['features/prejoin']?.showPrejoin && state['features/prejoin']?.showPrejoin
&& !(state['features/base/config'].enableForcedReload && state['features/prejoin'].skipPrejoinOnReload); && !(state['features/base/config'].enableForcedReload && state['features/prejoin'].skipPrejoinOnReload));
} }
/** /**
* Returns true if we should auto-knock in case lobby is enabled for the room. * Returns true if we should auto-knock in case lobby is enabled for the room.
* *
* @param {Object} state - The state of the app. * @param {IState} state - The state of the app.
* @returns {boolean} * @returns {boolean}
*/ */
export function shouldAutoKnock(state: Object): boolean { export function shouldAutoKnock(state: IState): boolean {
const { iAmRecorder, iAmSipGateway, autoKnockLobby, prejoinConfig } = state['features/base/config']; const { iAmRecorder, iAmSipGateway, autoKnockLobby, prejoinConfig } = state['features/base/config'];
const { userSelectedSkipPrejoin } = state['features/base/settings']; const { userSelectedSkipPrejoin } = state['features/base/settings'];
const isPrejoinEnabled = prejoinConfig?.enabled; const isPrejoinEnabled = prejoinConfig?.enabled;
return ((isPrejoinEnabled && !userSelectedSkipPrejoin) return Boolean(((isPrejoinEnabled && !userSelectedSkipPrejoin)
|| autoKnockLobby || (iAmRecorder && iAmSipGateway)) || autoKnockLobby || (iAmRecorder && iAmSipGateway))
&& !state['features/lobby'].knocking; && !state['features/lobby'].knocking);
} }