ref(TS) Convert some features to TS (#12149)
Convert AV Moderation and Breakout Rooms to TS
This commit is contained in:
parent
95084e1004
commit
a1d20dc188
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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<Object>}
|
||||
*/
|
||||
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);
|
|
@ -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));
|
|
@ -409,7 +409,7 @@ export function getParticipantPresenceStatus(stateful: IStateful, id: string) {
|
|||
* features/base/participants.
|
||||
* @returns {Map<string, Object>}
|
||||
*/
|
||||
export function getRemoteParticipants(stateful: IStateful) {
|
||||
export function getRemoteParticipants(stateful: IStateful): Map<string, Participant> {
|
||||
return toState(stateful)['features/base/participants'].remote;
|
||||
}
|
||||
|
||||
|
@ -635,7 +635,7 @@ async function _getFirstLoadableAvatarUrl(participant: Participant, store: IStor
|
|||
* features/base/participants.
|
||||
* @returns {Array<Object>}
|
||||
*/
|
||||
export function getRaiseHandsQueue(stateful: IStateful): Array<Object> {
|
||||
export function getRaiseHandsQueue(stateful: IStateful): Array<{ id: string; raisedHandTimestamp: number; }> {
|
||||
const { raisedHandsQueue } = toState(stateful)['features/base/participants'];
|
||||
|
||||
return raisedHandsQueue;
|
||||
|
|
|
@ -80,7 +80,7 @@ export interface IParticipantsState {
|
|||
fakeParticipants: Map<string, Participant>;
|
||||
local?: LocalParticipant;
|
||||
localScreenShare?: Participant;
|
||||
overwrittenNameList: Object;
|
||||
overwrittenNameList: { [id: string]: string; };
|
||||
pinnedParticipant?: string;
|
||||
raisedHandsQueue: Array<{ id: string; raisedHandTimestamp: number; }>;
|
||||
remote: Map<string, Participant>;
|
||||
|
|
|
@ -3,6 +3,7 @@ export interface Participant {
|
|||
botType?: string;
|
||||
conference?: Object;
|
||||
connectionStatus?: string;
|
||||
displayName?: string;
|
||||
dominantSpeaker?: boolean;
|
||||
e2eeSupported?: boolean;
|
||||
email?: string;
|
||||
|
|
|
@ -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<any, any>) {
|
||||
register(middleware: Middleware<any, IState>) {
|
||||
this._elements.push(middleware);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<any>, 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<any>, 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<any>, 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<any>, 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<any>, 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<any>, 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) {
|
|
@ -1,5 +1,3 @@
|
|||
// @flow
|
||||
|
||||
/**
|
||||
* Key for this feature.
|
||||
*/
|
||||
|
|
|
@ -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);
|
|
@ -1,5 +1,3 @@
|
|||
// @flow
|
||||
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
import { FEATURE_KEY } from './constants';
|
|
@ -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));
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -118,8 +118,6 @@ function MeetingParticipants({
|
|||
<>
|
||||
<div className = { styles.heading }>
|
||||
{currentRoom?.name
|
||||
|
||||
// $FlowExpectedError
|
||||
? `${currentRoom.name} (${participantsCount})`
|
||||
: t('participantsPane.headings.participantsList', { count: participantsCount })}
|
||||
</div>
|
||||
|
|
|
@ -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<string>}
|
||||
*/
|
||||
export function getSortedParticipantIds(stateful: Object | Function): Array<string> {
|
||||
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<stri
|
|||
|
||||
const dominant = [];
|
||||
const dominantId = dominantSpeaker?.id;
|
||||
const local = remoteRaisedHandParticipants.has(id) ? [] : [ id ];
|
||||
const local = remoteRaisedHandParticipants.has(id ?? '') ? [] : [ id ];
|
||||
|
||||
// In case dominat speaker has raised hand, keep the order in the raised hand queue.
|
||||
// In case they don't have raised hand, goes first in the participants list.
|
||||
|
@ -257,7 +261,8 @@ export function getSortedParticipantIds(stateful: Object | Function): Array<stri
|
|||
* @param {string} searchString - The participants search string.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function participantMatchesSearch(participant: Object, searchString: string) {
|
||||
export function participantMatchesSearch(participant: { displayName: string; jid: string; name?: string; },
|
||||
searchString: string) {
|
||||
if (searchString === '') {
|
||||
return true;
|
||||
}
|
||||
|
@ -279,10 +284,10 @@ export function participantMatchesSearch(participant: Object, searchString: stri
|
|||
/**
|
||||
* Returns whether the more actions button is visible.
|
||||
*
|
||||
* @param {Object} state - Global state.
|
||||
* @param {IState} state - Global state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isMoreActionsVisible = (state: Object) => {
|
||||
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);
|
|
@ -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 {
|
||||
|
|
|
@ -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<IState>(state =>
|
||||
isForceMuted(participant, MEDIA_TYPE.AUDIO, state));
|
||||
const _isVideoForceMuted = useSelector(state =>
|
||||
const _isVideoForceMuted = useSelector<IState>(state =>
|
||||
isForceMuted(participant, MEDIA_TYPE.VIDEO, state));
|
||||
const _isAudioMuted = useSelector(state => isParticipantAudioMuted(participant, state));
|
||||
const _overflowDrawer: boolean = useSelector(showOverflowDrawer);
|
||||
|
|
Loading…
Reference in New Issue