diff --git a/conference.js b/conference.js index 0b1f2445c70d..14d1c0f769fa 100644 --- a/conference.js +++ b/conference.js @@ -27,6 +27,7 @@ import { conferenceJoinInProgress, conferenceJoined, conferenceLeft, + conferencePropertiesChanged, conferenceSubjectChanged, conferenceTimestampChanged, conferenceUniqueIdSet, @@ -1759,6 +1760,10 @@ export default { JitsiConferenceEvents.LOCK_STATE_CHANGED, (...args) => APP.store.dispatch(lockStateChanged(room, ...args))); + room.on( + JitsiConferenceEvents.PROPERTIES_CHANGED, + properties => APP.store.dispatch(conferencePropertiesChanged(properties))); + room.on(JitsiConferenceEvents.KICKED, (participant, reason, isReplaced) => { if (isReplaced) { // this event triggers when the local participant is kicked, `participant` diff --git a/react/features/base/conference/actionTypes.ts b/react/features/base/conference/actionTypes.ts index 8ff83f09a413..4e4c7a140aa6 100644 --- a/react/features/base/conference/actionTypes.ts +++ b/react/features/base/conference/actionTypes.ts @@ -82,6 +82,19 @@ export const CONFERENCE_FOCUSED = 'CONFERENCE_FOCUSED'; */ export const CONFERENCE_LOCAL_SUBJECT_CHANGED = 'CONFERENCE_LOCAL_SUBJECT_CHANGED'; +/** + * The type of (redux) action, which indicates conference properties change. + * + * { + * type: CONFERENCE_PROPERTIES_CHANGED + * properties: { +* audio-recording-enabled: boolean, + * visitor-count: number + * } + * } + */ + export const CONFERENCE_PROPERTIES_CHANGED = 'CONFERENCE_PROPERTIES_CHANGED'; + /** * The type of (redux) action, which indicates conference subject changes. * diff --git a/react/features/base/conference/actions.any.ts b/react/features/base/conference/actions.any.ts index 6a390f1ab45c..76b183b9be10 100644 --- a/react/features/base/conference/actions.any.ts +++ b/react/features/base/conference/actions.any.ts @@ -45,6 +45,7 @@ import { CONFERENCE_JOIN_IN_PROGRESS, CONFERENCE_LEFT, CONFERENCE_LOCAL_SUBJECT_CHANGED, + CONFERENCE_PROPERTIES_CHANGED, CONFERENCE_SUBJECT_CHANGED, CONFERENCE_TIMESTAMP_CHANGED, CONFERENCE_UNIQUE_ID_SET, @@ -156,6 +157,10 @@ function _addConferenceListeners(conference: IJitsiConference, dispatch: IStore[ JitsiConferenceEvents.LOCK_STATE_CHANGED, (locked: boolean) => dispatch(lockStateChanged(conference, locked))); + conference.on( + JitsiConferenceEvents.PROPERTIES_CHANGED, + (properties: Object) => dispatch(conferencePropertiesChanged(properties))); + // Dispatches into features/base/media follow: conference.on( @@ -449,6 +454,23 @@ export function conferenceLeft(conference?: IJitsiConference) { }; } +/** + * Signals that the conference properties have been changed. + * + * @param {Object} properties - The new properties set. + * @returns {{ + * type: CONFERENCE_PROPERTIES_CHANGED, + * properties: Object + * }} + */ +export function conferencePropertiesChanged(properties: object) { + return { + type: CONFERENCE_PROPERTIES_CHANGED, + properties + }; +} + + /** * Signals that the conference subject has been changed. * diff --git a/react/features/base/conference/functions.ts b/react/features/base/conference/functions.ts index 5561ad853873..d702123f9721 100644 --- a/react/features/base/conference/functions.ts +++ b/react/features/base/conference/functions.ts @@ -393,6 +393,19 @@ export function isP2pActive(stateful: IStateful): boolean | null { return conference.isP2PActive(); } +/** + * Returns whether the current conference has audio recording property which is on. + * + * @param {IStateful} stateful - The redux store, state, or {@code getState} function. + * @returns {boolean|null} + */ +export function isConferenceAudioRecordingOn(stateful: IStateful): boolean | null { + const state = getConferenceState(toState(stateful)); + + // @ts-ignore + return state.properties?.['audio-recording-enabled'] === 'true'; +} + /** * Returns the stored room name. * diff --git a/react/features/base/conference/reducer.ts b/react/features/base/conference/reducer.ts index 10cda798e95d..d081ee0a6cb5 100644 --- a/react/features/base/conference/reducer.ts +++ b/react/features/base/conference/reducer.ts @@ -8,7 +8,7 @@ import { IConfig } from '../config/configType'; import { CONNECTION_WILL_CONNECT, SET_LOCATION_URL } from '../connection/actionTypes'; import { JitsiConferenceErrors } from '../lib-jitsi-meet'; import ReducerRegistry from '../redux/ReducerRegistry'; -import { assign, set } from '../redux/functions'; +import { assign, equals, set } from '../redux/functions'; import { AUTH_STATUS_CHANGED, @@ -16,6 +16,7 @@ import { CONFERENCE_JOINED, CONFERENCE_LEFT, CONFERENCE_LOCAL_SUBJECT_CHANGED, + CONFERENCE_PROPERTIES_CHANGED, CONFERENCE_SUBJECT_CHANGED, CONFERENCE_TIMESTAMP_CHANGED, CONFERENCE_WILL_JOIN, @@ -48,7 +49,8 @@ const DEFAULT_STATE = { membersOnly: undefined, metadata: undefined, password: undefined, - passwordRequired: undefined + passwordRequired: undefined, + properties: undefined }; export interface IConferenceMetadata { @@ -176,6 +178,7 @@ export interface IConferenceState { password?: string; passwordRequired?: IJitsiConference; pendingSubjectChange?: string; + properties?: object; room?: string; startAudioMutedPolicy?: boolean; startReactionsMuted?: boolean; @@ -220,6 +223,9 @@ ReducerRegistry.register('features/base/conference', case CONFERENCE_LOCAL_SUBJECT_CHANGED: return set(state, 'localSubject', action.localSubject); + case CONFERENCE_PROPERTIES_CHANGED: + return _conferencePropertiesChanged(state, action); + case CONFERENCE_TIMESTAMP_CHANGED: return set(state, 'conferenceTimestamp', action.conferenceTimestamp); @@ -518,6 +524,26 @@ function _conferenceLeftOrWillLeave(state: IConferenceState, { conference, type return nextState; } +/** + * Reduces a specific Redux action CONFERENCE_PROPERTIES_CHANGED of the feature + * base/conference. + * + * @param {Object} state - The Redux state of the feature base/conference. + * @param {Action} action - The Redux action CONFERENCE_PROPERTIES_CHANGED to reduce. + * @private + * @returns {Object} The new state of the feature base/conference after the + * reduction of the specified action. + */ +function _conferencePropertiesChanged(state: IConferenceState, { properties }: { properties: Object; }) { + if (!equals(state.properties, properties)) { + return assign(state, { + properties + }); + } + + return state; +} + /** * Reduces a specific Redux action CONFERENCE_WILL_JOIN of the feature * base/conference. diff --git a/react/features/transcribing/subscriber.ts b/react/features/transcribing/subscriber.ts index 37f4f290db24..08a42cbe0f4a 100644 --- a/react/features/transcribing/subscriber.ts +++ b/react/features/transcribing/subscriber.ts @@ -1,6 +1,7 @@ import { batch } from 'react-redux'; import { IStore } from '../app/types'; +import { isConferenceAudioRecordingOn } from '../base/conference/functions'; import { JitsiRecordingConstants } from '../base/lib-jitsi-meet'; import StateListenerRegistry from '../base/redux/StateListenerRegistry'; import { playSound } from '../base/sounds/actions'; @@ -27,6 +28,16 @@ StateListenerRegistry.register( } ); +/** + * Listens for audio-recording-enabled conference property change. + */ +StateListenerRegistry.register( + /* selector */ isConferenceAudioRecordingOn, + /* listener */ (audioRecordingOn, { getState, dispatch }) => { + maybeEmitRecordingNotification(dispatch, getState, audioRecordingOn); + } +); + /** * Emit a recording started / stopped notification if the transcription started / stopped. Only * if there is no recording in progress. diff --git a/react/features/visitors/actionTypes.ts b/react/features/visitors/actionTypes.ts index bdfe11f6f52d..464183d9cabe 100644 --- a/react/features/visitors/actionTypes.ts +++ b/react/features/visitors/actionTypes.ts @@ -1,13 +1,3 @@ -/** - * The type of (redux) action to update visitors count. - * - * { - * type: UPDATE_VISITORS_COUNT, - * count: number - * } - */ -export const UPDATE_VISITORS_COUNT = 'UPDATE_VISITORS_COUNT'; - /** * The type of (redux) action to update visitors in queue count. * diff --git a/react/features/visitors/actions.ts b/react/features/visitors/actions.ts index 3e36cbbd1775..71357e3d665f 100644 --- a/react/features/visitors/actions.ts +++ b/react/features/visitors/actions.ts @@ -11,7 +11,6 @@ import { SET_IN_VISITORS_QUEUE, SET_VISITORS_SUPPORTED, SET_VISITOR_DEMOTE_ACTOR, - UPDATE_VISITORS_COUNT, UPDATE_VISITORS_IN_QUEUE_COUNT, VISITOR_PROMOTION_REQUEST } from './actionTypes'; @@ -202,21 +201,6 @@ export function setVisitorsSupported(value: boolean) { }; } -/** - * Visitors count has been updated. - * - * @param {number} count - The new visitors count. - * @returns {{ - * type: UPDATE_VISITORS_COUNT, - * }} - */ -export function updateVisitorsCount(count: number) { - return { - type: UPDATE_VISITORS_COUNT, - count - }; -} - /** * Visitors in queue count has been updated. * diff --git a/react/features/visitors/middleware.ts b/react/features/visitors/middleware.ts index ba444302141b..39c9a2c27141 100644 --- a/react/features/visitors/middleware.ts +++ b/react/features/visitors/middleware.ts @@ -5,7 +5,6 @@ import { IStore } from '../app/types'; import { IStateful } from '../base/app/types'; import { CONFERENCE_JOINED, - CONFERENCE_JOIN_IN_PROGRESS, ENDPOINT_MESSAGE_RECEIVED, UPDATE_CONFERENCE_METADATA } from '../base/conference/actionTypes'; @@ -46,28 +45,15 @@ import { setInVisitorsQueue, setVisitorDemoteActor, setVisitorsSupported, - updateVisitorsCount, updateVisitorsInQueueCount } from './actions'; import { JoinMeetingDialog } from './components'; -import { getPromotionRequests, getVisitorsCount, getVisitorsInQueueCount } from './functions'; +import { getPromotionRequests, getVisitorsInQueueCount } from './functions'; import logger from './logger'; import { WebsocketClient } from './websocket-client'; MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { switch (action.type) { - case CONFERENCE_JOIN_IN_PROGRESS: { - const { conference } = action; - - conference.on(JitsiConferenceEvents.PROPERTIES_CHANGED, (properties: { 'visitor-count': number; }) => { - const visitorCount = Number(properties?.['visitor-count']); - - if (!isNaN(visitorCount) && getVisitorsCount(getState) !== visitorCount) { - dispatch(updateVisitorsCount(visitorCount)); - } - }); - break; - } case CONFERENCE_JOINED: { const { conference } = action; diff --git a/react/features/visitors/reducer.ts b/react/features/visitors/reducer.ts index a5bf62b8b040..c1f3c7771123 100644 --- a/react/features/visitors/reducer.ts +++ b/react/features/visitors/reducer.ts @@ -1,4 +1,4 @@ -import { CONFERENCE_WILL_LEAVE } from '../base/conference/actionTypes'; +import { CONFERENCE_PROPERTIES_CHANGED, CONFERENCE_WILL_LEAVE } from '../base/conference/actionTypes'; import ReducerRegistry from '../base/redux/ReducerRegistry'; import { @@ -7,7 +7,6 @@ import { SET_IN_VISITORS_QUEUE, SET_VISITORS_SUPPORTED, SET_VISITOR_DEMOTE_ACTOR, - UPDATE_VISITORS_COUNT, UPDATE_VISITORS_IN_QUEUE_COUNT, VISITOR_PROMOTION_REQUEST } from './actionTypes'; @@ -34,6 +33,18 @@ export interface IVisitorsState { } ReducerRegistry.register('features/visitors', (state = DEFAULT_STATE, action): IVisitorsState => { switch (action.type) { + case CONFERENCE_PROPERTIES_CHANGED: { + const visitorCount = Number(action.properties?.['visitor-count']); + + if (!isNaN(visitorCount) && state.count !== visitorCount) { + return { + ...state, + count: visitorCount + }; + } + + break; + } case CONFERENCE_WILL_LEAVE: { return { ...state, @@ -45,16 +56,6 @@ ReducerRegistry.register('features/visitors', (state = DEFAULT_S iAmVisitor: action.isRedirect ? state.iAmVisitor : DEFAULT_STATE.iAmVisitor }; } - case UPDATE_VISITORS_COUNT: { - if (state.count === action.count) { - return state; - } - - return { - ...state, - count: action.count - }; - } case UPDATE_VISITORS_IN_QUEUE_COUNT: { if (state.count === action.count) { return state;