Merge remote-tracking branch 'origin/develop' into dbkr/symmetric-ssss-migrate

pull/21833/head
David Baker 2020-03-19 14:18:55 +00:00
commit 32f70dfec1
14 changed files with 451 additions and 15 deletions

View File

@ -116,6 +116,7 @@
"@babel/preset-typescript": "^7.7.4", "@babel/preset-typescript": "^7.7.4",
"@babel/register": "^7.7.4", "@babel/register": "^7.7.4",
"@peculiar/webcrypto": "^1.0.22", "@peculiar/webcrypto": "^1.0.22",
"@types/classnames": "^2.2.10",
"@types/react": "16.9", "@types/react": "16.9",
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0", "babel-jest": "^24.9.0",

View File

@ -65,6 +65,7 @@
@import "./views/dialogs/_GroupAddressPicker.scss"; @import "./views/dialogs/_GroupAddressPicker.scss";
@import "./views/dialogs/_IncomingSasDialog.scss"; @import "./views/dialogs/_IncomingSasDialog.scss";
@import "./views/dialogs/_InviteDialog.scss"; @import "./views/dialogs/_InviteDialog.scss";
@import "./views/dialogs/_KeyboardShortcutsDialog.scss";
@import "./views/dialogs/_MessageEditHistoryDialog.scss"; @import "./views/dialogs/_MessageEditHistoryDialog.scss";
@import "./views/dialogs/_NewSessionReviewDialog.scss"; @import "./views/dialogs/_NewSessionReviewDialog.scss";
@import "./views/dialogs/_RoomSettingsDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss";

View File

@ -23,7 +23,7 @@ limitations under the License.
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
height: 100%; min-height: 0;
} }
.mx_TagPanel_items_selected { .mx_TagPanel_items_selected {

View File

@ -0,0 +1,65 @@
/*
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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_KeyboardShortcutsDialog {
display: flex;
flex-wrap: wrap;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
flex-direction: column;
margin-bottom: -50px;
max-height: 700px; // XXX: this may need adjusting when adding new shortcuts
.mx_KeyboardShortcutsDialog_category {
width: 33.3333%; // 3 columns
margin: 0 0 40px;
& > div {
padding-left: 5px;
}
}
h3 {
margin: 0 0 10px;
}
h5 {
margin: 15px 0 5px;
font-weight: normal;
}
kbd {
padding: 5px;
border-radius: 4px;
background-color: $reaction-row-button-bg-color;
margin-right: 5px;
min-width: 20px;
text-align: center;
display: inline-block;
border: 1px solid $kbd-border-color;
box-shadow: 0 2px $kbd-border-color;
margin-bottom: 4px;
text-transform: capitalize;
& + kbd {
margin-left: 5px;
}
}
.mx_KeyboardShortcutsDialog_inline div {
display: inline;
}
}

View File

@ -165,6 +165,8 @@ $reaction-row-button-hover-border-color: $header-panel-text-primary-color;
$reaction-row-button-selected-bg-color: #1f6954; $reaction-row-button-selected-bg-color: #1f6954;
$reaction-row-button-selected-border-color: $accent-color; $reaction-row-button-selected-border-color: $accent-color;
$kbd-border-color: #000000;
$tooltip-timeline-bg-color: $tagpanel-bg-color; $tooltip-timeline-bg-color: $tagpanel-bg-color;
$tooltip-timeline-fg-color: #ffffff; $tooltip-timeline-fg-color: #ffffff;

View File

@ -290,6 +290,8 @@ $reaction-row-button-hover-border-color: $focus-bg-color;
$reaction-row-button-selected-bg-color: #e9fff9; $reaction-row-button-selected-bg-color: #e9fff9;
$reaction-row-button-selected-border-color: $accent-color; $reaction-row-button-selected-border-color: $accent-color;
$kbd-border-color: $reaction-row-button-border-color;
$tooltip-timeline-bg-color: $tagpanel-bg-color; $tooltip-timeline-bg-color: $tagpanel-bg-color;
$tooltip-timeline-fg-color: #ffffff; $tooltip-timeline-fg-color: #ffffff;

View File

@ -6,16 +6,8 @@
set -ev set -ev
upload_logs() {
echo "--- Uploading logs"
buildkite-agent artifact upload "logs/**/*;synapse/installations/consent/homeserver.log"
}
handle_error() { handle_error() {
EXIT_CODE=$? EXIT_CODE=$?
if [ $TESTS_STARTED -eq 1 ]; then
upload_logs
fi
exit $EXIT_CODE exit $EXIT_CODE
} }

View File

@ -40,6 +40,7 @@ export const Key = {
GREATER_THAN: ">", GREATER_THAN: ">",
BACKTICK: "`", BACKTICK: "`",
SPACE: " ", SPACE: " ",
SLASH: "/",
A: "a", A: "a",
B: "b", B: "b",
C: "c", C: "c",
@ -68,8 +69,9 @@ export const Key = {
Z: "z", Z: "z",
}; };
export const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
export function isOnlyCtrlOrCmdKeyEvent(ev) { export function isOnlyCtrlOrCmdKeyEvent(ev) {
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
if (isMac) { if (isMac) {
return ev.metaKey && !ev.altKey && !ev.ctrlKey && !ev.shiftKey; return ev.metaKey && !ev.altKey && !ev.ctrlKey && !ev.shiftKey;
} else { } else {
@ -78,7 +80,6 @@ export function isOnlyCtrlOrCmdKeyEvent(ev) {
} }
export function isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) { export function isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) {
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
if (isMac) { if (isMac) {
return ev.metaKey && !ev.altKey && !ev.ctrlKey; return ev.metaKey && !ev.altKey && !ev.ctrlKey;
} else { } else {

View File

@ -0,0 +1,315 @@
/*
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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as React from "react";
import classNames from "classnames";
import * as sdk from "../index";
import Modal from "../Modal";
import { _t, _td } from "../languageHandler";
import {isMac, Key} from "../Keyboard";
// 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",
CALLS = "Calls",
COMPOSER = "Composer",
ROOM_LIST = "Room List",
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",
SHIFT = "Shift",
SUPER = "Super", // should this be "Windows"?
// Instead of using below, consider CMD_OR_CTRL
COMMAND = "Command", // This gets displayed as an Icon
CONTROL = "Ctrl",
}
// Meta-modifier: isMac ? CMD : CONTROL
export const CMD_OR_CTRL = isMac ? Modifiers.COMMAND : Modifiers.CONTROL;
interface IKeybind {
modifiers?: Modifiers[];
key: string; // TS: fix this once Key is an enum
}
interface IShortcut {
keybinds: IKeybind[];
description: string;
}
const shortcuts: Record<Categories, IShortcut[]> = {
[Categories.COMPOSER]: [
{
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.B,
}],
description: _td("Toggle Bold"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.I,
}],
description: _td("Toggle Italics"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.GREATER_THAN,
}],
description: _td("Toggle Quote"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.M,
}],
description: _td("Toggle Markdown"),
}, {
keybinds: [{
modifiers: [Modifiers.SHIFT],
key: Key.ENTER,
}],
description: _td("New line"),
}, {
keybinds: [{
key: Key.ARROW_UP,
}, {
key: Key.ARROW_DOWN,
}],
description: _td("Navigate recent messages to edit"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.HOME,
}, {
modifiers: [CMD_OR_CTRL],
key: Key.END,
}],
description: _td("Jump to start/end of the composer"),
},
],
[Categories.CALLS]: [
{
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.D,
}],
description: _td("Toggle microphone mute"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.E,
}],
description: _td("Toggle video on/off"),
},
],
[Categories.ROOM_LIST]: [
{
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.K,
}],
description: _td("Jump to room search"),
}, {
keybinds: [{
key: Key.ARROW_UP,
}, {
key: Key.ARROW_DOWN,
}],
description: _td("Navigate up/down in the room list"),
}, {
keybinds: [{
key: Key.ENTER,
}],
description: _td("Select room from the room list"),
}, {
keybinds: [{
key: Key.ARROW_LEFT,
}],
description: _td("Collapse room list section"),
}, {
keybinds: [{
key: Key.ARROW_RIGHT,
}],
description: _td("Expand room list section"),
}, {
keybinds: [{
key: Key.ESCAPE,
}],
description: _td("Clear room list filter field"),
},
],
[Categories.NAVIGATION]: [
{
keybinds: [{
key: Key.PAGE_UP,
}, {
key: Key.PAGE_DOWN,
}],
description: _td("Scroll up/down in the timeline"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.BACKTICK,
}],
description: _td("Toggle the top left menu"),
}, {
keybinds: [{
key: Key.ESCAPE,
}],
description: _td("Close dialog or context menu"),
}, {
keybinds: [{
key: Key.ENTER,
}, {
key: Key.SPACE,
}],
description: _td("Activate selected button"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.SLASH,
}],
description: _td("Toggle this dialog"),
},
],
[Categories.AUTOCOMPLETE]: [
{
keybinds: [{
key: Key.ARROW_UP,
}, {
key: Key.ARROW_DOWN,
}],
description: _td("Move autocomplete selection up/down"),
}, {
keybinds: [{
key: Key.ESCAPE,
}],
description: _td("Cancel autocomplete"),
},
],
};
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"),
};
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 && 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 = Object.entries(shortcuts).map(([category, list]) => {
return <div className="mx_KeyboardShortcutsDialog_category" key={category}>
<h3>{_t(category)}</h3>
<div>{list.map(shortcut => <Shortcut key={shortcut.description} shortcut={shortcut} />)}</div>
</div>;
});
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
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;
},
});
};

View File

@ -39,6 +39,7 @@ import RoomListActions from '../../actions/RoomListActions';
import ResizeHandle from '../views/elements/ResizeHandle'; import ResizeHandle from '../views/elements/ResizeHandle';
import {Resizer, CollapseDistributor} from '../../resizer'; import {Resizer, CollapseDistributor} from '../../resizer';
import MatrixClientContext from "../../contexts/MatrixClientContext"; import MatrixClientContext from "../../contexts/MatrixClientContext";
import * as KeyboardShortcuts from "../../accessibility/KeyboardShortcuts";
// We need to fetch each pinned message individually (if we don't already have it) // We need to fetch each pinned message individually (if we don't already have it)
// so each pinned message may trigger a request. Limit the number per room for sanity. // so each pinned message may trigger a request. Limit the number per room for sanity.
// NB. this is just for server notices rather than pinned messages in general. // NB. this is just for server notices rather than pinned messages in general.
@ -365,8 +366,6 @@ const LoggedInView = createReactClass({
} }
break; break;
case Key.BACKTICK: case Key.BACKTICK:
if (ev.key !== "`") break;
// Ideally this would be CTRL+P for "Profile", but that's // Ideally this would be CTRL+P for "Profile", but that's
// taken by the print dialog. CTRL+I for "Information" // taken by the print dialog. CTRL+I for "Information"
// was previously chosen but conflicted with italics in // was previously chosen but conflicted with italics in
@ -379,6 +378,13 @@ const LoggedInView = createReactClass({
handled = true; handled = true;
} }
break; break;
case Key.SLASH:
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
KeyboardShortcuts.toggleDialog();
handled = true;
}
break;
} }
if (handled) { if (handled) {

View File

@ -32,6 +32,7 @@ export default createReactClass({
button: PropTypes.string, button: PropTypes.string,
onFinished: PropTypes.func, onFinished: PropTypes.func,
hasCloseButton: PropTypes.bool, hasCloseButton: PropTypes.bool,
onKeyDown: PropTypes.func,
}, },
getDefaultProps: function() { getDefaultProps: function() {
@ -50,10 +51,13 @@ export default createReactClass({
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return ( return (
<BaseDialog className="mx_InfoDialog" onFinished={this.props.onFinished} <BaseDialog
className="mx_InfoDialog"
onFinished={this.props.onFinished}
title={this.props.title} title={this.props.title}
contentId='mx_Dialog_content' contentId='mx_Dialog_content'
hasCancel={this.props.hasCloseButton} hasCancel={this.props.hasCloseButton}
onKeyDown={this.props.onKeyDown}
> >
<div className={classNames("mx_Dialog_content", this.props.className)} id="mx_Dialog_content"> <div className={classNames("mx_Dialog_content", this.props.className)} id="mx_Dialog_content">
{ this.props.description } { this.props.description }

View File

@ -24,6 +24,7 @@ import createRoom from "../../../../../createRoom";
import Modal from "../../../../../Modal"; import Modal from "../../../../../Modal";
import * as sdk from "../../../../../"; import * as sdk from "../../../../../";
import PlatformPeg from "../../../../../PlatformPeg"; import PlatformPeg from "../../../../../PlatformPeg";
import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts";
export default class HelpUserSettingsTab extends React.Component { export default class HelpUserSettingsTab extends React.Component {
static propTypes = { static propTypes = {
@ -224,6 +225,9 @@ export default class HelpUserSettingsTab extends React.Component {
<div className='mx_SettingsTab_subsectionText'> <div className='mx_SettingsTab_subsectionText'>
{faqText} {faqText}
</div> </div>
<AccessibleButton kind="primary" onClick={KeyboardShortcuts.toggleDialog}>
{ _t("Keyboard Shortcuts") }
</AccessibleButton>
</div> </div>
<div className='mx_SettingsTab_section mx_HelpUserSettingsTab_versions'> <div className='mx_SettingsTab_section mx_HelpUserSettingsTab_versions'>
<span className='mx_SettingsTab_subheading'>{_t("Versions")}</span> <span className='mx_SettingsTab_subheading'>{_t("Versions")}</span>

View File

@ -742,6 +742,7 @@
"Clear cache and reload": "Clear cache and reload", "Clear cache and reload": "Clear cache and reload",
"To report a Matrix-related security issue, please read the Matrix.org <a>Security Disclosure Policy</a>.": "To report a Matrix-related security issue, please read the Matrix.org <a>Security Disclosure Policy</a>.", "To report a Matrix-related security issue, please read the Matrix.org <a>Security Disclosure Policy</a>.": "To report a Matrix-related security issue, please read the Matrix.org <a>Security Disclosure Policy</a>.",
"FAQ": "FAQ", "FAQ": "FAQ",
"Keyboard Shortcuts": "Keyboard Shortcuts",
"Versions": "Versions", "Versions": "Versions",
"riot-web version:": "riot-web version:", "riot-web version:": "riot-web version:",
"olm version:": "olm version:", "olm version:": "olm version:",
@ -2169,5 +2170,42 @@
"Message downloading sleep time(ms)": "Message downloading sleep time(ms)", "Message downloading sleep time(ms)": "Message downloading sleep time(ms)",
"Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to set direct chat tag": "Failed to set direct chat tag",
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room",
"Navigation": "Navigation",
"Calls": "Calls",
"Room List": "Room List",
"Autocomplete": "Autocomplete",
"Alt": "Alt",
"Alt Gr": "Alt Gr",
"Shift": "Shift",
"Super": "Super",
"Ctrl": "Ctrl",
"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",
"Toggle microphone mute": "Toggle microphone mute",
"Toggle video on/off": "Toggle video on/off",
"Jump to room search": "Jump to room search",
"Navigate up/down in the room list": "Navigate up/down in the room list",
"Select room from the room list": "Select room from the room list",
"Collapse room list section": "Collapse room list section",
"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",
"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",
"Toggle this dialog": "Toggle this dialog",
"Move autocomplete selection up/down": "Move autocomplete selection up/down",
"Cancel autocomplete": "Cancel autocomplete",
"Page Up": "Page Up",
"Page Down": "Page Down",
"Esc": "Esc",
"Enter": "Enter",
"Space": "Space",
"End": "End"
} }

View File

@ -1178,6 +1178,11 @@
dependencies: dependencies:
"@babel/types" "^7.3.0" "@babel/types" "^7.3.0"
"@types/classnames@^2.2.10":
version "2.2.10"
resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.10.tgz#cc658ca319b6355399efc1f5b9e818f1a24bf999"
integrity sha512-1UzDldn9GfYYEsWWnn/P4wkTlkZDH7lDb0wBMGbtIQc9zXEQq7FlKBdZUn6OBqD8sKZZ2RQO2mAjGpXiDGoRmQ==
"@types/events@*": "@types/events@*":
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"