Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Make the Keyboard Shortcuts dialog into a settings tab (#7198)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonBrandner committed Jan 17, 2022
1 parent cb42173 commit 1f29825
Show file tree
Hide file tree
Showing 13 changed files with 191 additions and 155 deletions.
2 changes: 1 addition & 1 deletion res/css/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@
@import "./views/dialogs/_IncomingSasDialog.scss";
@import "./views/dialogs/_InviteDialog.scss";
@import "./views/dialogs/_JoinRuleDropdown.scss";
@import "./views/dialogs/_KeyboardShortcutsDialog.scss";
@import "./views/dialogs/_LeaveSpaceDialog.scss";
@import "./views/dialogs/_LocationViewDialog.scss";
@import "./views/dialogs/_ManageRestrictedJoinRuleDialog.scss";
Expand Down Expand Up @@ -286,6 +285,7 @@
@import "./views/settings/tabs/user/_AppearanceUserSettingsTab.scss";
@import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss";
@import "./views/settings/tabs/user/_HelpUserSettingsTab.scss";
@import "./views/settings/tabs/user/_KeyboardUserSettingsTab.scss";
@import "./views/settings/tabs/user/_LabsUserSettingsTab.scss";
@import "./views/settings/tabs/user/_MjolnirUserSettingsTab.scss";
@import "./views/settings/tabs/user/_NotificationUserSettingsTab.scss";
Expand Down
4 changes: 4 additions & 0 deletions res/css/views/dialogs/_UserSettingsDialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ limitations under the License.
mask-image: url('$(res)/img/element-icons/settings/preference.svg');
}

.mx_UserSettingsDialog_keyboardIcon::before {
mask-image: url('$(res)/img/element-icons/settings/keyboard.svg');
}

.mx_UserSettingsDialog_sidebarIcon::before {
mask-image: url('$(res)/img/element-icons/settings/sidebar.svg');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -14,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

.mx_KeyboardShortcutsDialog {
.mx_KeyboardUserSettingsTab .mx_SettingsTab_section {
display: flex;
flex-wrap: wrap;
-webkit-box-orient: vertical;
Expand Down
3 changes: 3 additions & 0 deletions res/img/element-icons/settings/keyboard.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/KeyBindingsDefaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,14 +355,14 @@ const navigationBindings = (): KeyBinding<NavigationAction>[] => {
},
},
{
action: NavigationAction.ToggleShortCutDialog,
action: NavigationAction.OpenShortCutDialog,
keyCombo: {
key: Key.SLASH,
ctrlOrCmd: true,
},
},
{
action: NavigationAction.ToggleShortCutDialog,
action: NavigationAction.OpenShortCutDialog,
keyCombo: {
key: Key.SLASH,
ctrlOrCmd: true,
Expand Down
2 changes: 1 addition & 1 deletion src/KeyBindingsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export enum NavigationAction {
/** Toggle the user menu */
ToggleUserMenu = 'ToggleUserMenu',
/** Toggle the short cut help dialog */
ToggleShortCutDialog = 'ToggleShortCutDialog',
OpenShortCutDialog = 'OpenShortCutDialog',
/** Got to the Element home screen */
GoToHome = 'GoToHome',
/** Select prev room */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import * as React from "react";
import classNames from "classnames";

import Modal from "../Modal";
import { _t, _td } from "../languageHandler";
import { _td } from "../languageHandler";
import { isMac, Key } from "../Keyboard";
import InfoDialog from "../components/views/dialogs/InfoDialog";

// TS: once languageHandler is TS we can probably inline this into the enum
_td("Navigation");
_td("Calls");
_td("Composer");
_td("Room List");
_td("Autocomplete");

export enum Categories {
NAVIGATION = "Navigation",
Expand All @@ -38,13 +26,6 @@ export enum Categories {
AUTOCOMPLETE = "Autocomplete",
}

// TS: once languageHandler is TS we can probably inline this into the enum
_td("Alt");
_td("Alt Gr");
_td("Shift");
_td("Super");
_td("Ctrl");

export enum Modifiers {
ALT = "Alt", // Option on Mac and displayed as an Icon
ALT_GR = "Alt Gr",
Expand All @@ -65,12 +46,12 @@ interface IKeybind {
key: string; // TS: fix this once Key is an enum
}

interface IShortcut {
export interface IShortcut {
keybinds: IKeybind[];
description: string;
}

const shortcuts: Record<Categories, IShortcut[]> = {
export const shortcuts: Record<Categories, IShortcut[]> = {
[Categories.COMPOSER]: [
{
keybinds: [{
Expand Down Expand Up @@ -270,7 +251,7 @@ const shortcuts: Record<Categories, IShortcut[]> = {
modifiers: [CMD_OR_CTRL],
key: Key.SLASH,
}],
description: _td("Toggle this dialog"),
description: _td("Open this settings tab"),
}, {
keybinds: [{
modifiers: [Modifiers.CONTROL, isMac ? Modifiers.SHIFT : Modifiers.ALT],
Expand All @@ -297,107 +278,6 @@ const shortcuts: Record<Categories, IShortcut[]> = {
],
};

const categoryOrder = [
Categories.COMPOSER,
Categories.AUTOCOMPLETE,
Categories.ROOM,
Categories.ROOM_LIST,
Categories.NAVIGATION,
Categories.CALLS,
];

interface IModal {
close: () => void;
finished: Promise<any[]>;
}

const modifierIcon: Record<string, string> = {
[Modifiers.COMMAND]: "⌘",
};

if (isMac) {
modifierIcon[Modifiers.ALT] = "⌥";
}

const alternateKeyName: Record<string, string> = {
[Key.PAGE_UP]: _td("Page Up"),
[Key.PAGE_DOWN]: _td("Page Down"),
[Key.ESCAPE]: _td("Esc"),
[Key.ENTER]: _td("Enter"),
[Key.SPACE]: _td("Space"),
[Key.HOME]: _td("Home"),
[Key.END]: _td("End"),
[DIGITS]: _td("[number]"),
};
const keyIcon: Record<string, string> = {
[Key.ARROW_UP]: "↑",
[Key.ARROW_DOWN]: "↓",
[Key.ARROW_LEFT]: "←",
[Key.ARROW_RIGHT]: "→",
};

const Shortcut: React.FC<{
shortcut: IShortcut;
}> = ({ shortcut }) => {
const classes = classNames({
"mx_KeyboardShortcutsDialog_inline": shortcut.keybinds.every(k => !k.modifiers || k.modifiers.length === 0),
});

return <div className={classes}>
<h5>{ _t(shortcut.description) }</h5>
{ shortcut.keybinds.map(s => {
let text = s.key;
if (alternateKeyName[s.key]) {
text = _t(alternateKeyName[s.key]);
} else if (keyIcon[s.key]) {
text = keyIcon[s.key];
}

return <div key={s.key}>
{ s.modifiers?.map(m => {
return <React.Fragment key={m}>
<kbd>{ modifierIcon[m] || _t(m) }</kbd>+
</React.Fragment>;
}) }
<kbd>{ text }</kbd>
</div>;
}) }
</div>;
};

let activeModal: IModal = null;
export const toggleDialog = () => {
if (activeModal) {
activeModal.close();
activeModal = null;
return;
}

const sections = categoryOrder.map(category => {
const list = shortcuts[category];
return <div className="mx_KeyboardShortcutsDialog_category" key={category}>
<h3>{ _t(category) }</h3>
<div>{ list.map(shortcut => <Shortcut key={shortcut.description} shortcut={shortcut} />) }</div>
</div>;
});

activeModal = Modal.createTrackedDialog("Keyboard Shortcuts", "", InfoDialog, {
className: "mx_KeyboardShortcutsDialog",
title: _t("Keyboard Shortcuts"),
description: sections,
hasCloseButton: true,
onKeyDown: (ev) => {
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey && ev.key === Key.SLASH) { // Ctrl + /
ev.stopPropagation();
activeModal.close();
}
},
onFinished: () => {
activeModal = null;
},
});
};

export const registerShortcut = (category: Categories, defn: IShortcut) => {
shortcuts[category].push(defn);
};
10 changes: 7 additions & 3 deletions src/components/structures/LoggedInView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import SettingsStore from "../../settings/SettingsStore";
import ResizeHandle from '../views/elements/ResizeHandle';
import { CollapseDistributor, Resizer } from '../../resizer';
import MatrixClientContext from "../../contexts/MatrixClientContext";
import * as KeyboardShortcuts from "../../accessibility/KeyboardShortcuts";
import HomePage from "./HomePage";
import ResizeNotifier from "../../utils/ResizeNotifier";
import PlatformPeg from "../../PlatformPeg";
Expand Down Expand Up @@ -67,6 +66,8 @@ import GroupFilterPanel from './GroupFilterPanel';
import CustomRoomTagPanel from './CustomRoomTagPanel';
import { mediaFromMxc } from "../../customisations/Media";
import LegacyCommunityPreview from "./LegacyCommunityPreview";
import { UserTab } from "../views/dialogs/UserSettingsDialog";
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
import RightPanelStore from '../../stores/right-panel/RightPanelStore';

// We need to fetch each pinned message individually (if we don't already have it)
Expand Down Expand Up @@ -472,8 +473,11 @@ class LoggedInView extends React.Component<IProps, IState> {
dis.fire(Action.ToggleUserMenu);
handled = true;
break;
case NavigationAction.ToggleShortCutDialog:
KeyboardShortcuts.toggleDialog();
case NavigationAction.OpenShortCutDialog:
dis.dispatch<OpenToTabPayload>({
action: Action.ViewUserSettings,
initialTabId: UserTab.Keyboard,
});
handled = true;
break;
case NavigationAction.GoToHome:
Expand Down
8 changes: 8 additions & 0 deletions src/components/views/dialogs/UserSettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
import { IDialogProps } from "./IDialogProps";
import SidebarUserSettingsTab from "../settings/tabs/user/SidebarUserSettingsTab";
import KeyboardUserSettingsTab from "../settings/tabs/user/KeyboardUserSettingsTab";

export enum UserTab {
General = "USER_GENERAL_TAB",
Appearance = "USER_APPEARANCE_TAB",
Flair = "USER_FLAIR_TAB",
Notifications = "USER_NOTIFICATIONS_TAB",
Preferences = "USER_PREFERENCES_TAB",
Keyboard = "USER_KEYBOARD_TAB",
Sidebar = "USER_SIDEBAR_TAB",
Voice = "USER_VOICE_TAB",
Security = "USER_SECURITY_TAB",
Expand Down Expand Up @@ -119,6 +121,12 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
"mx_UserSettingsDialog_preferencesIcon",
<PreferencesUserSettingsTab closeSettingsFn={this.props.onFinished} />,
));
tabs.push(new Tab(
UserTab.Keyboard,
_td("Keyboard"),
"mx_UserSettingsDialog_keyboardIcon",
<KeyboardUserSettingsTab />,
));

if (SettingsStore.getValue("feature_spaces_metaspaces")) {
tabs.push(new Tab(
Expand Down
14 changes: 12 additions & 2 deletions src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@ import SdkConfig from "../../../../../SdkConfig";
import createRoom from "../../../../../createRoom";
import Modal from "../../../../../Modal";
import PlatformPeg from "../../../../../PlatformPeg";
import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts";
import UpdateCheckButton from "../../UpdateCheckButton";
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
import { copyPlaintext } from "../../../../../utils/strings";
import * as ContextMenu from "../../../../structures/ContextMenu";
import { toRightOf } from "../../../../structures/ContextMenu";
import BugReportDialog from '../../../dialogs/BugReportDialog';
import GenericTextContextMenu from "../../../context_menus/GenericTextContextMenu";
import { OpenToTabPayload } from "../../../../../dispatcher/payloads/OpenToTabPayload";
import { Action } from "../../../../../dispatcher/actions";
import { UserTab } from "../../../dialogs/UserSettingsDialog";
import dis from "../../../../../dispatcher/dispatcher";

interface IProps {
closeSettingsFn: () => void;
Expand Down Expand Up @@ -211,6 +214,13 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
this.copy(`${appVersion}\n${olmVersion}`, e);
};

private onKeyboardShortcutsClicked = (): void => {
dis.dispatch<OpenToTabPayload>({
action: Action.ViewUserSettings,
initialTabId: UserTab.Keyboard,
});
};

render() {
const brand = SdkConfig.get().brand;

Expand Down Expand Up @@ -307,7 +317,7 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
<div className='mx_SettingsTab_subsectionText'>
{ faqText }
</div>
<AccessibleButton kind="primary" onClick={KeyboardShortcuts.toggleDialog}>
<AccessibleButton kind="primary" onClick={this.onKeyboardShortcutsClicked}>
{ _t("Keyboard Shortcuts") }
</AccessibleButton>
</div>
Expand Down
Loading

0 comments on commit 1f29825

Please sign in to comment.