feat(reactions,chat): expand emoji features across reactions, chat picker, and message reactions
Add 8 new floating reactions (party, thinking, wave, cry, eyes, mindblown, pray, rocket) in a two-row grid layout without sounds or shortcuts. Replace the basic SmileysPanel in chat with a full @emoji-mart/react picker featuring categories, search, and skin tones. Expand message reaction quick-access from 5 to 8 emojis with a "+" button to open the full picker. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ef6a5a1ffe
commit
325960fa15
|
|
@ -1,7 +1,7 @@
|
|||
@use 'sass:math';
|
||||
|
||||
.reactions-menu {
|
||||
width: 330px;
|
||||
width: 360px;
|
||||
background: #242528;
|
||||
box-shadow: 0px 3px 16px rgba(0, 0, 0, 0.6), 0px 0px 4px 1px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 6px;
|
||||
|
|
@ -70,15 +70,15 @@
|
|||
}
|
||||
|
||||
.reactions-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
|
||||
.toolbox-button {
|
||||
margin-right: 8px;
|
||||
touch-action: manipulation;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toolbox-button:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.raise-hand-row {
|
||||
|
|
|
|||
|
|
@ -1314,6 +1314,7 @@
|
|||
"audioRoute": "Select the sound device",
|
||||
"boo": "Boo",
|
||||
"breakoutRooms": "Breakout rooms",
|
||||
"cry": "Cry",
|
||||
"callQuality": "Manage video quality",
|
||||
"carmode": "Car Mode",
|
||||
"cc": "Toggle subtitles",
|
||||
|
|
@ -1332,6 +1333,7 @@
|
|||
"endConference": "End meeting for all",
|
||||
"enterFullScreen": "View full screen",
|
||||
"enterTileView": "Enter tile view",
|
||||
"eyes": "Eyes",
|
||||
"exitFullScreen": "Exit full screen",
|
||||
"exitTileView": "Exit tile view",
|
||||
"expand": "Expand",
|
||||
|
|
@ -1349,6 +1351,7 @@
|
|||
"leaveConference": "Leave meeting",
|
||||
"like": "Thumbs Up",
|
||||
"linkToSalesforce": "Link to Salesforce",
|
||||
"mindblown": "Mind Blown",
|
||||
"lobbyButton": "Enable/disable lobby mode",
|
||||
"localRecording": "Toggle local recording controls",
|
||||
"lockRoom": "Toggle meeting password",
|
||||
|
|
@ -1365,6 +1368,8 @@
|
|||
"muteGUMPending": "Connecting your microphone",
|
||||
"noiseSuppression": "Extra noise suppression",
|
||||
"openChat": "Open chat",
|
||||
"party": "Party",
|
||||
"pray": "Pray",
|
||||
"participants": "Open participants panel. {{participantsCount}} participants",
|
||||
"pip": "Toggle Picture-in-Picture mode",
|
||||
"privateMessage": "Send private message",
|
||||
|
|
@ -1376,6 +1381,7 @@
|
|||
"recording": "Toggle recording",
|
||||
"remoteMute": "Mute participant",
|
||||
"remoteVideoMute": "Disable camera of participant",
|
||||
"rocket": "Rocket",
|
||||
"security": "Security options",
|
||||
"selectBackground": "Select Background",
|
||||
"selfView": "Toggle self view",
|
||||
|
|
@ -1396,13 +1402,15 @@
|
|||
"stopSharedVideo": "Stop video",
|
||||
"surprised": "Surprised",
|
||||
"tileView": "Toggle tile view",
|
||||
"thinking": "Thinking",
|
||||
"toggleCamera": "Toggle camera",
|
||||
"toggleFilmstrip": "Toggle filmstrip",
|
||||
"unmute": "Unmute microphone",
|
||||
"videoblur": "Toggle video blur",
|
||||
"videomute": "Stop camera",
|
||||
"videomuteGUMPending": "Connecting your camera",
|
||||
"videounmute": "Start camera"
|
||||
"videounmute": "Start camera",
|
||||
"wave": "Wave"
|
||||
},
|
||||
"addPeople": "Add people to your call",
|
||||
"advancedAudioSettings": {
|
||||
|
|
@ -1426,6 +1434,7 @@
|
|||
"authenticate": "Authenticate",
|
||||
"boo": "Boo",
|
||||
"callQuality": "Manage video quality",
|
||||
"cry": "Cry",
|
||||
"chat": "Open / Close chat",
|
||||
"clap": "Clap",
|
||||
"closeChat": "Close chat",
|
||||
|
|
@ -1445,6 +1454,7 @@
|
|||
"enterTileView": "Enter tile view",
|
||||
"exitFullScreen": "Exit full screen",
|
||||
"exitTileView": "Exit tile view",
|
||||
"eyes": "Eyes",
|
||||
"feedback": "Leave feedback",
|
||||
"fileSharing": "File sharing",
|
||||
"giphy": "Toggle GIPHY menu",
|
||||
|
|
@ -1464,6 +1474,7 @@
|
|||
"logout": "Log Out",
|
||||
"love": "Heart",
|
||||
"lowerYourHand": "Lower your hand",
|
||||
"mindblown": "Mind Blown",
|
||||
"moreActions": "More actions",
|
||||
"moreOptions": "More options",
|
||||
"mute": "Mute microphone",
|
||||
|
|
@ -1480,9 +1491,11 @@
|
|||
"noisyAudioInputTitle": "Your microphone appears to be noisy!",
|
||||
"openChat": "Open chat",
|
||||
"openReactionsMenu": "Open reactions menu",
|
||||
"party": "Party",
|
||||
"participants": "Participants",
|
||||
"pip": "Enter Picture-in-Picture mode",
|
||||
"polls": "Polls",
|
||||
"pray": "Pray",
|
||||
"privateMessage": "Send private message",
|
||||
"profile": "Edit your profile",
|
||||
"raiseHand": "Raise your hand",
|
||||
|
|
@ -1496,6 +1509,7 @@
|
|||
"reactionSilence": "Send silence reaction",
|
||||
"reactionSurprised": "Send surprised reaction",
|
||||
"reactions": "Reactions",
|
||||
"rocket": "Rocket",
|
||||
"security": "Security options",
|
||||
"selectBackground": "Select background",
|
||||
"shareRoom": "Invite someone",
|
||||
|
|
@ -1518,13 +1532,15 @@
|
|||
"stopSubtitles": "Stop subtitles",
|
||||
"surprised": "Surprised",
|
||||
"talkWhileMutedPopup": "Trying to speak? You are muted.",
|
||||
"thinking": "Thinking",
|
||||
"tileViewToggle": "Toggle tile view",
|
||||
"toggleCamera": "Toggle camera",
|
||||
"unmute": "Unmute microphone",
|
||||
"videoSettings": "Video settings",
|
||||
"videomute": "Stop camera",
|
||||
"videomuteGUMPending": "Connecting your camera",
|
||||
"videounmute": "Start camera"
|
||||
"videounmute": "Start camera",
|
||||
"wave": "Wave"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Start / Stop subtitles",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
"@amplitude/analytics-browser": "2.17.12",
|
||||
"@amplitude/analytics-react-native": "1.4.13",
|
||||
"@braintree/sanitize-url": "7.0.0",
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
"@emotion/react": "11.10.6",
|
||||
"@emotion/styled": "11.10.6",
|
||||
"@giphy/js-fetch-api": "4.9.3",
|
||||
|
|
@ -2678,6 +2680,22 @@
|
|||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emoji-mart/data": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz",
|
||||
"integrity": "sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emoji-mart/react": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@emoji-mart/react/-/react-1.1.1.tgz",
|
||||
"integrity": "sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"emoji-mart": "^5.2",
|
||||
"react": "^16.8 || ^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin": {
|
||||
"version": "11.10.6",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz",
|
||||
|
|
@ -28840,6 +28858,16 @@
|
|||
"@types/hammerjs": "^2.0.36"
|
||||
}
|
||||
},
|
||||
"@emoji-mart/data": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz",
|
||||
"integrity": "sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw=="
|
||||
},
|
||||
"@emoji-mart/react": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@emoji-mart/react/-/react-1.1.1.tgz",
|
||||
"integrity": "sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g=="
|
||||
},
|
||||
"@emotion/babel-plugin": {
|
||||
"version": "11.10.6",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
"@amplitude/analytics-browser": "2.17.12",
|
||||
"@amplitude/analytics-react-native": "1.4.13",
|
||||
"@braintree/sanitize-url": "7.0.0",
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
"@emotion/react": "11.10.6",
|
||||
"@emotion/styled": "11.10.6",
|
||||
"@giphy/js-fetch-api": "4.9.3",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import Input from '../../../base/ui/components/web/Input';
|
|||
import { CHAT_SIZE } from '../../constants';
|
||||
import { areSmileysDisabled, isSendGroupChatDisabled } from '../../functions';
|
||||
|
||||
import SmileysPanel from './SmileysPanel';
|
||||
import EmojiPicker from './EmojiPicker';
|
||||
|
||||
|
||||
const styles = (_theme: Theme, { _chatWidth }: IProps) => {
|
||||
|
|
@ -23,18 +23,14 @@ const styles = (_theme: Theme, { _chatWidth }: IProps) => {
|
|||
boxSizing: 'border-box' as const,
|
||||
backgroundColor: 'rgba(0, 0, 0, .6) !important',
|
||||
height: 'auto',
|
||||
maxHeight: '435px',
|
||||
display: 'flex' as const,
|
||||
overflow: 'hidden',
|
||||
position: 'absolute' as const,
|
||||
width: `${_chatWidth - 32}px`,
|
||||
marginBottom: '5px',
|
||||
marginLeft: '-5px',
|
||||
transition: 'max-height 0.3s',
|
||||
|
||||
'& #smileysContainer': {
|
||||
backgroundColor: '#131519',
|
||||
borderTop: '1px solid #A4B8D1'
|
||||
}
|
||||
borderRadius: '10px'
|
||||
},
|
||||
chatDisabled: {
|
||||
borderTop: `1px solid ${_theme.palette.ui02}`,
|
||||
|
|
@ -183,8 +179,8 @@ class ChatInput extends Component<IProps, IState> {
|
|||
className = 'smiley-input'>
|
||||
<div
|
||||
className = { classes.smileysPanel } >
|
||||
<SmileysPanel
|
||||
onSmileySelect = { this._onSmileySelect } />
|
||||
<EmojiPicker
|
||||
onEmojiSelect = { this._onSmileySelect } />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
const useStyles = makeStyles()(() => {
|
||||
return {
|
||||
loading: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
height: '435px',
|
||||
justifyContent: 'center'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
interface IProps {
|
||||
onEmojiSelect: (emoji: string) => void;
|
||||
}
|
||||
|
||||
const EmojiPicker: React.FC<IProps> = ({ onEmojiSelect }) => {
|
||||
const { classes } = useStyles();
|
||||
const [ PickerComponent, setPickerComponent ] = useState<React.ComponentType<any> | null>(null);
|
||||
const [ data, setData ] = useState<object | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
import('@emoji-mart/react'),
|
||||
import('@emoji-mart/data')
|
||||
]).then(([ pickerMod, dataMod ]) => {
|
||||
setPickerComponent(() => pickerMod.default);
|
||||
setData(dataMod.default);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleSelect = useCallback((emoji: { native: string; }) => {
|
||||
onEmojiSelect(emoji.native);
|
||||
}, [ onEmojiSelect ]);
|
||||
|
||||
if (!PickerComponent || !data) {
|
||||
return (
|
||||
<div className = { classes.loading }>
|
||||
Loading...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PickerComponent
|
||||
data = { data }
|
||||
onEmojiSelect = { handleSelect }
|
||||
previewPosition = 'none'
|
||||
skinTonePosition = 'search'
|
||||
theme = 'dark' />
|
||||
);
|
||||
};
|
||||
|
||||
export default EmojiPicker;
|
||||
|
|
@ -3,6 +3,7 @@ import React, { useCallback } from 'react';
|
|||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
interface IProps {
|
||||
onExpand?: () => void;
|
||||
onSelect: (emoji: string) => void;
|
||||
}
|
||||
|
||||
|
|
@ -19,11 +20,25 @@ const useStyles = makeStyles()((theme: Theme) => {
|
|||
cursor: 'pointer',
|
||||
padding: '5px',
|
||||
fontSize: '1.5em'
|
||||
},
|
||||
|
||||
expandButton: {
|
||||
cursor: 'pointer',
|
||||
padding: '5px',
|
||||
fontSize: '1.2em',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
opacity: 0.7,
|
||||
|
||||
'&:hover': {
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const EmojiSelector: React.FC<IProps> = ({ onSelect }) => {
|
||||
const EmojiSelector: React.FC<IProps> = ({ onSelect, onExpand }) => {
|
||||
const { classes } = useStyles();
|
||||
|
||||
const emojiMap: Record<string, string> = {
|
||||
|
|
@ -31,7 +46,10 @@ const EmojiSelector: React.FC<IProps> = ({ onSelect }) => {
|
|||
redHeart: '❤️',
|
||||
faceWithTearsOfJoy: '😂',
|
||||
faceWithOpenMouth: '😮',
|
||||
fire: '🔥'
|
||||
fire: '🔥',
|
||||
clap: '👏',
|
||||
party: '🎉',
|
||||
thinking: '🤔'
|
||||
};
|
||||
const emojiNames = Object.keys(emojiMap);
|
||||
|
||||
|
|
@ -43,6 +61,14 @@ const EmojiSelector: React.FC<IProps> = ({ onSelect }) => {
|
|||
[ onSelect ]
|
||||
);
|
||||
|
||||
const handleExpand = useCallback(
|
||||
(event: React.MouseEvent<HTMLSpanElement>) => {
|
||||
event.preventDefault();
|
||||
onExpand?.();
|
||||
},
|
||||
[ onExpand ]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className = { classes.emojiGrid }>
|
||||
{emojiNames.map(name => (
|
||||
|
|
@ -53,6 +79,13 @@ const EmojiSelector: React.FC<IProps> = ({ onSelect }) => {
|
|||
{emojiMap[name]}
|
||||
</span>
|
||||
))}
|
||||
{onExpand && (
|
||||
<span
|
||||
className = { classes.expandButton }
|
||||
onClick = { handleExpand }>
|
||||
+
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import Button from '../../../base/ui/components/web/Button';
|
|||
import { BUTTON_TYPES } from '../../../base/ui/constants.any';
|
||||
import { sendReaction } from '../../actions.any';
|
||||
|
||||
import EmojiPicker from './EmojiPicker';
|
||||
import EmojiSelector from './EmojiSelector';
|
||||
|
||||
interface IProps {
|
||||
|
|
@ -40,18 +41,21 @@ const ReactButton = ({ messageId, receiverId }: IProps) => {
|
|||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onSendReaction = useCallback(emoji => {
|
||||
const onSendReaction = useCallback((emoji: string) => {
|
||||
dispatch(sendReaction(emoji, messageId, receiverId));
|
||||
}, [ dispatch, messageId, receiverId ]);
|
||||
|
||||
const [ isPopoverOpen, setIsPopoverOpen ] = useState(false);
|
||||
const [ showFullPicker, setShowFullPicker ] = useState(false);
|
||||
|
||||
const handleReactClick = useCallback(() => {
|
||||
setIsPopoverOpen(true);
|
||||
setShowFullPicker(false);
|
||||
}, []);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setIsPopoverOpen(false);
|
||||
setShowFullPicker(false);
|
||||
}, []);
|
||||
|
||||
const handleEmojiSelect = useCallback((emoji: string) => {
|
||||
|
|
@ -59,9 +63,18 @@ const ReactButton = ({ messageId, receiverId }: IProps) => {
|
|||
handleClose();
|
||||
}, [ onSendReaction, handleClose ]);
|
||||
|
||||
const handleExpand = useCallback(() => {
|
||||
setShowFullPicker(true);
|
||||
}, []);
|
||||
|
||||
const popoverContent = (
|
||||
<div className = { classes.popoverContent }>
|
||||
<EmojiSelector onSelect = { handleEmojiSelect } />
|
||||
{showFullPicker
|
||||
? <EmojiPicker onEmojiSelect = { handleEmojiSelect } />
|
||||
: <EmojiSelector
|
||||
onExpand = { handleExpand }
|
||||
onSelect = { handleEmojiSelect } />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class ReactionEmoji extends Component<IProps, IState> {
|
|||
<div
|
||||
className = { `reaction-emoji reaction-${index}` }
|
||||
id = { uid }>
|
||||
{ REACTIONS[reaction].emoji }
|
||||
{ REACTIONS[reaction]?.emoji ?? reaction }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,6 +143,11 @@ const _getReactionButtons = (dispatch: IStore['dispatch'], t: Function) => {
|
|||
sendAnalytics(createReactionMenuEvent(key));
|
||||
}
|
||||
|
||||
const { shortcutChar } = REACTIONS[key];
|
||||
const tooltip = shortcutChar
|
||||
? `${t(`toolbar.${key}`)} (${modifierKey} + ${shortcutChar})`
|
||||
: t(`toolbar.${key}`);
|
||||
|
||||
return (<ReactionButton
|
||||
accessibilityLabel = { t(`toolbar.accessibilityLabel.${key}`) }
|
||||
icon = { REACTIONS[key].emoji }
|
||||
|
|
@ -150,7 +155,7 @@ const _getReactionButtons = (dispatch: IStore['dispatch'], t: Function) => {
|
|||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick = { doSendReaction }
|
||||
toggled = { false }
|
||||
tooltip = { `${t(`toolbar.${key}`)} (${modifierKey} + ${REACTIONS[key].shortcutChar})` } />);
|
||||
tooltip = { tooltip } />);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -128,9 +128,9 @@ interface IReactions {
|
|||
[key: string]: {
|
||||
emoji: string;
|
||||
message: string;
|
||||
shortcutChar: string;
|
||||
soundFiles: string[];
|
||||
soundId: string;
|
||||
shortcutChar?: string;
|
||||
soundFiles?: string[];
|
||||
soundId?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -183,6 +183,38 @@ export const REACTIONS: IReactions = {
|
|||
shortcutChar: 'H',
|
||||
soundId: HEART_SOUND_ID,
|
||||
soundFiles: HEART_SOUND_FILES
|
||||
},
|
||||
party: {
|
||||
message: ':party:',
|
||||
emoji: '🎉'
|
||||
},
|
||||
thinking: {
|
||||
message: ':thinking:',
|
||||
emoji: '🤔'
|
||||
},
|
||||
wave: {
|
||||
message: ':wave:',
|
||||
emoji: '👋'
|
||||
},
|
||||
cry: {
|
||||
message: ':cry:',
|
||||
emoji: '😢'
|
||||
},
|
||||
eyes: {
|
||||
message: ':eyes:',
|
||||
emoji: '👀'
|
||||
},
|
||||
mindblown: {
|
||||
message: ':mindblown:',
|
||||
emoji: '🤯'
|
||||
},
|
||||
pray: {
|
||||
message: ':pray:',
|
||||
emoji: '🙏'
|
||||
},
|
||||
rocket: {
|
||||
message: ':rocket:',
|
||||
emoji: '🚀'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,10 @@ export function getReactionsQueue(state: IReduxState): Array<IReactionEmojiProps
|
|||
* @returns {string}
|
||||
*/
|
||||
export function getReactionMessageFromBuffer(buffer: Array<string>): string {
|
||||
return buffer.map<string>(reaction => REACTIONS[reaction].message).reduce((acc, val) => `${acc}${val}`);
|
||||
return buffer
|
||||
.filter(reaction => REACTIONS[reaction]?.message)
|
||||
.map<string>(reaction => REACTIONS[reaction].message)
|
||||
.reduce((acc, val) => `${acc}${val}`, '');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -139,12 +142,14 @@ function getSoundThresholdByFrequency(frequency: number): number {
|
|||
export function getReactionsSoundsThresholds(reactions: Array<string>): Array<ReactionThreshold> {
|
||||
const unique = getUniqueReactions(reactions);
|
||||
|
||||
return unique.map<ReactionThreshold>(reaction => {
|
||||
return {
|
||||
reaction,
|
||||
threshold: getSoundThresholdByFrequency(getReactionFrequency(reactions, reaction))
|
||||
};
|
||||
});
|
||||
return unique
|
||||
.filter(reaction => REACTIONS[reaction]?.soundId)
|
||||
.map<ReactionThreshold>(reaction => {
|
||||
return {
|
||||
reaction,
|
||||
threshold: getSoundThresholdByFrequency(getReactionFrequency(reactions, reaction))
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -67,15 +67,17 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
|
|||
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]
|
||||
)
|
||||
);
|
||||
const { soundId, soundFiles } = REACTIONS[key];
|
||||
|
||||
if (soundId && soundFiles) {
|
||||
for (let i = 0; i < SOUNDS_THRESHOLDS.length; i++) {
|
||||
dispatch(registerSound(
|
||||
`${soundId}${SOUNDS_THRESHOLDS[i]}`,
|
||||
soundFiles[i]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
dispatch(registerSound(RAISE_HAND_SOUND_ID, RAISE_HAND_SOUND_FILE));
|
||||
});
|
||||
break;
|
||||
|
|
@ -83,8 +85,12 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
|
|||
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]}`));
|
||||
const { soundId } = REACTIONS[key];
|
||||
|
||||
if (soundId) {
|
||||
for (let i = 0; i < SOUNDS_THRESHOLDS.length; i++) {
|
||||
dispatch(unregisterSound(`${soundId}${SOUNDS_THRESHOLDS[i]}`));
|
||||
}
|
||||
}
|
||||
});
|
||||
dispatch(unregisterSound(RAISE_HAND_SOUND_ID));
|
||||
|
|
@ -146,9 +152,13 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
|
|||
if (soundsReactions) {
|
||||
const reactionSoundsThresholds = getReactionsSoundsThresholds(reactions);
|
||||
|
||||
reactionSoundsThresholds.forEach(reaction =>
|
||||
dispatch(playSound(`${REACTIONS[reaction.reaction].soundId}${reaction.threshold}`))
|
||||
);
|
||||
reactionSoundsThresholds.forEach(reaction => {
|
||||
const { soundId } = REACTIONS[reaction.reaction] ?? {};
|
||||
|
||||
if (soundId) {
|
||||
dispatch(playSound(`${soundId}${reaction.threshold}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
dispatch(setReactionQueue([ ...queue, ...getReactionsWithId(reactions) ]));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -590,21 +590,23 @@ export const useKeyboardShortcuts = (toolbarButtons: Array<string>) => {
|
|||
|
||||
// If the buttons for sending reactions are not displayed we should disable the shortcuts too.
|
||||
if (_shouldDisplayReactionsButtons) {
|
||||
const REACTION_SHORTCUTS = Object.keys(REACTIONS).map(key => {
|
||||
const onShortcutSendReaction = () => {
|
||||
dispatch(addReactionToBuffer(key));
|
||||
sendAnalytics(createShortcutEvent(
|
||||
`reaction.${key}`
|
||||
));
|
||||
};
|
||||
const REACTION_SHORTCUTS = Object.keys(REACTIONS)
|
||||
.filter(key => REACTIONS[key].shortcutChar)
|
||||
.map(key => {
|
||||
const onShortcutSendReaction = () => {
|
||||
dispatch(addReactionToBuffer(key));
|
||||
sendAnalytics(createShortcutEvent(
|
||||
`reaction.${key}`
|
||||
));
|
||||
};
|
||||
|
||||
return {
|
||||
character: REACTIONS[key].shortcutChar,
|
||||
exec: onShortcutSendReaction,
|
||||
helpDescription: `toolbar.reaction${key.charAt(0).toUpperCase()}${key.slice(1)}`,
|
||||
altKey: true
|
||||
};
|
||||
});
|
||||
return {
|
||||
character: REACTIONS[key].shortcutChar!,
|
||||
exec: onShortcutSendReaction,
|
||||
helpDescription: `toolbar.reaction${key.charAt(0).toUpperCase()}${key.slice(1)}`,
|
||||
altKey: true
|
||||
};
|
||||
});
|
||||
|
||||
REACTION_SHORTCUTS.forEach(shortcut => {
|
||||
dispatch(registerShortcut({
|
||||
|
|
@ -636,7 +638,9 @@ export const useKeyboardShortcuts = (toolbarButtons: Array<string>) => {
|
|||
dispatch(unregisterShortcut(letter)));
|
||||
|
||||
if (_shouldDisplayReactionsButtons) {
|
||||
Object.keys(REACTIONS).map(key => REACTIONS[key].shortcutChar)
|
||||
Object.keys(REACTIONS)
|
||||
.filter(key => REACTIONS[key].shortcutChar)
|
||||
.map(key => REACTIONS[key].shortcutChar!)
|
||||
.forEach(letter =>
|
||||
dispatch(unregisterShortcut(letter, true)));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue