diff --git a/conference.js b/conference.js index 0539f5e8eb..904737c965 100644 --- a/conference.js +++ b/conference.js @@ -78,10 +78,7 @@ import { setVideoAvailable, setVideoMuted } from './react/features/base/media'; -import { - hideNotification, - showNotification -} from './react/features/notifications'; +import { showNotification } from './react/features/notifications'; import { dominantSpeakerChanged, getLocalParticipant, @@ -1798,30 +1795,12 @@ export default { APP.UI.setAudioLevel(id, newLvl); }); - // we store the last start muted notification id that we showed, - // so we can hide it when unmuted mic is detected - let lastNotificationId; - room.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, (track, participantThatMutedUs) => { if (participantThatMutedUs) { APP.store.dispatch(participantMutedUs(participantThatMutedUs)); } - - if (lastNotificationId && track.isAudioTrack() && track.isLocal() && !track.isMuted()) { - APP.store.dispatch(hideNotification(lastNotificationId)); - lastNotificationId = undefined; - } }); - room.on(JitsiConferenceEvents.TALK_WHILE_MUTED, () => { - const action = APP.store.dispatch(showNotification({ - titleKey: 'toolbar.talkWhileMutedPopup', - customActionNameKey: 'notify.unmute', - customActionHandler: muteLocalAudio.bind(this, false) - })); - - lastNotificationId = action.uid; - }); room.on(JitsiConferenceEvents.SUBJECT_CHANGED, subject => APP.store.dispatch(conferenceSubjectChanged(subject))); diff --git a/react/features/app/components/App.web.js b/react/features/app/components/App.web.js index 56509b9c8e..dad4a49894 100644 --- a/react/features/app/components/App.web.js +++ b/react/features/app/components/App.web.js @@ -10,6 +10,7 @@ import '../../chat'; import '../../external-api'; import '../../power-monitor'; import '../../room-lock'; +import '../../talk-while-muted'; import '../../video-layout'; import { AbstractApp } from './AbstractApp'; diff --git a/react/features/talk-while-muted/actionTypes.js b/react/features/talk-while-muted/actionTypes.js new file mode 100644 index 0000000000..6bc5d6971f --- /dev/null +++ b/react/features/talk-while-muted/actionTypes.js @@ -0,0 +1,12 @@ +/** + * The type of Redux action which sets the pending notification UID + * to use it for when hiding the notification is necessary, or unsets it when + * undefined (or no param) is passed. + * + * { + * type: SET_CURRENT_NOTIFICATION_UID, + * uid: ?number + * } + * @public + */ +export const SET_CURRENT_NOTIFICATION_UID = 'SET_CURRENT_NOTIFICATION_UID'; diff --git a/react/features/talk-while-muted/actions.js b/react/features/talk-while-muted/actions.js new file mode 100644 index 0000000000..95a2345822 --- /dev/null +++ b/react/features/talk-while-muted/actions.js @@ -0,0 +1,21 @@ +// @flow + +import { SET_CURRENT_NOTIFICATION_UID } from './actionTypes'; + +/** + * Sets UID of the the pending notification to use it when hiding + * the notification is necessary, or unsets it when undefined (or no param) is + * passed. + * + * @param {?number} uid - The UID of the notification. + * @returns {{ + * type: SET_CURRENT_NOTIFICATION_UID, + * uid: number + * }} + */ +export function setCurrentNotificationUid(uid: ?number) { + return { + type: SET_CURRENT_NOTIFICATION_UID, + uid + }; +} diff --git a/react/features/talk-while-muted/constants.js b/react/features/talk-while-muted/constants.js new file mode 100644 index 0000000000..1f4d5b6122 --- /dev/null +++ b/react/features/talk-while-muted/constants.js @@ -0,0 +1,6 @@ +/** + * The identifier of the sound to be played when we got event for talking while muted. + * + * @type {string} + */ +export const TALK_WHILE_MUTED_SOUND_ID = 'TALK_WHILE_MUTED_SOUND_ID'; diff --git a/react/features/talk-while-muted/index.js b/react/features/talk-while-muted/index.js new file mode 100644 index 0000000000..d040422d06 --- /dev/null +++ b/react/features/talk-while-muted/index.js @@ -0,0 +1,4 @@ +// @flow + +import './middleware'; +import './reducer'; diff --git a/react/features/talk-while-muted/middleware.js b/react/features/talk-while-muted/middleware.js new file mode 100644 index 0000000000..ff73845f58 --- /dev/null +++ b/react/features/talk-while-muted/middleware.js @@ -0,0 +1,63 @@ +// @flow + +import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app'; +import { CONFERENCE_JOINED } from '../base/conference'; +import { JitsiConferenceEvents } from '../base/lib-jitsi-meet'; +import { setAudioMuted } from '../base/media'; +import { MiddlewareRegistry } from '../base/redux'; +import { playSound, registerSound, unregisterSound } from '../base/sounds'; +import { + hideNotification, + showNotification +} from '../notifications'; + +import { setCurrentNotificationUid } from './actions'; +import { TALK_WHILE_MUTED_SOUND_ID } from './constants'; +import { TALK_WHILE_MUTED_SOUND_FILE } from './sounds'; + +MiddlewareRegistry.register(store => next => action => { + const result = next(action); + const { dispatch, getState } = store; + const { conference } = action; + + switch (action.type) { + case APP_WILL_MOUNT: + dispatch(registerSound(TALK_WHILE_MUTED_SOUND_ID, TALK_WHILE_MUTED_SOUND_FILE)); + break; + case APP_WILL_UNMOUNT: + dispatch(unregisterSound(TALK_WHILE_MUTED_SOUND_ID)); + break; + + case CONFERENCE_JOINED: { + conference.on( + JitsiConferenceEvents.TRACK_MUTE_CHANGED, + track => { + const { currentNotificationUid } = getState()['features/talk-while-muted']; + + if (currentNotificationUid && track.isAudioTrack() && track.isLocal() && !track.isMuted()) { + dispatch(hideNotification(currentNotificationUid)); + dispatch(setCurrentNotificationUid()); + } + }); + conference.on( + JitsiConferenceEvents.TALK_WHILE_MUTED, () => { + const notification = showNotification({ + titleKey: 'toolbar.talkWhileMutedPopup', + customActionNameKey: 'notify.unmute', + customActionHandler: () => dispatch(setAudioMuted(false)) + }); + + dispatch(notification); + + dispatch(playSound(TALK_WHILE_MUTED_SOUND_ID)); + + // we store the last start muted notification id that we showed, + // so we can hide it when unmuted mic is detected + dispatch(setCurrentNotificationUid(notification.uid)); + }); + break; + } + } + + return result; +}); diff --git a/react/features/talk-while-muted/reducer.js b/react/features/talk-while-muted/reducer.js new file mode 100644 index 0000000000..27e532538f --- /dev/null +++ b/react/features/talk-while-muted/reducer.js @@ -0,0 +1,17 @@ +// @flow + +import { ReducerRegistry, set } from '../base/redux'; + +import { SET_CURRENT_NOTIFICATION_UID } from './actionTypes'; + +/** + * Reduces the redux actions of the feature talk while muted. + */ +ReducerRegistry.register('features/talk-while-muted', (state = { }, action) => { + switch (action.type) { + case SET_CURRENT_NOTIFICATION_UID: + return set(state, 'currentNotificationUid', action.uid); + } + + return state; +}); diff --git a/react/features/talk-while-muted/sounds.js b/react/features/talk-while-muted/sounds.js new file mode 100644 index 0000000000..e28f610af3 --- /dev/null +++ b/react/features/talk-while-muted/sounds.js @@ -0,0 +1,6 @@ +/** + * The file used for the talk while muted sound notification. + * + * @type {string} + */ +export const TALK_WHILE_MUTED_SOUND_FILE = 'talkWhileMuted.mp3'; diff --git a/sounds/talkWhileMuted.mp3 b/sounds/talkWhileMuted.mp3 new file mode 100644 index 0000000000..cf07fb704c Binary files /dev/null and b/sounds/talkWhileMuted.mp3 differ diff --git a/webpack.config.js b/webpack.config.js index 3ae4af15fd..d8f56f90c4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -186,6 +186,7 @@ module.exports = [ function devServerProxyBypass({ path }) { if (path.startsWith('/css/') || path.startsWith('/doc/') || path.startsWith('/fonts/') || path.startsWith('/images/') + || path.startsWith('/sounds/') || path.startsWith('/static/')) { return path; }