Break 'Cryptography' settings into a separate component

This commit is contained in:
Andy Balaam 2021-10-15 15:06:55 +01:00
parent af55ac7b8c
commit a547ee4654
7 changed files with 174 additions and 97 deletions

View file

@ -244,6 +244,7 @@
@import "./views/rooms/_WhoIsTypingTile.scss";
@import "./views/settings/_AvatarSetting.scss";
@import "./views/settings/_CrossSigningPanel.scss";
@import "./views/settings/_CryptographyPanel.scss";
@import "./views/settings/_DevicesPanel.scss";
@import "./views/settings/_E2eAdvancedPanel.scss";
@import "./views/settings/_EmailAddresses.scss";

View file

@ -0,0 +1,22 @@
.mx_CryptographyPanel {
display: table;
padding-left: 0;
}
.mx_CryptographyPanel > li {
display: table-row;
}
.mx_CryptographyPanel > li > label,
.mx_CryptographyPanel > li > span {
display: table-cell;
padding-right: 1em;
}
.mx_CryptographyPanel_importExportButtons .mx_AccessibleButton {
margin-right: 10px;
}
.mx_CryptographyPanel_importExportButtons {
margin-bottom: 15px;
}

View file

@ -14,33 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_SecurityUserSettingsTab_deviceInfo {
display: table;
padding-left: 0;
}
.mx_SecurityUserSettingsTab_deviceInfo > li {
display: table-row;
}
.mx_SecurityUserSettingsTab_deviceInfo > li > label,
.mx_SecurityUserSettingsTab_deviceInfo > li > span {
display: table-cell;
padding-right: 1em;
}
.mx_SecurityUserSettingsTab_importExportButtons .mx_AccessibleButton {
margin-right: 10px;
}
.mx_SecurityUserSettingsTab_bulkOptions .mx_AccessibleButton {
margin-right: 10px;
}
.mx_SecurityUserSettingsTab_importExportButtons {
margin-bottom: 15px;
}
.mx_SecurityUserSettingsTab_ignoredUser {
margin-bottom: 5px;
}

View file

@ -0,0 +1,110 @@
/*
Copyright 2021 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 React from 'react';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import { _t } from '../../../languageHandler';
import Modal from '../../../Modal';
import { replaceableComponent } from "../../../utils/replaceableComponent";
import AccessibleButton from "../elements/AccessibleButton";
import * as FormattingUtils from "../../../utils/FormattingUtils";
import SettingsStore from "../../../settings/SettingsStore";
import SettingsFlag from "../elements/SettingsFlag";
import { SettingLevel } from "../../../settings/SettingLevel";
interface IProps {
}
interface IState {
}
@replaceableComponent("views.settings.CryptographyPanel")
export default class CryptographyPanel extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
}
public render(): JSX.Element {
const client = MatrixClientPeg.get();
const deviceId = client.deviceId;
let identityKey = client.getDeviceEd25519Key();
if (!identityKey) {
identityKey = _t("<not supported>");
} else {
identityKey = FormattingUtils.formatCryptoKey(identityKey);
}
let importExportButtons = null;
if (client.isCryptoEnabled()) {
importExportButtons = (
<div className='mx_CryptographyPanel_importExportButtons'>
<AccessibleButton kind='primary' onClick={this.onExportE2eKeysClicked}>
{ _t("Export E2E room keys") }
</AccessibleButton>
<AccessibleButton kind='primary' onClick={this.onImportE2eKeysClicked}>
{ _t("Import E2E room keys") }
</AccessibleButton>
</div>
);
}
let noSendUnverifiedSetting;
if (SettingsStore.isEnabled("blacklistUnverifiedDevices")) {
noSendUnverifiedSetting = <SettingsFlag
name='blacklistUnverifiedDevices'
level={SettingLevel.DEVICE}
onChange={this.updateBlacklistDevicesFlag}
/>;
}
return (
<div className='mx_SettingsTab_section'>
<span className='mx_SettingsTab_subheading'>{ _t("Cryptography") }</span>
<ul className='mx_SettingsTab_subsectionText mx_CryptographyPanel'>
<li>
<label>{ _t("Session ID:") }</label>
<span><code>{ deviceId }</code></span>
</li>
<li>
<label>{ _t("Session key:") }</label>
<span><code><b>{ identityKey }</b></code></span>
</li>
</ul>
{ importExportButtons }
{ noSendUnverifiedSetting }
</div>
);
}
private onExportE2eKeysClicked = (): void => {
Modal.createTrackedDialogAsync('Export E2E Keys', '',
import('../../../async-components/views/dialogs/security/ExportE2eKeysDialog'),
{ matrixClient: MatrixClientPeg.get() },
);
};
private onImportE2eKeysClicked = (): void => {
Modal.createTrackedDialogAsync('Import E2E Keys', '',
import('../../../async-components/views/dialogs/security/ImportE2eKeysDialog'),
{ matrixClient: MatrixClientPeg.get() },
);
};
private updateBlacklistDevicesFlag = (checked): void => {
MatrixClientPeg.get().setGlobalBlacklistUnverifiedDevices(checked);
};
}

View file

@ -21,10 +21,8 @@ import { sleep } from "matrix-js-sdk/src/utils";
import { _t } from "../../../../../languageHandler";
import SdkConfig from "../../../../../SdkConfig";
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
import * as FormattingUtils from "../../../../../utils/FormattingUtils";
import AccessibleButton from "../../../elements/AccessibleButton";
import Analytics from "../../../../../Analytics";
import Modal from "../../../../../Modal";
import dis from "../../../../../dispatcher/dispatcher";
import { privateShouldBeEncrypted } from "../../../../../createRoom";
import { SettingLevel } from "../../../../../settings/SettingLevel";
@ -37,6 +35,7 @@ import { replaceableComponent } from "../../../../../utils/replaceableComponent"
import { PosthogAnalytics } from "../../../../../PosthogAnalytics";
import { ActionPayload } from "../../../../../dispatcher/payloads";
import { Room } from "matrix-js-sdk/src/models/room";
import CryptographyPanel from "../../CryptographyPanel";
import DevicesPanel from "../../DevicesPanel";
import SettingsFlag from "../../../elements/SettingsFlag";
import CrossSigningPanel from "../../CrossSigningPanel";
@ -112,30 +111,12 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
dis.unregister(this.dispatcherRef);
}
private updateBlacklistDevicesFlag = (checked): void => {
MatrixClientPeg.get().setGlobalBlacklistUnverifiedDevices(checked);
};
private updateAnalytics = (checked: boolean): void => {
checked ? Analytics.enable() : Analytics.disable();
CountlyAnalytics.instance.enable(/* anonymous = */ !checked);
PosthogAnalytics.instance.updateAnonymityFromSettings(MatrixClientPeg.get().getUserId());
};
private onExportE2eKeysClicked = (): void => {
Modal.createTrackedDialogAsync('Export E2E Keys', '',
import('../../../../../async-components/views/dialogs/security/ExportE2eKeysDialog'),
{ matrixClient: MatrixClientPeg.get() },
);
};
private onImportE2eKeysClicked = (): void => {
Modal.createTrackedDialogAsync('Import E2E Keys', '',
import('../../../../../async-components/views/dialogs/security/ImportE2eKeysDialog'),
{ matrixClient: MatrixClientPeg.get() },
);
};
private onGoToUserProfileClick = (): void => {
dis.dispatch({
action: 'view_user_info',
@ -211,58 +192,6 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
this.manageInvites(false);
};
private renderCurrentDeviceInfo(): JSX.Element {
const client = MatrixClientPeg.get();
const deviceId = client.deviceId;
let identityKey = client.getDeviceEd25519Key();
if (!identityKey) {
identityKey = _t("<not supported>");
} else {
identityKey = FormattingUtils.formatCryptoKey(identityKey);
}
let importExportButtons = null;
if (client.isCryptoEnabled()) {
importExportButtons = (
<div className='mx_SecurityUserSettingsTab_importExportButtons'>
<AccessibleButton kind='primary' onClick={this.onExportE2eKeysClicked}>
{ _t("Export E2E room keys") }
</AccessibleButton>
<AccessibleButton kind='primary' onClick={this.onImportE2eKeysClicked}>
{ _t("Import E2E room keys") }
</AccessibleButton>
</div>
);
}
let noSendUnverifiedSetting;
if (SettingsStore.isEnabled("blacklistUnverifiedDevices")) {
noSendUnverifiedSetting = <SettingsFlag
name='blacklistUnverifiedDevices'
level={SettingLevel.DEVICE}
onChange={this.updateBlacklistDevicesFlag}
/>;
}
return (
<div className='mx_SettingsTab_section'>
<span className='mx_SettingsTab_subheading'>{ _t("Cryptography") }</span>
<ul className='mx_SettingsTab_subsectionText mx_SecurityUserSettingsTab_deviceInfo'>
<li>
<label>{ _t("Session ID:") }</label>
<span><code>{ deviceId }</code></span>
</li>
<li>
<label>{ _t("Session key:") }</label>
<span><code><b>{ identityKey }</b></code></span>
</li>
</ul>
{ importExportButtons }
{ noSendUnverifiedSetting }
</div>
);
}
private renderIgnoredUsers(): JSX.Element {
const { waitingUnignored, ignoredUserIds } = this.state;
@ -418,7 +347,7 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
{ secureBackup }
{ eventIndex }
{ crossSigning }
{ this.renderCurrentDeviceInfo() }
<CryptographyPanel />
</div>
{ privacySection }
{ advancedSection }

View file

@ -0,0 +1,38 @@
import '../../../skinned-sdk';
import * as TestUtils from '../../../test-utils';
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
import React, { ReactElement } from 'react';
import ReactDOM from 'react-dom';
import { MatrixClient } from 'matrix-js-sdk';
import CryptographyPanel from '../../../../src/components/views/settings/CryptographyPanel';
describe('CryptographyPanel', () => {
it('shows the session ID and key', () => {
const sessionId = "ABCDEFGHIJ";
const sessionKey = "AbCDeFghIJK7L/m4nOPqRSTUVW4xyzaBCDef6gHIJkl";
const sessionKeyFormatted = "<b>AbCD eFgh IJK7 L/m4 nOPq RSTU VW4x yzaB CDef 6gHI Jkl</b>";
TestUtils.stubClient();
const client: MatrixClient = MatrixClientPeg.get();
client.deviceId = sessionId;
client.getDeviceEd25519Key = () => sessionKey;
// When we render the CryptographyPanel
const rendered = render(<CryptographyPanel />);
// Then it displays info about the user's session
const codes = rendered.querySelectorAll("code");
expect(codes.length).toEqual(2);
expect(codes[0].innerHTML).toEqual(sessionId);
expect(codes[1].innerHTML).toEqual(sessionKeyFormatted);
});
});
function render(component: ReactElement<CryptographyPanel>): HTMLDivElement {
const parentDiv = document.createElement('div');
document.body.appendChild(parentDiv);
ReactDOM.render(component, parentDiv);
return parentDiv;
}

View file

@ -44,7 +44,7 @@ module.exports.enableLazyLoading = async function(session) {
module.exports.getE2EDeviceFromSettings = async function(session) {
session.log.step(`gets e2e device/key from settings`);
await openSettings(session, "security");
const deviceAndKey = await session.queryAll(".mx_SettingsTab_section .mx_SecurityUserSettingsTab_deviceInfo code");
const deviceAndKey = await session.queryAll(".mx_SettingsTab_section .mx_CryptographyPanel code");
assert.equal(deviceAndKey.length, 2);
const id = await (await deviceAndKey[0].getProperty("innerText")).jsonValue();
const key = await (await deviceAndKey[1].getProperty("innerText")).jsonValue();