feat(shortcuts) Update dialog (#12993)
Create Shortcuts tab in Settings Dialog Move keyboard shortcut option from More to this tab Move shortcuts info from KeyboardShortcuts dialog to this tab Remove KeyboardShortcuts dialog
This commit is contained in:
parent
036286a1d6
commit
7b8b911fee
|
@ -1006,6 +1006,7 @@
|
||||||
"selectCamera": "Camera",
|
"selectCamera": "Camera",
|
||||||
"selectMic": "Microphone",
|
"selectMic": "Microphone",
|
||||||
"selfView": "Self view",
|
"selfView": "Self view",
|
||||||
|
"shortcuts": "Shortcuts",
|
||||||
"speakers": "Speakers",
|
"speakers": "Speakers",
|
||||||
"startAudioMuted": "Everyone starts muted",
|
"startAudioMuted": "Everyone starts muted",
|
||||||
"startReactionsMuted": "Mute reaction sounds for everyone",
|
"startReactionsMuted": "Mute reaction sounds for everyone",
|
||||||
|
|
|
@ -8,10 +8,9 @@ import {
|
||||||
createShortcutEvent,
|
createShortcutEvent,
|
||||||
sendAnalytics
|
sendAnalytics
|
||||||
} from '../../react/features/analytics';
|
} from '../../react/features/analytics';
|
||||||
import { toggleDialog } from '../../react/features/base/dialog';
|
|
||||||
import { clickOnVideo } from '../../react/features/filmstrip/actions';
|
import { clickOnVideo } from '../../react/features/filmstrip/actions';
|
||||||
import { KeyboardShortcutsDialog }
|
import { openSettingsDialog } from '../../react/features/settings/actions';
|
||||||
from '../../react/features/keyboard-shortcuts';
|
import { SETTINGS_TABS } from '../../react/features/settings/constants';
|
||||||
|
|
||||||
const logger = Logger.getLogger(__filename);
|
const logger = Logger.getLogger(__filename);
|
||||||
|
|
||||||
|
@ -120,15 +119,17 @@ const KeyboardShortcut = {
|
||||||
return jitsiLocalStorage.getItem(_enableShortcutsKey) === 'false' ? false : true;
|
return jitsiLocalStorage.getItem(_enableShortcutsKey) === 'false' ? false : true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getShortcutsDescriptions() {
|
||||||
|
return _shortcutsHelp;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the {@KeyboardShortcutsDialog} dialog.
|
* Opens the {@SettingsDialog} dialog on the Shortcuts page.
|
||||||
*
|
*
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
openDialog() {
|
openDialog() {
|
||||||
APP.store.dispatch(toggleDialog(KeyboardShortcutsDialog, {
|
APP.store.dispatch(openSettingsDialog(SETTINGS_TABS.SHORTCUTS, false));
|
||||||
shortcutDescriptions: _shortcutsHelp
|
|
||||||
}));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,7 +5,6 @@ import '../base/media/middleware';
|
||||||
import '../dynamic-branding/middleware';
|
import '../dynamic-branding/middleware';
|
||||||
import '../e2ee/middleware';
|
import '../e2ee/middleware';
|
||||||
import '../external-api/middleware';
|
import '../external-api/middleware';
|
||||||
import '../keyboard-shortcuts/middleware';
|
|
||||||
import '../no-audio-signal/middleware';
|
import '../no-audio-signal/middleware';
|
||||||
import '../notifications/middleware';
|
import '../notifications/middleware';
|
||||||
import '../noise-detection/middleware';
|
import '../noise-detection/middleware';
|
||||||
|
|
|
@ -131,6 +131,7 @@ const useStyles = makeStyles()(theme => {
|
||||||
|
|
||||||
footer: {
|
footer: {
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
|
paddingTop: theme.spacing(4),
|
||||||
|
|
||||||
'& button:last-child': {
|
'& button:last-child': {
|
||||||
marginLeft: '16px'
|
marginLeft: '16px'
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
/**
|
|
||||||
* The type of the action which signals the keyboard shortcuts dialog should
|
|
||||||
* be displayed.
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* type: OPEN_KEYBOARD_SHORTCUTS_DIALOG
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
export const OPEN_KEYBOARD_SHORTCUTS_DIALOG
|
|
||||||
= 'OPEN_KEYBOARD_SHORTCUTS_DIALOG';
|
|
|
@ -1,14 +0,0 @@
|
||||||
import { OPEN_KEYBOARD_SHORTCUTS_DIALOG } from './actionTypes';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the dialog showing available keyboard shortcuts.
|
|
||||||
*
|
|
||||||
* @returns {{
|
|
||||||
* type: OPEN_KEYBOARD_SHORTCUTS_DIALOG
|
|
||||||
* }}
|
|
||||||
*/
|
|
||||||
export function openKeyboardShortcutsDialog() {
|
|
||||||
return {
|
|
||||||
type: OPEN_KEYBOARD_SHORTCUTS_DIALOG
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -5,7 +5,8 @@ import { translate } from '../../../base/i18n';
|
||||||
import { IconShortcuts } from '../../../base/icons';
|
import { IconShortcuts } from '../../../base/icons';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
|
||||||
import { openKeyboardShortcutsDialog } from '../../actions';
|
import { openSettingsDialog } from '../../../settings/actions';
|
||||||
|
import { SETTINGS_TABS } from '../../../settings/constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the React {@code Component} props of {@link KeyboardShortcutsButton}.
|
* The type of the React {@code Component} props of {@link KeyboardShortcutsButton}.
|
||||||
|
@ -37,7 +38,7 @@ class KeyboardShortcutsButton extends AbstractButton<Props, *> {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
|
|
||||||
sendAnalytics(createToolbarEvent('shortcuts'));
|
sendAnalytics(createToolbarEvent('shortcuts'));
|
||||||
dispatch(openKeyboardShortcutsDialog());
|
dispatch(openSettingsDialog(SETTINGS_TABS.SHORTCUTS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { makeStyles } from 'tss-react/mui';
|
|
||||||
|
|
||||||
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
|
||||||
import Dialog from '../../../base/ui/components/web/Dialog';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the React {@code Component} props of
|
|
||||||
* {@link KeyboardShortcutsDialog}.
|
|
||||||
*/
|
|
||||||
interface IProps {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Map with keyboard keys as keys and translation keys as values.
|
|
||||||
*/
|
|
||||||
shortcutDescriptions: Map<string, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the styles for the component.
|
|
||||||
*
|
|
||||||
* @param {Object} theme - The current UI theme.
|
|
||||||
*
|
|
||||||
* @returns {Object}
|
|
||||||
*/
|
|
||||||
const useStyles = makeStyles()(theme => {
|
|
||||||
return {
|
|
||||||
list: {
|
|
||||||
listStyleType: 'none',
|
|
||||||
padding: 0,
|
|
||||||
|
|
||||||
'& .shortcuts-list__item': {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: `${theme.spacing(1)} 0`,
|
|
||||||
...withPixelLineHeight(theme.typography.bodyShortRegular),
|
|
||||||
color: theme.palette.text01
|
|
||||||
},
|
|
||||||
|
|
||||||
'& .item-action': {
|
|
||||||
backgroundColor: theme.palette.ui04,
|
|
||||||
...withPixelLineHeight(theme.typography.labelBold),
|
|
||||||
padding: `${theme.spacing(1)} ${theme.spacing(2)}`,
|
|
||||||
borderRadius: `${Number(theme.shape.borderRadius) / 2}px`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const KeyboardShortcutsDialog = ({ shortcutDescriptions }: IProps) => {
|
|
||||||
const { classes, cx } = useStyles();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/no-multi-comp
|
|
||||||
const _renderShortcutsListItem = (keyboardKey: string, translationKey: string) => {
|
|
||||||
let modifierKey = 'Alt';
|
|
||||||
|
|
||||||
if (window.navigator?.platform) {
|
|
||||||
if (window.navigator.platform.indexOf('Mac') !== -1) {
|
|
||||||
modifierKey = '⌥';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
className = 'shortcuts-list__item'
|
|
||||||
key = { keyboardKey }>
|
|
||||||
<span
|
|
||||||
aria-label = { t(translationKey) }
|
|
||||||
className = 'shortcuts-list__description'>
|
|
||||||
{t(translationKey)}
|
|
||||||
</span>
|
|
||||||
<span className = 'item-action'>
|
|
||||||
{keyboardKey.startsWith(':')
|
|
||||||
? `${modifierKey} + ${keyboardKey.slice(1)}`
|
|
||||||
: keyboardKey}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
cancel = {{ hidden: true }}
|
|
||||||
ok = {{ hidden: true }}
|
|
||||||
titleKey = 'keyboardShortcuts.keyboardShortcuts'>
|
|
||||||
<div
|
|
||||||
id = 'keyboard-shortcuts'>
|
|
||||||
<ul
|
|
||||||
className = { cx('shortcuts-list', classes.list) }
|
|
||||||
id = 'keyboard-shortcuts-list'>
|
|
||||||
{Array.from(shortcutDescriptions)
|
|
||||||
.map(description => _renderShortcutsListItem(...description))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default KeyboardShortcutsDialog;
|
|
|
@ -1,2 +1 @@
|
||||||
export { default as KeyboardShortcutsButton } from './KeyboardShortcutsButton';
|
export { default as KeyboardShortcutsButton } from './KeyboardShortcutsButton';
|
||||||
export { default as KeyboardShortcutsDialog } from './KeyboardShortcutsDialog';
|
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
export * from './actions';
|
|
||||||
export * from './components';
|
export * from './components';
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
|
||||||
|
|
||||||
import { OPEN_KEYBOARD_SHORTCUTS_DIALOG } from './actionTypes';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements the middleware of the feature keyboard-shortcuts.
|
|
||||||
*
|
|
||||||
* @param {Store} store - The redux store.
|
|
||||||
* @returns {Function}
|
|
||||||
*/
|
|
||||||
MiddlewareRegistry.register(_store => next => action => {
|
|
||||||
switch (action.type) {
|
|
||||||
case OPEN_KEYBOARD_SHORTCUTS_DIALOG:
|
|
||||||
if (typeof APP === 'object') {
|
|
||||||
APP.keyboardshortcut.openDialog();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return next(action);
|
|
||||||
});
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { batch } from 'react-redux';
|
import { batch } from 'react-redux';
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
import keyboardShortcut from '../../../modules/keyboardshortcut/keyboardshortcut';
|
||||||
import { IStore } from '../app/types';
|
import { IStore } from '../app/types';
|
||||||
import {
|
import {
|
||||||
setFollowMe,
|
setFollowMe,
|
||||||
|
@ -22,7 +24,8 @@ import {
|
||||||
getModeratorTabProps,
|
getModeratorTabProps,
|
||||||
getMoreTabProps,
|
getMoreTabProps,
|
||||||
getNotificationsTabProps,
|
getNotificationsTabProps,
|
||||||
getProfileTabProps
|
getProfileTabProps,
|
||||||
|
getShortcutsTabProps
|
||||||
} from './functions';
|
} from './functions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -237,3 +240,19 @@ export function toggleVideoSettings() {
|
||||||
dispatch(setVideoSettingsVisibility(!value));
|
dispatch(setVideoSettingsVisibility(!value));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits the settings from the "Shortcuts" tab of the settings dialog.
|
||||||
|
*
|
||||||
|
* @param {Object} newState - The new settings.
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export function submitShortcutsTab(newState: any) {
|
||||||
|
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||||
|
const currentState = getShortcutsTabProps(getState());
|
||||||
|
|
||||||
|
if (newState.keyboardShortcutsEnabled !== currentState.keyboardShortcutsEnabled) {
|
||||||
|
keyboardShortcut.enable(newState.keyboardShortcutsEnabled);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { WithTranslation } from 'react-i18next';
|
import { WithTranslation } from 'react-i18next';
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
import keyboardShortcut from '../../../../../modules/keyboardshortcut/keyboardshortcut';
|
|
||||||
import AbstractDialogTab, {
|
import AbstractDialogTab, {
|
||||||
IProps as AbstractDialogTabProps
|
IProps as AbstractDialogTabProps
|
||||||
} from '../../../base/dialog/components/web/AbstractDialogTab';
|
} from '../../../base/dialog/components/web/AbstractDialogTab';
|
||||||
|
@ -81,7 +79,6 @@ class MoreTab extends AbstractDialogTab<Props, any> {
|
||||||
// Bind event handler so it is only bound once for every instance.
|
// Bind event handler so it is only bound once for every instance.
|
||||||
this._onFramerateItemSelect = this._onFramerateItemSelect.bind(this);
|
this._onFramerateItemSelect = this._onFramerateItemSelect.bind(this);
|
||||||
this._onShowPrejoinPageChanged = this._onShowPrejoinPageChanged.bind(this);
|
this._onShowPrejoinPageChanged = this._onShowPrejoinPageChanged.bind(this);
|
||||||
this._onKeyboardShortcutEnableChanged = this._onKeyboardShortcutEnableChanged.bind(this);
|
|
||||||
this._renderMaxStageParticipantsSelect = this._renderMaxStageParticipantsSelect.bind(this);
|
this._renderMaxStageParticipantsSelect = this._renderMaxStageParticipantsSelect.bind(this);
|
||||||
this._onMaxStageParticipantsSelect = this._onMaxStageParticipantsSelect.bind(this);
|
this._onMaxStageParticipantsSelect = this._onMaxStageParticipantsSelect.bind(this);
|
||||||
}
|
}
|
||||||
|
@ -132,19 +129,6 @@ class MoreTab extends AbstractDialogTab<Props, any> {
|
||||||
super._onChange({ showPrejoinPage: checked });
|
super._onChange({ showPrejoinPage: checked });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback invoked to select if global keyboard shortcuts
|
|
||||||
* should be enabled.
|
|
||||||
*
|
|
||||||
* @param {Object} e - The key event to handle.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onKeyboardShortcutEnableChanged({ target: { checked } }: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
keyboardShortcut.enable(checked);
|
|
||||||
super._onChange({ keyboardShortcutEnable: checked });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback invoked to select a max number of stage participants from the select dropdown.
|
* Callback invoked to select a max number of stage participants from the select dropdown.
|
||||||
*
|
*
|
||||||
|
@ -190,31 +174,6 @@ class MoreTab extends AbstractDialogTab<Props, any> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the React Element for keyboardShortcut settings.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
_renderKeyboardShortcutCheckbox() {
|
|
||||||
const { t } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className = 'settings-sub-pane-element'
|
|
||||||
key = 'keyboard-shortcut'>
|
|
||||||
<span className = 'checkbox-label'>
|
|
||||||
{ t('keyboardShortcuts.keyboardShortcuts') }
|
|
||||||
</span>
|
|
||||||
<Checkbox
|
|
||||||
checked = { keyboardShortcut.getEnabled() }
|
|
||||||
label = { t('prejoin.keyboardShortcuts') }
|
|
||||||
name = 'enable-keyboard-shortcuts'
|
|
||||||
onChange = { this._onKeyboardShortcutEnableChanged } />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the React Element for modifying prejoin screen settings.
|
* Returns the React Element for modifying prejoin screen settings.
|
||||||
*
|
*
|
||||||
|
@ -304,7 +263,6 @@ class MoreTab extends AbstractDialogTab<Props, any> {
|
||||||
className = 'settings-sub-pane left'
|
className = 'settings-sub-pane left'
|
||||||
key = 'settings-sub-pane-left'>
|
key = 'settings-sub-pane-left'>
|
||||||
{ showPrejoinSettings && this._renderPrejoinScreenSettings() }
|
{ showPrejoinSettings && this._renderPrejoinScreenSettings() }
|
||||||
{ this._renderKeyboardShortcutCheckbox() }
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,15 @@ import { withStyles } from '@mui/styles';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
import { IReduxState } from '../../../app/types';
|
import { IReduxState } from '../../../app/types';
|
||||||
import { IconBell, IconCalendar, IconGear, IconHost, IconUser, IconVolumeUp } from '../../../base/icons/svg';
|
import {
|
||||||
|
IconBell,
|
||||||
|
IconCalendar,
|
||||||
|
IconGear,
|
||||||
|
IconHost,
|
||||||
|
IconShortcuts,
|
||||||
|
IconUser,
|
||||||
|
IconVolumeUp
|
||||||
|
} from '../../../base/icons/svg';
|
||||||
import { connect } from '../../../base/redux/functions';
|
import { connect } from '../../../base/redux/functions';
|
||||||
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
||||||
import DialogWithTabs, { IDialogTab } from '../../../base/ui/components/web/DialogWithTabs';
|
import DialogWithTabs, { IDialogTab } from '../../../base/ui/components/web/DialogWithTabs';
|
||||||
|
@ -19,7 +27,8 @@ import {
|
||||||
submitModeratorTab,
|
submitModeratorTab,
|
||||||
submitMoreTab,
|
submitMoreTab,
|
||||||
submitNotificationsTab,
|
submitNotificationsTab,
|
||||||
submitProfileTab
|
submitProfileTab,
|
||||||
|
submitShortcutsTab
|
||||||
} from '../../actions';
|
} from '../../actions';
|
||||||
import { SETTINGS_TABS } from '../../constants';
|
import { SETTINGS_TABS } from '../../constants';
|
||||||
import {
|
import {
|
||||||
|
@ -27,7 +36,8 @@ import {
|
||||||
getMoreTabProps,
|
getMoreTabProps,
|
||||||
getNotificationsMap,
|
getNotificationsMap,
|
||||||
getNotificationsTabProps,
|
getNotificationsTabProps,
|
||||||
getProfileTabProps
|
getProfileTabProps,
|
||||||
|
getShortcutsTabProps
|
||||||
} from '../../functions';
|
} from '../../functions';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -36,6 +46,7 @@ import ModeratorTab from './ModeratorTab';
|
||||||
import MoreTab from './MoreTab';
|
import MoreTab from './MoreTab';
|
||||||
import NotificationsTab from './NotificationsTab';
|
import NotificationsTab from './NotificationsTab';
|
||||||
import ProfileTab from './ProfileTab';
|
import ProfileTab from './ProfileTab';
|
||||||
|
import ShortcutsTab from './ShortcutsTab';
|
||||||
/* eslint-enable lines-around-comment */
|
/* eslint-enable lines-around-comment */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -342,6 +353,24 @@ function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tabs.push({
|
||||||
|
name: SETTINGS_TABS.SHORTCUTS,
|
||||||
|
component: ShortcutsTab,
|
||||||
|
labelKey: 'settings.shortcuts',
|
||||||
|
props: getShortcutsTabProps(state, isDisplayedOnWelcomePage),
|
||||||
|
propsUpdateFunction: (tabState: any, newProps: any) => {
|
||||||
|
// Updates tab props, keeping users selection
|
||||||
|
|
||||||
|
return {
|
||||||
|
...newProps,
|
||||||
|
keyboardShortcutsEnabled: tabState?.keyboardShortcutsEnabled
|
||||||
|
};
|
||||||
|
},
|
||||||
|
className: `settings-pane ${classes.settingsDialog}`,
|
||||||
|
submit: submitShortcutsTab,
|
||||||
|
icon: IconShortcuts
|
||||||
|
});
|
||||||
|
|
||||||
if (showMoreTab) {
|
if (showMoreTab) {
|
||||||
tabs.push({
|
tabs.push({
|
||||||
name: SETTINGS_TABS.MORE,
|
name: SETTINGS_TABS.MORE,
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
import { Theme } from '@mui/material';
|
||||||
|
import { withStyles } from '@mui/styles';
|
||||||
|
import React from 'react';
|
||||||
|
import { WithTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
import keyboardShortcut from '../../../../../modules/keyboardshortcut/keyboardshortcut';
|
||||||
|
import AbstractDialogTab, {
|
||||||
|
IProps as AbstractDialogTabProps } from '../../../base/dialog/components/web/AbstractDialogTab';
|
||||||
|
import { translate } from '../../../base/i18n/functions';
|
||||||
|
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
||||||
|
import Checkbox from '../../../base/ui/components/web/Checkbox';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the React {@code Component} props of {@link ShortcutsTab}.
|
||||||
|
*/
|
||||||
|
export interface IProps extends AbstractDialogTabProps, WithTranslation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSS classes object.
|
||||||
|
*/
|
||||||
|
classes: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to display the shortcuts or not.
|
||||||
|
*/
|
||||||
|
displayShortcuts: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wether the keyboard shortcuts are enabled or not.
|
||||||
|
*/
|
||||||
|
keyboardShortcutsEnabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) => {
|
||||||
|
return {
|
||||||
|
container: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column' as const,
|
||||||
|
width: '100%',
|
||||||
|
paddingBottom: theme.spacing(3)
|
||||||
|
},
|
||||||
|
|
||||||
|
checkbox: {
|
||||||
|
marginBottom: theme.spacing(3)
|
||||||
|
},
|
||||||
|
|
||||||
|
listContainer: {
|
||||||
|
listStyleType: 'none',
|
||||||
|
padding: 0,
|
||||||
|
margin: 0
|
||||||
|
},
|
||||||
|
|
||||||
|
listItem: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: `${theme.spacing(1)} 0`,
|
||||||
|
...withPixelLineHeight(theme.typography.bodyShortRegular),
|
||||||
|
color: theme.palette.text01
|
||||||
|
},
|
||||||
|
|
||||||
|
listItemKey: {
|
||||||
|
backgroundColor: theme.palette.ui04,
|
||||||
|
...withPixelLineHeight(theme.typography.labelBold),
|
||||||
|
padding: `${theme.spacing(1)} ${theme.spacing(2)}`,
|
||||||
|
borderRadius: `${Number(theme.shape.borderRadius) / 2}px`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React {@code Component} for modifying the local user's profile.
|
||||||
|
*
|
||||||
|
* @augments Component
|
||||||
|
*/
|
||||||
|
class ShortcutsTab extends AbstractDialogTab<IProps, any> {
|
||||||
|
/**
|
||||||
|
* Initializes a new {@code MoreTab} instance.
|
||||||
|
*
|
||||||
|
* @param {Object} props - The read-only properties with which the new
|
||||||
|
* instance is to be initialized.
|
||||||
|
*/
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
// Bind event handler so it is only bound once for every instance.
|
||||||
|
this._onKeyboardShortcutEnableChanged = this._onKeyboardShortcutEnableChanged.bind(this);
|
||||||
|
this._renderShortcutsListItem = this._renderShortcutsListItem.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked to select if global keyboard shortcuts
|
||||||
|
* should be enabled.
|
||||||
|
*
|
||||||
|
* @param {Object} e - The key event to handle.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onKeyboardShortcutEnableChanged({ target: { checked } }: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
super._onChange({ keyboardShortcutsEnabled: checked });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a keyboard shortcut with key and description.
|
||||||
|
*
|
||||||
|
* @param {string} keyboardKey - The keyboard key for the shortcut.
|
||||||
|
* @param {string} translationKey - The translation key for the shortcut description.
|
||||||
|
* @returns {JSX}
|
||||||
|
*/
|
||||||
|
_renderShortcutsListItem(keyboardKey: string, translationKey: string) {
|
||||||
|
const { classes, t } = this.props;
|
||||||
|
let modifierKey = 'Alt';
|
||||||
|
|
||||||
|
if (window.navigator?.platform) {
|
||||||
|
if (window.navigator.platform.indexOf('Mac') !== -1) {
|
||||||
|
modifierKey = '⌥';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
className = { classes.listItem }
|
||||||
|
key = { keyboardKey }>
|
||||||
|
<span
|
||||||
|
aria-label = { t(translationKey) }>
|
||||||
|
{t(translationKey)}
|
||||||
|
</span>
|
||||||
|
<span className = { classes.listItemKey }>
|
||||||
|
{keyboardKey.startsWith(':')
|
||||||
|
? `${modifierKey} + ${keyboardKey.slice(1)}`
|
||||||
|
: keyboardKey}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
classes,
|
||||||
|
displayShortcuts,
|
||||||
|
keyboardShortcutsEnabled,
|
||||||
|
t
|
||||||
|
} = this.props;
|
||||||
|
const shortcutDescriptions: Map<string, string> = displayShortcuts
|
||||||
|
? keyboardShortcut.getShortcutsDescriptions()
|
||||||
|
: new Map();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className = { classes.container }>
|
||||||
|
<Checkbox
|
||||||
|
checked = { keyboardShortcutsEnabled }
|
||||||
|
className = { classes.checkbox }
|
||||||
|
label = { t('prejoin.keyboardShortcuts') }
|
||||||
|
name = 'enable-keyboard-shortcuts'
|
||||||
|
onChange = { this._onKeyboardShortcutEnableChanged } />
|
||||||
|
{displayShortcuts && (
|
||||||
|
<ul className = { classes.listContainer }>
|
||||||
|
{Array.from(shortcutDescriptions)
|
||||||
|
.map(description => this._renderShortcutsListItem(...description))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withStyles(styles)(translate(ShortcutsTab));
|
|
@ -4,7 +4,8 @@ export const SETTINGS_TABS = {
|
||||||
MORE: 'more_tab',
|
MORE: 'more_tab',
|
||||||
MODERATOR: 'moderator-tab',
|
MODERATOR: 'moderator-tab',
|
||||||
NOTIFICATIONS: 'notifications_tab',
|
NOTIFICATIONS: 'notifications_tab',
|
||||||
PROFILE: 'profile_tab'
|
PROFILE: 'profile_tab',
|
||||||
|
SHORTCUTS: 'shortcuts_tab'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// @ts-expect-error
|
||||||
|
import keyboardShortcut from '../../../modules/keyboardshortcut/keyboardshortcut';
|
||||||
import { IReduxState } from '../app/types';
|
import { IReduxState } from '../app/types';
|
||||||
import { IStateful } from '../base/app/types';
|
import { IStateful } from '../base/app/types';
|
||||||
import { isNameReadOnly } from '../base/config/functions';
|
import { isNameReadOnly } from '../base/config/functions';
|
||||||
|
@ -14,6 +16,7 @@ import { parseStandardURIString } from '../base/util/uri';
|
||||||
import { isStageFilmstripEnabled } from '../filmstrip/functions';
|
import { isStageFilmstripEnabled } from '../filmstrip/functions';
|
||||||
import { isFollowMeActive } from '../follow-me/functions';
|
import { isFollowMeActive } from '../follow-me/functions';
|
||||||
import { getParticipantsPaneConfig } from '../participants-pane/functions';
|
import { getParticipantsPaneConfig } from '../participants-pane/functions';
|
||||||
|
import { isPrejoinPageVisible } from '../prejoin/functions';
|
||||||
import { isReactionsEnabled } from '../reactions/functions.any';
|
import { isReactionsEnabled } from '../reactions/functions.any';
|
||||||
|
|
||||||
import { SS_DEFAULT_FRAME_RATE, SS_SUPPORTED_FRAMERATES } from './constants';
|
import { SS_DEFAULT_FRAME_RATE, SS_SUPPORTED_FRAMERATES } from './constants';
|
||||||
|
@ -275,3 +278,23 @@ export function getAudioSettingsVisibility(state: IReduxState) {
|
||||||
export function getVideoSettingsVisibility(state: IReduxState) {
|
export function getVideoSettingsVisibility(state: IReduxState) {
|
||||||
return state['features/settings'].videoSettingsVisible;
|
return state['features/settings'].videoSettingsVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the properties for the "Shortcuts" tab from settings dialog from Redux
|
||||||
|
* state.
|
||||||
|
*
|
||||||
|
* @param {(Function|Object)} stateful -The (whole) redux state, or redux's
|
||||||
|
* {@code getState} function to be used to retrieve the state.
|
||||||
|
* @param {boolean} isDisplayedOnWelcomePage - Indicates whether the shortcuts dialog is displayed on the
|
||||||
|
* welcome page or not.
|
||||||
|
* @returns {Object} - The properties for the "Shortcuts" tab from settings
|
||||||
|
* dialog.
|
||||||
|
*/
|
||||||
|
export function getShortcutsTabProps(stateful: IStateful, isDisplayedOnWelcomePage?: boolean) {
|
||||||
|
const state = toState(stateful);
|
||||||
|
|
||||||
|
return {
|
||||||
|
displayShortcuts: !isDisplayedOnWelcomePage && !isPrejoinPageVisible(state),
|
||||||
|
keyboardShortcutsEnabled: keyboardShortcut.getEnabled()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue