Merge branches 'develop' and 't3chguy/e2eedefault' of github.com:matrix-org/matrix-react-sdk into t3chguy/e2eedefault
Conflicts: src/components/views/dialogs/CreateRoomDialog.js src/components/views/dialogs/InviteDialog.js src/components/views/right_panel/UserInfo.js src/createRoom.js
This commit is contained in:
commit
eb536ff2f7
41 changed files with 242 additions and 2951 deletions
|
@ -1514,13 +1514,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
});
|
||||
|
||||
cli.on("crypto.verification.request", request => {
|
||||
const isFlagOn = SettingsStore.getValue("feature_cross_signing");
|
||||
|
||||
if (!isFlagOn && !request.channel.deviceId) {
|
||||
request.cancel({code: "m.invalid_message", reason: "This client has cross-signing disabled"});
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.verifier) {
|
||||
const IncomingSasDialog = sdk.getComponent("views.dialogs.IncomingSasDialog");
|
||||
Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, {
|
||||
|
@ -1563,9 +1556,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
// be aware of will be signalled through the room shield
|
||||
// changing colour. More advanced behaviour will come once
|
||||
// we implement more settings.
|
||||
cli.setGlobalErrorOnUnknownDevices(
|
||||
!SettingsStore.getValue("feature_cross_signing"),
|
||||
);
|
||||
cli.setGlobalErrorOnUnknownDevices(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1909,21 +1900,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
return setLoggedInPromise;
|
||||
}
|
||||
|
||||
// Test for the master cross-signing key in SSSS as a quick proxy for
|
||||
// whether cross-signing has been set up on the account.
|
||||
const masterKeyInStorage = !!cli.getAccountData("m.cross_signing.master");
|
||||
if (masterKeyInStorage) {
|
||||
// Auto-enable cross-signing for the new session when key found in
|
||||
// secret storage.
|
||||
SettingsStore.setValue("feature_cross_signing", null, SettingLevel.DEVICE, true);
|
||||
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
|
||||
} else if (
|
||||
SettingsStore.getValue("feature_cross_signing") &&
|
||||
await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")
|
||||
) {
|
||||
// This will only work if the feature is set to 'enable' in the config,
|
||||
// since it's too early in the lifecycle for users to have turned the
|
||||
// labs flag on.
|
||||
if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
|
||||
this.setStateForNewView({ view: Views.E2E_SETUP });
|
||||
} else {
|
||||
this.onLoggedIn();
|
||||
|
|
|
@ -26,7 +26,6 @@ import dis from '../../dispatcher/dispatcher';
|
|||
import RateLimitedFunc from '../../ratelimitedfunc';
|
||||
import { showGroupInviteDialog, showGroupAddRoomDialog } from '../../GroupAddressPicker';
|
||||
import GroupStore from '../../stores/GroupStore';
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "../../stores/RightPanelStorePhases";
|
||||
import RightPanelStore from "../../stores/RightPanelStore";
|
||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||
|
@ -189,16 +188,37 @@ export default class RightPanel extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
onCloseUserInfo = () => {
|
||||
// XXX: There are three different ways of 'closing' this panel depending on what state
|
||||
// things are in... this knows far more than it should do about the state of the rest
|
||||
// of the app and is generally a bit silly.
|
||||
if (this.props.user) {
|
||||
// If we have a user prop then we're displaying a user from the 'user' page type
|
||||
// in LoggedInView, so need to change the page type to close the panel (we switch
|
||||
// to the home page which is not obviously the correct thing to do, but I'm not sure
|
||||
// anything else is - we could hide the close button altogether?)
|
||||
dis.dispatch({
|
||||
action: "view_home_page",
|
||||
});
|
||||
} else {
|
||||
// Otherwise we have got our user from RoomViewStore which means we're being shown
|
||||
// within a room/group, so go back to the member panel if we were in the encryption panel,
|
||||
// or the member list if we were in the member panel... phew.
|
||||
dis.dispatch({
|
||||
action: Action.ViewUser,
|
||||
member: this.state.phase === RIGHT_PANEL_PHASES.EncryptionPanel ? this.state.member : null,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const MemberList = sdk.getComponent('rooms.MemberList');
|
||||
const MemberInfo = sdk.getComponent('rooms.MemberInfo');
|
||||
const UserInfo = sdk.getComponent('right_panel.UserInfo');
|
||||
const ThirdPartyMemberInfo = sdk.getComponent('rooms.ThirdPartyMemberInfo');
|
||||
const NotificationPanel = sdk.getComponent('structures.NotificationPanel');
|
||||
const FilePanel = sdk.getComponent('structures.FilePanel');
|
||||
|
||||
const GroupMemberList = sdk.getComponent('groups.GroupMemberList');
|
||||
const GroupMemberInfo = sdk.getComponent('groups.GroupMemberInfo');
|
||||
const GroupRoomList = sdk.getComponent('groups.GroupRoomList');
|
||||
const GroupRoomInfo = sdk.getComponent('groups.GroupRoomInfo');
|
||||
|
||||
|
@ -220,71 +240,25 @@ export default class RightPanel extends React.Component {
|
|||
break;
|
||||
case RIGHT_PANEL_PHASES.RoomMemberInfo:
|
||||
case RIGHT_PANEL_PHASES.EncryptionPanel:
|
||||
if (SettingsStore.getValue("feature_cross_signing")) {
|
||||
const onClose = () => {
|
||||
// XXX: There are three different ways of 'closing' this panel depending on what state
|
||||
// things are in... this knows far more than it should do about the state of the rest
|
||||
// of the app and is generally a bit silly.
|
||||
if (this.props.user) {
|
||||
// If we have a user prop then we're displaying a user from the 'user' page type
|
||||
// in LoggedInView, so need to change the page type to close the panel (we switch
|
||||
// to the home page which is not obviously the correct thing to do, but I'm not sure
|
||||
// anything else is - we could hide the close button altogether?)
|
||||
dis.dispatch({
|
||||
action: "view_home_page",
|
||||
});
|
||||
} else {
|
||||
// Otherwise we have got our user from RoomViewStore which means we're being shown
|
||||
// within a room, so go back to the member panel if we were in the encryption panel,
|
||||
// or the member list if we were in the member panel... phew.
|
||||
dis.dispatch({
|
||||
action: Action.ViewUser,
|
||||
member: this.state.phase === RIGHT_PANEL_PHASES.EncryptionPanel ?
|
||||
this.state.member : null,
|
||||
});
|
||||
}
|
||||
};
|
||||
panel = <UserInfo
|
||||
user={this.state.member}
|
||||
roomId={this.props.roomId}
|
||||
key={this.props.roomId || this.state.member.userId}
|
||||
onClose={onClose}
|
||||
phase={this.state.phase}
|
||||
verificationRequest={this.state.verificationRequest}
|
||||
verificationRequestPromise={this.state.verificationRequestPromise}
|
||||
/>;
|
||||
} else {
|
||||
panel = <MemberInfo
|
||||
member={this.state.member}
|
||||
key={this.props.roomId || this.state.member.userId}
|
||||
/>;
|
||||
}
|
||||
panel = <UserInfo
|
||||
user={this.state.member}
|
||||
roomId={this.props.roomId}
|
||||
key={this.props.roomId || this.state.member.userId}
|
||||
onClose={this.onCloseUserInfo}
|
||||
phase={this.state.phase}
|
||||
verificationRequest={this.state.verificationRequest}
|
||||
verificationRequestPromise={this.state.verificationRequestPromise}
|
||||
/>;
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.Room3pidMemberInfo:
|
||||
panel = <ThirdPartyMemberInfo event={this.state.event} key={this.props.roomId} />;
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.GroupMemberInfo:
|
||||
if (SettingsStore.getValue("feature_cross_signing")) {
|
||||
const onClose = () => {
|
||||
dis.dispatch({
|
||||
action: Action.ViewUser,
|
||||
member: null,
|
||||
});
|
||||
};
|
||||
panel = <UserInfo
|
||||
user={this.state.member}
|
||||
groupId={this.props.groupId}
|
||||
key={this.state.member.userId}
|
||||
onClose={onClose} />;
|
||||
} else {
|
||||
panel = (
|
||||
<GroupMemberInfo
|
||||
groupMember={this.state.member}
|
||||
groupId={this.props.groupId}
|
||||
key={this.state.member.user_id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
panel = <UserInfo
|
||||
user={this.state.member}
|
||||
groupId={this.props.groupId}
|
||||
key={this.state.member.userId}
|
||||
onClose={this.onCloseUserInfo} />;
|
||||
break;
|
||||
case RIGHT_PANEL_PHASES.GroupRoomInfo:
|
||||
panel = <GroupRoomInfo
|
||||
|
|
|
@ -24,7 +24,6 @@ import { _t, _td } from '../../languageHandler';
|
|||
import * as sdk from '../../index';
|
||||
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
||||
import Resend from '../../Resend';
|
||||
import * as cryptodevices from '../../cryptodevices';
|
||||
import dis from '../../dispatcher/dispatcher';
|
||||
import {messageForResourceLimitError, messageForSendError} from '../../utils/ErrorUtils';
|
||||
|
||||
|
@ -126,13 +125,6 @@ export default createReactClass({
|
|||
});
|
||||
},
|
||||
|
||||
_onSendWithoutVerifyingClick: function() {
|
||||
cryptodevices.getUnknownDevicesForRoom(MatrixClientPeg.get(), this.props.room).then((devices) => {
|
||||
cryptodevices.markAllDevicesKnown(MatrixClientPeg.get(), devices);
|
||||
Resend.resendUnsentEvents(this.props.room);
|
||||
});
|
||||
},
|
||||
|
||||
_onResendAllClick: function() {
|
||||
Resend.resendUnsentEvents(this.props.room);
|
||||
dis.dispatch({action: 'focus_composer'});
|
||||
|
@ -143,10 +135,6 @@ export default createReactClass({
|
|||
dis.dispatch({action: 'focus_composer'});
|
||||
},
|
||||
|
||||
_onShowDevicesClick: function() {
|
||||
cryptodevices.showUnknownDeviceDialogForMessages(MatrixClientPeg.get(), this.props.room);
|
||||
},
|
||||
|
||||
_onRoomLocalEchoUpdated: function(event, room, oldEventId, oldStatus) {
|
||||
if (room.roomId !== this.props.room.roomId) return;
|
||||
|
||||
|
@ -213,82 +201,65 @@ export default createReactClass({
|
|||
if (!unsentMessages.length) return null;
|
||||
|
||||
let title;
|
||||
let content;
|
||||
|
||||
const hasUDE = unsentMessages.some((m) => {
|
||||
return m.error && m.error.name === "UnknownDeviceError";
|
||||
});
|
||||
|
||||
if (hasUDE) {
|
||||
title = _t("Message not sent due to unknown sessions being present");
|
||||
content = _t(
|
||||
"<showSessionsText>Show sessions</showSessionsText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>.",
|
||||
let consentError = null;
|
||||
let resourceLimitError = null;
|
||||
for (const m of unsentMessages) {
|
||||
if (m.error && m.error.errcode === 'M_CONSENT_NOT_GIVEN') {
|
||||
consentError = m.error;
|
||||
break;
|
||||
} else if (m.error && m.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') {
|
||||
resourceLimitError = m.error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (consentError) {
|
||||
title = _t(
|
||||
"You can't send any messages until you review and agree to " +
|
||||
"<consentLink>our terms and conditions</consentLink>.",
|
||||
{},
|
||||
{
|
||||
'showSessionsText': (sub) => <a className="mx_RoomStatusBar_resend_link" key="resend" onClick={this._onShowDevicesClick}>{ sub }</a>,
|
||||
'sendAnywayText': (sub) => <a className="mx_RoomStatusBar_resend_link" key="sendAnyway" onClick={this._onSendWithoutVerifyingClick}>{ sub }</a>,
|
||||
'cancelText': (sub) => <a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={this._onCancelAllClick}>{ sub }</a>,
|
||||
'consentLink': (sub) =>
|
||||
<a href={consentError.data && consentError.data.consent_uri} target="_blank">
|
||||
{ sub }
|
||||
</a>,
|
||||
},
|
||||
);
|
||||
} else if (resourceLimitError) {
|
||||
title = messageForResourceLimitError(
|
||||
resourceLimitError.data.limit_type,
|
||||
resourceLimitError.data.admin_contact, {
|
||||
'monthly_active_user': _td(
|
||||
"Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. " +
|
||||
"Please <a>contact your service administrator</a> to continue using the service.",
|
||||
),
|
||||
'': _td(
|
||||
"Your message wasn't sent because this homeserver has exceeded a resource limit. " +
|
||||
"Please <a>contact your service administrator</a> to continue using the service.",
|
||||
),
|
||||
});
|
||||
} else if (
|
||||
unsentMessages.length === 1 &&
|
||||
unsentMessages[0].error &&
|
||||
unsentMessages[0].error.data &&
|
||||
unsentMessages[0].error.data.error
|
||||
) {
|
||||
title = messageForSendError(unsentMessages[0].error.data) || unsentMessages[0].error.data.error;
|
||||
} else {
|
||||
let consentError = null;
|
||||
let resourceLimitError = null;
|
||||
for (const m of unsentMessages) {
|
||||
if (m.error && m.error.errcode === 'M_CONSENT_NOT_GIVEN') {
|
||||
consentError = m.error;
|
||||
break;
|
||||
} else if (m.error && m.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') {
|
||||
resourceLimitError = m.error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (consentError) {
|
||||
title = _t(
|
||||
"You can't send any messages until you review and agree to " +
|
||||
"<consentLink>our terms and conditions</consentLink>.",
|
||||
{},
|
||||
{
|
||||
'consentLink': (sub) =>
|
||||
<a href={consentError.data && consentError.data.consent_uri} target="_blank">
|
||||
{ sub }
|
||||
</a>,
|
||||
},
|
||||
);
|
||||
} else if (resourceLimitError) {
|
||||
title = messageForResourceLimitError(
|
||||
resourceLimitError.data.limit_type,
|
||||
resourceLimitError.data.admin_contact, {
|
||||
'monthly_active_user': _td(
|
||||
"Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. " +
|
||||
"Please <a>contact your service administrator</a> to continue using the service.",
|
||||
),
|
||||
'': _td(
|
||||
"Your message wasn't sent because this homeserver has exceeded a resource limit. " +
|
||||
"Please <a>contact your service administrator</a> to continue using the service.",
|
||||
),
|
||||
});
|
||||
} else if (
|
||||
unsentMessages.length === 1 &&
|
||||
unsentMessages[0].error &&
|
||||
unsentMessages[0].error.data &&
|
||||
unsentMessages[0].error.data.error
|
||||
) {
|
||||
title = messageForSendError(unsentMessages[0].error.data) || unsentMessages[0].error.data.error;
|
||||
} else {
|
||||
title = _t('%(count)s of your messages have not been sent.', { count: unsentMessages.length });
|
||||
}
|
||||
content = _t("%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. " +
|
||||
"You can also select individual messages to resend or cancel.",
|
||||
{ count: unsentMessages.length },
|
||||
{
|
||||
'resendText': (sub) =>
|
||||
<a className="mx_RoomStatusBar_resend_link" key="resend" onClick={this._onResendAllClick}>{ sub }</a>,
|
||||
'cancelText': (sub) =>
|
||||
<a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={this._onCancelAllClick}>{ sub }</a>,
|
||||
},
|
||||
);
|
||||
title = _t('%(count)s of your messages have not been sent.', { count: unsentMessages.length });
|
||||
}
|
||||
|
||||
const content = _t("%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> " +
|
||||
"now. You can also select individual messages to resend or cancel.",
|
||||
{ count: unsentMessages.length },
|
||||
{
|
||||
'resendText': (sub) =>
|
||||
<a className="mx_RoomStatusBar_resend_link" key="resend" onClick={this._onResendAllClick}>{ sub }</a>,
|
||||
'cancelText': (sub) =>
|
||||
<a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={this._onCancelAllClick}>{ sub }</a>,
|
||||
},
|
||||
);
|
||||
|
||||
return <div className="mx_RoomStatusBar_connectionLostBar">
|
||||
<img src={require("../../../res/img/feather-customised/warning-triangle.svg")} width="24" height="24" title={_t("Warning")} alt="" />
|
||||
<div>
|
||||
|
|
|
@ -879,15 +879,6 @@ export default createReactClass({
|
|||
});
|
||||
return;
|
||||
}
|
||||
if (!SettingsStore.getValue("feature_cross_signing")) {
|
||||
room.hasUnverifiedDevices().then((hasUnverifiedDevices) => {
|
||||
this.setState({
|
||||
e2eStatus: hasUnverifiedDevices ? "warning" : "verified",
|
||||
});
|
||||
});
|
||||
debuglog("e2e check is warning/verified only as cross-signing is off");
|
||||
return;
|
||||
}
|
||||
|
||||
/* At this point, the user has encryption on and cross-signing on */
|
||||
this.setState({
|
||||
|
|
|
@ -24,7 +24,6 @@ import withValidation from '../elements/Validation';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import {Key} from "../../../Keyboard";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {privateShouldBeEncrypted} from "../../../createRoom";
|
||||
|
||||
export default createReactClass({
|
||||
|
@ -67,7 +66,7 @@ export default createReactClass({
|
|||
createOpts.creation_content = {'m.federate': false};
|
||||
}
|
||||
|
||||
if (!this.state.isPublic && SettingsStore.getValue("feature_cross_signing")) {
|
||||
if (!this.state.isPublic) {
|
||||
opts.encryption = this.state.isEncrypted;
|
||||
}
|
||||
|
||||
|
@ -194,7 +193,7 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
let e2eeSection;
|
||||
if (!this.state.isPublic && SettingsStore.getValue("feature_cross_signing")) {
|
||||
if (!this.state.isPublic) {
|
||||
let microcopy;
|
||||
if (privateShouldBeEncrypted()) {
|
||||
microcopy = _t("You can’t disable this later. Bridges & most bots won’t work yet.");
|
||||
|
@ -202,7 +201,6 @@ export default createReactClass({
|
|||
microcopy = _t("Your server admin has disabled end-to-end encryption by default " +
|
||||
"in private rooms & Direct Messages.");
|
||||
}
|
||||
|
||||
e2eeSection = <React.Fragment>
|
||||
<LabelledToggleSwitch
|
||||
label={ _t("Enable end-to-end encryption")}
|
||||
|
|
|
@ -1,377 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2019 New Vector Ltd
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
|
||||
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 PropTypes from 'prop-types';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import * as sdk from '../../../index';
|
||||
import * as FormattingUtils from '../../../utils/FormattingUtils';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
|
||||
import {ensureDMExists} from "../../../createRoom";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import SettingsStore from '../../../settings/SettingsStore';
|
||||
import {SHOW_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
|
||||
import VerificationQREmojiOptions from "../verification/VerificationQREmojiOptions";
|
||||
|
||||
const MODE_LEGACY = 'legacy';
|
||||
const MODE_SAS = 'sas';
|
||||
|
||||
const PHASE_START = 0;
|
||||
const PHASE_WAIT_FOR_PARTNER_TO_ACCEPT = 1;
|
||||
const PHASE_PICK_VERIFICATION_OPTION = 2;
|
||||
const PHASE_SHOW_SAS = 3;
|
||||
const PHASE_WAIT_FOR_PARTNER_TO_CONFIRM = 4;
|
||||
const PHASE_VERIFIED = 5;
|
||||
const PHASE_CANCELLED = 6;
|
||||
|
||||
export default class DeviceVerifyDialog extends React.Component {
|
||||
static propTypes = {
|
||||
userId: PropTypes.string.isRequired,
|
||||
device: PropTypes.object.isRequired,
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._verifier = null;
|
||||
this._showSasEvent = null;
|
||||
this._request = null;
|
||||
this.state = {
|
||||
phase: PHASE_START,
|
||||
mode: MODE_SAS,
|
||||
sasVerified: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._verifier) {
|
||||
this._verifier.removeListener('show_sas', this._onVerifierShowSas);
|
||||
this._verifier.cancel('User cancel');
|
||||
}
|
||||
}
|
||||
|
||||
_onSwitchToLegacyClick = () => {
|
||||
if (this._verifier) {
|
||||
this._verifier.removeListener('show_sas', this._onVerifierShowSas);
|
||||
this._verifier.cancel('User cancel');
|
||||
this._verifier = null;
|
||||
}
|
||||
this.setState({mode: MODE_LEGACY});
|
||||
}
|
||||
|
||||
_onSwitchToSasClick = () => {
|
||||
this.setState({mode: MODE_SAS});
|
||||
}
|
||||
|
||||
_onCancelClick = () => {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
|
||||
_onUseSasClick = async () => {
|
||||
try {
|
||||
this._verifier = this._request.beginKeyVerification(verificationMethods.SAS);
|
||||
this._verifier.on('show_sas', this._onVerifierShowSas);
|
||||
// throws upon cancellation
|
||||
await this._verifier.verify();
|
||||
this.setState({phase: PHASE_VERIFIED});
|
||||
this._verifier.removeListener('show_sas', this._onVerifierShowSas);
|
||||
this._verifier = null;
|
||||
} catch (e) {
|
||||
console.log("Verification failed", e);
|
||||
this.setState({
|
||||
phase: PHASE_CANCELLED,
|
||||
});
|
||||
this._verifier = null;
|
||||
this._request = null;
|
||||
}
|
||||
};
|
||||
|
||||
_onLegacyFinished = (confirm) => {
|
||||
if (confirm) {
|
||||
MatrixClientPeg.get().setDeviceVerified(
|
||||
this.props.userId, this.props.device.deviceId, true,
|
||||
);
|
||||
}
|
||||
this.props.onFinished(confirm);
|
||||
}
|
||||
|
||||
_onSasRequestClick = async () => {
|
||||
this.setState({
|
||||
phase: PHASE_WAIT_FOR_PARTNER_TO_ACCEPT,
|
||||
});
|
||||
const client = MatrixClientPeg.get();
|
||||
const verifyingOwnDevice = this.props.userId === client.getUserId();
|
||||
try {
|
||||
if (!verifyingOwnDevice && SettingsStore.getValue("feature_cross_signing")) {
|
||||
const roomId = await ensureDMExistsAndOpen(this.props.userId);
|
||||
// throws upon cancellation before having started
|
||||
const request = await client.requestVerificationDM(
|
||||
this.props.userId, roomId,
|
||||
);
|
||||
await request.waitFor(r => r.ready || r.started);
|
||||
if (request.ready) {
|
||||
this._verifier = request.beginKeyVerification(verificationMethods.SAS);
|
||||
} else {
|
||||
this._verifier = request.verifier;
|
||||
}
|
||||
} else if (verifyingOwnDevice && SettingsStore.getValue("feature_cross_signing")) {
|
||||
this._request = await client.requestVerification(this.props.userId, [
|
||||
verificationMethods.SAS,
|
||||
SHOW_QR_CODE_METHOD,
|
||||
verificationMethods.RECIPROCATE_QR_CODE,
|
||||
]);
|
||||
|
||||
await this._request.waitFor(r => r.ready || r.started);
|
||||
this.setState({phase: PHASE_PICK_VERIFICATION_OPTION});
|
||||
} else {
|
||||
this._verifier = client.beginKeyVerification(
|
||||
verificationMethods.SAS, this.props.userId, this.props.device.deviceId,
|
||||
);
|
||||
}
|
||||
if (!this._verifier) return;
|
||||
this._verifier.on('show_sas', this._onVerifierShowSas);
|
||||
// throws upon cancellation
|
||||
await this._verifier.verify();
|
||||
this.setState({phase: PHASE_VERIFIED});
|
||||
this._verifier.removeListener('show_sas', this._onVerifierShowSas);
|
||||
this._verifier = null;
|
||||
} catch (e) {
|
||||
console.log("Verification failed", e);
|
||||
this.setState({
|
||||
phase: PHASE_CANCELLED,
|
||||
});
|
||||
this._verifier = null;
|
||||
}
|
||||
}
|
||||
|
||||
_onSasMatchesClick = () => {
|
||||
this._showSasEvent.confirm();
|
||||
this.setState({
|
||||
phase: PHASE_WAIT_FOR_PARTNER_TO_CONFIRM,
|
||||
});
|
||||
}
|
||||
|
||||
_onVerifiedDoneClick = () => {
|
||||
this.props.onFinished(true);
|
||||
}
|
||||
|
||||
_onVerifierShowSas = (e) => {
|
||||
this._showSasEvent = e;
|
||||
this.setState({
|
||||
phase: PHASE_SHOW_SAS,
|
||||
});
|
||||
}
|
||||
|
||||
_renderSasVerification() {
|
||||
let body;
|
||||
switch (this.state.phase) {
|
||||
case PHASE_START:
|
||||
body = this._renderVerificationPhaseStart();
|
||||
break;
|
||||
case PHASE_WAIT_FOR_PARTNER_TO_ACCEPT:
|
||||
body = this._renderVerificationPhaseWaitAccept();
|
||||
break;
|
||||
case PHASE_PICK_VERIFICATION_OPTION:
|
||||
body = this._renderVerificationPhasePick();
|
||||
break;
|
||||
case PHASE_SHOW_SAS:
|
||||
body = this._renderSasVerificationPhaseShowSas();
|
||||
break;
|
||||
case PHASE_WAIT_FOR_PARTNER_TO_CONFIRM:
|
||||
body = this._renderSasVerificationPhaseWaitForPartnerToConfirm();
|
||||
break;
|
||||
case PHASE_VERIFIED:
|
||||
body = this._renderVerificationPhaseVerified();
|
||||
break;
|
||||
case PHASE_CANCELLED:
|
||||
body = this._renderVerificationPhaseCancelled();
|
||||
break;
|
||||
}
|
||||
|
||||
const BaseDialog = sdk.getComponent("dialogs.BaseDialog");
|
||||
return (
|
||||
<BaseDialog
|
||||
title={_t("Verify session")}
|
||||
onFinished={this._onCancelClick}
|
||||
>
|
||||
{body}
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
|
||||
_renderVerificationPhaseStart() {
|
||||
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return (
|
||||
<div>
|
||||
<AccessibleButton
|
||||
element="span" className="mx_linkButton" onClick={this._onSwitchToLegacyClick}
|
||||
>
|
||||
{_t("Use Legacy Verification (for older clients)")}
|
||||
</AccessibleButton>
|
||||
<p>
|
||||
{ _t("Verify by comparing a short text string.") }
|
||||
</p>
|
||||
<p>
|
||||
{_t("To be secure, do this in person or use a trusted way to communicate.")}
|
||||
</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t('Begin Verifying')}
|
||||
hasCancel={true}
|
||||
onPrimaryButtonClick={this._onSasRequestClick}
|
||||
onCancel={this._onCancelClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderVerificationPhaseWaitAccept() {
|
||||
const Spinner = sdk.getComponent("views.elements.Spinner");
|
||||
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Spinner />
|
||||
<p>{_t("Waiting for partner to accept...")}</p>
|
||||
<p>{_t(
|
||||
"Nothing appearing? Not all clients support interactive verification yet. " +
|
||||
"<button>Use legacy verification</button>.",
|
||||
{}, {button: sub => <AccessibleButton element='span' className="mx_linkButton"
|
||||
onClick={this._onSwitchToLegacyClick}
|
||||
>
|
||||
{sub}
|
||||
</AccessibleButton>},
|
||||
)}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderVerificationPhasePick() {
|
||||
return <VerificationQREmojiOptions
|
||||
request={this._request}
|
||||
onCancel={this._onCancelClick}
|
||||
onStartEmoji={this._onUseSasClick}
|
||||
/>;
|
||||
}
|
||||
|
||||
_renderSasVerificationPhaseShowSas() {
|
||||
const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas');
|
||||
return <VerificationShowSas
|
||||
sas={this._showSasEvent.sas}
|
||||
onCancel={this._onCancelClick}
|
||||
onDone={this._onSasMatchesClick}
|
||||
isSelf={MatrixClientPeg.get().getUserId() === this.props.userId}
|
||||
onStartEmoji={this._onUseSasClick}
|
||||
inDialog={true}
|
||||
/>;
|
||||
}
|
||||
|
||||
_renderSasVerificationPhaseWaitForPartnerToConfirm() {
|
||||
const Spinner = sdk.getComponent('views.elements.Spinner');
|
||||
return <div>
|
||||
<Spinner />
|
||||
<p>{_t(
|
||||
"Waiting for %(userId)s to confirm...", {userId: this.props.userId},
|
||||
)}</p>
|
||||
</div>;
|
||||
}
|
||||
|
||||
_renderVerificationPhaseVerified() {
|
||||
const VerificationComplete = sdk.getComponent('views.verification.VerificationComplete');
|
||||
return <VerificationComplete onDone={this._onVerifiedDoneClick} />;
|
||||
}
|
||||
|
||||
_renderVerificationPhaseCancelled() {
|
||||
const VerificationCancelled = sdk.getComponent('views.verification.VerificationCancelled');
|
||||
return <VerificationCancelled onDone={this._onCancelClick} />;
|
||||
}
|
||||
|
||||
_renderLegacyVerification() {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
|
||||
|
||||
let text;
|
||||
if (MatrixClientPeg.get().getUserId() === this.props.userId) {
|
||||
text = _t("To verify that this session can be trusted, please check that the key you see " +
|
||||
"in User Settings on that device matches the key below:");
|
||||
} else {
|
||||
text = _t("To verify that this session can be trusted, please contact its owner using some other " +
|
||||
"means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings " +
|
||||
"for this session matches the key below:");
|
||||
}
|
||||
|
||||
const key = FormattingUtils.formatCryptoKey(this.props.device.getFingerprint());
|
||||
const body = (
|
||||
<div>
|
||||
<AccessibleButton
|
||||
element="span" className="mx_linkButton" onClick={this._onSwitchToSasClick}
|
||||
>
|
||||
{_t("Use two-way text verification")}
|
||||
</AccessibleButton>
|
||||
<p>
|
||||
{ text }
|
||||
</p>
|
||||
<div className="mx_DeviceVerifyDialog_cryptoSection">
|
||||
<ul>
|
||||
<li><label>{ _t("Session name") }:</label> <span>{ this.props.device.getDisplayName() }</span></li>
|
||||
<li><label>{ _t("Session ID") }:</label> <span><code>{ this.props.device.deviceId }</code></span></li>
|
||||
<li><label>{ _t("Session key") }:</label> <span><code><b>{ key }</b></code></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>
|
||||
{ _t("If it matches, press the verify button below. " +
|
||||
"If it doesn't, then someone else is intercepting this session " +
|
||||
"and you probably want to press the blacklist button instead.") }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<QuestionDialog
|
||||
title={_t("Verify session")}
|
||||
description={body}
|
||||
button={_t("I verify that the keys match")}
|
||||
onFinished={this._onLegacyFinished}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.mode === MODE_LEGACY) {
|
||||
return this._renderLegacyVerification();
|
||||
} else {
|
||||
return <div>
|
||||
{this._renderSasVerification()}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureDMExistsAndOpen(userId) {
|
||||
const roomId = await ensureDMExists(MatrixClientPeg.get(), userId);
|
||||
// don't use andView and spinner in createRoom, together, they cause this dialog to close and reopen,
|
||||
// we causes us to loose the verifier and restart, and we end up having two verification requests
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: roomId,
|
||||
should_peek: false,
|
||||
});
|
||||
return roomId;
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
|
||||
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 createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { markAllDevicesKnown } from '../../../cryptodevices';
|
||||
|
||||
function UserUnknownDeviceList(props) {
|
||||
const MemberDeviceInfo = sdk.getComponent('rooms.MemberDeviceInfo');
|
||||
const {userId, userDevices} = props;
|
||||
|
||||
const deviceListEntries = Object.keys(userDevices).map((deviceId) =>
|
||||
<li key={deviceId}><MemberDeviceInfo device={userDevices[deviceId]} userId={userId} showDeviceId={true} /></li>,
|
||||
);
|
||||
|
||||
return (
|
||||
<ul className="mx_UnknownDeviceDialog_deviceList">
|
||||
{ deviceListEntries }
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
UserUnknownDeviceList.propTypes = {
|
||||
userId: PropTypes.string.isRequired,
|
||||
|
||||
// map from deviceid -> deviceinfo
|
||||
userDevices: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
||||
function UnknownDeviceList(props) {
|
||||
const {devices} = props;
|
||||
|
||||
const userListEntries = Object.keys(devices).map((userId) =>
|
||||
<li key={userId}>
|
||||
<p>{ userId }:</p>
|
||||
<UserUnknownDeviceList userId={userId} userDevices={devices[userId]} />
|
||||
</li>,
|
||||
);
|
||||
|
||||
return <ul>{ userListEntries }</ul>;
|
||||
}
|
||||
|
||||
UnknownDeviceList.propTypes = {
|
||||
// map from userid -> deviceid -> deviceinfo
|
||||
devices: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'UnknownDeviceDialog',
|
||||
|
||||
propTypes: {
|
||||
room: PropTypes.object.isRequired,
|
||||
|
||||
// map from userid -> deviceid -> deviceinfo or null if devices are not yet loaded
|
||||
devices: PropTypes.object,
|
||||
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
|
||||
// Label for the button that marks all devices known and tries the send again
|
||||
sendAnywayLabel: PropTypes.string.isRequired,
|
||||
|
||||
// Label for the button that to send the event if you've verified all devices
|
||||
sendLabel: PropTypes.string.isRequired,
|
||||
|
||||
// function to retry the request once all devices are verified / known
|
||||
onSend: PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
MatrixClientPeg.get().on("deviceVerificationChanged", this._onDeviceVerificationChanged);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get().removeListener("deviceVerificationChanged", this._onDeviceVerificationChanged);
|
||||
}
|
||||
},
|
||||
|
||||
_onDeviceVerificationChanged: function(userId, deviceId, deviceInfo) {
|
||||
if (this.props.devices[userId] && this.props.devices[userId][deviceId]) {
|
||||
// XXX: Mutating props :/
|
||||
this.props.devices[userId][deviceId] = deviceInfo;
|
||||
this.forceUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
_onDismissClicked: function() {
|
||||
this.props.onFinished();
|
||||
},
|
||||
|
||||
_onSendAnywayClicked: function() {
|
||||
markAllDevicesKnown(MatrixClientPeg.get(), this.props.devices);
|
||||
|
||||
this.props.onFinished();
|
||||
this.props.onSend();
|
||||
},
|
||||
|
||||
_onSendClicked: function() {
|
||||
this.props.onFinished();
|
||||
this.props.onSend();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.props.devices === null) {
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
let warning;
|
||||
if (SettingsStore.getValue("blacklistUnverifiedDevices", this.props.room.roomId)) {
|
||||
warning = (
|
||||
<h4>
|
||||
{ _t("You are currently blacklisting unverified sessions; to send " +
|
||||
"messages to these sessions you must verify them.") }
|
||||
</h4>
|
||||
);
|
||||
} else {
|
||||
warning = (
|
||||
<div>
|
||||
<p>
|
||||
{ _t("We recommend you go through the verification process " +
|
||||
"for each session to confirm they belong to their legitimate owner, " +
|
||||
"but you can resend the message without verifying if you prefer.") }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let haveUnknownDevices = false;
|
||||
Object.keys(this.props.devices).forEach((userId) => {
|
||||
Object.keys(this.props.devices[userId]).map((deviceId) => {
|
||||
const device = this.props.devices[userId][deviceId];
|
||||
if (device.isUnverified() && !device.isKnown()) {
|
||||
haveUnknownDevices = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
const sendButtonOnClick = haveUnknownDevices ? this._onSendAnywayClicked : this._onSendClicked;
|
||||
const sendButtonLabel = haveUnknownDevices ? this.props.sendAnywayLabel : this.props.sendAnywayLabel;
|
||||
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return (
|
||||
<BaseDialog className='mx_UnknownDeviceDialog'
|
||||
onFinished={this.props.onFinished}
|
||||
title={_t('Room contains unknown sessions')}
|
||||
contentId='mx_Dialog_content'
|
||||
>
|
||||
<div className="mx_Dialog_content" id='mx_Dialog_content'>
|
||||
<h4>
|
||||
{ _t('"%(RoomName)s" contains sessions that you haven\'t seen before.', {RoomName: this.props.room.name}) }
|
||||
</h4>
|
||||
{ warning }
|
||||
{ _t("Unknown sessions") }:
|
||||
|
||||
<UnknownDeviceList devices={this.props.devices} />
|
||||
</div>
|
||||
<DialogButtons primaryButton={sendButtonLabel}
|
||||
onPrimaryButtonClick={sendButtonOnClick}
|
||||
onCancel={this._onDismissClicked} />
|
||||
</BaseDialog>
|
||||
);
|
||||
// XXX: do we want to give the user the option to enable blacklistUnverifiedDevices for this room (or globally) at this point?
|
||||
// It feels like confused users will likely turn it on and then disappear in a cloud of UISIs...
|
||||
},
|
||||
});
|
|
@ -20,10 +20,8 @@ import PropTypes from 'prop-types';
|
|||
import * as sdk from '../../../../index';
|
||||
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import Modal from '../../../../Modal';
|
||||
import { _t } from '../../../../languageHandler';
|
||||
import { accessSecretStorage } from '../../../../CrossSigningManager';
|
||||
import SettingsStore from "../../../../settings/SettingsStore";
|
||||
|
||||
const RESTORE_TYPE_PASSPHRASE = 0;
|
||||
const RESTORE_TYPE_RECOVERYKEY = 1;
|
||||
|
@ -90,21 +88,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
|
||||
_onResetRecoveryClick = () => {
|
||||
this.props.onFinished(false);
|
||||
|
||||
if (SettingsStore.getValue("feature_cross_signing")) {
|
||||
// If cross-signing is enabled, we reset the SSSS recovery passphrase (and cross-signing keys)
|
||||
this.props.onFinished(false);
|
||||
accessSecretStorage(() => {}, /* forceReset = */ true);
|
||||
} else {
|
||||
Modal.createTrackedDialogAsync('Key Backup', 'Key Backup',
|
||||
import('../../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'),
|
||||
{
|
||||
onFinished: () => {
|
||||
this._loadBackupStatus();
|
||||
},
|
||||
}, null, /* priority = */ false, /* static = */ true,
|
||||
);
|
||||
}
|
||||
accessSecretStorage(() => {}, /* forceReset = */ true);
|
||||
}
|
||||
|
||||
_onRecoveryKeyChange = (e) => {
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
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 PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import * as sdk from '../../../index';
|
||||
import Modal from '../../../Modal';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
// XXX: This component is *not* cross-signing aware. Once everything is
|
||||
// cross-signing, this component should just go away.
|
||||
export default createReactClass({
|
||||
displayName: 'DeviceVerifyButtons',
|
||||
|
||||
propTypes: {
|
||||
userId: PropTypes.string.isRequired,
|
||||
device: PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
device: this.props.device,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
cli.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (cli) {
|
||||
cli.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
}
|
||||
},
|
||||
|
||||
onDeviceVerificationChanged: function(userId, deviceId, deviceInfo) {
|
||||
if (userId === this.props.userId && deviceId === this.props.device.deviceId) {
|
||||
this.setState({ device: deviceInfo });
|
||||
}
|
||||
},
|
||||
|
||||
onVerifyClick: function() {
|
||||
const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog');
|
||||
Modal.createTrackedDialog('Device Verify Dialog', '', DeviceVerifyDialog, {
|
||||
userId: this.props.userId,
|
||||
device: this.state.device,
|
||||
}, null, /* priority = */ false, /* static = */ true);
|
||||
},
|
||||
|
||||
onUnverifyClick: function() {
|
||||
MatrixClientPeg.get().setDeviceVerified(
|
||||
this.props.userId, this.state.device.deviceId, false,
|
||||
);
|
||||
},
|
||||
|
||||
onBlacklistClick: function() {
|
||||
MatrixClientPeg.get().setDeviceBlocked(
|
||||
this.props.userId, this.state.device.deviceId, true,
|
||||
);
|
||||
},
|
||||
|
||||
onUnblacklistClick: function() {
|
||||
MatrixClientPeg.get().setDeviceBlocked(
|
||||
this.props.userId, this.state.device.deviceId, false,
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let blacklistButton = null; let verifyButton = null;
|
||||
|
||||
if (this.state.device.isBlocked()) {
|
||||
blacklistButton = (
|
||||
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_unblacklist"
|
||||
onClick={this.onUnblacklistClick}>
|
||||
{ _t("Unblacklist") }
|
||||
</button>
|
||||
);
|
||||
} else {
|
||||
blacklistButton = (
|
||||
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_blacklist"
|
||||
onClick={this.onBlacklistClick}>
|
||||
{ _t("Blacklist") }
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.device.isVerified()) {
|
||||
verifyButton = (
|
||||
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_unverify"
|
||||
onClick={this.onUnverifyClick}>
|
||||
{ _t("Unverify") }
|
||||
</button>
|
||||
);
|
||||
} else {
|
||||
verifyButton = (
|
||||
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_verify"
|
||||
onClick={this.onVerifyClick}>
|
||||
{ _t("Verify...") }
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_DeviceVerifyButtons" >
|
||||
{ verifyButton }
|
||||
{ blacklistButton }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -1,208 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
Copyright 2019 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 PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import Modal from '../../../Modal';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { GroupMemberType } from '../../../groups';
|
||||
import GroupStore from '../../../stores/GroupStore';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'GroupMemberInfo',
|
||||
|
||||
statics: {
|
||||
contextType: MatrixClientContext,
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
groupId: PropTypes.string,
|
||||
groupMember: GroupMemberType,
|
||||
isInvited: PropTypes.bool,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
removingUser: false,
|
||||
isUserPrivilegedInGroup: null,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this._unmounted = false;
|
||||
this._initGroupStore(this.props.groupId);
|
||||
},
|
||||
|
||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||
UNSAFE_componentWillReceiveProps(newProps) {
|
||||
if (newProps.groupId !== this.props.groupId) {
|
||||
this._unregisterGroupStore(this.props.groupId);
|
||||
this._initGroupStore(newProps.groupId);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
this._unmounted = true;
|
||||
this._unregisterGroupStore(this.props.groupId);
|
||||
},
|
||||
|
||||
_initGroupStore(groupId) {
|
||||
GroupStore.registerListener(groupId, this.onGroupStoreUpdated);
|
||||
},
|
||||
|
||||
_unregisterGroupStore(groupId) {
|
||||
GroupStore.unregisterListener(this.onGroupStoreUpdated);
|
||||
},
|
||||
|
||||
onGroupStoreUpdated: function() {
|
||||
if (this._unmounted) return;
|
||||
this.setState({
|
||||
isUserInvited: GroupStore.getGroupInvitedMembers(this.props.groupId).some(
|
||||
(m) => m.userId === this.props.groupMember.userId,
|
||||
),
|
||||
isUserPrivilegedInGroup: GroupStore.isUserPrivileged(this.props.groupId),
|
||||
});
|
||||
},
|
||||
|
||||
_onKick: function() {
|
||||
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
|
||||
Modal.createDialog(ConfirmUserActionDialog, {
|
||||
matrixClient: this.context,
|
||||
groupMember: this.props.groupMember,
|
||||
action: this.state.isUserInvited ? _t('Disinvite') : _t('Remove from community'),
|
||||
title: this.state.isUserInvited ? _t('Disinvite this user from community?')
|
||||
: _t('Remove this user from community?'),
|
||||
danger: true,
|
||||
onFinished: (proceed) => {
|
||||
if (!proceed) return;
|
||||
|
||||
this.setState({removingUser: true});
|
||||
this.context.removeUserFromGroup(
|
||||
this.props.groupId, this.props.groupMember.userId,
|
||||
).then(() => {
|
||||
// return to the user list
|
||||
dis.dispatch({
|
||||
action: Action.ViewUser,
|
||||
member: null,
|
||||
});
|
||||
}).catch((e) => {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Failed to remove user from group', '', ErrorDialog, {
|
||||
title: _t('Error'),
|
||||
description: this.state.isUserInvited ?
|
||||
_t('Failed to withdraw invitation') :
|
||||
_t('Failed to remove user from community'),
|
||||
});
|
||||
}).finally(() => {
|
||||
this.setState({removingUser: false});
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
_onCancel: function(e) {
|
||||
// Go back to the user list
|
||||
dis.dispatch({
|
||||
action: Action.ViewUser,
|
||||
member: null,
|
||||
});
|
||||
},
|
||||
|
||||
onRoomTileClick(roomId) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: roomId,
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.state.removingUser) {
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
return <div className="mx_MemberInfo">
|
||||
<Spinner />
|
||||
</div>;
|
||||
}
|
||||
|
||||
let adminTools;
|
||||
if (this.state.isUserPrivilegedInGroup) {
|
||||
const kickButton = (
|
||||
<AccessibleButton className="mx_MemberInfo_field"
|
||||
onClick={this._onKick}>
|
||||
{ this.state.isUserInvited ? _t('Disinvite') : _t('Remove from community') }
|
||||
</AccessibleButton>
|
||||
);
|
||||
|
||||
// No make/revoke admin API yet
|
||||
/*const opLabel = this.state.isTargetMod ? _t("Revoke Moderator") : _t("Make Moderator");
|
||||
giveModButton = <AccessibleButton className="mx_MemberInfo_field" onClick={this.onModToggle}>
|
||||
{giveOpLabel}
|
||||
</AccessibleButton>;*/
|
||||
|
||||
if (kickButton) {
|
||||
adminTools =
|
||||
<div className="mx_MemberInfo_adminTools">
|
||||
<h3>{ _t("Admin Tools") }</h3>
|
||||
<div className="mx_MemberInfo_buttons">
|
||||
{ kickButton }
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const avatarUrl = this.props.groupMember.avatarUrl;
|
||||
let avatarElement;
|
||||
if (avatarUrl) {
|
||||
const httpUrl = this.context.mxcUrlToHttp(avatarUrl, 800, 800);
|
||||
avatarElement = (<div className="mx_MemberInfo_avatar">
|
||||
<img src={httpUrl} />
|
||||
</div>);
|
||||
}
|
||||
|
||||
const groupMemberName = (
|
||||
this.props.groupMember.displayname || this.props.groupMember.userId
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mx_MemberInfo" role="tabpanel">
|
||||
<AutoHideScrollbar>
|
||||
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
|
||||
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />
|
||||
</AccessibleButton>
|
||||
{ avatarElement }
|
||||
<h2>{ groupMemberName }</h2>
|
||||
|
||||
<div className="mx_MemberInfo_profile">
|
||||
<div className="mx_MemberInfo_profileField">
|
||||
{ this.props.groupMember.userId }
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ adminTools }
|
||||
</AutoHideScrollbar>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -64,10 +64,6 @@ const _disambiguateDevices = (devices) => {
|
|||
};
|
||||
|
||||
export const getE2EStatus = (cli, userId, devices) => {
|
||||
if (!SettingsStore.getValue("feature_cross_signing")) {
|
||||
const hasUnverifiedDevice = devices.some((device) => device.isUnverified());
|
||||
return hasUnverifiedDevice ? "warning" : "verified";
|
||||
}
|
||||
const isMe = userId === cli.getUserId();
|
||||
const userTrust = cli.checkUserTrust(userId);
|
||||
if (!userTrust.isCrossSigningVerified()) {
|
||||
|
@ -167,9 +163,7 @@ function DeviceItem({userId, device}) {
|
|||
// cross-signing so that other users can then safely trust you.
|
||||
// For other people's devices, the more general verified check that
|
||||
// includes locally verified devices can be used.
|
||||
const isVerified = (isMe && SettingsStore.getValue("feature_cross_signing")) ?
|
||||
deviceTrust.isCrossSigningVerified() :
|
||||
deviceTrust.isVerified();
|
||||
const isVerified = isMe ? deviceTrust.isCrossSigningVerified() : deviceTrust.isVerified();
|
||||
|
||||
const classes = classNames("mx_UserInfo_device", {
|
||||
mx_UserInfo_device_verified: isVerified,
|
||||
|
@ -248,9 +242,7 @@ function DevicesSection({devices, userId, loading}) {
|
|||
// cross-signing so that other users can then safely trust you.
|
||||
// For other people's devices, the more general verified check that
|
||||
// includes locally verified devices can be used.
|
||||
const isVerified = (isMe && SettingsStore.getValue("feature_cross_signing")) ?
|
||||
deviceTrust.isCrossSigningVerified() :
|
||||
deviceTrust.isVerified();
|
||||
const isVerified = isMe ? deviceTrust.isCrossSigningVerified() : deviceTrust.isVerified();
|
||||
|
||||
if (isVerified) {
|
||||
expandSectionDevices.push(device);
|
||||
|
@ -1309,8 +1301,7 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => {
|
|||
const userTrust = cli.checkUserTrust(member.userId);
|
||||
const userVerified = userTrust.isCrossSigningVerified();
|
||||
const isMe = member.userId === cli.getUserId();
|
||||
const canVerify = SettingsStore.getValue("feature_cross_signing") &&
|
||||
homeserverSupportsCrossSigning && !userVerified && !isMe;
|
||||
const canVerify = homeserverSupportsCrossSigning && !userVerified && !isMe;
|
||||
|
||||
const setUpdating = (updating) => {
|
||||
setPendingUpdateCount(count => count + (updating ? 1 : -1));
|
||||
|
|
|
@ -15,8 +15,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import React, {createRef} from 'react';
|
||||
import classNames from 'classnames';
|
||||
import flatMap from 'lodash/flatMap';
|
||||
import {ICompletion, ISelectionRange, IProviderCompletions} from '../../../autocomplete/Autocompleter';
|
||||
|
@ -54,7 +53,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
|
|||
autocompleter: Autocompleter;
|
||||
queryRequested: string;
|
||||
debounceCompletionsRequest: NodeJS.Timeout;
|
||||
containerRef: React.RefObject<HTMLDivElement>;
|
||||
private containerRef = createRef<HTMLDivElement>();
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -78,8 +77,6 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
|
|||
|
||||
forceComplete: false,
|
||||
};
|
||||
|
||||
this.containerRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -256,14 +253,15 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
|
|||
componentDidUpdate(prevProps: IProps) {
|
||||
this.applyNewProps(prevProps.query, prevProps.room);
|
||||
// this is the selected completion, so scroll it into view if needed
|
||||
const selectedCompletion = this.refs[`completion${this.state.selectionOffset}`];
|
||||
if (selectedCompletion && this.containerRef.current) {
|
||||
const domNode = ReactDOM.findDOMNode(selectedCompletion);
|
||||
const offsetTop = domNode && (domNode as HTMLElement).offsetTop;
|
||||
if (offsetTop > this.containerRef.current.scrollTop + this.containerRef.current.offsetHeight ||
|
||||
offsetTop < this.containerRef.current.scrollTop) {
|
||||
this.containerRef.current.scrollTop = offsetTop - this.containerRef.current.offsetTop;
|
||||
}
|
||||
const selectedCompletion = this.refs[`completion${this.state.selectionOffset}`] as HTMLElement;
|
||||
|
||||
if (selectedCompletion) {
|
||||
selectedCompletion.scrollIntoView({
|
||||
behavior: "auto",
|
||||
block: "nearest",
|
||||
});
|
||||
} else {
|
||||
this.containerRef.current.scrollTo({ top: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import PropTypes from "prop-types";
|
|||
import classNames from 'classnames';
|
||||
|
||||
import {_t, _td} from '../../../languageHandler';
|
||||
import {useSettingValue} from "../../../hooks/useSettings";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import Tooltip from "../elements/Tooltip";
|
||||
|
||||
|
@ -42,15 +41,6 @@ const crossSigningRoomTitles = {
|
|||
[E2E_STATE.VERIFIED]: _td("Everyone in this room is verified"),
|
||||
};
|
||||
|
||||
const legacyUserTitles = {
|
||||
[E2E_STATE.WARNING]: _td("Some sessions for this user are not trusted"),
|
||||
[E2E_STATE.VERIFIED]: _td("All sessions for this user are trusted"),
|
||||
};
|
||||
const legacyRoomTitles = {
|
||||
[E2E_STATE.WARNING]: _td("Some sessions in this encrypted room are not trusted"),
|
||||
[E2E_STATE.VERIFIED]: _td("All sessions in this encrypted room are trusted"),
|
||||
};
|
||||
|
||||
const E2EIcon = ({isUser, status, className, size, onClick, hideTooltip}) => {
|
||||
const [hover, setHover] = useState(false);
|
||||
|
||||
|
@ -62,15 +52,10 @@ const E2EIcon = ({isUser, status, className, size, onClick, hideTooltip}) => {
|
|||
}, className);
|
||||
|
||||
let e2eTitle;
|
||||
const crossSigning = useSettingValue("feature_cross_signing");
|
||||
if (crossSigning && isUser) {
|
||||
if (isUser) {
|
||||
e2eTitle = crossSigningUserTitles[status];
|
||||
} else if (crossSigning && !isUser) {
|
||||
} else {
|
||||
e2eTitle = crossSigningRoomTitles[status];
|
||||
} else if (!crossSigning && isUser) {
|
||||
e2eTitle = legacyUserTitles[status];
|
||||
} else if (!crossSigning && !isUser) {
|
||||
e2eTitle = legacyRoomTitles[status];
|
||||
}
|
||||
|
||||
let style;
|
||||
|
|
|
@ -325,15 +325,6 @@ export default createReactClass({
|
|||
return;
|
||||
}
|
||||
|
||||
// If cross-signing is off, the old behaviour is to scream at the user
|
||||
// as if they've done something wrong, which they haven't
|
||||
if (!SettingsStore.getValue("feature_cross_signing")) {
|
||||
this.setState({
|
||||
verified: E2E_STATE.WARNING,
|
||||
}, this.props.onHeightChanged);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.context.checkUserTrust(mxEvent.getSender()).isCrossSigningVerified()) {
|
||||
this.setState({
|
||||
verified: E2E_STATE.NORMAL,
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
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 PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export default class MemberDeviceInfo extends React.Component {
|
||||
render() {
|
||||
const DeviceVerifyButtons = sdk.getComponent('elements.DeviceVerifyButtons');
|
||||
// XXX: These checks are not cross-signing aware but this component is only used
|
||||
// from the old, pre-cross-signing memberinfopanel
|
||||
const iconClasses = classNames({
|
||||
mx_MemberDeviceInfo_icon: true,
|
||||
mx_MemberDeviceInfo_icon_blacklisted: this.props.device.isBlocked(),
|
||||
mx_MemberDeviceInfo_icon_verified: this.props.device.isVerified(),
|
||||
mx_MemberDeviceInfo_icon_unverified: this.props.device.isUnverified(),
|
||||
});
|
||||
const indicator = (<div className={iconClasses} />);
|
||||
const deviceName = (this.props.device.ambiguous || this.props.showDeviceId) ?
|
||||
(this.props.device.getDisplayName() ? this.props.device.getDisplayName() : "") + " (" + this.props.device.deviceId + ")" :
|
||||
this.props.device.getDisplayName();
|
||||
|
||||
// add the deviceId as a titletext to help with debugging
|
||||
return (
|
||||
<div className="mx_MemberDeviceInfo"
|
||||
title={_t("device id: ") + this.props.device.deviceId} >
|
||||
{ indicator }
|
||||
<div className="mx_MemberDeviceInfo_deviceInfo">
|
||||
<div className="mx_MemberDeviceInfo_deviceId">
|
||||
{ deviceName }
|
||||
</div>
|
||||
</div>
|
||||
<DeviceVerifyButtons userId={this.props.userId} device={this.props.device} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MemberDeviceInfo.displayName = 'MemberDeviceInfo';
|
||||
MemberDeviceInfo.propTypes = {
|
||||
userId: PropTypes.string.isRequired,
|
||||
device: PropTypes.object.isRequired,
|
||||
};
|
File diff suppressed because it is too large
Load diff
|
@ -57,21 +57,19 @@ export default createReactClass({
|
|||
}
|
||||
}
|
||||
|
||||
if (SettingsStore.getValue("feature_cross_signing")) {
|
||||
const { roomId } = this.props.member;
|
||||
if (roomId) {
|
||||
const isRoomEncrypted = cli.isRoomEncrypted(roomId);
|
||||
this.setState({
|
||||
isRoomEncrypted,
|
||||
});
|
||||
if (isRoomEncrypted) {
|
||||
cli.on("userTrustStatusChanged", this.onUserTrustStatusChanged);
|
||||
cli.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
this.updateE2EStatus();
|
||||
} else {
|
||||
// Listen for room to become encrypted
|
||||
cli.on("RoomState.events", this.onRoomStateEvents);
|
||||
}
|
||||
const { roomId } = this.props.member;
|
||||
if (roomId) {
|
||||
const isRoomEncrypted = cli.isRoomEncrypted(roomId);
|
||||
this.setState({
|
||||
isRoomEncrypted,
|
||||
});
|
||||
if (isRoomEncrypted) {
|
||||
cli.on("userTrustStatusChanged", this.onUserTrustStatusChanged);
|
||||
cli.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
this.updateE2EStatus();
|
||||
} else {
|
||||
// Listen for room to become encrypted
|
||||
cli.on("RoomState.events", this.onRoomStateEvents);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -308,33 +308,17 @@ export default class MessageComposer extends React.Component {
|
|||
}
|
||||
|
||||
renderPlaceholderText() {
|
||||
if (SettingsStore.getValue("feature_cross_signing")) {
|
||||
if (this.state.isQuoting) {
|
||||
if (this.props.e2eStatus) {
|
||||
return _t('Send an encrypted reply…');
|
||||
} else {
|
||||
return _t('Send a reply…');
|
||||
}
|
||||
if (this.state.isQuoting) {
|
||||
if (this.props.e2eStatus) {
|
||||
return _t('Send an encrypted reply…');
|
||||
} else {
|
||||
if (this.props.e2eStatus) {
|
||||
return _t('Send an encrypted message…');
|
||||
} else {
|
||||
return _t('Send a message…');
|
||||
}
|
||||
return _t('Send a reply…');
|
||||
}
|
||||
} else {
|
||||
if (this.state.isQuoting) {
|
||||
if (this.props.e2eStatus) {
|
||||
return _t('Send an encrypted reply…');
|
||||
} else {
|
||||
return _t('Send a reply (unencrypted)…');
|
||||
}
|
||||
if (this.props.e2eStatus) {
|
||||
return _t('Send an encrypted message…');
|
||||
} else {
|
||||
if (this.props.e2eStatus) {
|
||||
return _t('Send an encrypted message…');
|
||||
} else {
|
||||
return _t('Send a message (unencrypted)…');
|
||||
}
|
||||
return _t('Send a message…');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,10 +168,8 @@ export default createReactClass({
|
|||
const joinRule = joinRules && joinRules.getContent().join_rule;
|
||||
let privateIcon;
|
||||
// Don't show an invite-only icon for DMs. Users know they're invite-only.
|
||||
if (!dmUserId && SettingsStore.getValue("feature_cross_signing")) {
|
||||
if (joinRule == "invite") {
|
||||
privateIcon = <InviteOnlyIcon />;
|
||||
}
|
||||
if (!dmUserId && joinRule === "invite") {
|
||||
privateIcon = <InviteOnlyIcon />;
|
||||
}
|
||||
|
||||
if (this.props.onCancelClick) {
|
||||
|
|
|
@ -155,9 +155,6 @@ export default createReactClass({
|
|||
if (!cli.isRoomEncrypted(this.props.room.roomId)) {
|
||||
return;
|
||||
}
|
||||
if (!SettingsStore.getValue("feature_cross_signing")) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* At this point, the user has encryption on and cross-signing on */
|
||||
this.setState({
|
||||
|
@ -515,10 +512,8 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
let privateIcon = null;
|
||||
if (SettingsStore.getValue("feature_cross_signing")) {
|
||||
if (this.state.joinRule == "invite" && !dmUserId) {
|
||||
privateIcon = <InviteOnlyIcon collapsedPanel={this.props.collapsed} />;
|
||||
}
|
||||
if (this.state.joinRule === "invite" && !dmUserId) {
|
||||
privateIcon = <InviteOnlyIcon collapsedPanel={this.props.collapsed} />;
|
||||
}
|
||||
|
||||
let e2eIcon = null;
|
||||
|
|
|
@ -194,6 +194,8 @@ export default class CrossSigningPanel extends React.PureComponent {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: determine how better to expose this to users in addition to prompts at login/toast
|
||||
let bootstrapButton;
|
||||
if (
|
||||
(!enabledForAccount || !crossSigningPublicKeysOnDevice) &&
|
||||
|
|
|
@ -21,7 +21,6 @@ import * as sdk from '../../../index';
|
|||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import Modal from '../../../Modal';
|
||||
import SettingsStore from '../../../settings/SettingsStore';
|
||||
|
||||
export default class KeyBackupPanel extends React.PureComponent {
|
||||
constructor(props) {
|
||||
|
@ -316,7 +315,7 @@ export default class KeyBackupPanel extends React.PureComponent {
|
|||
trustedLocally = _t("This backup is trusted because it has been restored on this session");
|
||||
}
|
||||
|
||||
let buttonRow = (
|
||||
const buttonRow = (
|
||||
<div className="mx_KeyBackupPanel_buttonRow">
|
||||
<AccessibleButton kind="primary" onClick={this._restoreBackup}>
|
||||
{restoreButtonCaption}
|
||||
|
@ -326,13 +325,6 @@ export default class KeyBackupPanel extends React.PureComponent {
|
|||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
if (this.state.backupKeyStored && !SettingsStore.getValue("feature_cross_signing")) {
|
||||
buttonRow = <p>⚠️ {_t(
|
||||
"Backup key stored in secret storage, but this feature is not " +
|
||||
"enabled on this session. Please enable cross-signing in Labs to " +
|
||||
"modify key backup state.",
|
||||
)}</p>;
|
||||
}
|
||||
|
||||
return <div>
|
||||
<div>{clientBackupStatus}</div>
|
||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {_t} from "../../../../../languageHandler";
|
||||
import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore";
|
||||
import {SettingLevel} from "../../../../../settings/SettingsStore";
|
||||
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
|
||||
import * as FormattingUtils from "../../../../../utils/FormattingUtils";
|
||||
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||
|
@ -307,9 +307,7 @@ export default class SecurityUserSettingsTab extends React.Component {
|
|||
// in having advanced details here once all flows are implemented, we
|
||||
// can remove this.
|
||||
const CrossSigningPanel = sdk.getComponent('views.settings.CrossSigningPanel');
|
||||
let crossSigning;
|
||||
if (SettingsStore.getValue("feature_cross_signing")) {
|
||||
crossSigning = (
|
||||
const crossSigning = (
|
||||
<div className='mx_SettingsTab_section'>
|
||||
<span className="mx_SettingsTab_subheading">{_t("Cross-signing")}</span>
|
||||
<div className='mx_SettingsTab_subsectionText'>
|
||||
|
@ -317,7 +315,6 @@ export default class SecurityUserSettingsTab extends React.Component {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const E2eAdvancedPanel = sdk.getComponent('views.settings.E2eAdvancedPanel');
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue