Improve Keyboard Shortcuts. Add alt-arrows & alt-shift-arrows.

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
pull/21833/head
Michael Telatynski 2020-03-19 19:07:33 +00:00
parent 3c2101fa31
commit 578b3f2b97
7 changed files with 127 additions and 14 deletions

View File

@ -21,7 +21,7 @@ limitations under the License.
-webkit-box-direction: normal;
flex-direction: column;
margin-bottom: -50px;
max-height: 700px; // XXX: this may need adjusting when adding new shortcuts
max-height: 1100px; // XXX: this may need adjusting when adding new shortcuts
.mx_KeyboardShortcutsDialog_category {
width: 33.3333%; // 3 columns

View File

@ -87,12 +87,6 @@ const shortcuts: Record<Categories, IShortcut[]> = {
key: Key.GREATER_THAN,
}],
description: _td("Toggle Quote"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.M,
}],
description: _td("Toggle Markdown"),
}, {
keybinds: [{
modifiers: [Modifiers.SHIFT],
@ -115,6 +109,15 @@ const shortcuts: Record<Categories, IShortcut[]> = {
key: Key.END,
}],
description: _td("Jump to start/end of the composer"),
}, {
keybinds: [{
modifiers: [Modifiers.CONTROL, Modifiers.ALT],
key: Key.ARROW_UP,
}, {
modifiers: [Modifiers.CONTROL, Modifiers.ALT],
key: Key.ARROW_DOWN,
}],
description: _td("Navigate composer history"),
},
],
@ -179,6 +182,24 @@ const shortcuts: Record<Categories, IShortcut[]> = {
key: Key.PAGE_DOWN,
}],
description: _td("Scroll up/down in the timeline"),
}, {
keybinds: [{
modifiers: [Modifiers.ALT, Modifiers.SHIFT],
key: Key.ARROW_UP,
}, {
modifiers: [Modifiers.ALT, Modifiers.SHIFT],
key: Key.ARROW_DOWN,
}],
description: _td("Previous/next unread room or DM"),
}, {
keybinds: [{
modifiers: [Modifiers.ALT],
key: Key.ARROW_UP,
}, {
modifiers: [Modifiers.ALT],
key: Key.ARROW_DOWN,
}],
description: _td("Previous/next room or DM"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL],
@ -223,6 +244,14 @@ const shortcuts: Record<Categories, IShortcut[]> = {
],
};
const categoryOrder = [
Categories.COMPOSER,
Categories.CALLS,
Categories.ROOM_LIST,
Categories.AUTOCOMPLETE,
Categories.NAVIGATION,
];
interface IModal {
close: () => void;
finished: Promise<any[]>;
@ -289,7 +318,8 @@ export const toggleDialog = () => {
return;
}
const sections = Object.entries(shortcuts).map(([category, list]) => {
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>

View File

@ -385,6 +385,18 @@ const LoggedInView = createReactClass({
handled = true;
}
break;
case Key.ARROW_UP:
case Key.ARROW_DOWN:
if (ev.altKey && !ev.ctrlKey && !ev.metaKey) {
dis.dispatch({
action: 'view_room_delta',
delta: ev.key === Key.ARROW_UP ? -1 : 1,
unread: ev.shiftKey,
});
handled = true;
}
break;
}
if (handled) {

View File

@ -1,6 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017, 2018 Vector Creations Ltd
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -40,6 +41,8 @@ import * as Receipt from "../../../utils/Receipt";
import {Resizer} from '../../../resizer';
import {Layout, Distributor} from '../../../resizer/distributors/roomsublist2';
import {RovingTabIndexProvider} from "../../../accessibility/RovingTabIndex";
import * as Unread from "../../../Unread";
import RoomViewStore from "../../../stores/RoomViewStore";
const HIDE_CONFERENCE_CHANS = true;
const STANDARD_TAGS_REGEX = /^(m\.(favourite|lowpriority|server_notice)|im\.vector\.fake\.(invite|recent|direct|archived))$/;
@ -242,6 +245,56 @@ export default createReactClass({
});
}
break;
case 'view_room_delta': {
const currentRoomId = RoomViewStore.getRoomId();
const {
"im.vector.fake.invite": inviteRooms,
"m.favourite": favouriteRooms,
[TAG_DM]: dmRooms,
"im.vector.fake.recent": recentRooms,
"m.lowpriority": lowPriorityRooms,
"im.vector.fake.archived": historicalRooms,
"m.server_notice": serverNoticeRooms,
...tags
} = this.state.lists;
const shownCustomTagRooms = Object.keys(tags).filter(tagName => {
return (!this.state.customTags || this.state.customTags[tagName]) &&
!tagName.match(STANDARD_TAGS_REGEX);
}).map(tagName => tags[tagName]);
// this order matches the one when generating the room sublists below.
let rooms = this._applySearchFilter([
...inviteRooms,
...favouriteRooms,
...dmRooms,
...recentRooms,
// eslint-disable-next-line prefer-spread
...[].concat.apply([], shownCustomTagRooms),
...lowPriorityRooms,
...historicalRooms,
...serverNoticeRooms,
], this.props.searchFilter); // TODO optimize
if (payload.unread) {
// filter to only notification rooms (and our current active room so we can index properly)
rooms = rooms.filter(room => {
return room.roomId === currentRoomId || Unread.doesRoomHaveUnreadMessages(room);
});
}
const currentIndex = rooms.findIndex(room => room.roomId === currentRoomId);
const [room] = rooms.slice((currentIndex + payload.delta) % rooms.length);
// console.log("DEBUG", currentIndex, room, rooms);
if (room) {
dis.dispatch({
action: 'view_room',
room_id: room.roomId,
});
}
break;
}
}
},

View File

@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import classNames from 'classnames';
@ -225,6 +225,16 @@ export default createReactClass({
case 'feature_custom_status_changed':
this.forceUpdate();
break;
case 'view_room':
// when the room is selected make sure its tile is visible, for breadcrumbs/keyboard shortcuts
if (payload.room_id === this.props.room.roomId && this._roomTile.current) {
this._roomTile.current.scrollIntoView({
block: "nearest",
behavior: "auto",
});
}
break;
}
},
@ -234,6 +244,10 @@ export default createReactClass({
});
},
UNSAFE_componentWillMount: function() {
this._roomTile = createRef();
},
componentDidMount: function() {
/* We bind here rather than in the definition because otherwise we wind up with the
method only being callable once every 500ms across all instances, which would be wrong */
@ -538,7 +552,7 @@ export default createReactClass({
}
return <React.Fragment>
<RovingTabIndexWrapper>
<RovingTabIndexWrapper inputRef={this._roomTile}>
{({onFocus, isActive, ref}) =>
<AccessibleButton
onFocus={onFocus}

View File

@ -125,10 +125,12 @@ export default class SendMessageComposer extends React.Component {
}
onVerticalArrow(e, up) {
if (e.ctrlKey || e.shiftKey || e.metaKey) return;
// arrows from an initial-caret composer navigates recent messages to edit
// ctrl-alt-arrows navigate send history
if (e.shiftKey || e.metaKey) return;
const shouldSelectHistory = e.altKey;
const shouldEditLastMessage = !e.altKey && up && !RoomViewStore.getQuotingEvent();
const shouldSelectHistory = e.altKey && e.ctrlKey;
const shouldEditLastMessage = !e.altKey && !e.ctrlKey && up && !RoomViewStore.getQuotingEvent();
if (shouldSelectHistory) {
// Try select composer history

View File

@ -2183,10 +2183,10 @@
"Toggle Bold": "Toggle Bold",
"Toggle Italics": "Toggle Italics",
"Toggle Quote": "Toggle Quote",
"Toggle Markdown": "Toggle Markdown",
"New line": "New line",
"Navigate recent messages to edit": "Navigate recent messages to edit",
"Jump to start/end of the composer": "Jump to start/end of the composer",
"Navigate composer history": "Navigate composer history",
"Toggle microphone mute": "Toggle microphone mute",
"Toggle video on/off": "Toggle video on/off",
"Jump to room search": "Jump to room search",
@ -2196,6 +2196,8 @@
"Expand room list section": "Expand room list section",
"Clear room list filter field": "Clear room list filter field",
"Scroll up/down in the timeline": "Scroll up/down in the timeline",
"Previous/next unread room or DM": "Previous/next unread room or DM",
"Previous/next room or DM": "Previous/next room or DM",
"Toggle the top left menu": "Toggle the top left menu",
"Close dialog or context menu": "Close dialog or context menu",
"Activate selected button": "Activate selected button",