snapshot of https://github.com/jitsi/jitsi-meet.git @ fd1749dd28f4138aa80234fc7f311b19aaffa0e7 (2026-09-15)
This commit is contained in:
692
react/features/toolbox/hooks.web.ts
Normal file
692
react/features/toolbox/hooks.web.ts
Normal file
@@ -0,0 +1,692 @@
|
||||
import { useEffect } from 'react';
|
||||
import { batch, useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { ACTION_SHORTCUT_TRIGGERED, createShortcutEvent } from '../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../analytics/functions';
|
||||
import { IReduxState } from '../app/types';
|
||||
import { useAudioTranslationButton } from '../audio-translation/hooks.web';
|
||||
import { toggleDialog } from '../base/dialog/actions';
|
||||
import { isIosMobileBrowser, isIpadMobileBrowser } from '../base/environment/utils';
|
||||
import { HELP_BUTTON_ENABLED } from '../base/flags/constants';
|
||||
import { getFeatureFlag } from '../base/flags/functions';
|
||||
import JitsiMeetJS from '../base/lib-jitsi-meet';
|
||||
import { raiseHand } from '../base/participants/actions';
|
||||
import { getLocalParticipant, hasRaisedHand } from '../base/participants/functions';
|
||||
import { isToggleCameraEnabled } from '../base/tracks/functions.web';
|
||||
import { isInBreakoutRoom } from '../breakout-rooms/functions';
|
||||
import { toggleChat } from '../chat/actions.web';
|
||||
import { isChatDisabled } from '../chat/functions';
|
||||
import { useChatButton } from '../chat/hooks.web';
|
||||
import { useCustomPanelButton } from '../custom-panel/hooks.web';
|
||||
import { useEmbedButton } from '../embed-meeting/hooks';
|
||||
import { useEtherpadButton } from '../etherpad/hooks';
|
||||
import { useFeedbackButton } from '../feedback/hooks.web';
|
||||
import { useFileSharingButton } from '../file-sharing/hooks.web';
|
||||
import { setGifMenuVisibility } from '../gifs/actions';
|
||||
import { isGifEnabled } from '../gifs/function.any';
|
||||
import InviteButton from '../invite/components/add-people-dialog/web/InviteButton';
|
||||
import { registerShortcut, unregisterShortcut } from '../keyboard-shortcuts/actions';
|
||||
import { areCtrlAltReactionShortcutsEnabled } from '../keyboard-shortcuts/functions';
|
||||
import { useKeyboardShortcutsButton } from '../keyboard-shortcuts/hooks';
|
||||
import NoiseSuppressionButton from '../noise-suppression/components/NoiseSuppressionButton';
|
||||
import {
|
||||
close as closeParticipantsPane,
|
||||
open as openParticipantsPane
|
||||
} from '../participants-pane/actions.web';
|
||||
import {
|
||||
getParticipantsPaneOpen,
|
||||
isParticipantsPaneEnabled
|
||||
} from '../participants-pane/functions';
|
||||
import { useParticipantPaneButton } from '../participants-pane/hooks.web';
|
||||
import { usePipToggleButton } from '../pip/hooks';
|
||||
import { usePollsButton } from '../polls/hooks.web';
|
||||
import { addReactionToBuffer } from '../reactions/actions.any';
|
||||
import { toggleReactionsMenuVisibility } from '../reactions/actions.web';
|
||||
import RaiseHandContainerButton from '../reactions/components/web/RaiseHandContainerButtons';
|
||||
import { REACTIONS } from '../reactions/constants';
|
||||
import { shouldDisplayReactionsButtons } from '../reactions/functions.any';
|
||||
import { useReactionsButton } from '../reactions/hooks.web';
|
||||
import { useLiveStreamingButton, useRecordingButton } from '../recording/hooks.web';
|
||||
import { isSalesforceEnabled } from '../salesforce/functions';
|
||||
import { startScreenShareFlow } from '../screen-share/actions.web';
|
||||
import ShareAudioButton from '../screen-share/components/web/ShareAudioButton';
|
||||
import { isScreenAudioSupported, isScreenVideoShared } from '../screen-share/functions';
|
||||
import { useSecurityDialogButton } from '../security/hooks.web';
|
||||
import SettingsButton from '../settings/components/web/SettingsButton';
|
||||
import { useSharedVideoButton } from '../shared-video/hooks';
|
||||
import SpeakerStats from '../speaker-stats/components/web/SpeakerStats';
|
||||
import { isSpeakerStatsDisabled } from '../speaker-stats/functions';
|
||||
import { useSpeakerStatsButton } from '../speaker-stats/hooks.web';
|
||||
import { useClosedCaptionButton } from '../subtitles/hooks.web';
|
||||
import { toggleTileView } from '../video-layout/actions.any';
|
||||
import { shouldDisplayTileView } from '../video-layout/functions.web';
|
||||
import { useTileViewButton } from '../video-layout/hooks';
|
||||
import VideoQualityButton from '../video-quality/components/VideoQualityButton.web';
|
||||
import VideoQualityDialog from '../video-quality/components/VideoQualityDialog.web';
|
||||
import { useVirtualBackgroundButton } from '../virtual-background/hooks';
|
||||
import { useWhiteboardButton } from '../whiteboard/hooks';
|
||||
|
||||
import { setFullScreen } from './actions.web';
|
||||
import DownloadButton from './components/DownloadButton';
|
||||
import HelpButton from './components/HelpButton';
|
||||
import AudioSettingsButton from './components/web/AudioSettingsButton';
|
||||
import CustomOptionButton from './components/web/CustomOptionButton';
|
||||
import FullscreenButton from './components/web/FullscreenButton';
|
||||
import LinkToSalesforceButton from './components/web/LinkToSalesforceButton';
|
||||
import ProfileButton from './components/web/ProfileButton';
|
||||
import ShareDesktopButton from './components/web/ShareDesktopButton';
|
||||
import ToggleCameraButton from './components/web/ToggleCameraButton';
|
||||
import VideoSettingsButton from './components/web/VideoSettingsButton';
|
||||
import { isButtonEnabled, isDesktopShareButtonDisabled } from './functions.web';
|
||||
import { ICustomToolbarButton, IToolboxButton, ToolbarButton } from './types';
|
||||
|
||||
|
||||
const microphone = {
|
||||
key: 'microphone',
|
||||
Content: AudioSettingsButton,
|
||||
group: 0
|
||||
};
|
||||
|
||||
const camera = {
|
||||
key: 'camera',
|
||||
Content: VideoSettingsButton,
|
||||
group: 0
|
||||
};
|
||||
|
||||
const profile = {
|
||||
key: 'profile',
|
||||
Content: ProfileButton,
|
||||
group: 1
|
||||
};
|
||||
|
||||
const desktop = {
|
||||
key: 'desktop',
|
||||
Content: ShareDesktopButton,
|
||||
group: 2
|
||||
};
|
||||
|
||||
// In Narrow layout and mobile web we are using drawer for popups and that is why it is better to include
|
||||
// all forms of reactions in the overflow menu. Otherwise the toolbox will be hidden and the reactions popup
|
||||
// misaligned.
|
||||
const raisehand = {
|
||||
key: 'raisehand',
|
||||
Content: RaiseHandContainerButton,
|
||||
group: 2
|
||||
};
|
||||
|
||||
const invite = {
|
||||
key: 'invite',
|
||||
Content: InviteButton,
|
||||
group: 2
|
||||
};
|
||||
|
||||
const toggleCamera = {
|
||||
key: 'toggle-camera',
|
||||
Content: ToggleCameraButton,
|
||||
group: 2
|
||||
};
|
||||
|
||||
const videoQuality = {
|
||||
key: 'videoquality',
|
||||
Content: VideoQualityButton,
|
||||
group: 2
|
||||
};
|
||||
|
||||
const fullscreen = {
|
||||
key: 'fullscreen',
|
||||
Content: FullscreenButton,
|
||||
group: 2
|
||||
};
|
||||
|
||||
const linkToSalesforce = {
|
||||
key: 'linktosalesforce',
|
||||
Content: LinkToSalesforceButton,
|
||||
group: 2
|
||||
};
|
||||
|
||||
const shareAudio = {
|
||||
key: 'shareaudio',
|
||||
Content: ShareAudioButton,
|
||||
group: 3
|
||||
};
|
||||
|
||||
const noiseSuppression = {
|
||||
key: 'noisesuppression',
|
||||
Content: NoiseSuppressionButton,
|
||||
group: 3
|
||||
};
|
||||
|
||||
const settings = {
|
||||
key: 'settings',
|
||||
Content: SettingsButton,
|
||||
group: 4
|
||||
};
|
||||
|
||||
const download = {
|
||||
key: 'download',
|
||||
Content: DownloadButton,
|
||||
group: 4
|
||||
};
|
||||
|
||||
const help = {
|
||||
key: 'help',
|
||||
Content: HelpButton,
|
||||
group: 4
|
||||
};
|
||||
|
||||
/**
|
||||
* A hook that returns the toggle camera button if it is enabled and undefined otherwise.
|
||||
*
|
||||
* @returns {Object | undefined}
|
||||
*/
|
||||
function useToggleCameraButton() {
|
||||
const toggleCameraEnabled = useSelector(isToggleCameraEnabled);
|
||||
|
||||
if (toggleCameraEnabled) {
|
||||
return toggleCamera;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook that returns the desktop sharing button if it is enabled and undefined otherwise.
|
||||
*
|
||||
* @returns {Object | undefined}
|
||||
*/
|
||||
function getDesktopSharingButton() {
|
||||
if (JitsiMeetJS.isDesktopSharingEnabled()) {
|
||||
return desktop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook that returns the fullscreen button if it is enabled and undefined otherwise.
|
||||
*
|
||||
* @returns {Object | undefined}
|
||||
*/
|
||||
function getFullscreenButton() {
|
||||
if (!isIosMobileBrowser() || isIpadMobileBrowser()) {
|
||||
return fullscreen;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook that returns the "link to salesforce" button if it is enabled and undefined otherwise.
|
||||
*
|
||||
* @returns {Object | undefined}
|
||||
*/
|
||||
function useLinkToSalesforceButton() {
|
||||
const _isSalesforceEnabled = useSelector(isSalesforceEnabled);
|
||||
|
||||
if (_isSalesforceEnabled) {
|
||||
return linkToSalesforce;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A hook that returns the share audio button if it is enabled and undefined otherwise.
|
||||
*
|
||||
* @returns {Object | undefined}
|
||||
*/
|
||||
function getShareAudioButton() {
|
||||
if (JitsiMeetJS.isDesktopSharingEnabled() && isScreenAudioSupported()) {
|
||||
return shareAudio;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook that returns the download button if it is enabled and undefined otherwise.
|
||||
*
|
||||
* @returns {Object | undefined}
|
||||
*/
|
||||
function useDownloadButton() {
|
||||
const visible = useSelector(
|
||||
(state: IReduxState) => typeof state['features/base/config'].deploymentUrls?.downloadAppsUrl === 'string');
|
||||
|
||||
if (visible) {
|
||||
return download;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook that returns the help button if it is enabled and undefined otherwise.
|
||||
*
|
||||
* @returns {Object | undefined}
|
||||
*/
|
||||
function useHelpButton() {
|
||||
const visible = useSelector(
|
||||
(state: IReduxState) =>
|
||||
typeof state['features/base/config'].deploymentUrls?.userDocumentationURL === 'string'
|
||||
&& getFeatureFlag(state, HELP_BUTTON_ENABLED, true));
|
||||
|
||||
if (visible) {
|
||||
return help;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide invite button for breakout-rooms.
|
||||
*
|
||||
* @returns {Object | undefined}
|
||||
*/
|
||||
function useInviteButton() {
|
||||
const visible = useSelector((state: IReduxState) => !isInBreakoutRoom(state));
|
||||
|
||||
if (visible) {
|
||||
return invite;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all buttons that could be rendered.
|
||||
*
|
||||
* @param {Object} _customToolbarButtons - An array containing custom buttons objects.
|
||||
* @returns {Object} The button maps mainMenuButtons and overflowMenuButtons.
|
||||
*/
|
||||
export function useToolboxButtons(
|
||||
_customToolbarButtons?: ICustomToolbarButton[]): { [key: string]: IToolboxButton; } {
|
||||
const desktopSharing = getDesktopSharingButton();
|
||||
const toggleCameraButton = useToggleCameraButton();
|
||||
const _fullscreen = getFullscreenButton();
|
||||
const security = useSecurityDialogButton();
|
||||
const reactions = useReactionsButton();
|
||||
const participants = useParticipantPaneButton();
|
||||
const tileview = useTileViewButton();
|
||||
const chat = useChatButton();
|
||||
const cc = useClosedCaptionButton();
|
||||
const audioTranslation = useAudioTranslationButton();
|
||||
const polls = usePollsButton();
|
||||
const filesharing = useFileSharingButton();
|
||||
const recording = useRecordingButton();
|
||||
const liveStreaming = useLiveStreamingButton();
|
||||
const linktosalesforce = useLinkToSalesforceButton();
|
||||
const shareaudio = getShareAudioButton();
|
||||
const shareVideo = useSharedVideoButton();
|
||||
const whiteboard = useWhiteboardButton();
|
||||
const etherpad = useEtherpadButton();
|
||||
const virtualBackground = useVirtualBackgroundButton();
|
||||
const speakerStats = useSpeakerStatsButton();
|
||||
const shortcuts = useKeyboardShortcutsButton();
|
||||
const embed = useEmbedButton();
|
||||
const feedback = useFeedbackButton();
|
||||
const _download = useDownloadButton();
|
||||
const _help = useHelpButton();
|
||||
const _invite = useInviteButton();
|
||||
const customPanel = useCustomPanelButton();
|
||||
const togglePiPButton = usePipToggleButton();
|
||||
|
||||
const buttons: { [key in ToolbarButton]?: IToolboxButton; } = {
|
||||
microphone,
|
||||
camera,
|
||||
profile,
|
||||
desktop: desktopSharing,
|
||||
chat,
|
||||
raisehand,
|
||||
reactions,
|
||||
'participants-pane': participants,
|
||||
invite: _invite,
|
||||
tileview,
|
||||
'toggle-camera': toggleCameraButton,
|
||||
'toggle-pip': togglePiPButton,
|
||||
videoquality: videoQuality,
|
||||
fullscreen: _fullscreen,
|
||||
security,
|
||||
closedcaptions: cc,
|
||||
audiotranslation: audioTranslation,
|
||||
polls,
|
||||
filesharing,
|
||||
recording,
|
||||
livestreaming: liveStreaming,
|
||||
linktosalesforce,
|
||||
sharedvideo: shareVideo,
|
||||
shareaudio,
|
||||
noisesuppression: noiseSuppression,
|
||||
whiteboard,
|
||||
etherpad,
|
||||
'select-background': virtualBackground,
|
||||
stats: speakerStats,
|
||||
settings,
|
||||
shortcuts,
|
||||
embedmeeting: embed,
|
||||
feedback,
|
||||
download: _download,
|
||||
help: _help,
|
||||
'custom-panel': customPanel
|
||||
};
|
||||
const buttonKeys = Object.keys(buttons) as ToolbarButton[];
|
||||
|
||||
buttonKeys.forEach(
|
||||
key => typeof buttons[key] === 'undefined' && delete buttons[key]);
|
||||
|
||||
const customButtons = _customToolbarButtons?.reduce((prev, { backgroundColor, icon, id, text }) => {
|
||||
prev[id] = {
|
||||
backgroundColor,
|
||||
key: id,
|
||||
id,
|
||||
Content: CustomOptionButton,
|
||||
group: 4,
|
||||
icon,
|
||||
text
|
||||
};
|
||||
|
||||
return prev;
|
||||
}, {} as { [key: string]: ICustomToolbarButton; });
|
||||
|
||||
return {
|
||||
...buttons,
|
||||
...customButtons
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export const useKeyboardShortcuts = (toolbarButtons: Array<string>) => {
|
||||
const dispatch = useDispatch();
|
||||
const _ctrlAltReactionShortcutsEnabled = useSelector(areCtrlAltReactionShortcutsEnabled);
|
||||
const _isSpeakerStatsDisabled = useSelector(isSpeakerStatsDisabled);
|
||||
const _isParticipantsPaneEnabled = useSelector(isParticipantsPaneEnabled);
|
||||
const _shouldDisplayReactionsButtons = useSelector(shouldDisplayReactionsButtons);
|
||||
const _toolbarButtons = useSelector(
|
||||
(state: IReduxState) => toolbarButtons || state['features/toolbox'].toolbarButtons);
|
||||
const chatOpen = useSelector((state: IReduxState) => state['features/chat'].isOpen);
|
||||
const _isChatDisabled = useSelector(isChatDisabled);
|
||||
const desktopSharingButtonDisabled = useSelector(isDesktopShareButtonDisabled);
|
||||
const desktopSharingEnabled = JitsiMeetJS.isDesktopSharingEnabled();
|
||||
const fullScreen = useSelector((state: IReduxState) => state['features/toolbox'].fullScreen);
|
||||
const gifsEnabled = useSelector(isGifEnabled);
|
||||
const participantsPaneOpen = useSelector(getParticipantsPaneOpen);
|
||||
const raisedHand = useSelector((state: IReduxState) => hasRaisedHand(getLocalParticipant(state)));
|
||||
const screenSharing = useSelector(isScreenVideoShared);
|
||||
const tileViewEnabled = useSelector(shouldDisplayTileView);
|
||||
|
||||
/**
|
||||
* Creates an analytics keyboard shortcut event and dispatches an action for
|
||||
* toggling the display of chat.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function onToggleChat() {
|
||||
// Don't toggle chat if it's disabled.
|
||||
if (_isChatDisabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.chat',
|
||||
ACTION_SHORTCUT_TRIGGERED,
|
||||
{
|
||||
enable: !chatOpen
|
||||
}));
|
||||
|
||||
// Checks if there was any text selected by the user.
|
||||
// Used for when we press simultaneously keys for copying
|
||||
// text messages from the chat board
|
||||
if (window.getSelection()?.toString() !== '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
dispatch(toggleChat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an analytics keyboard shortcut event and dispatches an action for
|
||||
* toggling the display of the participants pane.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function onToggleParticipantsPane() {
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.participants-pane',
|
||||
ACTION_SHORTCUT_TRIGGERED,
|
||||
{
|
||||
enable: !participantsPaneOpen
|
||||
}));
|
||||
|
||||
if (participantsPaneOpen) {
|
||||
dispatch(closeParticipantsPane());
|
||||
} else {
|
||||
dispatch(openParticipantsPane());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an analytics keyboard shortcut event and dispatches an action for
|
||||
* toggling the display of Video Quality.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function onToggleVideoQuality() {
|
||||
sendAnalytics(createShortcutEvent('video.quality'));
|
||||
|
||||
dispatch(toggleDialog('VideoQualityDialog', VideoQualityDialog));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an action for toggling the tile view.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function onToggleTileView() {
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.tileview',
|
||||
ACTION_SHORTCUT_TRIGGERED,
|
||||
{
|
||||
enable: !tileViewEnabled
|
||||
}));
|
||||
|
||||
dispatch(toggleTileView());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an analytics keyboard shortcut event and dispatches an action for
|
||||
* toggling full screen mode.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function onToggleFullScreen() {
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.fullscreen',
|
||||
ACTION_SHORTCUT_TRIGGERED,
|
||||
{
|
||||
enable: !fullScreen
|
||||
}));
|
||||
dispatch(setFullScreen(!fullScreen));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an analytics keyboard shortcut event and dispatches an action for
|
||||
* toggling raise hand.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function onToggleRaiseHand() {
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.raise.hand',
|
||||
ACTION_SHORTCUT_TRIGGERED,
|
||||
{ enable: !raisedHand }));
|
||||
|
||||
dispatch(raiseHand(!raisedHand));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an analytics keyboard shortcut event and dispatches an action for
|
||||
* toggling screensharing.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function onToggleScreenshare() {
|
||||
// Ignore the shortcut if the button is disabled.
|
||||
if (desktopSharingButtonDisabled) {
|
||||
return;
|
||||
}
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'toggle.screen.sharing',
|
||||
ACTION_SHORTCUT_TRIGGERED,
|
||||
{
|
||||
enable: !screenSharing
|
||||
}));
|
||||
|
||||
if (desktopSharingEnabled && !desktopSharingButtonDisabled) {
|
||||
dispatch(startScreenShareFlow(!screenSharing));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an analytics keyboard shortcut event and dispatches an action for
|
||||
* toggling speaker stats.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function onSpeakerStats() {
|
||||
sendAnalytics(createShortcutEvent(
|
||||
'speaker.stats'
|
||||
));
|
||||
|
||||
dispatch(toggleDialog('SpeakerStats', SpeakerStats, {
|
||||
conference: APP.conference
|
||||
}));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const KEYBOARD_SHORTCUTS = [
|
||||
isButtonEnabled('videoquality', _toolbarButtons) && {
|
||||
character: 'A',
|
||||
exec: onToggleVideoQuality,
|
||||
helpDescription: 'toolbar.callQuality'
|
||||
},
|
||||
!_isChatDisabled && isButtonEnabled('chat', _toolbarButtons) && {
|
||||
character: 'C',
|
||||
exec: onToggleChat,
|
||||
helpDescription: 'keyboardShortcuts.toggleChat'
|
||||
},
|
||||
isButtonEnabled('desktop', _toolbarButtons) && {
|
||||
character: 'D',
|
||||
exec: onToggleScreenshare,
|
||||
helpDescription: 'keyboardShortcuts.toggleScreensharing'
|
||||
},
|
||||
_isParticipantsPaneEnabled && isButtonEnabled('participants-pane', _toolbarButtons) && {
|
||||
character: 'P',
|
||||
exec: onToggleParticipantsPane,
|
||||
helpDescription: 'keyboardShortcuts.toggleParticipantsPane'
|
||||
},
|
||||
isButtonEnabled('raisehand', _toolbarButtons) && {
|
||||
character: 'R',
|
||||
exec: onToggleRaiseHand,
|
||||
helpDescription: 'keyboardShortcuts.raiseHand'
|
||||
},
|
||||
isButtonEnabled('fullscreen', _toolbarButtons) && {
|
||||
character: 'S',
|
||||
exec: onToggleFullScreen,
|
||||
helpDescription: 'keyboardShortcuts.fullScreen'
|
||||
},
|
||||
isButtonEnabled('tileview', _toolbarButtons) && {
|
||||
character: 'W',
|
||||
exec: onToggleTileView,
|
||||
helpDescription: 'toolbar.tileViewToggle'
|
||||
},
|
||||
!_isSpeakerStatsDisabled && isButtonEnabled('stats', _toolbarButtons) && {
|
||||
character: 'T',
|
||||
exec: onSpeakerStats,
|
||||
helpDescription: 'keyboardShortcuts.showSpeakerStats'
|
||||
}
|
||||
];
|
||||
|
||||
KEYBOARD_SHORTCUTS.forEach(shortcut => {
|
||||
if (typeof shortcut === 'object') {
|
||||
dispatch(registerShortcut({
|
||||
character: shortcut.character,
|
||||
handler: shortcut.exec,
|
||||
helpDescription: shortcut.helpDescription
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
// 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}`
|
||||
));
|
||||
};
|
||||
|
||||
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({
|
||||
alt: shortcut.altKey,
|
||||
character: shortcut.character,
|
||||
handler: shortcut.exec,
|
||||
helpDescription: shortcut.helpDescription
|
||||
}));
|
||||
|
||||
if (_ctrlAltReactionShortcutsEnabled) {
|
||||
dispatch(registerShortcut({
|
||||
alt: true,
|
||||
character: shortcut.character,
|
||||
ctrl: true,
|
||||
handler: shortcut.exec,
|
||||
helpDescription: shortcut.helpDescription
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
if (gifsEnabled) {
|
||||
const onGifShortcut = () => {
|
||||
batch(() => {
|
||||
dispatch(toggleReactionsMenuVisibility());
|
||||
dispatch(setGifMenuVisibility(true));
|
||||
});
|
||||
};
|
||||
|
||||
dispatch(registerShortcut({
|
||||
character: 'G',
|
||||
handler: onGifShortcut,
|
||||
helpDescription: 'keyboardShortcuts.giphyMenu'
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
[ 'A', 'C', 'D', 'P', 'R', 'S', 'W', 'T', 'G' ].forEach(letter =>
|
||||
dispatch(unregisterShortcut(letter)));
|
||||
|
||||
if (_shouldDisplayReactionsButtons) {
|
||||
Object.keys(REACTIONS).map(key => REACTIONS[key].shortcutChar)
|
||||
.forEach(letter => {
|
||||
dispatch(unregisterShortcut(letter, true));
|
||||
|
||||
if (_ctrlAltReactionShortcutsEnabled) {
|
||||
dispatch(unregisterShortcut(letter, true, true));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [
|
||||
_ctrlAltReactionShortcutsEnabled,
|
||||
_shouldDisplayReactionsButtons,
|
||||
chatOpen,
|
||||
desktopSharingButtonDisabled,
|
||||
desktopSharingEnabled,
|
||||
fullScreen,
|
||||
gifsEnabled,
|
||||
participantsPaneOpen,
|
||||
raisedHand,
|
||||
screenSharing,
|
||||
tileViewEnabled
|
||||
]);
|
||||
};
|
||||
Reference in New Issue
Block a user