diff --git a/lang/main.json b/lang/main.json index a53c65a1f9d..7cd24b31495 100644 --- a/lang/main.json +++ b/lang/main.json @@ -754,6 +754,7 @@ "invitedThreePlusMembers": "{{name}} and {{count}} others have been invited", "invitedTwoMembers": "{{first}} and {{second}} have been invited", "joinMeeting": "Join", + "keepHandsRaised": "Keep it Raised", "kickParticipant": "{{kicked}} was kicked by {{kicker}}", "leftOneMember": "{{name}} left the meeting", "leftThreePlusMembers": "{{name}} and many others left the meeting", @@ -766,6 +767,7 @@ "linkToSalesforceSuccess": "The meeting was linked to Salesforce", "localRecordingStarted": "{{name}} has started a local recording.", "localRecordingStopped": "{{name}} has stopped a local recording.", + "lowerRaisedHandsNotificationTitle": "It sounds like you've said something, so your hand will be lowered", "me": "Me", "moderationInEffectCSDescription": "Please raise hand if you want to share your screen.", "moderationInEffectCSTitle": "Screen sharing is blocked by the moderator", diff --git a/react/features/base/config/configType.ts b/react/features/base/config/configType.ts index 6a183e8ca9c..c924cfc8615 100644 --- a/react/features/base/config/configType.ts +++ b/react/features/base/config/configType.ts @@ -489,9 +489,9 @@ export interface IConfig { prejoinPageEnabled?: boolean; raisedHands?: { disableLowerHandByModerator?: boolean; - disableLowerHandNotification?: boolean; disableNextSpeakerNotification?: boolean; disableRemoveRaisedHandOnFocus?: boolean; + disableRemoveRaisedHandOnFocusNotification?: boolean; }; readOnlyName?: boolean; recordingLimit?: { diff --git a/react/features/base/config/functions.any.ts b/react/features/base/config/functions.any.ts index 035e7a557aa..0328abdea1f 100644 --- a/react/features/base/config/functions.any.ts +++ b/react/features/base/config/functions.any.ts @@ -103,23 +103,23 @@ export function getDisableLowerHandByModerator(state: IReduxState) { } /** - * Selector used to get the disableLowerHandNotification. + * Selector used to get the disableNextSpeakerNotification. * * @param {Object} state - The global state. * @returns {boolean} */ -export function getDisableLowerHandNotification(state: IReduxState) { - return state['features/base/config']?.raisedHands?.disableLowerHandNotification || true; +export function getDisableNextSpeakerNotification(state: IReduxState) { + return state['features/base/config']?.raisedHands?.disableNextSpeakerNotification || false; } /** - * Selector used to get the disableNextSpeakerNotification. + * Selector used to get the disableRemoveRaisedHandOnFocusNotification. * * @param {Object} state - The global state. * @returns {boolean} */ -export function getDisableNextSpeakerNotification(state: IReduxState) { - return state['features/base/config']?.raisedHands?.disableNextSpeakerNotification || false; +export function getDisableRemoveRaisedHandOnFocusNotification(state: IReduxState) { + return state['features/base/config']?.raisedHands?.disableRemoveRaisedHandOnFocusNotification || false; } /** diff --git a/react/features/base/participants/middleware.ts b/react/features/base/participants/middleware.ts index fd408bb4188..963966b0fe5 100644 --- a/react/features/base/participants/middleware.ts +++ b/react/features/base/participants/middleware.ts @@ -8,9 +8,11 @@ import { UPDATE_BREAKOUT_ROOMS } from '../../breakout-rooms/actionTypes'; import { getBreakoutRooms } from '../../breakout-rooms/functions'; import { toggleE2EE } from '../../e2ee/actions'; import { MAX_MODE } from '../../e2ee/constants'; -import { showNotification } from '../../notifications/actions'; +import { hideNotification, showNotification } from '../../notifications/actions'; import { LOCAL_RECORDING_NOTIFICATION_ID, + LOWER_RAISED_HANDS_ID, + NOTIFICATION_TIMEOUT, NOTIFICATION_TIMEOUT_TYPE, RAISE_HAND_NOTIFICATION_ID } from '../../notifications/constants'; @@ -24,7 +26,10 @@ import { CONFERENCE_JOINED, CONFERENCE_WILL_JOIN } from '../conference/actionTyp import { forEachConference, getCurrentConference } from '../conference/functions'; import { IJitsiConference } from '../conference/reducer'; import { SET_CONFIG } from '../config/actionTypes'; -import { getDisableRemoveRaisedHandOnFocus } from '../config/functions.any'; +import { + getDisableRemoveRaisedHandOnFocus, + getDisableRemoveRaisedHandOnFocusNotification +} from '../config/functions.any'; import { JitsiConferenceEvents } from '../lib-jitsi-meet'; import { MEDIA_TYPE } from '../media/constants'; import MiddlewareRegistry from '../redux/MiddlewareRegistry'; @@ -117,7 +122,23 @@ MiddlewareRegistry.register(store => next => action => { if (isLocal && dominantSpeaker?.id !== id && hasRaisedHand(participant) && !getDisableRemoveRaisedHandOnFocus(state)) { - store.dispatch(raiseHand(false)); + if (getDisableRemoveRaisedHandOnFocusNotification(state)) { + store.dispatch(raiseHand(false)); + } else { + const timeoutId = setTimeout(() => { + store.dispatch(raiseHand(false)); + }, NOTIFICATION_TIMEOUT.SHORT); + + store.dispatch(showNotification({ + titleKey: 'notify.lowerRaisedHandsNotificationTitle', + uid: LOWER_RAISED_HANDS_ID, + customActionNameKey: [ 'notify.keepHandsRaised' ], + customActionHandler: [ () => { + clearTimeout(timeoutId); + store.dispatch(hideNotification(LOWER_RAISED_HANDS_ID)); + } ] + }, NOTIFICATION_TIMEOUT_TYPE.SHORT)); + } } break; diff --git a/react/features/notifications/constants.ts b/react/features/notifications/constants.ts index 7bf47d81f95..87d4dad4e48 100644 --- a/react/features/notifications/constants.ts +++ b/react/features/notifications/constants.ts @@ -89,6 +89,13 @@ export const LOBBY_NOTIFICATION_ID = 'LOBBY_NOTIFICATION'; */ export const LOCAL_RECORDING_NOTIFICATION_ID = 'LOCAL_RECORDING_NOTIFICATION_ID'; +/** + * The identifier of the lower raised hand notification. + * + * @type {string} + */ +export const LOWER_RAISED_HANDS_ID = 'LOWER_RAISED_HANDS_ID'; + /** * The identifier of the raise hand notification. *