Skip to content

Commit

Permalink
feat: (moderate-reaction-sounds) enable moderator to mute reaction so…
Browse files Browse the repository at this point in the history
…unds
  • Loading branch information
andioltean authored and Calinteodor committed Dec 3, 2021
1 parent 646fdef commit a077043
Show file tree
Hide file tree
Showing 27 changed files with 576 additions and 193 deletions.
5 changes: 5 additions & 0 deletions css/modals/settings/_settings.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
text-align: left;
flex: 1;
}

.moderator-settings-wrapper {
padding-top: 20px;
}

.profile-edit-field {
margin-right: 20px;
}
Expand Down
5 changes: 4 additions & 1 deletion lang/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,7 @@
"moderationToggleDescription": "by {{participantDisplayName}}",
"raiseHandAction": "Raise hand",
"reactionSounds": "Disable sounds",
"reactionSoundsForAll": "Disable sounds for all",
"groupTitle": "Notifications",
"videoUnmuteBlockedTitle": "Camera unmute blocked!",
"videoUnmuteBlockedDescription": "Camera unmute operation has been temporarily blocked because of system limits."
Expand Down Expand Up @@ -660,7 +661,8 @@
"stopEveryonesVideo": "Stop everyone's video",
"stopVideo": "Stop video",
"unblockEveryoneMicCamera": "Unblock everyone's mic and camera",
"videoModeration": "Start their video"
"videoModeration": "Start their video",
"moreModerationControls": "More moderation controls"
},
"search": "Search participants"
},
Expand Down Expand Up @@ -855,6 +857,7 @@
"sounds": "Sounds",
"speakers": "Speakers",
"startAudioMuted": "Everyone starts muted",
"startReactionsMuted": "Mute reaction sounds for everyone",
"startVideoMuted": "Everyone starts hidden",
"talkWhileMuted": "Talk while muted",
"title": "Settings"
Expand Down
10 changes: 5 additions & 5 deletions react/features/av-moderation/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
}

dispatch(showNotification({
customActionNameKey: 'notify.raiseHandAction',
customActionHandler: () => batch(() => {
customActionNameKey: [ 'notify.raiseHandAction' ],
customActionHandler: [ () => batch(() => {
dispatch(raiseHand(true));
dispatch(hideNotification(uid));
}),
}) ],
descriptionKey,
sticky: true,
titleKey,
Expand Down Expand Up @@ -221,8 +221,8 @@ StateListenerRegistry.register(
dispatch(showNotification({
titleKey: 'notify.hostAskedUnmute',
sticky: true,
customActionNameKey: 'notify.unmute',
customActionHandler: () => dispatch(muteLocal(false, MEDIA_TYPE.AUDIO))
customActionNameKey: [ 'notify.unmute' ],
customActionHandler: [ () => dispatch(muteLocal(false, MEDIA_TYPE.AUDIO)) ]
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
dispatch(playSound(ASKED_TO_UNMUTE_SOUND_ID));
}
Expand Down
11 changes: 11 additions & 0 deletions react/features/base/conference/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,17 @@ export const SEND_TONES = 'SEND_TONES';
*/
export const SET_FOLLOW_ME = 'SET_FOLLOW_ME';

/**
* The type of (redux) action which updates the current known status of the
* Mute Reactions Sound feature.
*
* {
* type: SET_START_REACTIONS_MUTED,
* enabled: boolean
* }
*/
export const SET_START_REACTIONS_MUTED = 'SET_START_REACTIONS_MUTED';

/**
* The type of (redux) action which sets the password to join or lock a specific
* {@code JitsiConference}.
Expand Down
19 changes: 18 additions & 1 deletion react/features/base/conference/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ import {
SET_PASSWORD_FAILED,
SET_ROOM,
SET_PENDING_SUBJECT_CHANGE,
SET_START_MUTED_POLICY
SET_START_MUTED_POLICY,
SET_START_REACTIONS_MUTED
} from './actionTypes';
import {
AVATAR_URL_COMMAND,
Expand Down Expand Up @@ -669,6 +670,22 @@ export function setFollowMe(enabled: boolean) {
};
}

/**
* Enables or disables the Mute reaction sounds feature.
*
* @param {boolean} muted - Whether or not reaction sounds should be muted for all participants.
* @returns {{
* type: SET_START_REACTIONS_MUTED,
* muted: boolean
* }}
*/
export function setStartReactionsMuted(muted: boolean) {
return {
type: SET_START_REACTIONS_MUTED,
muted
};
}

/**
* Sets the password to join or lock a specific JitsiConference.
*
Expand Down
6 changes: 5 additions & 1 deletion react/features/base/conference/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import {
SET_PASSWORD,
SET_PENDING_SUBJECT_CHANGE,
SET_ROOM,
SET_START_MUTED_POLICY
SET_START_MUTED_POLICY,
SET_START_REACTIONS_MUTED
} from './actionTypes';
import { isRoomValid } from './functions';

Expand Down Expand Up @@ -77,6 +78,9 @@ ReducerRegistry.register(
case SET_FOLLOW_ME:
return set(state, 'followMeEnabled', action.enabled);

case SET_START_REACTIONS_MUTED:
return set(state, 'startReactionsMuted', action.muted);

case SET_LOCATION_URL:
return set(state, 'room', undefined);

Expand Down
10 changes: 7 additions & 3 deletions react/features/base/devices/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

import UIEvents from '../../../../service/UI/UIEvents';
import { processExternalDeviceRequest } from '../../device-selection';
import { NOTIFICATION_TIMEOUT_TYPE, showNotification, showWarningNotification } from '../../notifications';
import {
NOTIFICATION_TIMEOUT_TYPE,
showNotification,
showWarningNotification
} from '../../notifications';
import { replaceAudioTrackById, replaceVideoTrackById, setDeviceStatusWarning } from '../../prejoin/actions';
import { isPrejoinPageVisible } from '../../prejoin/functions';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app';
Expand Down Expand Up @@ -294,8 +298,8 @@ function _checkAndNotifyForNewDevice(store, newDevices, oldDevices) {
dispatch(showNotification({
description,
titleKey,
customActionNameKey: 'notify.newDeviceAction',
customActionHandler: _useDevice.bind(undefined, store, devicesArray)
customActionNameKey: [ 'notify.newDeviceAction' ],
customActionHandler: [ _useDevice.bind(undefined, store, devicesArray) ]
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
}
});
Expand Down
4 changes: 2 additions & 2 deletions react/features/base/participants/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -555,8 +555,8 @@ function _raiseHandUpdated({ dispatch, getState }, conference, participantId, ne
}

const action = shouldDisplayAllowAction ? {
customActionNameKey: 'notify.allowAction',
customActionHandler: () => dispatch(approveParticipant(participantId))
customActionNameKey: [ 'notify.allowAction' ],
customActionHandler: [ () => dispatch(approveParticipant(participantId)) ]
} : {};

if (raisedHandTimestamp) {
Expand Down
3 changes: 2 additions & 1 deletion react/features/base/settings/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
* serverURL: string,
* startAudioOnly: boolean,
* startWithAudioMuted: boolean,
* startWithVideoMuted: boolean
* startWithVideoMuted: boolean,
* startWithReactionsMuted: boolean
* }
* }
*/
Expand Down
4 changes: 3 additions & 1 deletion react/features/base/settings/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ import { SETTINGS_UPDATED } from './actionTypes';
* localFlipX: boolean,
* micDeviceId: string,
* serverURL: string,
* soundsReactions: boolean,
* startAudioOnly: boolean,
* startWithAudioMuted: boolean,
* startWithVideoMuted: boolean
* startWithVideoMuted: boolean,
* startWithReactionsMuted: boolean
* }
* }}
*/
Expand Down
6 changes: 3 additions & 3 deletions react/features/no-audio-signal/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ async function _handleNoAudioSignalNotification({ dispatch, getState }, action)
// at the point of the implementation the showNotification function only supports doing that for
// the description.
// TODO Add support for arguments to showNotification title and customAction strings.
customActionNameKey = `Switch to ${formatDeviceLabel(activeDevice.deviceLabel)}`;
customActionHandler = () => {
customActionNameKey = [ `Switch to ${formatDeviceLabel(activeDevice.deviceLabel)}` ];
customActionHandler = [ () => {
// Select device callback
dispatch(
updateSettings({
Expand All @@ -105,7 +105,7 @@ async function _handleNoAudioSignalNotification({ dispatch, getState }, action)
);

dispatch(setAudioInputDevice(activeDevice.deviceId));
};
} ];
}

const notification = await dispatch(showNotification({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ export type Props = {
/**
* Callback invoked when the custom button is clicked.
*/
customActionHandler: Function,
customActionHandler: Function[],

/**
* The text to display as button in the notification for the custom action.
*/
customActionNameKey: string,
customActionNameKey: string[],

/**
* The text to display in the body of the notification. If not passed
Expand Down
14 changes: 7 additions & 7 deletions react/features/notifications/components/web/Notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,17 +128,17 @@ class Notification extends AbstractNotification<Props> {
];

default:
if (this.props.customActionNameKey && this.props.customActionHandler) {
return [
{
content: this.props.t(this.props.customActionNameKey),
if (this.props.customActionNameKey?.length && this.props.customActionHandler?.length) {
return this.props.customActionNameKey.map((customAction: string, customActionIndex: number) => {
return {
content: this.props.t(customAction),
onClick: () => {
if (this.props.customActionHandler()) {
if (this.props.customActionHandler[customActionIndex]()) {
this._onDismissed();
}
}
}
];
};
});
}

return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@ import {
} from '../../../av-moderation/functions';
import { ContextMenu, ContextMenuItemGroup } from '../../../base/components';
import { openDialog } from '../../../base/dialog';
import { IconCheck, IconVideoOff } from '../../../base/icons';
import {
IconCheck,
IconHorizontalPoints,
IconVideoOff
} from '../../../base/icons';
import { MEDIA_TYPE } from '../../../base/media';
import {
getParticipantCount,
isEveryoneModerator
} from '../../../base/participants';
import { openSettingsDialog, SETTINGS_TABS } from '../../../settings';
import { MuteEveryonesVideoDialog } from '../../../video-menu/components';

const useStyles = makeStyles(theme => {
Expand Down Expand Up @@ -95,6 +100,8 @@ export const FooterContextMenu = ({ isOpen, onDrawerClose, onMouseLeave }: Props
const muteAllVideo = useCallback(
() => dispatch(openDialog(MuteEveryonesVideoDialog)), [ dispatch ]);

const openModeratorSettings = () => dispatch(openSettingsDialog(SETTINGS_TABS.MODERATOR));

const actions = [
{
accessibilityLabel: t('participantsPane.actions.audioModeration'),
Expand Down Expand Up @@ -139,6 +146,14 @@ export const FooterContextMenu = ({ isOpen, onDrawerClose, onMouseLeave }: Props
</div>
</ContextMenuItemGroup>
)}
<ContextMenuItemGroup
actions = { [ {
accessibilityLabel: t('participantsPane.actions.moreModerationControls'),
id: 'participants-pane-open-moderation-control-settings',
icon: IconHorizontalPoints,
onClick: openModeratorSettings,
text: t('participantsPane.actions.moreModerationControls')
} ] } />
</ContextMenu>
);
};
7 changes: 7 additions & 0 deletions react/features/reactions/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ import {
*/
export const ENDPOINT_REACTION_NAME = 'endpoint-reaction';

/**
* The (name of the) command which transports the state (represented by
* {State} for the local state at the time of this writing) of a {MuteReactions}
* (instance) between moderator and participants.
*/
export const MUTE_REACTIONS_COMMAND = 'mute-reactions';

/**
* The prefix for all reaction sound IDs. Also the ID used in config to disable reaction sounds.
*/
Expand Down
Loading

0 comments on commit a077043

Please sign in to comment.