diff --git a/react-native-sdk/index.tsx b/react-native-sdk/index.tsx index 03bc8bf966..f7de9aa429 100644 --- a/react-native-sdk/index.tsx +++ b/react-native-sdk/index.tsx @@ -19,6 +19,8 @@ import { setAudioMuted, setVideoMuted } from './react/features/base/media/action interface IEventListeners { + onAudioMutedChanged?: Function; + onVideoMutedChanged?: Function; onConferenceBlurred?: Function; onConferenceFocused?: Function; onConferenceJoined?: Function; @@ -107,6 +109,8 @@ export const JitsiMeeting = forwardRef((props: IAppProps, ref) => { setAppProps({ 'flags': flags, 'rnSdkHandlers': { + onAudioMutedChanged: eventListeners?.onAudioMutedChanged, + onVideoMutedChanged: eventListeners?.onVideoMutedChanged, onConferenceBlurred: eventListeners?.onConferenceBlurred, onConferenceFocused: eventListeners?.onConferenceFocused, onConferenceJoined: eventListeners?.onConferenceJoined, diff --git a/react/features/base/conference/actionTypes.ts b/react/features/base/conference/actionTypes.ts index 6ea2748393..f03312dded 100644 --- a/react/features/base/conference/actionTypes.ts +++ b/react/features/base/conference/actionTypes.ts @@ -72,6 +72,24 @@ export const CONFERENCE_BLURRED = 'CONFERENCE_BLURRED'; */ export const CONFERENCE_FOCUSED = 'CONFERENCE_FOCUSED'; +/** + * The type of (redux) action which signals that the audio mute state is changed. + * + * { + * type: AUDIO_MUTED_CHANGED, + * } + */ +export const AUDIO_MUTED_CHANGED = 'AUDIO_MUTED_CHANGED'; + +/** + * The type of (redux) action which signals that the video mute state is changed. + * + * { + * type: VIDEO_MUTED_CHANGED, + * } + */ +export const VIDEO_MUTED_CHANGED = 'VIDEO_MUTED_CHANGED'; + /** * The type of (redux) action, which indicates conference local subject changes. * diff --git a/react/features/mobile/external-api/middleware.ts b/react/features/mobile/external-api/middleware.ts index ef28d47c31..31ef385236 100644 --- a/react/features/mobile/external-api/middleware.ts +++ b/react/features/mobile/external-api/middleware.ts @@ -10,13 +10,15 @@ import { appNavigate } from '../../app/actions.native'; import { IStore } from '../../app/types'; import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app/actionTypes'; import { + AUDIO_MUTED_CHANGED, CONFERENCE_BLURRED, CONFERENCE_FAILED, CONFERENCE_FOCUSED, CONFERENCE_JOINED, CONFERENCE_LEFT, CONFERENCE_WILL_JOIN, - SET_ROOM + SET_ROOM, + VIDEO_MUTED_CHANGED } from '../../base/conference/actionTypes'; import { JITSI_CONFERENCE_URL_KEY } from '../../base/conference/constants'; import { @@ -158,6 +160,16 @@ externalAPIEnabled && MiddlewareRegistry.register(store => next => action => { sendEvent(store, CONFERENCE_BLURRED, {}); break; + case AUDIO_MUTED_CHANGED: + sendEvent(store, AUDIO_MUTED_CHANGED, + /* data */ { muted: action.muted }); + break; + + case VIDEO_MUTED_CHANGED: + sendEvent(store, VIDEO_MUTED_CHANGED, + /* data */ { muted: action.muted }); + break; + case CONFERENCE_FOCUSED: sendEvent(store, CONFERENCE_FOCUSED, {}); break; diff --git a/react/features/mobile/react-native-sdk/middleware.js b/react/features/mobile/react-native-sdk/middleware.js index 432aa0d362..ab6bf18073 100644 --- a/react/features/mobile/react-native-sdk/middleware.js +++ b/react/features/mobile/react-native-sdk/middleware.js @@ -2,11 +2,13 @@ import { NativeModules } from 'react-native'; import { getAppProp } from '../../base/app/functions'; import { + AUDIO_MUTED_CHANGED, CONFERENCE_BLURRED, CONFERENCE_FOCUSED, CONFERENCE_JOINED, CONFERENCE_LEFT, - CONFERENCE_WILL_JOIN + CONFERENCE_WILL_JOIN, + VIDEO_MUTED_CHANGED } from '../../base/conference/actionTypes'; import { PARTICIPANT_JOINED } from '../../base/participants/actionTypes'; import MiddlewareRegistry from '../../base/redux/MiddlewareRegistry'; @@ -31,6 +33,12 @@ const { JMOngoingConference } = NativeModules; const rnSdkHandlers = getAppProp(store, 'rnSdkHandlers'); switch (type) { + case AUDIO_MUTED_CHANGED: + rnSdkHandlers?.onAudioMutedChanged && rnSdkHandlers?.onAudioMutedChanged(action.muted); + break; + case VIDEO_MUTED_CHANGED: + rnSdkHandlers?.onVideoMutedChanged && rnSdkHandlers?.onVideoMutedChanged(action.muted); + break; case CONFERENCE_BLURRED: rnSdkHandlers?.onConferenceBlurred && rnSdkHandlers?.onConferenceBlurred(); break; diff --git a/react/features/toolbox/components/native/AudioMuteButton.tsx b/react/features/toolbox/components/native/AudioMuteButton.tsx index 9aa75fbf75..fddd4c6a26 100644 --- a/react/features/toolbox/components/native/AudioMuteButton.tsx +++ b/react/features/toolbox/components/native/AudioMuteButton.tsx @@ -1,6 +1,30 @@ import { connect } from 'react-redux'; +import { AUDIO_MUTED_CHANGED } from '../../../base/conference/actionTypes'; import { translate } from '../../../base/i18n/functions'; import AbstractAudioMuteButton, { IProps, mapStateToProps } from '../AbstractAudioMuteButton'; -export default translate(connect(mapStateToProps)(AbstractAudioMuteButton)); +/** + * Component that renders native toolbar button for toggling audio mute. + * + * @augments AbstractAudioMuteButton + */ +class AudioMuteButton extends AbstractAudioMuteButton { + /** + * Changes audio muted state and dispatches the state to redux. + * + * @param {boolean} audioMuted - Whether audio should be muted or not. + * @protected + * @returns {void} + */ + _setAudioMuted(audioMuted: boolean) { + this.props.dispatch?.({ + type: AUDIO_MUTED_CHANGED, + muted: audioMuted + }); + + super._setAudioMuted(audioMuted); + } +} + +export default translate(connect(mapStateToProps)(AudioMuteButton)); diff --git a/react/features/toolbox/components/native/VideoMuteButton.tsx b/react/features/toolbox/components/native/VideoMuteButton.tsx index 9b92b0e49a..334dedb496 100644 --- a/react/features/toolbox/components/native/VideoMuteButton.tsx +++ b/react/features/toolbox/components/native/VideoMuteButton.tsx @@ -1,7 +1,31 @@ import { connect } from 'react-redux'; +import { VIDEO_MUTED_CHANGED } from '../../../base/conference/actionTypes'; import { translate } from '../../../base/i18n/functions'; import AbstractVideoMuteButton, { IProps, mapStateToProps } from '../AbstractVideoMuteButton'; +/** + * Component that renders native toolbar button for toggling video mute. + * + * @augments AbstractVideoMuteButton + */ +class VideoMuteButton extends AbstractVideoMuteButton { + /** + * Changes video muted state and dispatches the state to redux. + * + * @override + * @param {boolean} videoMuted - Whether video should be muted or not. + * @protected + * @returns {void} + */ + _setVideoMuted(videoMuted: boolean) { + this.props.dispatch?.({ + type: VIDEO_MUTED_CHANGED, + muted: videoMuted + }); -export default translate(connect(mapStateToProps)(AbstractVideoMuteButton)); + super._setVideoMuted(videoMuted); + } +} + +export default translate(connect(mapStateToProps)(VideoMuteButton));