feat(reaction-sounds) Added sounds for reactions (#9775)
* Added sounds for reactions * Updated reactions list * Added reactions to sound settings * Added support for multiple sounds * Added feature flag for sounds * Updated sound settings Moved reactions toggle at the top of the list * Added disable reaction sounds notification * Added reaction button zoom for burst intensity * Fixed raise hand sound * Fixed register sounds for reactions * Changed boo emoji * Updated sounds * Fixed lint errors * Fixed reaction sounds file names * Fix raise hand sound Play sound only on raise hand not on lower hand * Fixed types for sound constants * Fixed type for raise hand sound constant
This commit is contained in:
parent
fe41eef398
commit
c7a91e1974
|
@ -48,6 +48,13 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: font-size ease .1s;
|
||||
|
||||
@for $i from 1 through 12 {
|
||||
&.increase-#{$i}{
|
||||
font-size: calc(20px + #{$i}px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -591,6 +591,7 @@
|
|||
"moderationStoppedTitle": "Moderation stopped",
|
||||
"moderationToggleDescription": "by {{participantDisplayName}}",
|
||||
"raiseHandAction": "Raise hand",
|
||||
"reactionSounds": "Disable sounds",
|
||||
"groupTitle": "Notifications"
|
||||
},
|
||||
"participantsPane": {
|
||||
|
@ -794,6 +795,7 @@
|
|||
"participantJoined": "Participant Joined",
|
||||
"participantLeft": "Participant Left",
|
||||
"playSounds": "Play sound on",
|
||||
"reactions": "Meeting reactions",
|
||||
"sameAsSystem": "Same as system ({{label}})",
|
||||
"selectAudioOutput": "Audio output",
|
||||
"selectCamera": "Camera",
|
||||
|
@ -884,7 +886,6 @@
|
|||
"muteEveryonesVideo": "Disable everyone's camera",
|
||||
"muteEveryoneElsesVideo": "Disable everyone else's camera",
|
||||
"participants": "Participants",
|
||||
"party": "Party Popper",
|
||||
"pip": "Toggle Picture-in-Picture mode",
|
||||
"privateMessage": "Send private message",
|
||||
"profile": "Edit your profile",
|
||||
|
@ -901,6 +902,7 @@
|
|||
"shareYourScreen": "Start / Stop sharing your screen",
|
||||
"shortcuts": "Toggle shortcuts",
|
||||
"show": "Show on stage",
|
||||
"silence": "Silence",
|
||||
"speakerStats": "Toggle speaker statistics",
|
||||
"surprised": "Surprised",
|
||||
"tileView": "Toggle tile view",
|
||||
|
@ -925,6 +927,7 @@
|
|||
"clap": "Clap",
|
||||
"closeChat": "Close chat",
|
||||
"closeReactionsMenu": "Close reactions menu",
|
||||
"disableReactionSounds": "You can disable reaction sounds for this meeting",
|
||||
"documentClose": "Close shared document",
|
||||
"documentOpen": "Open shared document",
|
||||
"download": "Download our apps",
|
||||
|
@ -960,7 +963,6 @@
|
|||
"openChat": "Open chat",
|
||||
"openReactionsMenu": "Open reactions menu",
|
||||
"participants": "Participants",
|
||||
"party": "Celebration",
|
||||
"pip": "Enter Picture-in-Picture mode",
|
||||
"privateMessage": "Send private message",
|
||||
"profile": "Edit your profile",
|
||||
|
@ -970,7 +972,7 @@
|
|||
"reactionClap": "Send clap reaction",
|
||||
"reactionLaugh": "Send laugh reaction",
|
||||
"reactionLike": "Send thumbs up reaction",
|
||||
"reactionParty": "Send party popper reaction",
|
||||
"reactionSilence": "Send silence reaction",
|
||||
"reactionSurprised": "Send surprised reaction",
|
||||
"security": "Security options",
|
||||
"Settings": "Settings",
|
||||
|
@ -978,6 +980,7 @@
|
|||
"sharedvideo": "Share video",
|
||||
"shareRoom": "Invite someone",
|
||||
"shortcuts": "View shortcuts",
|
||||
"silence": "Silence",
|
||||
"speakerStats": "Speaker stats",
|
||||
"startScreenSharing": "Start screen sharing",
|
||||
"startSubtitles": "Start subtitles",
|
||||
|
|
|
@ -31,6 +31,7 @@ const DEFAULT_STATE = {
|
|||
soundsParticipantJoined: true,
|
||||
soundsParticipantLeft: true,
|
||||
soundsTalkWhileMuted: true,
|
||||
soundsReactions: true,
|
||||
startAudioOnly: false,
|
||||
startWithAudioMuted: false,
|
||||
startWithVideoMuted: false,
|
||||
|
|
|
@ -58,3 +58,8 @@ export const SEND_REACTIONS = 'SEND_REACTIONS';
|
|||
* The type of action to adds reactions to the queue.
|
||||
*/
|
||||
export const PUSH_REACTIONS = 'PUSH_REACTIONS';
|
||||
|
||||
/**
|
||||
* The type of action to display disable notification sounds.
|
||||
*/
|
||||
export const SHOW_SOUNDS_NOTIFICATION = 'SHOW_SOUNDS_NOTIFICATION';
|
||||
|
|
|
@ -1,16 +1,28 @@
|
|||
// @flow
|
||||
|
||||
import {
|
||||
SHOW_SOUNDS_NOTIFICATION,
|
||||
TOGGLE_REACTIONS_VISIBLE
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the reactions menu.
|
||||
*
|
||||
* @returns {Function}
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function toggleReactionsMenuVisibility() {
|
||||
return {
|
||||
type: TOGGLE_REACTIONS_VISIBLE
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the disable sounds notification.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function displayReactionSoundsNotification() {
|
||||
return {
|
||||
type: SHOW_SOUNDS_NOTIFICATION
|
||||
};
|
||||
}
|
||||
|
|
|
@ -28,12 +28,28 @@ type Props = AbstractToolbarButtonProps & {
|
|||
label?: string
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link ReactionButton}.
|
||||
*/
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* Used to determine zoom level on reaction burst.
|
||||
*/
|
||||
increaseLevel: number,
|
||||
|
||||
/**
|
||||
* Timeout ID to reset reaction burst.
|
||||
*/
|
||||
increaseTimeout: TimeoutID | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a button in the reactions menu.
|
||||
*
|
||||
* @extends AbstractToolbarButton
|
||||
*/
|
||||
class ReactionButton extends AbstractToolbarButton<Props> {
|
||||
class ReactionButton extends AbstractToolbarButton<Props, State> {
|
||||
/**
|
||||
* Default values for {@code ReactionButton} component's properties.
|
||||
*
|
||||
|
@ -52,10 +68,18 @@ class ReactionButton extends AbstractToolbarButton<Props> {
|
|||
super(props);
|
||||
|
||||
this._onKeyDown = this._onKeyDown.bind(this);
|
||||
this._onClickHandler = this._onClickHandler.bind(this);
|
||||
|
||||
this.state = {
|
||||
increaseLevel: 0,
|
||||
increaseTimeout: null
|
||||
};
|
||||
}
|
||||
|
||||
_onKeyDown: (Object) => void;
|
||||
|
||||
_onClickHandler: () => void;
|
||||
|
||||
/**
|
||||
* Handles 'Enter' key on the button to trigger onClick for accessibility.
|
||||
* We should be handling Space onKeyUp but it conflicts with PTT.
|
||||
|
@ -78,6 +102,28 @@ class ReactionButton extends AbstractToolbarButton<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles reaction button click.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onClickHandler() {
|
||||
this.props.onClick();
|
||||
clearTimeout(this.state.increaseTimeout);
|
||||
const timeout = setTimeout(() => {
|
||||
this.setState({
|
||||
increaseLevel: 0
|
||||
});
|
||||
}, 500);
|
||||
|
||||
this.setState(state => {
|
||||
return {
|
||||
increaseLevel: state.increaseLevel + 1,
|
||||
increaseTimeout: timeout
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the button of this {@code ReactionButton}.
|
||||
*
|
||||
|
@ -92,7 +138,7 @@ class ReactionButton extends AbstractToolbarButton<Props> {
|
|||
aria-label = { this.props.accessibilityLabel }
|
||||
aria-pressed = { this.props.toggled }
|
||||
className = 'toolbox-button'
|
||||
onClick = { this.props.onClick }
|
||||
onClick = { this._onClickHandler }
|
||||
onKeyDown = { this._onKeyDown }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }>
|
||||
|
@ -113,10 +159,13 @@ class ReactionButton extends AbstractToolbarButton<Props> {
|
|||
* @inheritdoc
|
||||
*/
|
||||
_renderIcon() {
|
||||
const { toggled, icon, label } = this.props;
|
||||
const { increaseLevel } = this.state;
|
||||
|
||||
return (
|
||||
<div className = { `toolbox-icon ${this.props.toggled ? 'toggled' : ''}` }>
|
||||
<span className = 'emoji'>{this.props.icon}</span>
|
||||
{this.props.label && <span className = 'text'>{this.props.label}</span>}
|
||||
<div className = { `toolbox-icon ${toggled ? 'toggled' : ''}` }>
|
||||
<span className = { `emoji increase-${increaseLevel > 12 ? 12 : increaseLevel}` }>{icon}</span>
|
||||
{label && <span className = 'text'>{label}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,10 +11,11 @@ import {
|
|||
import { translate } from '../../../base/i18n';
|
||||
import { getLocalParticipant, getParticipantCount, participantUpdated } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { playSound } from '../../../base/sounds';
|
||||
import { dockToolbox } from '../../../toolbox/actions.web';
|
||||
import { addReactionToBuffer } from '../../actions.any';
|
||||
import { toggleReactionsMenuVisibility } from '../../actions.web';
|
||||
import { REACTIONS } from '../../constants';
|
||||
import { RAISE_HAND_SOUND_ID, REACTIONS } from '../../constants';
|
||||
|
||||
import ReactionButton from './ReactionButton';
|
||||
|
||||
|
@ -53,7 +54,12 @@ type Props = {
|
|||
/**
|
||||
* Whether or not it's displayed in the overflow menu.
|
||||
*/
|
||||
overflowMenu: boolean
|
||||
overflowMenu: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not reaction sounds are enabled.
|
||||
*/
|
||||
_reactionSounds: boolean
|
||||
};
|
||||
|
||||
declare var APP: Object;
|
||||
|
@ -106,11 +112,16 @@ class ReactionsMenu extends Component<Props> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_onToolbarToggleRaiseHand() {
|
||||
const { dispatch, _raisedHand, _reactionSounds } = this.props;
|
||||
|
||||
sendAnalytics(createToolbarEvent(
|
||||
'raise.hand',
|
||||
{ enable: !this.props._raisedHand }));
|
||||
{ enable: !_raisedHand }));
|
||||
this._doToggleRaiseHand();
|
||||
this.props.dispatch(toggleReactionsMenuVisibility());
|
||||
dispatch(toggleReactionsMenuVisibility());
|
||||
if (_reactionSounds && _raisedHand) {
|
||||
dispatch(playSound(RAISE_HAND_SOUND_ID));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,11 +223,13 @@ class ReactionsMenu extends Component<Props> {
|
|||
*/
|
||||
function mapStateToProps(state) {
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
const { soundsReactions } = state['features/base/settings'];
|
||||
|
||||
return {
|
||||
_localParticipantID: localParticipant.id,
|
||||
_raisedHand: localParticipant.raisedHand,
|
||||
_participantCount: getParticipantCount(state)
|
||||
_participantCount: getParticipantCount(state),
|
||||
_reactionSounds: soundsReactions
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,37 +1,69 @@
|
|||
// @flow
|
||||
|
||||
export const REACTIONS = {
|
||||
like: {
|
||||
message: ':thumbs_up:',
|
||||
emoji: '👍',
|
||||
shortcutChar: 'T'
|
||||
},
|
||||
clap: {
|
||||
message: ':clap:',
|
||||
emoji: '👏',
|
||||
shortcutChar: 'C'
|
||||
},
|
||||
laugh: {
|
||||
message: ':grinning_face:',
|
||||
emoji: '😀',
|
||||
shortcutChar: 'L'
|
||||
},
|
||||
surprised: {
|
||||
message: ':face_with_open_mouth:',
|
||||
emoji: '😮',
|
||||
shortcutChar: 'O'
|
||||
},
|
||||
boo: {
|
||||
message: ':slightly_frowning_face:',
|
||||
emoji: '🙁',
|
||||
shortcutChar: 'B'
|
||||
},
|
||||
party: {
|
||||
message: ':party_popper:',
|
||||
emoji: '🎉',
|
||||
shortcutChar: 'P'
|
||||
}
|
||||
};
|
||||
import {
|
||||
CLAP_SOUND_FILES,
|
||||
LAUGH_SOUND_FILES,
|
||||
LIKE_SOUND_FILES,
|
||||
BOO_SOUND_FILES,
|
||||
SURPRISE_SOUND_FILES,
|
||||
SILENCE_SOUND_FILES
|
||||
} from './sounds';
|
||||
|
||||
/**
|
||||
* The audio ID prefix of the audio element for which the {@link playAudio} action is
|
||||
* triggered when a new laugh reaction is received.
|
||||
*
|
||||
* @type { string }
|
||||
*/
|
||||
export const LAUGH_SOUND_ID = 'LAUGH_SOUND_';
|
||||
|
||||
/**
|
||||
* The audio ID prefix of the audio element for which the {@link playAudio} action is
|
||||
* triggered when a new clap reaction is received.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const CLAP_SOUND_ID = 'CLAP_SOUND_';
|
||||
|
||||
/**
|
||||
* The audio ID prefix of the audio element for which the {@link playAudio} action is
|
||||
* triggered when a new like reaction is received.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const LIKE_SOUND_ID = 'LIKE_SOUND_';
|
||||
|
||||
/**
|
||||
* The audio ID prefix of the audio element for which the {@link playAudio} action is
|
||||
* triggered when a new boo reaction is received.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const BOO_SOUND_ID = 'BOO_SOUND_';
|
||||
|
||||
/**
|
||||
* The audio ID prefix of the audio element for which the {@link playAudio} action is
|
||||
* triggered when a new surprised reaction is received.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const SURPRISE_SOUND_ID = 'SURPRISE_SOUND_';
|
||||
|
||||
/**
|
||||
* The audio ID prefix of the audio element for which the {@link playAudio} action is
|
||||
* triggered when a new silence reaction is received.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const SILENCE_SOUND_ID = 'SILENCE_SOUND_';
|
||||
|
||||
/**
|
||||
* The audio ID of the audio element for which the {@link playAudio} action is
|
||||
* triggered when a new raise hand event is received.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const RAISE_HAND_SOUND_ID = 'RAISE_HAND_SOUND_ID';
|
||||
|
||||
export type ReactionEmojiProps = {
|
||||
|
||||
|
@ -45,3 +77,51 @@ export type ReactionEmojiProps = {
|
|||
*/
|
||||
uid: number
|
||||
}
|
||||
|
||||
export const SOUNDS_THRESHOLDS = [ 1, 4, 10 ];
|
||||
|
||||
|
||||
export const REACTIONS = {
|
||||
like: {
|
||||
message: ':thumbs_up:',
|
||||
emoji: '👍',
|
||||
shortcutChar: 'T',
|
||||
soundId: LIKE_SOUND_ID,
|
||||
soundFiles: LIKE_SOUND_FILES
|
||||
},
|
||||
clap: {
|
||||
message: ':clap:',
|
||||
emoji: '👏',
|
||||
shortcutChar: 'C',
|
||||
soundId: CLAP_SOUND_ID,
|
||||
soundFiles: CLAP_SOUND_FILES
|
||||
},
|
||||
laugh: {
|
||||
message: ':grinning_face:',
|
||||
emoji: '😀',
|
||||
shortcutChar: 'L',
|
||||
soundId: LAUGH_SOUND_ID,
|
||||
soundFiles: LAUGH_SOUND_FILES
|
||||
},
|
||||
surprised: {
|
||||
message: ':face_with_open_mouth:',
|
||||
emoji: '😮',
|
||||
shortcutChar: 'O',
|
||||
soundId: SURPRISE_SOUND_ID,
|
||||
soundFiles: SURPRISE_SOUND_FILES
|
||||
},
|
||||
boo: {
|
||||
message: ':slightly_frowning_face:',
|
||||
emoji: '🙁',
|
||||
shortcutChar: 'B',
|
||||
soundId: BOO_SOUND_ID,
|
||||
soundFiles: BOO_SOUND_FILES
|
||||
},
|
||||
silence: {
|
||||
message: ':face_without_mouth:',
|
||||
emoji: '😶',
|
||||
shortcutChar: 'S',
|
||||
soundId: SILENCE_SOUND_ID,
|
||||
soundFiles: SILENCE_SOUND_FILES
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ import uuid from 'uuid';
|
|||
import { getLocalParticipant } from '../base/participants';
|
||||
import { extractFqnFromPath } from '../dynamic-branding/functions';
|
||||
|
||||
import { REACTIONS } from './constants';
|
||||
import { REACTIONS, SOUNDS_THRESHOLDS } from './constants';
|
||||
import logger from './logger';
|
||||
|
||||
/**
|
||||
|
@ -88,3 +88,57 @@ export async function sendReactionsWebhook(state: Object, reactions: Array<?stri
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unique reactions from the reactions buffer.
|
||||
*
|
||||
* @param {Array} reactions - The reactions buffer.
|
||||
* @returns {Array}
|
||||
*/
|
||||
function getUniqueReactions(reactions: Array<string>) {
|
||||
return [ ...new Set(reactions) ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns frequency of given reaction in array.
|
||||
*
|
||||
* @param {Array} reactions - Array of reactions.
|
||||
* @param {string} reaction - Reaction to get frequency for.
|
||||
* @returns {number}
|
||||
*/
|
||||
function getReactionFrequency(reactions: Array<string>, reaction: string) {
|
||||
return reactions.filter(r => r === reaction).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the threshold number for a given frequency.
|
||||
*
|
||||
* @param {number} frequency - Frequency of reaction.
|
||||
* @returns {number}
|
||||
*/
|
||||
function getSoundThresholdByFrequency(frequency) {
|
||||
for (const i of SOUNDS_THRESHOLDS) {
|
||||
if (frequency <= i) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return SOUNDS_THRESHOLDS[SOUNDS_THRESHOLDS.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unique reactions with threshold.
|
||||
*
|
||||
* @param {Array} reactions - The reactions buffer.
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function getReactionsSoundsThresholds(reactions: Array<string>) {
|
||||
const unique = getUniqueReactions(reactions);
|
||||
|
||||
return unique.map<Object>(reaction => {
|
||||
return {
|
||||
reaction,
|
||||
threshold: getSoundThresholdByFrequency(getReactionFrequency(reactions, reaction))
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,14 +3,19 @@
|
|||
import { batch } from 'react-redux';
|
||||
|
||||
import { ENDPOINT_REACTION_NAME } from '../../../modules/API/constants';
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
import { updateSettings } from '../base/settings';
|
||||
import { playSound, registerSound, unregisterSound } from '../base/sounds';
|
||||
import { isVpaasMeeting } from '../jaas/functions';
|
||||
import { NOTIFICATION_TIMEOUT, showNotification } from '../notifications';
|
||||
|
||||
import {
|
||||
ADD_REACTION_BUFFER,
|
||||
FLUSH_REACTION_BUFFER,
|
||||
SEND_REACTIONS,
|
||||
PUSH_REACTIONS
|
||||
PUSH_REACTIONS,
|
||||
SHOW_SOUNDS_NOTIFICATION
|
||||
} from './actionTypes';
|
||||
import {
|
||||
addReactionsToChat,
|
||||
|
@ -19,7 +24,15 @@ import {
|
|||
sendReactions,
|
||||
setReactionQueue
|
||||
} from './actions.any';
|
||||
import { getReactionMessageFromBuffer, getReactionsWithId, sendReactionsWebhook } from './functions.any';
|
||||
import { displayReactionSoundsNotification } from './actions.web';
|
||||
import { RAISE_HAND_SOUND_ID, REACTIONS, SOUNDS_THRESHOLDS } from './constants';
|
||||
import {
|
||||
getReactionMessageFromBuffer,
|
||||
getReactionsSoundsThresholds,
|
||||
getReactionsWithId,
|
||||
sendReactionsWebhook
|
||||
} from './functions.any';
|
||||
import { RAISE_HAND_SOUND_FILE } from './sounds';
|
||||
|
||||
|
||||
declare var APP: Object;
|
||||
|
@ -35,6 +48,33 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
const { dispatch, getState } = store;
|
||||
|
||||
switch (action.type) {
|
||||
case APP_WILL_MOUNT:
|
||||
batch(() => {
|
||||
Object.keys(REACTIONS).forEach(key => {
|
||||
for (let i = 0; i < SOUNDS_THRESHOLDS.length; i++) {
|
||||
dispatch(registerSound(
|
||||
`${REACTIONS[key].soundId}${SOUNDS_THRESHOLDS[i]}`,
|
||||
REACTIONS[key].soundFiles[i]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
dispatch(registerSound(RAISE_HAND_SOUND_ID, RAISE_HAND_SOUND_FILE));
|
||||
});
|
||||
break;
|
||||
|
||||
case APP_WILL_UNMOUNT:
|
||||
batch(() => {
|
||||
Object.keys(REACTIONS).forEach(key => {
|
||||
for (let i = 0; i < SOUNDS_THRESHOLDS.length; i++) {
|
||||
dispatch(unregisterSound(`${REACTIONS[key].soundId}${SOUNDS_THRESHOLDS[i]}`));
|
||||
}
|
||||
});
|
||||
dispatch(unregisterSound(RAISE_HAND_SOUND_ID));
|
||||
});
|
||||
break;
|
||||
|
||||
case ADD_REACTION_BUFFER: {
|
||||
const { timeoutID, buffer } = getState()['features/reactions'];
|
||||
const { reaction } = action;
|
||||
|
@ -82,10 +122,36 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
}
|
||||
|
||||
case PUSH_REACTIONS: {
|
||||
const queue = store.getState()['features/reactions'].queue;
|
||||
const state = getState();
|
||||
const { queue, notificationDisplayed } = state['features/reactions'];
|
||||
const { soundsReactions } = state['features/base/settings'];
|
||||
const reactions = action.reactions;
|
||||
|
||||
dispatch(setReactionQueue([ ...queue, ...getReactionsWithId(reactions) ]));
|
||||
batch(() => {
|
||||
if (!notificationDisplayed && soundsReactions) {
|
||||
dispatch(displayReactionSoundsNotification());
|
||||
}
|
||||
if (soundsReactions) {
|
||||
const reactionSoundsThresholds = getReactionsSoundsThresholds(reactions);
|
||||
|
||||
reactionSoundsThresholds.forEach(reaction =>
|
||||
dispatch(playSound(`${REACTIONS[reaction.reaction].soundId}${reaction.threshold}`))
|
||||
);
|
||||
}
|
||||
dispatch(setReactionQueue([ ...queue, ...getReactionsWithId(reactions) ]));
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case SHOW_SOUNDS_NOTIFICATION: {
|
||||
dispatch(showNotification({
|
||||
titleKey: 'toolbar.disableReactionSounds',
|
||||
customActionNameKey: 'notify.reactionSounds',
|
||||
customActionHandler: () => dispatch(updateSettings({
|
||||
soundsReactions: false
|
||||
}))
|
||||
}, NOTIFICATION_TIMEOUT));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@ import {
|
|||
TOGGLE_REACTIONS_VISIBLE,
|
||||
SET_REACTION_QUEUE,
|
||||
ADD_REACTION_BUFFER,
|
||||
FLUSH_REACTION_BUFFER
|
||||
FLUSH_REACTION_BUFFER,
|
||||
SHOW_SOUNDS_NOTIFICATION
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
|
@ -17,7 +18,8 @@ import {
|
|||
* visible: boolean,
|
||||
* message: string,
|
||||
* timeoutID: number,
|
||||
* queue: Array
|
||||
* queue: Array,
|
||||
* notificationDisplayed: boolean
|
||||
* }}
|
||||
*/
|
||||
function _getInitialState() {
|
||||
|
@ -49,7 +51,12 @@ function _getInitialState() {
|
|||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
queue: []
|
||||
queue: [],
|
||||
|
||||
/**
|
||||
* Whether or not the disable reaction sounds notification was shown
|
||||
*/
|
||||
notificationDisplayed: false
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -84,6 +91,13 @@ ReducerRegistry.register(
|
|||
queue: action.value
|
||||
};
|
||||
}
|
||||
|
||||
case SHOW_SOUNDS_NOTIFICATION: {
|
||||
return {
|
||||
...state,
|
||||
notificationDisplayed: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* The name of the bundled audio files which will be played for the laugh reaction sound.
|
||||
*
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
export const LAUGH_SOUND_FILES = [ 'reactions-laughter.mp3', 'reactions-laughter.mp3', 'reactions-laughter.mp3' ];
|
||||
|
||||
/**
|
||||
* The name of the bundled audio file which will be played for the clap reaction sound.
|
||||
*
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
export const CLAP_SOUND_FILES = [ 'reactions–applause.mp3', 'reactions–applause.mp3', 'reactions–applause.mp3' ];
|
||||
|
||||
/**
|
||||
* The name of the bundled audio file which will be played for the like reaction sound.
|
||||
*
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
export const LIKE_SOUND_FILES = [ 'reactions–thumbs-up.mp3', 'reactions–thumbs-up.mp3', 'reactions–thumbs-up.mp3' ];
|
||||
|
||||
/**
|
||||
* The name of the bundled audio file which will be played for the boo reaction sound.
|
||||
*
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
export const BOO_SOUND_FILES = [ 'reactions–boo.mp3', 'reactions–boo.mp3', 'reactions–boo.mp3' ];
|
||||
|
||||
/**
|
||||
* The name of the bundled audio file which will be played for the surprised reaction sound.
|
||||
*
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
export const SURPRISE_SOUND_FILES = [ 'reactions–surprise.mp3', 'reactions–surprise.mp3', 'reactions–surprise.mp3' ];
|
||||
|
||||
/**
|
||||
* The name of the bundled audio file which will be played for the silence reaction sound.
|
||||
*
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
export const SILENCE_SOUND_FILES = [ 'reactions–crickets.mp3', 'reactions–crickets.mp3', 'reactions–crickets.mp3' ];
|
||||
|
||||
/**
|
||||
* The name of the bundled audio file which will be played for the raise hand sound.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const RAISE_HAND_SOUND_FILE = 'reactions–raised-hand.mp3';
|
|
@ -142,14 +142,16 @@ export function submitSoundsTab(newState: Object): Function {
|
|||
const shouldUpdate = (newState.soundsIncomingMessage !== currentState.soundsIncomingMessage)
|
||||
|| (newState.soundsParticipantJoined !== currentState.soundsParticipantJoined)
|
||||
|| (newState.soundsParticipantLeft !== currentState.soundsParticipantLeft)
|
||||
|| (newState.soundsTalkWhileMuted !== currentState.soundsTalkWhileMuted);
|
||||
|| (newState.soundsTalkWhileMuted !== currentState.soundsTalkWhileMuted)
|
||||
|| (newState.soundsReactions !== currentState.soundsReactions);
|
||||
|
||||
if (shouldUpdate) {
|
||||
dispatch(updateSettings({
|
||||
soundsIncomingMessage: newState.soundsIncomingMessage,
|
||||
soundsParticipantJoined: newState.soundsParticipantJoined,
|
||||
soundsParticipantLeft: newState.soundsParticipantLeft,
|
||||
soundsTalkWhileMuted: newState.soundsTalkWhileMuted
|
||||
soundsTalkWhileMuted: newState.soundsTalkWhileMuted,
|
||||
soundsReactions: newState.soundsReactions
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -35,6 +35,16 @@ export type Props = {
|
|||
*/
|
||||
soundsTalkWhileMuted: Boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the sound for reactions should play.
|
||||
*/
|
||||
soundsReactions: Boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the reactions feature is enabled.
|
||||
*/
|
||||
enableReactions: Boolean,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
|
@ -85,6 +95,8 @@ class SoundsTab extends AbstractDialogTab<Props> {
|
|||
soundsParticipantJoined,
|
||||
soundsParticipantLeft,
|
||||
soundsTalkWhileMuted,
|
||||
soundsReactions,
|
||||
enableReactions,
|
||||
t
|
||||
} = this.props;
|
||||
|
||||
|
@ -95,6 +107,12 @@ class SoundsTab extends AbstractDialogTab<Props> {
|
|||
<h2 className = 'mock-atlaskit-label'>
|
||||
{t('settings.playSounds')}
|
||||
</h2>
|
||||
{enableReactions && <Checkbox
|
||||
isChecked = { soundsReactions }
|
||||
label = { t('settings.reactions') }
|
||||
name = 'soundsReactions'
|
||||
onChange = { this._onChange } />
|
||||
}
|
||||
<Checkbox
|
||||
isChecked = { soundsIncomingMessage }
|
||||
label = { t('settings.incomingMessage') }
|
||||
|
|
|
@ -171,14 +171,18 @@ export function getSoundsTabProps(stateful: Object | Function) {
|
|||
soundsIncomingMessage,
|
||||
soundsParticipantJoined,
|
||||
soundsParticipantLeft,
|
||||
soundsTalkWhileMuted
|
||||
soundsTalkWhileMuted,
|
||||
soundsReactions
|
||||
} = state['features/base/settings'];
|
||||
const { enableReactions } = state['features/base/config'];
|
||||
|
||||
return {
|
||||
soundsIncomingMessage,
|
||||
soundsParticipantJoined,
|
||||
soundsParticipantLeft,
|
||||
soundsTalkWhileMuted
|
||||
soundsTalkWhileMuted,
|
||||
soundsReactions,
|
||||
enableReactions
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ export type Props = {
|
|||
*
|
||||
* @abstract
|
||||
*/
|
||||
export default class AbstractToolbarButton<P: Props> extends Component<P> {
|
||||
export default class AbstractToolbarButton<P: Props, State=void> extends Component<P, State> {
|
||||
/**
|
||||
* Initializes a new {@code AbstractToolbarButton} instance.
|
||||
*
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue