diff --git a/react/features/app/types.ts b/react/features/app/types.ts index e518c6748..efffa8b80 100644 --- a/react/features/app/types.ts +++ b/react/features/app/types.ts @@ -55,6 +55,7 @@ import { INoiseDetectionState } from '../noise-detection/reducer'; import { INoiseSuppressionState } from '../noise-suppression/reducer'; import { INotificationsState } from '../notifications/reducer'; import { IOverlayState } from '../overlay/reducer'; +import { IParticipantsPaneState } from '../participants-pane/reducer'; import { IPollsState } from '../polls/reducer'; import { IPowerMonitorState } from '../power-monitor/reducer'; import { IPrejoinState } from '../prejoin/reducer'; @@ -135,7 +136,7 @@ export interface IState { 'features/noise-suppression': INoiseSuppressionState; 'features/notifications': INotificationsState; 'features/overlay': IOverlayState; - 'features/participants-pane': IParticipantsState; + 'features/participants-pane': IParticipantsPaneState; 'features/polls': IPollsState; 'features/power-monitor': IPowerMonitorState; 'features/prejoin': IPrejoinState; diff --git a/react/features/av-moderation/actions.js b/react/features/av-moderation/actions.ts similarity index 90% rename from react/features/av-moderation/actions.js rename to react/features/av-moderation/actions.ts index d98c8df3d..a034ce2d0 100644 --- a/react/features/av-moderation/actions.js +++ b/react/features/av-moderation/actions.ts @@ -1,8 +1,10 @@ -// @flow - +import { IStore } from '../app/types'; +// eslint-disable-next-line lines-around-comment +// @ts-ignore import { getConferenceState } from '../base/conference'; import { MEDIA_TYPE, type MediaType } from '../base/media/constants'; -import { getParticipantById, isParticipantModerator } from '../base/participants'; +import { getParticipantById, isParticipantModerator } from '../base/participants/functions'; +import { Participant } from '../base/participants/types'; import { isForceMuted } from '../participants-pane/functions'; import { @@ -28,7 +30,7 @@ import { isEnabledFromState } from './functions'; * @param {staring} id - The id of the participant to be approved. * @returns {void} */ -export const approveParticipantAudio = (id: string) => (dispatch: Function, getState: Function) => { +export const approveParticipantAudio = (id: string) => (dispatch: IStore['dispatch'], getState: IStore['getState']) => { const state = getState(); const { conference } = getConferenceState(state); const participant = getParticipantById(state, id); @@ -48,7 +50,7 @@ export const approveParticipantAudio = (id: string) => (dispatch: Function, getS * @param {staring} id - The id of the participant to be approved. * @returns {void} */ -export const approveParticipantVideo = (id: string) => (dispatch: Function, getState: Function) => { +export const approveParticipantVideo = (id: string) => (dispatch: IStore['dispatch'], getState: IStore['getState']) => { const state = getState(); const { conference } = getConferenceState(state); const participant = getParticipantById(state, id); @@ -67,7 +69,7 @@ export const approveParticipantVideo = (id: string) => (dispatch: Function, getS * @param {staring} id - The id of the participant to be approved. * @returns {void} */ -export const approveParticipant = (id: string) => (dispatch: Function) => { +export const approveParticipant = (id: string) => (dispatch: IStore['dispatch']) => { dispatch(approveParticipantAudio(id)); dispatch(approveParticipantVideo(id)); }; @@ -78,7 +80,7 @@ export const approveParticipant = (id: string) => (dispatch: Function) => { * @param {staring} id - The id of the participant to be rejected. * @returns {void} */ -export const rejectParticipantAudio = (id: string) => (dispatch: Function, getState: Function) => { +export const rejectParticipantAudio = (id: string) => (dispatch: IStore['dispatch'], getState: IStore['getState']) => { const state = getState(); const { conference } = getConferenceState(state); const audioModeration = isEnabledFromState(MEDIA_TYPE.AUDIO, state); @@ -98,7 +100,7 @@ export const rejectParticipantAudio = (id: string) => (dispatch: Function, getSt * @param {staring} id - The id of the participant to be rejected. * @returns {void} */ -export const rejectParticipantVideo = (id: string) => (dispatch: Function, getState: Function) => { +export const rejectParticipantVideo = (id: string) => (dispatch: IStore['dispatch'], getState: IStore['getState']) => { const state = getState(); const { conference } = getConferenceState(state); const videoModeration = isEnabledFromState(MEDIA_TYPE.VIDEO, state); @@ -133,10 +135,10 @@ export const disableModeration = (mediaType: MediaType, actor: Object) => { /** * Hides the notification with the participant that asked to unmute audio. * - * @param {Object} participant - The participant for which the notification to be hidden. + * @param {Participant} participant - The participant for which the notification to be hidden. * @returns {Object} */ -export function dismissPendingAudioParticipant(participant: Object) { +export function dismissPendingAudioParticipant(participant: Participant) { return dismissPendingParticipant(participant.id, MEDIA_TYPE.AUDIO); } @@ -270,10 +272,10 @@ export function showModeratedNotification(mediaType: MediaType) { /** * Shows a notification with the participant that asked to audio unmute. * - * @param {Object} participant - The participant for which is the notification. + * @param {Participant} participant - The participant for which is the notification. * @returns {Object} */ -export function participantPendingAudio(participant: Object) { +export function participantPendingAudio(participant: Participant) { return { type: PARTICIPANT_PENDING_AUDIO, participant diff --git a/react/features/av-moderation/functions.js b/react/features/av-moderation/functions.ts similarity index 75% rename from react/features/av-moderation/functions.js rename to react/features/av-moderation/functions.ts index 9d05330a9..0d8d2ca10 100644 --- a/react/features/av-moderation/functions.js +++ b/react/features/av-moderation/functions.ts @@ -1,7 +1,7 @@ -// @flow - +import { IState } from '../app/types'; import { MEDIA_TYPE, type MediaType } from '../base/media/constants'; import { isLocalParticipantModerator } from '../base/participants/functions'; +import { Participant } from '../base/participants/types'; import { isInBreakoutRoom } from '../breakout-rooms/functions'; import { MEDIA_TYPE_TO_WHITELIST_STORE_KEY, MEDIA_TYPE_TO_PENDING_STORE_KEY } from './constants'; @@ -9,27 +9,27 @@ import { MEDIA_TYPE_TO_WHITELIST_STORE_KEY, MEDIA_TYPE_TO_PENDING_STORE_KEY } fr /** * Returns this feature's root state. * - * @param {Object} state - Global state. + * @param {IState} state - Global state. * @returns {Object} Feature state. */ -const getState = state => state['features/av-moderation']; +const getState = (state: IState) => state['features/av-moderation']; /** * We use to construct once the empty array so we can keep the same instance between calls * of getParticipantsAskingToAudioUnmute. * - * @type {*[]} + * @type {any[]} */ -const EMPTY_ARRAY = []; +const EMPTY_ARRAY: any[] = []; /** * Returns whether moderation is enabled per media type. * * @param {MEDIA_TYPE} mediaType - The media type to check. - * @param {Object} state - Global state. - * @returns {null|boolean|*} + * @param {IState} state - Global state. + * @returns {boolean} */ -export const isEnabledFromState = (mediaType: MediaType, state: Object) => +export const isEnabledFromState = (mediaType: MediaType, state: IState) => (mediaType === MEDIA_TYPE.AUDIO ? getState(state)?.audioModerationEnabled : getState(state)?.videoModerationEnabled) === true; @@ -38,16 +38,16 @@ export const isEnabledFromState = (mediaType: MediaType, state: Object) => * Returns whether moderation is enabled per media type. * * @param {MEDIA_TYPE} mediaType - The media type to check. - * @returns {null|boolean|*} + * @returns {boolean} */ -export const isEnabled = (mediaType: MediaType) => (state: Object) => isEnabledFromState(mediaType, state); +export const isEnabled = (mediaType: MediaType) => (state: IState) => isEnabledFromState(mediaType, state); /** * Returns whether moderation is supported by the backend. * - * @returns {null|boolean} + * @returns {boolean} */ -export const isSupported = () => (state: Object) => { +export const isSupported = () => (state: IState) => { const { conference } = state['features/base/conference']; return Boolean(!isInBreakoutRoom(state) && conference?.isAVModerationSupported()); @@ -57,10 +57,10 @@ export const isSupported = () => (state: Object) => { * Returns whether local participant is approved to unmute a media type. * * @param {MEDIA_TYPE} mediaType - The media type to check. - * @param {Object} state - Global state. + * @param {IState} state - Global state. * @returns {boolean} */ -export const isLocalParticipantApprovedFromState = (mediaType: MediaType, state: Object) => { +export const isLocalParticipantApprovedFromState = (mediaType: MediaType, state: IState) => { const approved = (mediaType === MEDIA_TYPE.AUDIO ? getState(state).audioUnmuteApproved : getState(state).videoUnmuteApproved) === true; @@ -72,10 +72,10 @@ export const isLocalParticipantApprovedFromState = (mediaType: MediaType, state: * Returns whether local participant is approved to unmute a media type. * * @param {MEDIA_TYPE} mediaType - The media type to check. - * @returns {null|boolean|*} + * @returns {boolean} */ export const isLocalParticipantApproved = (mediaType: MediaType) => - (state: Object) => + (state: IState) => isLocalParticipantApprovedFromState(mediaType, state); /** @@ -85,10 +85,13 @@ export const isLocalParticipantApproved = (mediaType: MediaType) => * @param {MEDIA_TYPE} mediaType - The media type to check. * @returns {boolean} */ -export const isParticipantApproved = (id: string, mediaType: MediaType) => (state: Object) => { +export const isParticipantApproved = (id: string, mediaType: MediaType) => (state: IState) => { const storeKey = MEDIA_TYPE_TO_WHITELIST_STORE_KEY[mediaType]; - return Boolean(getState(state)[storeKey][id]); + const avModerationState = getState(state); + const stateForMediaType = avModerationState[storeKey as keyof typeof avModerationState]; + + return Boolean(stateForMediaType && stateForMediaType[id as keyof typeof stateForMediaType]); }; /** @@ -98,7 +101,7 @@ export const isParticipantApproved = (id: string, mediaType: MediaType) => (stat * @param {MEDIA_TYPE} mediaType - The media type to check. * @returns {boolean} */ -export const isParticipantPending = (participant: Object, mediaType: MediaType) => (state: Object) => { +export const isParticipantPending = (participant: Participant, mediaType: MediaType) => (state: IState) => { const storeKey = MEDIA_TYPE_TO_PENDING_STORE_KEY[mediaType]; const arr = getState(state)[storeKey]; @@ -112,7 +115,7 @@ export const isParticipantPending = (participant: Object, mediaType: MediaType) * @param {Object} state - The global state. * @returns {Array} */ -export const getParticipantsAskingToAudioUnmute = (state: Object) => { +export const getParticipantsAskingToAudioUnmute = (state: IState) => { if (isLocalParticipantModerator(state)) { return getState(state).pendingAudio; } @@ -128,6 +131,6 @@ export const getParticipantsAskingToAudioUnmute = (state: Object) => { * @param {Object} state - The global state. * @returns {boolean} */ -export const shouldShowModeratedNotification = (mediaType: MediaType, state: Object) => +export const shouldShowModeratedNotification = (mediaType: MediaType, state: IState) => isEnabledFromState(mediaType, state) && !isLocalParticipantApprovedFromState(mediaType, state); diff --git a/react/features/av-moderation/middleware.js b/react/features/av-moderation/middleware.ts similarity index 85% rename from react/features/av-moderation/middleware.js rename to react/features/av-moderation/middleware.ts index 1bac5fde1..9a626cd24 100644 --- a/react/features/av-moderation/middleware.js +++ b/react/features/av-moderation/middleware.ts @@ -1,26 +1,28 @@ -// @flow +/* eslint-disable lines-around-comment */ import { batch } from 'react-redux'; -import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app'; +import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app/actionTypes'; +// @ts-ignore import { getConferenceState } from '../base/conference'; import { JitsiConferenceEvents } from '../base/lib-jitsi-meet'; -import { MEDIA_TYPE } from '../base/media'; +import { MediaType, MEDIA_TYPE } from '../base/media/constants'; +import { PARTICIPANT_UPDATED } from '../base/participants/actionTypes'; +import { raiseHand } from '../base/participants/actions'; import { getLocalParticipant, getRemoteParticipants, hasRaisedHand, isLocalParticipantModerator, - isParticipantModerator, - PARTICIPANT_UPDATED, - raiseHand -} from '../base/participants'; -import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux'; + isParticipantModerator +} from '../base/participants/functions'; +import MiddlewareRegistry from '../base/redux/MiddlewareRegistry'; +import StateListenerRegistry from '../base/redux/StateListenerRegistry'; +// @ts-ignore import { playSound, registerSound, unregisterSound } from '../base/sounds'; -import { - NOTIFICATION_TIMEOUT_TYPE, - hideNotification, - showNotification -} from '../notifications'; +// @ts-ignore +import { hideNotification, showNotification } from '../notifications'; +import { NOTIFICATION_TIMEOUT_TYPE } from '../notifications/constants'; +// @ts-ignore import { muteLocal } from '../video-menu/actions.any'; import { @@ -61,7 +63,7 @@ import { } from './functions'; import { ASKED_TO_UNMUTE_FILE } from './sounds'; -declare var APP: Object; +declare const APP: any; MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { const { type } = action; @@ -79,7 +81,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { case LOCAL_PARTICIPANT_MODERATION_NOTIFICATION: { let descriptionKey; let titleKey; - let uid; + let uid: string | undefined; const localParticipant = getLocalParticipant(getState); const raisedHand = hasRaisedHand(localParticipant); @@ -149,7 +151,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { isParticipantPending(participant, MEDIA_TYPE.AUDIO)(state) && dispatch(dismissPendingAudioParticipant(participant)); } - } else if (participant.id === getLocalParticipant(state).id + } else if (participant.id === getLocalParticipant(state)?.id && /* the new role */ isParticipantModerator(participant)) { // this is the granted moderator case @@ -178,7 +180,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { if (typeof APP !== 'undefined') { const local = getLocalParticipant(getState()); - APP.API.notifyParticipantApproved(local.id, action.mediaType); + APP.API.notifyParticipantApproved(local?.id, action.mediaType); } break; } @@ -192,7 +194,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { if (typeof APP !== 'undefined') { const local = getLocalParticipant(getState()); - APP.API.notifyParticipantRejected(local.id, action.mediaType); + APP.API.notifyParticipantRejected(local?.id, action.mediaType); } break; } @@ -216,7 +218,7 @@ StateListenerRegistry.register( (conference, { dispatch }, previousConference) => { if (conference && !previousConference) { // local participant is allowed to unmute - conference.on(JitsiConferenceEvents.AV_MODERATION_APPROVED, ({ mediaType }) => { + conference.on(JitsiConferenceEvents.AV_MODERATION_APPROVED, ({ mediaType }: { mediaType: MediaType; }) => { dispatch(localParticipantApproved(mediaType)); // Audio & video moderation are both enabled at the same time. @@ -233,18 +235,20 @@ StateListenerRegistry.register( } }); - conference.on(JitsiConferenceEvents.AV_MODERATION_REJECTED, ({ mediaType }) => { + conference.on(JitsiConferenceEvents.AV_MODERATION_REJECTED, ({ mediaType }: { mediaType: MediaType; }) => { dispatch(localParticipantRejected(mediaType)); }); - conference.on(JitsiConferenceEvents.AV_MODERATION_CHANGED, ({ enabled, mediaType, actor }) => { + conference.on(JitsiConferenceEvents.AV_MODERATION_CHANGED, ({ enabled, mediaType, actor }: { + actor: Object; enabled: boolean; mediaType: MediaType; + }) => { enabled ? dispatch(enableModeration(mediaType, actor)) : dispatch(disableModeration(mediaType, actor)); }); // this is received by moderators conference.on( JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_APPROVED, - ({ participant, mediaType }) => { + ({ participant, mediaType }: { mediaType: MediaType; participant: { _id: string; }; }) => { const { _id: id } = participant; batch(() => { @@ -259,7 +263,7 @@ StateListenerRegistry.register( // this is received by moderators conference.on( JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_REJECTED, - ({ participant, mediaType }) => { + ({ participant, mediaType }: { mediaType: MediaType; participant: { _id: string; }; }) => { const { _id: id } = participant; dispatch(participantRejected(id, mediaType)); diff --git a/react/features/av-moderation/sounds.js b/react/features/av-moderation/sounds.ts similarity index 100% rename from react/features/av-moderation/sounds.js rename to react/features/av-moderation/sounds.ts diff --git a/react/features/base/conference/constants.js b/react/features/base/conference/constants.ts similarity index 100% rename from react/features/base/conference/constants.js rename to react/features/base/conference/constants.ts diff --git a/react/features/base/participants/functions.ts b/react/features/base/participants/functions.ts index a2b0dabb4..11dc63117 100644 --- a/react/features/base/participants/functions.ts +++ b/react/features/base/participants/functions.ts @@ -409,7 +409,7 @@ export function getParticipantPresenceStatus(stateful: IStateful, id: string) { * features/base/participants. * @returns {Map} */ -export function getRemoteParticipants(stateful: IStateful) { +export function getRemoteParticipants(stateful: IStateful): Map { return toState(stateful)['features/base/participants'].remote; } @@ -635,7 +635,7 @@ async function _getFirstLoadableAvatarUrl(participant: Participant, store: IStor * features/base/participants. * @returns {Array} */ -export function getRaiseHandsQueue(stateful: IStateful): Array { +export function getRaiseHandsQueue(stateful: IStateful): Array<{ id: string; raisedHandTimestamp: number; }> { const { raisedHandsQueue } = toState(stateful)['features/base/participants']; return raisedHandsQueue; diff --git a/react/features/base/participants/reducer.ts b/react/features/base/participants/reducer.ts index d84f783d3..51eb03f7d 100644 --- a/react/features/base/participants/reducer.ts +++ b/react/features/base/participants/reducer.ts @@ -80,7 +80,7 @@ export interface IParticipantsState { fakeParticipants: Map; local?: LocalParticipant; localScreenShare?: Participant; - overwrittenNameList: Object; + overwrittenNameList: { [id: string]: string; }; pinnedParticipant?: string; raisedHandsQueue: Array<{ id: string; raisedHandTimestamp: number; }>; remote: Map; diff --git a/react/features/base/participants/types.ts b/react/features/base/participants/types.ts index 3ac619859..8c2bb7fcd 100644 --- a/react/features/base/participants/types.ts +++ b/react/features/base/participants/types.ts @@ -3,6 +3,7 @@ export interface Participant { botType?: string; conference?: Object; connectionStatus?: string; + displayName?: string; dominantSpeaker?: boolean; e2eeSupported?: boolean; email?: string; diff --git a/react/features/base/redux/MiddlewareRegistry.ts b/react/features/base/redux/MiddlewareRegistry.ts index 0660b69fa..8a97085b9 100644 --- a/react/features/base/redux/MiddlewareRegistry.ts +++ b/react/features/base/redux/MiddlewareRegistry.ts @@ -1,5 +1,7 @@ import { applyMiddleware, Middleware } from 'redux'; +import { IState } from '../../app/types'; + /** * A registry for Redux middleware, allowing features to register their * middleware without needing to create additional inter-feature dependencies. @@ -40,7 +42,7 @@ class MiddlewareRegistry { * @param {Middleware} middleware - A Redux middleware. * @returns {void} */ - register(middleware: Middleware) { + register(middleware: Middleware) { this._elements.push(middleware); } } diff --git a/react/features/base/redux/StateListenerRegistry.ts b/react/features/base/redux/StateListenerRegistry.ts index 883e7a2cf..0418f4955 100644 --- a/react/features/base/redux/StateListenerRegistry.ts +++ b/react/features/base/redux/StateListenerRegistry.ts @@ -1,5 +1,7 @@ import { Store } from 'redux'; +import { IState } from '../../app/types'; + import { equals } from './functions'; import logger from './logger'; @@ -34,7 +36,7 @@ type Listener * {@code prevSelection}. The associated {@code Listener} will only be invoked * if the returned value is other than {@code prevSelection}. */ -type Selector = (state: Object, prevSelection: any) => any; +type Selector = (state: IState, prevSelection: any) => any; /** * Options that can be passed to the register method. diff --git a/react/features/breakout-rooms/actions.js b/react/features/breakout-rooms/actions.ts similarity index 86% rename from react/features/breakout-rooms/actions.js rename to react/features/breakout-rooms/actions.ts index f2761f4b9..7a9a68ffa 100644 --- a/react/features/breakout-rooms/actions.js +++ b/react/features/breakout-rooms/actions.ts @@ -1,34 +1,36 @@ -// @flow - +/* eslint-disable lines-around-comment */ import i18next from 'i18next'; import _ from 'lodash'; -import type { Dispatch } from 'redux'; -import { createBreakoutRoomsEvent, sendAnalytics } from '../analytics'; +import { createBreakoutRoomsEvent } from '../analytics/AnalyticsEvents'; +import { sendAnalytics } from '../analytics/functions'; +import { IStore } from '../app/types'; import { - CONFERENCE_LEAVE_REASONS, conferenceLeft, conferenceWillLeave, createConference, getCurrentConference + // @ts-ignore } from '../base/conference'; +import { CONFERENCE_LEAVE_REASONS } from '../base/conference/constants'; import { - MEDIA_TYPE, setAudioMuted, setVideoMuted + // @ts-ignore } from '../base/media'; -import { getRemoteParticipants } from '../base/participants'; +import { MEDIA_TYPE } from '../base/media/constants'; +import { getRemoteParticipants } from '../base/participants/functions'; import { getLocalTracks, isLocalCameraTrackMuted, isLocalTrackMuted + // @ts-ignore } from '../base/tracks'; +// @ts-ignore import { createDesiredLocalTracks } from '../base/tracks/actions'; -import { - NOTIFICATION_TIMEOUT_TYPE, - clearNotifications, - showNotification -} from '../notifications'; +// @ts-ignore +import { clearNotifications, showNotification } from '../notifications'; +import { NOTIFICATION_TIMEOUT_TYPE } from '../notifications/constants'; import { _RESET_BREAKOUT_ROOMS, _UPDATE_ROOM_COUNTER } from './actionTypes'; import { FEATURE_KEY } from './constants'; @@ -39,7 +41,7 @@ import { } from './functions'; import logger from './logger'; -declare var APP: Object; +declare let APP: any; /** * Action to create a breakout room. @@ -48,7 +50,7 @@ declare var APP: Object; * @returns {Function} */ export function createBreakoutRoom(name?: string) { - return (dispatch: Dispatch, getState: Function) => { + return (dispatch: IStore['dispatch'], getState: IStore['getState']) => { const state = getState(); let { roomCounter } = state[FEATURE_KEY]; const subject = name || i18next.t('breakoutRooms.defaultName', { index: ++roomCounter }); @@ -60,7 +62,6 @@ export function createBreakoutRoom(name?: string) { roomCounter }); - // $FlowExpectedError getCurrentConference(state)?.getBreakoutRooms() ?.createBreakoutRoom(subject); }; @@ -73,7 +74,7 @@ export function createBreakoutRoom(name?: string) { * @returns {Function} */ export function closeBreakoutRoom(roomId: string) { - return (dispatch: Dispatch, getState: Function) => { + return (dispatch: IStore['dispatch'], getState: IStore['getState']) => { const rooms = getBreakoutRooms(getState); const room = rooms[roomId]; const mainRoom = getMainRoom(getState); @@ -82,8 +83,6 @@ export function closeBreakoutRoom(roomId: string) { if (room && mainRoom) { Object.values(room.participants).forEach(p => { - - // $FlowExpectedError dispatch(sendParticipantToRoom(p.jid, mainRoom.id)); }); } @@ -97,7 +96,7 @@ export function closeBreakoutRoom(roomId: string) { * @returns {Function} */ export function removeBreakoutRoom(breakoutRoomJid: string) { - return (dispatch: Dispatch, getState: Function) => { + return (dispatch: IStore['dispatch'], getState: IStore['getState']) => { sendAnalytics(createBreakoutRoomsEvent('remove')); const room = getRoomByJid(getState, breakoutRoomJid); @@ -110,8 +109,6 @@ export function removeBreakoutRoom(breakoutRoomJid: string) { if (Object.keys(room.participants).length > 0) { dispatch(closeBreakoutRoom(room.id)); } - - // $FlowExpectedError getCurrentConference(getState)?.getBreakoutRooms() ?.removeBreakoutRoom(breakoutRoomJid); }; @@ -123,9 +120,9 @@ export function removeBreakoutRoom(breakoutRoomJid: string) { * @returns {Function} */ export function autoAssignToBreakoutRooms() { - return (dispatch: Dispatch, getState: Function) => { + return (dispatch: IStore['dispatch'], getState: IStore['getState']) => { const rooms = getBreakoutRooms(getState); - const breakoutRooms = _.filter(rooms, (room: Object) => !room.isMainRoom); + const breakoutRooms = _.filter(rooms, room => !room.isMainRoom); if (breakoutRooms) { sendAnalytics(createBreakoutRoomsEvent('auto.assign')); @@ -149,7 +146,7 @@ export function autoAssignToBreakoutRooms() { * @returns {Function} */ export function sendParticipantToRoom(participantId: string, roomId: string) { - return (dispatch: Dispatch, getState: Function) => { + return (dispatch: IStore['dispatch'], getState: IStore['getState']) => { const rooms = getBreakoutRooms(getState); const room = rooms[roomId]; @@ -169,7 +166,6 @@ export function sendParticipantToRoom(participantId: string, roomId: string) { return; } - // $FlowExpectedError getCurrentConference(getState)?.getBreakoutRooms() ?.sendParticipantToRoom(participantJid, room.jid); }; @@ -182,14 +178,12 @@ export function sendParticipantToRoom(participantId: string, roomId: string) { * @returns {Function} */ export function moveToRoom(roomId?: string) { - return async (dispatch: Dispatch, getState: Function) => { + return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => { const mainRoomId = getMainRoom(getState)?.id; - let _roomId = roomId || mainRoomId; + let _roomId: string | undefined | String = roomId || mainRoomId; // Check if we got a full JID. - // $FlowExpectedError - if (_roomId?.indexOf('@') !== -1) { - // $FlowExpectedError + if (_roomId && _roomId?.indexOf('@') !== -1) { const [ id, ...domainParts ] = _roomId.split('@'); // On mobile we first store the room and the connection is created @@ -198,16 +192,14 @@ export function moveToRoom(roomId?: string) { // eslint-disable-next-line no-new-wrappers _roomId = new String(id); - - // $FlowExpectedError + // @ts-ignore _roomId.domain = domainParts.join('@'); } - // $FlowExpectedError const roomIdStr = _roomId?.toString(); const goToMainRoom = roomIdStr === mainRoomId; const rooms = getBreakoutRooms(getState); - const targetRoom = rooms[roomIdStr]; + const targetRoom = rooms[roomIdStr ?? '']; if (!targetRoom) { logger.warn(`Unknown room: ${targetRoom}`); @@ -302,7 +294,6 @@ function _findParticipantJid(getState: Function, participantId: string) { if (!participantId.includes('@')) { const p = conference.getParticipantById(participantId); - // $FlowExpectedError _participantId = p?.getJid(); // This will be the room JID. } @@ -310,11 +301,8 @@ function _findParticipantJid(getState: Function, participantId: string) { const rooms = getBreakoutRooms(getState); for (const room of Object.values(rooms)) { - // $FlowExpectedError const participants = room.participants || {}; const p = participants[_participantId] - - // $FlowExpectedError || Object.values(participants).find(item => item.jid === _participantId); if (p) { diff --git a/react/features/breakout-rooms/constants.ts b/react/features/breakout-rooms/constants.ts index 971777c58..8873a4c34 100644 --- a/react/features/breakout-rooms/constants.ts +++ b/react/features/breakout-rooms/constants.ts @@ -1,5 +1,3 @@ -// @flow - /** * Key for this feature. */ diff --git a/react/features/breakout-rooms/functions.js b/react/features/breakout-rooms/functions.ts similarity index 75% rename from react/features/breakout-rooms/functions.js rename to react/features/breakout-rooms/functions.ts index 057ffec18..20e6c4288 100644 --- a/react/features/breakout-rooms/functions.js +++ b/react/features/breakout-rooms/functions.ts @@ -1,36 +1,38 @@ -// @flow - import _ from 'lodash'; +import { IStateful } from '../base/app/types'; +// eslint-disable-next-line lines-around-comment +// @ts-ignore import { getCurrentConference } from '../base/conference'; -import { getParticipantById, getParticipantCount, isLocalParticipantModerator } from '../base/participants'; -import { toState } from '../base/redux'; +import { getParticipantById, getParticipantCount, isLocalParticipantModerator } from '../base/participants/functions'; +import { toState } from '../base/redux/functions'; import { FEATURE_KEY } from './constants'; +import { IRoom, IRooms } from './types'; /** * Returns the rooms object for breakout rooms. * - * @param {Function|Object} stateful - The redux store, the redux + * @param {IStateful} stateful - The redux store, the redux * {@code getState} function, or the redux state itself. * @returns {Object} Object of rooms. */ -export const getBreakoutRooms = (stateful: Function | Object) => toState(stateful)[FEATURE_KEY].rooms; +export const getBreakoutRooms = (stateful: IStateful): IRooms => toState(stateful)[FEATURE_KEY].rooms; /** * Returns the main room. * - * @param {Function|Object} stateful - The redux store, the redux + * @param {IStateful} stateful - The redux store, the redux * {@code getState} function, or the redux state itself. - * @returns {Object|undefined} The main room object, or undefined. + * @returns {IRoom|undefined} The main room object, or undefined. */ -export const getMainRoom = (stateful: Function | Object) => { +export const getMainRoom = (stateful: IStateful) => { const rooms = getBreakoutRooms(stateful); - return _.find(rooms, (room: Object) => room.isMainRoom); + return _.find(rooms, room => Boolean(room.isMainRoom)); }; -export const getRoomsInfo = (stateful: Function | Object) => { +export const getRoomsInfo = (stateful: IStateful) => { const breakoutRooms = getBreakoutRooms(stateful); const conference = getCurrentConference(stateful); @@ -46,7 +48,7 @@ export const getRoomsInfo = (stateful: Function | Object) => { isMainRoom: true, id: conference?.room?.roomjid, jid: conference?.room?.myroomjid, - participants: conference && conference.participants && Object.keys(conference.participants).length + participants: conference?.participants && Object.keys(conference.participants).length ? Object.keys(conference.participants).map(participantId => { const participantItem = conference?.participants[participantId]; const storeParticipant = getParticipantById(stateful, participantItem._id); @@ -96,42 +98,40 @@ export const getRoomsInfo = (stateful: Function | Object) => { /** * Returns the room by Jid. * - * @param {Function|Object} stateful - The redux store, the redux + * @param {IStateful} stateful - The redux store, the redux * {@code getState} function, or the redux state itself. * @param {string} roomJid - The jid of the room. - * @returns {Object|undefined} The main room object, or undefined. + * @returns {IRoom|undefined} The main room object, or undefined. */ -export const getRoomByJid = (stateful: Function | Object, roomJid: string): Object => { +export const getRoomByJid = (stateful: IStateful, roomJid: string) => { const rooms = getBreakoutRooms(stateful); - return _.find(rooms, (room: Object) => room.jid === roomJid); + return _.find(rooms, (room: IRoom) => room.jid === roomJid); }; /** * Returns the id of the current room. * - * @param {Function|Object} stateful - The redux store, the redux + * @param {IStateful} stateful - The redux store, the redux * {@code getState} function, or the redux state itself. * @returns {string} Room id or undefined. */ -export const getCurrentRoomId = (stateful: Function | Object) => { +export const getCurrentRoomId = (stateful: IStateful) => { const conference = getCurrentConference(stateful); - // $FlowExpectedError return conference?.getName(); }; /** * Determines whether the local participant is in a breakout room. * - * @param {Function|Object} stateful - The redux store, the redux + * @param {IStateful} stateful - The redux store, the redux * {@code getState} function, or the redux state itself. * @returns {boolean} */ -export const isInBreakoutRoom = (stateful: Function | Object) => { +export const isInBreakoutRoom = (stateful: IStateful) => { const conference = getCurrentConference(stateful); - // $FlowExpectedError return conference?.getBreakoutRooms() ?.isBreakoutRoom(); }; @@ -139,11 +139,11 @@ export const isInBreakoutRoom = (stateful: Function | Object) => { /** * Returns the breakout rooms config. * - * @param {Function|Object} stateful - The redux store, the redux + * @param {IStateful} stateful - The redux store, the redux * {@code getState} function, or the redux state itself. * @returns {Object} */ -export const getBreakoutRoomsConfig = (stateful: Function | Object) => { +export const getBreakoutRoomsConfig = (stateful: IStateful) => { const state = toState(stateful); const { breakoutRooms = {} } = state['features/base/config']; @@ -153,10 +153,10 @@ export const getBreakoutRoomsConfig = (stateful: Function | Object) => { /** * Returns whether the add breakout room button is visible. * - * @param {Function | Object} stateful - Global state. + * @param {IStateful} stateful - Global state. * @returns {boolean} */ -export const isAddBreakoutRoomButtonVisible = (stateful: Function | Object) => { +export const isAddBreakoutRoomButtonVisible = (stateful: IStateful) => { const state = toState(stateful); const isLocalModerator = isLocalParticipantModerator(state); const { conference } = state['features/base/conference']; @@ -169,10 +169,10 @@ export const isAddBreakoutRoomButtonVisible = (stateful: Function | Object) => { /** * Returns whether the auto assign participants to breakout rooms button is visible. * - * @param {Function | Object} stateful - Global state. + * @param {IStateful} stateful - Global state. * @returns {boolean} */ -export const isAutoAssignParticipantsVisible = (stateful: Function | Object) => { +export const isAutoAssignParticipantsVisible = (stateful: IStateful) => { const state = toState(stateful); const rooms = getBreakoutRooms(state); const inBreakoutRoom = isInBreakoutRoom(state); diff --git a/react/features/breakout-rooms/logger.js b/react/features/breakout-rooms/logger.ts similarity index 93% rename from react/features/breakout-rooms/logger.js rename to react/features/breakout-rooms/logger.ts index 818295b85..8fad5d3e2 100644 --- a/react/features/breakout-rooms/logger.js +++ b/react/features/breakout-rooms/logger.ts @@ -1,5 +1,3 @@ -// @flow - import { getLogger } from '../base/logging/functions'; import { FEATURE_KEY } from './constants'; diff --git a/react/features/breakout-rooms/middleware.js b/react/features/breakout-rooms/middleware.ts similarity index 79% rename from react/features/breakout-rooms/middleware.js rename to react/features/breakout-rooms/middleware.ts index 75f2f59a8..ffe12a3cc 100644 --- a/react/features/breakout-rooms/middleware.js +++ b/react/features/breakout-rooms/middleware.ts @@ -1,15 +1,18 @@ -// @flow - import { JitsiConferenceEvents } from '../base/lib-jitsi-meet'; -import { getParticipantById } from '../base/participants'; -import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux'; -import { editMessage, MESSAGE_TYPE_REMOTE } from '../chat'; +import { getParticipantById } from '../base/participants/functions'; +import MiddlewareRegistry from '../base/redux/MiddlewareRegistry'; +import StateListenerRegistry from '../base/redux/StateListenerRegistry'; +// eslint-disable-next-line lines-around-comment +// @ts-ignore +import { editMessage } from '../chat'; +import { MESSAGE_TYPE_REMOTE } from '../chat/constants'; import { UPDATE_BREAKOUT_ROOMS } from './actionTypes'; import { moveToRoom } from './actions'; import logger from './logger'; +import { IRooms } from './types'; -declare var APP: Object; +declare const APP: any; /** * Registers a change handler for state['features/base/conference'].conference to @@ -19,12 +22,14 @@ StateListenerRegistry.register( state => state['features/base/conference'].conference, (conference, { dispatch }, previousConference) => { if (conference && !previousConference) { - conference.on(JitsiConferenceEvents.BREAKOUT_ROOMS_MOVE_TO_ROOM, roomId => { + conference.on(JitsiConferenceEvents.BREAKOUT_ROOMS_MOVE_TO_ROOM, (roomId: string) => { logger.debug(`Moving to room: ${roomId}`); dispatch(moveToRoom(roomId)); }); - conference.on(JitsiConferenceEvents.BREAKOUT_ROOMS_UPDATED, ({ rooms, roomCounter }) => { + conference.on(JitsiConferenceEvents.BREAKOUT_ROOMS_UPDATED, ({ rooms, roomCounter }: { + roomCounter: number; rooms: IRooms; + }) => { logger.debug('Room list updated'); if (typeof APP !== 'undefined') { APP.API.notifyBreakoutRoomsUpdated(rooms); @@ -48,9 +53,9 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { const { overwrittenNameList } = getState()['features/base/participants']; if (Object.keys(overwrittenNameList).length > 0) { - const newRooms = {}; + const newRooms: IRooms = {}; - Object.entries(action.rooms).forEach(([ key, r ]) => { + Object.entries(action.rooms as IRooms).forEach(([ key, r ]) => { let participants = r?.participants || {}; let jid; @@ -62,7 +67,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { ...participants, [jid]: { ...participants[jid], - displayName: overwrittenNameList[id] + displayName: overwrittenNameList[id as keyof typeof overwrittenNameList] } }; } @@ -81,12 +86,11 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { // edit the chat history to match names for participants in breakout rooms const { messages } = getState()['features/chat']; - messages && messages.forEach(m => { + messages?.forEach(m => { if (m.messageType === MESSAGE_TYPE_REMOTE && !getParticipantById(getState(), m.id)) { - const rooms = action.rooms; + const rooms: IRooms = action.rooms; for (const room of Object.values(rooms)) { - // $FlowExpectedError const participants = room.participants || {}; const matchedJid = Object.keys(participants).find(jid => jid.endsWith(m.id)); diff --git a/react/features/breakout-rooms/reducer.ts b/react/features/breakout-rooms/reducer.ts index a30bc4895..0078ddded 100644 --- a/react/features/breakout-rooms/reducer.ts +++ b/react/features/breakout-rooms/reducer.ts @@ -6,6 +6,7 @@ import { UPDATE_BREAKOUT_ROOMS } from './actionTypes'; import { FEATURE_KEY } from './constants'; +import { IRooms } from './types'; const DEFAULT_STATE = { rooms: {}, @@ -14,21 +15,7 @@ const DEFAULT_STATE = { export interface IBreakoutRoomsState { roomCounter: number; - rooms: { - [id: string]: { - id: string; - isMainRoom?: boolean; - jid: string; - name: string; - participants: { - [jid: string]: { - displayName: string; - jid: string; - role: string; - }; - }; - }; - }; + rooms: IRooms; } /** diff --git a/react/features/breakout-rooms/types.ts b/react/features/breakout-rooms/types.ts new file mode 100644 index 000000000..8eedeb8e9 --- /dev/null +++ b/react/features/breakout-rooms/types.ts @@ -0,0 +1,17 @@ +export interface IRoom { + id: string; + isMainRoom?: boolean; + jid: string; + name: string; + participants: { + [jid: string]: { + displayName: string; + jid: string; + role: string; + }; + }; +} + +export interface IRooms { + [jid: string]: IRoom; +} diff --git a/react/features/participants-pane/components/breakout-rooms/components/web/CollapsibleRoom.tsx b/react/features/participants-pane/components/breakout-rooms/components/web/CollapsibleRoom.tsx index 2e5b128b5..611104265 100644 --- a/react/features/participants-pane/components/breakout-rooms/components/web/CollapsibleRoom.tsx +++ b/react/features/participants-pane/components/breakout-rooms/components/web/CollapsibleRoom.tsx @@ -15,7 +15,6 @@ import { withPixelLineHeight } from '../../../../../base/styles/functions.web'; // @ts-ignore import { showOverflowDrawer } from '../../../../../toolbox/functions.web'; import { ACTION_TRIGGER } from '../../../../constants'; -// @ts-ignore import { participantMatchesSearch } from '../../../../functions'; import ParticipantActionEllipsis from '../../../web/ParticipantActionEllipsis'; import ParticipantItem from '../../../web/ParticipantItem'; diff --git a/react/features/participants-pane/components/web/MeetingParticipants.tsx b/react/features/participants-pane/components/web/MeetingParticipants.tsx index 94f486c87..ebd949f01 100644 --- a/react/features/participants-pane/components/web/MeetingParticipants.tsx +++ b/react/features/participants-pane/components/web/MeetingParticipants.tsx @@ -118,8 +118,6 @@ function MeetingParticipants({ <>
{currentRoom?.name - - // $FlowExpectedError ? `${currentRoom.name} (${participantsCount})` : t('participantsPane.headings.participantsList', { count: participantsCount })}
diff --git a/react/features/participants-pane/functions.js b/react/features/participants-pane/functions.ts similarity index 75% rename from react/features/participants-pane/functions.js rename to react/features/participants-pane/functions.ts index 466155478..866319cb6 100644 --- a/react/features/participants-pane/functions.js +++ b/react/features/participants-pane/functions.ts @@ -1,11 +1,13 @@ -// @flow - +/* eslint-disable lines-around-comment */ +import { IState } from '../app/types'; import { isParticipantApproved, isEnabledFromState, isLocalParticipantApprovedFromState, isSupported } from '../av-moderation/functions'; +import { IStateful } from '../base/app/types'; +// @ts-ignore import { getFeatureFlag, INVITE_ENABLED } from '../base/flags'; import { MEDIA_TYPE, type MediaType } from '../base/media/constants'; import { @@ -16,7 +18,9 @@ import { getRemoteParticipantsSorted, getRaiseHandsQueue } from '../base/participants/functions'; -import { toState } from '../base/redux'; +import { Participant } from '../base/participants/types'; +import { toState } from '../base/redux/functions'; +// @ts-ignore import { normalizeAccents } from '../base/util/strings'; import { isInBreakoutRoom } from '../breakout-rooms/functions'; @@ -25,11 +29,11 @@ import { QUICK_ACTION_BUTTON, REDUCER_KEY, MEDIA_STATE } from './constants'; /** * Find the first styled ancestor component of an element. * - * @param {Element} target - Element to look up. + * @param {HTMLElement|null} target - Element to look up. * @param {string} cssClass - Styled component reference. - * @returns {Element|null} Ancestor. + * @returns {HTMLElement|null} Ancestor. */ -export const findAncestorByClass = (target: Object, cssClass: string) => { +export const findAncestorByClass = (target: HTMLElement | null, cssClass: string): HTMLElement | null => { if (!target || target.classList.contains(cssClass)) { return target; } @@ -40,14 +44,14 @@ export const findAncestorByClass = (target: Object, cssClass: string) => { /** * Checks if a participant is force muted. * - * @param {Object} participant - The participant. + * @param {Participant|undefined} participant - The participant. * @param {MediaType} mediaType - The media type. - * @param {Object} state - The redux state. + * @param {IState} state - The redux state. * @returns {MediaState} */ -export function isForceMuted(participant: Object, mediaType: MediaType, state: Object) { +export function isForceMuted(participant: Participant | undefined, mediaType: MediaType, state: IState) { if (isEnabledFromState(mediaType, state)) { - if (participant.local) { + if (participant?.local) { return !isLocalParticipantApprovedFromState(mediaType, state); } @@ -56,7 +60,7 @@ export function isForceMuted(participant: Object, mediaType: MediaType, state: O return false; } - return !isParticipantApproved(participant.id, mediaType)(state); + return !isParticipantApproved(participant?.id ?? '', mediaType)(state); } return false; @@ -65,12 +69,12 @@ export function isForceMuted(participant: Object, mediaType: MediaType, state: O /** * Determines the audio media state (the mic icon) for a participant. * - * @param {Object} participant - The participant. + * @param {Participant} participant - The participant. * @param {boolean} muted - The mute state of the participant. - * @param {Object} state - The redux state. + * @param {IState} state - The redux state. * @returns {MediaState} */ -export function getParticipantAudioMediaState(participant: Object, muted: Boolean, state: Object) { +export function getParticipantAudioMediaState(participant: Participant, muted: Boolean, state: IState) { const dominantSpeaker = getDominantSpeakerParticipant(state); if (muted) { @@ -91,12 +95,12 @@ export function getParticipantAudioMediaState(participant: Object, muted: Boolea /** * Determines the video media state (the mic icon) for a participant. * - * @param {Object} participant - The participant. + * @param {Participant} participant - The participant. * @param {boolean} muted - The mute state of the participant. - * @param {Object} state - The redux state. + * @param {IState} state - The redux state. * @returns {MediaState} */ -export function getParticipantVideoMediaState(participant: Object, muted: Boolean, state: Object) { +export function getParticipantVideoMediaState(participant: Participant, muted: Boolean, state: IState) { if (muted) { if (isForceMuted(participant, MEDIA_TYPE.VIDEO, state)) { return MEDIA_STATE.FORCE_MUTED; @@ -116,7 +120,7 @@ export function getParticipantVideoMediaState(participant: Object, muted: Boolea * @param {string} name - Property name. * @returns {number} Float value. */ -export const getFloatStyleProperty = (styles: Object, name: string) => +export const getFloatStyleProperty = (styles: CSSStyleDeclaration, name: string) => parseFloat(styles.getPropertyValue(name)); /** @@ -136,19 +140,19 @@ export const getComputedOuterHeight = (element: HTMLElement) => { /** * Returns this feature's root state. * - * @param {Object} state - Global state. + * @param {IState} state - Global state. * @returns {Object} Feature state. */ -const getState = (state: Object) => state[REDUCER_KEY]; +const getState = (state: IState) => state[REDUCER_KEY]; /** * Returns the participants pane config. * - * @param {Function|Object} stateful - The redux store, the redux + * @param {IStateful} stateful - The redux store, the redux * {@code getState} function, or the redux state itself. * @returns {Object} */ -export const getParticipantsPaneConfig = (stateful: Function | Object) => { +export const getParticipantsPaneConfig = (stateful: IStateful) => { const state = toState(stateful); const { participantsPane = {} } = state['features/base/config']; @@ -158,21 +162,21 @@ export const getParticipantsPaneConfig = (stateful: Function | Object) => { /** * Is the participants pane open. * - * @param {Object} state - Global state. + * @param {IState} state - Global state. * @returns {boolean} Is the participants pane open. */ -export const getParticipantsPaneOpen = (state: Object) => Boolean(getState(state)?.isOpen); +export const getParticipantsPaneOpen = (state: IState) => Boolean(getState(state)?.isOpen); /** * Returns the type of quick action button to be displayed for a participant. * The button is displayed when hovering a participant from the participant list. * - * @param {Object} participant - The participant. + * @param {Participant} participant - The participant. * @param {boolean} isAudioMuted - If audio is muted for the participant. - * @param {Object} state - The redux state. + * @param {IState} state - The redux state. * @returns {string} - The type of the quick action button. */ -export function getQuickActionButtonType(participant: Object, isAudioMuted: Boolean, state: Object) { +export function getQuickActionButtonType(participant: Participant, isAudioMuted: Boolean, state: IState) { // handled only by moderators if (isLocalParticipantModerator(state)) { if (!isAudioMuted) { @@ -189,10 +193,10 @@ export function getQuickActionButtonType(participant: Object, isAudioMuted: Bool /** * Returns true if the invite button should be rendered. * - * @param {Object} state - Global state. + * @param {IState} state - Global state. * @returns {boolean} */ -export const shouldRenderInviteButton = (state: Object) => { +export const shouldRenderInviteButton = (state: IState) => { const { disableInviteFunctions } = toState(state)['features/base/config']; const flagEnabled = getFeatureFlag(state, INVITE_ENABLED, true); const inBreakoutRoom = isInBreakoutRoom(state); @@ -211,12 +215,12 @@ export const shouldRenderInviteButton = (state: Object) => { * 6. Recent speakers sorted alphabetically by their display name. * 7. Rest of the participants sorted alphabetically by their display name. * - * @param {(Function|Object)} stateful - The (whole) redux state, or redux's + * @param {IStateful} stateful - The (whole) redux state, or redux's * {@code getState} function to be used to retrieve the state features/base/participants. * @returns {Array} */ -export function getSortedParticipantIds(stateful: Object | Function): Array { - const { id } = getLocalParticipant(stateful); +export function getSortedParticipantIds(stateful: IStateful) { + const id = getLocalParticipant(stateful)?.id; const remoteParticipants = getRemoteParticipantsSorted(stateful); const reorderedParticipants = new Set(remoteParticipants); const raisedHandParticipants = getRaiseHandsQueue(stateful).map(({ id: particId }) => particId); @@ -232,7 +236,7 @@ export function getSortedParticipantIds(stateful: Object | Function): Array { +export const isMoreActionsVisible = (state: IState) => { const isLocalModerator = isLocalParticipantModerator(state); const inBreakoutRoom = isInBreakoutRoom(state); const { hideMoreActionsButton } = getParticipantsPaneConfig(state); @@ -293,10 +298,10 @@ export const isMoreActionsVisible = (state: Object) => { /** * Returns whether the mute all button is visible. * - * @param {Object} state - Global state. + * @param {IState} state - Global state. * @returns {boolean} */ -export const isMuteAllVisible = (state: Object) => { +export const isMuteAllVisible = (state: IState) => { const isLocalModerator = isLocalParticipantModerator(state); const inBreakoutRoom = isInBreakoutRoom(state); const { hideMuteAllButton } = getParticipantsPaneConfig(state); diff --git a/react/features/participants-pane/reducer.ts b/react/features/participants-pane/reducer.ts index afd5318a8..d1d077b16 100644 --- a/react/features/participants-pane/reducer.ts +++ b/react/features/participants-pane/reducer.ts @@ -7,7 +7,7 @@ import { } from './actionTypes'; import { REDUCER_KEY } from './constants'; -export interface IParticipantsPane { +export interface IParticipantsPaneState { isOpen: boolean; participantsVolume: { [participantId: string]: number; @@ -23,7 +23,7 @@ const DEFAULT_STATE = { * Listen for actions that mutate the participants pane state. */ ReducerRegistry.register( - REDUCER_KEY, (state: IParticipantsPane = DEFAULT_STATE, action) => { + REDUCER_KEY, (state: IParticipantsPaneState = DEFAULT_STATE, action) => { switch (action.type) { case PARTICIPANTS_PANE_CLOSE: return { diff --git a/react/features/video-menu/components/web/ParticipantContextMenu.tsx b/react/features/video-menu/components/web/ParticipantContextMenu.tsx index e6c5ed606..5bf989dc8 100644 --- a/react/features/video-menu/components/web/ParticipantContextMenu.tsx +++ b/react/features/video-menu/components/web/ParticipantContextMenu.tsx @@ -27,7 +27,6 @@ import { getBreakoutRooms, getCurrentRoomId, isInBreakoutRoom } from '../../../b import { setVolume } from '../../../filmstrip/actions.web'; // @ts-ignore import { isStageFilmstripAvailable } from '../../../filmstrip/functions.web'; -// @ts-ignore import { isForceMuted } from '../../../participants-pane/functions'; // @ts-ignore import { requestRemoteControl, stopController } from '../../../remote-control'; @@ -152,9 +151,9 @@ const ParticipantContextMenu = ({ const localParticipant = useSelector(getLocalParticipant); const _isModerator = Boolean(localParticipant?.role === PARTICIPANT_ROLE.MODERATOR); - const _isAudioForceMuted = useSelector(state => + const _isAudioForceMuted = useSelector(state => isForceMuted(participant, MEDIA_TYPE.AUDIO, state)); - const _isVideoForceMuted = useSelector(state => + const _isVideoForceMuted = useSelector(state => isForceMuted(participant, MEDIA_TYPE.VIDEO, state)); const _isAudioMuted = useSelector(state => isParticipantAudioMuted(participant, state)); const _overflowDrawer: boolean = useSelector(showOverflowDrawer);