New keyboard shortcuts: Ctrl+1..9, Ctrl+Tab/Ctrl+Shift+Tab

* New keyboard shortcuts: Ctrl+1..9, Ctrl+Tab/Ctrl+Shift+Tab

* Add new shortcuts to the shortcut guide
This commit is contained in:
Scott Nonnenberg 2020-04-20 11:23:46 -07:00
parent aaa91c441b
commit ebb3864f58
4 changed files with 178 additions and 77 deletions

View file

@ -1969,6 +1969,10 @@
"message": "Remove all draft attachments", "message": "Remove all draft attachments",
"description": "Shown in the shortcuts guide" "description": "Shown in the shortcuts guide"
}, },
"Keyboard--conversation-by-index": {
"message": "Jump to conversation",
"description": "A shortcut allowing direct navigation to conversations 1 to 9 in list"
},
"Keyboard--Key--ctrl": { "Keyboard--Key--ctrl": {
"message": "Ctrl", "message": "Ctrl",
"description": "Key shown in shortcut combination in shortcuts guide" "description": "Key shown in shortcut combination in shortcuts guide"
@ -1989,6 +1993,14 @@
"message": "Enter", "message": "Enter",
"description": "Key shown in shortcut combination in shortcuts guide" "description": "Key shown in shortcut combination in shortcuts guide"
}, },
"Keyboard--Key--tab": {
"message": "Tab",
"description": "Key shown in shortcut combination in shortcuts guide"
},
"Keyboard--Key--one-to-nine-range": {
"message": "1 to 9",
"description": "Expresses that 1, 2, 3, up to 9 are available shortcut keys"
},
"Keyboard--header": { "Keyboard--header": {
"message": "Keyboard Shortcuts", "message": "Keyboard Shortcuts",
"description": "Title header of the keyboard shortcuts guide" "description": "Title header of the keyboard shortcuts guide"

View file

@ -702,6 +702,24 @@
} }
}; };
function getConversationByIndex(index) {
const state = store.getState();
const lists = Signal.State.Selectors.conversations.getLeftPaneLists(
state
);
const toSearch = state.conversations.showArchived
? lists.archivedConversations
: lists.conversations;
const target = toSearch[index];
if (target) {
return target.id;
}
return null;
}
function findConversation(conversationId, direction, unreadOnly) { function findConversation(conversationId, direction, unreadOnly) {
const state = store.getState(); const state = store.getState();
const lists = Signal.State.Selectors.conversations.getLeftPaneLists( const lists = Signal.State.Selectors.conversations.getLeftPaneLists(
@ -739,6 +757,18 @@
return null; return null;
} }
const NUMBERS = {
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
};
document.addEventListener('keydown', event => { document.addEventListener('keydown', event => {
const { altKey, ctrlKey, key, metaKey, shiftKey } = event; const { altKey, ctrlKey, key, metaKey, shiftKey } = event;
@ -911,8 +941,24 @@
return; return;
} }
// Change currently selected conversation - up/down, to next/previous unread // Change currently selected conversation by index
if (!isSearching && optionOrAlt && !shiftKey && key === 'ArrowUp') { if (!isSearching && commandOrCtrl && NUMBERS[key]) {
const targetId = getConversationByIndex(NUMBERS[key] - 1);
if (targetId) {
window.Whisper.events.trigger('showConversation', targetId);
event.preventDefault();
event.stopPropagation();
return;
}
}
// Change currently selected conversation
// up/previous
if (
(!isSearching && optionOrAlt && !shiftKey && key === 'ArrowUp') ||
(!isSearching && ctrlKey && shiftKey && key === 'Tab')
) {
const unreadOnly = false; const unreadOnly = false;
const targetId = findConversation( const targetId = findConversation(
conversation ? conversation.id : null, conversation ? conversation.id : null,
@ -927,7 +973,11 @@
return; return;
} }
} }
if (!isSearching && optionOrAlt && !shiftKey && key === 'ArrowDown') { // down/next
if (
(!isSearching && optionOrAlt && !shiftKey && key === 'ArrowDown') ||
(!isSearching && ctrlKey && key === 'Tab')
) {
const unreadOnly = false; const unreadOnly = false;
const targetId = findConversation( const targetId = findConversation(
conversation ? conversation.id : null, conversation ? conversation.id : null,
@ -942,6 +992,7 @@
return; return;
} }
} }
// previous unread
if (!isSearching && optionOrAlt && shiftKey && key === 'ArrowUp') { if (!isSearching && optionOrAlt && shiftKey && key === 'ArrowUp') {
const unreadOnly = true; const unreadOnly = true;
const targetId = findConversation( const targetId = findConversation(
@ -957,6 +1008,7 @@
return; return;
} }
} }
// next unread
if (!isSearching && optionOrAlt && shiftKey && key === 'ArrowDown') { if (!isSearching && optionOrAlt && shiftKey && key === 'ArrowDown') {
const unreadOnly = true; const unreadOnly = true;
const targetId = findConversation( const targetId = findConversation(

View file

@ -7697,11 +7697,15 @@ button.module-image__border-overlay:focus {
margin-bottom: 4px; margin-bottom: 4px;
} }
.module-shortcut-guide__shortcut__key-container { .module-shortcut-guide__shortcut__key-container {
text-align: right;
margin-right: 4px;
margin-bottom: 4px; margin-bottom: 4px;
}
.module-shortcut-guide__shortcut__key-inner-container {
text-align: right;
margin-top: 4px;
margin-right: 4px;
white-space: nowrap; white-space: nowrap;
} }
.module-shortcut-guide__shortcut__key { .module-shortcut-guide__shortcut__key {
display: inline-block; display: inline-block;
@ -7709,7 +7713,6 @@ button.module-image__border-overlay:focus {
padding: 3px; padding: 3px;
padding-left: 8px; padding-left: 8px;
padding-right: 8px; padding-right: 8px;
margin-top: 4px;
margin-left: 4px; margin-left: 4px;
height: 30px; height: 30px;

View file

@ -15,6 +15,8 @@ type KeyType =
| 'optionOrAlt' | 'optionOrAlt'
| 'shift' | 'shift'
| 'enter' | 'enter'
| 'tab'
| 'ctrl'
| '↑' | '↑'
| '↓' | '↓'
| ',' | ','
@ -33,142 +35,153 @@ type KeyType =
| 'T' | 'T'
| 'U' | 'U'
| 'V' | 'V'
| 'X'; | 'X'
| '1 to 9';
type ShortcutType = { type ShortcutType = {
description: string; description: string;
keys: Array<KeyType>; keys: Array<Array<KeyType>>;
}; };
const NAVIGATION_SHORTCUTS: Array<ShortcutType> = [ const NAVIGATION_SHORTCUTS: Array<ShortcutType> = [
{ {
description: 'Keyboard--navigate-by-section', description: 'Keyboard--navigate-by-section',
keys: ['commandOrCtrl', 'T'], keys: [['commandOrCtrl', 'T']],
}, },
{ {
description: 'Keyboard--previous-conversation', description: 'Keyboard--previous-conversation',
keys: ['optionOrAlt', '↑'], keys: [
['optionOrAlt', '↑'],
['ctrl', 'shift', 'tab'],
],
}, },
{ {
description: 'Keyboard--next-conversation', description: 'Keyboard--next-conversation',
keys: ['optionOrAlt', '↓'], keys: [
['optionOrAlt', '↓'],
['ctrl', 'tab'],
],
}, },
{ {
description: 'Keyboard--previous-unread-conversation', description: 'Keyboard--previous-unread-conversation',
keys: ['optionOrAlt', 'shift', '↑'], keys: [['optionOrAlt', 'shift', '↑']],
}, },
{ {
description: 'Keyboard--next-unread-conversation', description: 'Keyboard--next-unread-conversation',
keys: ['optionOrAlt', 'shift', '↓'], keys: [['optionOrAlt', 'shift', '↓']],
},
{
description: 'Keyboard--conversation-by-index',
keys: [['commandOrCtrl', '1 to 9']],
}, },
{ {
description: 'Keyboard--preferences', description: 'Keyboard--preferences',
keys: ['commandOrCtrl', ','], keys: [['commandOrCtrl', ',']],
}, },
{ {
description: 'Keyboard--open-conversation-menu', description: 'Keyboard--open-conversation-menu',
keys: ['commandOrCtrl', 'shift', 'L'], keys: [['commandOrCtrl', 'shift', 'L']],
}, },
{ {
description: 'Keyboard--search', description: 'Keyboard--search',
keys: ['commandOrCtrl', 'F'], keys: [['commandOrCtrl', 'F']],
}, },
{ {
description: 'Keyboard--search-in-conversation', description: 'Keyboard--search-in-conversation',
keys: ['commandOrCtrl', 'shift', 'F'], keys: [['commandOrCtrl', 'shift', 'F']],
}, },
{ {
description: 'Keyboard--focus-composer', description: 'Keyboard--focus-composer',
keys: ['commandOrCtrl', 'shift', 'T'], keys: [['commandOrCtrl', 'shift', 'T']],
}, },
{ {
description: 'Keyboard--open-all-media-view', description: 'Keyboard--open-all-media-view',
keys: ['commandOrCtrl', 'shift', 'M'], keys: [['commandOrCtrl', 'shift', 'M']],
}, },
{ {
description: 'Keyboard--open-emoji-chooser', description: 'Keyboard--open-emoji-chooser',
keys: ['commandOrCtrl', 'shift', 'J'], keys: [['commandOrCtrl', 'shift', 'J']],
}, },
{ {
description: 'Keyboard--open-sticker-chooser', description: 'Keyboard--open-sticker-chooser',
keys: ['commandOrCtrl', 'shift', 'S'], keys: [['commandOrCtrl', 'shift', 'S']],
}, },
{ {
description: 'Keyboard--begin-recording-voice-note', description: 'Keyboard--begin-recording-voice-note',
keys: ['commandOrCtrl', 'shift', 'V'], keys: [['commandOrCtrl', 'shift', 'V']],
}, },
{ {
description: 'Keyboard--archive-conversation', description: 'Keyboard--archive-conversation',
keys: ['commandOrCtrl', 'shift', 'A'], keys: [['commandOrCtrl', 'shift', 'A']],
}, },
{ {
description: 'Keyboard--unarchive-conversation', description: 'Keyboard--unarchive-conversation',
keys: ['commandOrCtrl', 'shift', 'U'], keys: [['commandOrCtrl', 'shift', 'U']],
}, },
{ {
description: 'Keyboard--scroll-to-top', description: 'Keyboard--scroll-to-top',
keys: ['commandOrCtrl', '↑'], keys: [['commandOrCtrl', '↑']],
}, },
{ {
description: 'Keyboard--scroll-to-bottom', description: 'Keyboard--scroll-to-bottom',
keys: ['commandOrCtrl', '↓'], keys: [['commandOrCtrl', '↓']],
}, },
{ {
description: 'Keyboard--close-curent-conversation', description: 'Keyboard--close-curent-conversation',
keys: ['commandOrCtrl', 'shift', 'C'], keys: [['commandOrCtrl', 'shift', 'C']],
}, },
]; ];
const MESSAGE_SHORTCUTS: Array<ShortcutType> = [ const MESSAGE_SHORTCUTS: Array<ShortcutType> = [
{ {
description: 'Keyboard--default-message-action', description: 'Keyboard--default-message-action',
keys: ['enter'], keys: [['enter']],
}, },
{ {
description: 'Keyboard--view-details-for-selected-message', description: 'Keyboard--view-details-for-selected-message',
keys: ['commandOrCtrl', 'D'], keys: [['commandOrCtrl', 'D']],
}, },
{ {
description: 'Keyboard--toggle-reply', description: 'Keyboard--toggle-reply',
keys: ['commandOrCtrl', 'shift', 'R'], keys: [['commandOrCtrl', 'shift', 'R']],
}, },
{ {
description: 'Keyboard--toggle-reaction-picker', description: 'Keyboard--toggle-reaction-picker',
keys: ['commandOrCtrl', 'shift', 'E'], keys: [['commandOrCtrl', 'shift', 'E']],
}, },
{ {
description: 'Keyboard--save-attachment', description: 'Keyboard--save-attachment',
keys: ['commandOrCtrl', 'S'], keys: [['commandOrCtrl', 'S']],
}, },
{ {
description: 'Keyboard--delete-message', description: 'Keyboard--delete-message',
keys: ['commandOrCtrl', 'shift', 'D'], keys: [['commandOrCtrl', 'shift', 'D']],
}, },
]; ];
const COMPOSER_SHORTCUTS: Array<ShortcutType> = [ const COMPOSER_SHORTCUTS: Array<ShortcutType> = [
{ {
description: 'Keyboard--add-newline', description: 'Keyboard--add-newline',
keys: ['shift', 'enter'], keys: [['shift', 'enter']],
}, },
{ {
description: 'Keyboard--expand-composer', description: 'Keyboard--expand-composer',
keys: ['commandOrCtrl', 'shift', 'X'], keys: [['commandOrCtrl', 'shift', 'X']],
}, },
{ {
description: 'Keyboard--send-in-expanded-composer', description: 'Keyboard--send-in-expanded-composer',
keys: ['commandOrCtrl', 'enter'], keys: [['commandOrCtrl', 'enter']],
}, },
{ {
description: 'Keyboard--attach-file', description: 'Keyboard--attach-file',
keys: ['commandOrCtrl', 'U'], keys: [['commandOrCtrl', 'U']],
}, },
{ {
description: 'Keyboard--remove-draft-link-preview', description: 'Keyboard--remove-draft-link-preview',
keys: ['commandOrCtrl', 'P'], keys: [['commandOrCtrl', 'P']],
}, },
{ {
description: 'Keyboard--remove-draft-attachments', description: 'Keyboard--remove-draft-attachments',
keys: ['commandOrCtrl', 'shift', 'P'], keys: [['commandOrCtrl', 'shift', 'P']],
}, },
]; ];
@ -253,46 +266,67 @@ function renderShortcut(
{i18n(shortcut.description)} {i18n(shortcut.description)}
</div> </div>
<div className="module-shortcut-guide__shortcut__key-container"> <div className="module-shortcut-guide__shortcut__key-container">
{shortcut.keys.map((key, mapIndex) => { {shortcut.keys.map((keys, outerIndex) => (
let label: string = key; <div
let isSquare = true; key={outerIndex}
className="module-shortcut-guide__shortcut__key-inner-container"
>
{keys.map((key, mapIndex) => {
let label: string = key;
let isSquare = true;
if (key === 'commandOrCtrl' && isMacOS) { if (key === 'commandOrCtrl' && isMacOS) {
label = '⌘'; label = '⌘';
} }
if (key === 'commandOrCtrl' && !isMacOS) { if (key === 'commandOrCtrl' && !isMacOS) {
label = i18n('Keyboard--Key--ctrl'); label = i18n('Keyboard--Key--ctrl');
isSquare = false; isSquare = false;
} }
if (key === 'optionOrAlt' && isMacOS) { if (key === 'optionOrAlt' && isMacOS) {
label = i18n('Keyboard--Key--option'); label = i18n('Keyboard--Key--option');
isSquare = false; isSquare = false;
} }
if (key === 'optionOrAlt' && !isMacOS) { if (key === 'optionOrAlt' && !isMacOS) {
label = i18n('Keyboard--Key--alt'); label = i18n('Keyboard--Key--alt');
isSquare = false; isSquare = false;
} }
if (key === 'shift') { if (key === 'ctrl') {
label = i18n('Keyboard--Key--shift'); label = i18n('Keyboard--Key--ctrl');
isSquare = false; isSquare = false;
} }
if (key === 'enter') { if (key === 'shift') {
label = i18n('Keyboard--Key--enter'); label = i18n('Keyboard--Key--shift');
isSquare = false; isSquare = false;
} }
if (key === 'enter') {
label = i18n('Keyboard--Key--enter');
isSquare = false;
}
if (key === 'tab') {
label = i18n('Keyboard--Key--tab');
isSquare = false;
}
if (key === '1 to 9') {
label = i18n('Keyboard--Key--one-to-nine-range');
isSquare = false;
}
return ( return (
<span <span
key={mapIndex} key={mapIndex}
className={classNames( className={classNames(
'module-shortcut-guide__shortcut__key', 'module-shortcut-guide__shortcut__key',
isSquare ? 'module-shortcut-guide__shortcut__key--square' : null isSquare
)} ? 'module-shortcut-guide__shortcut__key--square'
> : null
{label} )}
</span> >
); {label}
})} </span>
);
})}
</div>
))}
</div> </div>
</div> </div>
); );