Merge remote-tracking branch 'nv/develop' into joriks/irc-ui
This commit is contained in:
commit
2e325191bc
307 changed files with 2653 additions and 360 deletions
|
@ -623,7 +623,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
break;
|
||||
}
|
||||
case 'view_create_room':
|
||||
this.createRoom();
|
||||
this.createRoom(payload.public);
|
||||
break;
|
||||
case 'view_create_group': {
|
||||
const CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog");
|
||||
|
@ -1012,9 +1012,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
}).close;
|
||||
}
|
||||
|
||||
private async createRoom() {
|
||||
private async createRoom(defaultPublic = false) {
|
||||
const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog');
|
||||
const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog);
|
||||
const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog, { defaultPublic });
|
||||
|
||||
const [shouldCreate, opts] = await modal.finished;
|
||||
if (shouldCreate) {
|
||||
|
@ -1339,7 +1339,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
// this if we are not scrolled up in the view. To find out, delegate to
|
||||
// the timeline panel. If the timeline panel doesn't exist, then we assume
|
||||
// it is safe to reset the timeline.
|
||||
if (!this.loggedInView.current || !this.loggedInView.current) {
|
||||
if (!this.loggedInView.current) {
|
||||
return true;
|
||||
}
|
||||
return this.loggedInView.current.canResetTimelineInRoom(roomId);
|
||||
|
|
|
@ -227,7 +227,7 @@ export default class RightPanel extends React.Component {
|
|||
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 obviosuly the correct thing to do, but I'm not sure
|
||||
// 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",
|
||||
|
|
|
@ -367,7 +367,10 @@ export default createReactClass({
|
|||
|
||||
onCreateRoomClick: function(room) {
|
||||
this.props.onFinished();
|
||||
dis.dispatch({action: 'view_create_room'});
|
||||
dis.dispatch({
|
||||
action: 'view_create_room',
|
||||
public: true,
|
||||
});
|
||||
},
|
||||
|
||||
showRoomAlias: function(alias, autoJoin=false) {
|
||||
|
|
|
@ -1717,8 +1717,11 @@ export default createReactClass({
|
|||
} else {
|
||||
const myUserId = this.context.credentials.userId;
|
||||
const myMember = this.state.room.getMember(myUserId);
|
||||
const inviteEvent = myMember.events.member;
|
||||
var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender();
|
||||
const inviteEvent = myMember ? myMember.events.member : null;
|
||||
let inviterName = _t("Unknown");
|
||||
if (inviteEvent) {
|
||||
inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender();
|
||||
}
|
||||
|
||||
// We deliberately don't try to peek into invites, even if we have permission to peek
|
||||
// as they could be a spam vector.
|
||||
|
|
|
@ -42,7 +42,10 @@ export default class UserView extends React.Component {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.userId !== this.props.userId) {
|
||||
// XXX: We shouldn't need to null check the userId here, but we declare
|
||||
// it as optional and MatrixChat sometimes fires in a way which results
|
||||
// in an NPE when we try to update the profile info.
|
||||
if (prevProps.userId !== this.props.userId && this.props.userId) {
|
||||
this._loadProfileInfo();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -267,6 +267,7 @@ export default createReactClass({
|
|||
dis.dispatch({action: 'start_login'});
|
||||
} else {
|
||||
this.setState({
|
||||
serverErrorIsFatal: true, // fatal because user cannot continue on this server
|
||||
errorText: _t("Registration has been disabled on this homeserver."),
|
||||
// add empty flows array to get rid of spinner
|
||||
flows: [],
|
||||
|
|
|
@ -19,7 +19,7 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import * as sdk from '../../../index';
|
||||
|
||||
import { COUNTRIES } from '../../../phonenumber';
|
||||
import {COUNTRIES, getEmojiFlag} from '../../../phonenumber';
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
|
@ -80,7 +80,7 @@ export default class CountryDropdown extends React.Component {
|
|||
}
|
||||
|
||||
_flagImgForIso2(iso2) {
|
||||
return <img src={require(`../../../../res/img/flags/${iso2}.png`)} />;
|
||||
return <div className="mx_Dropdown_option_emoji">{ getEmojiFlag(iso2) }</div>;
|
||||
}
|
||||
|
||||
_getShortOption(iso2) {
|
||||
|
|
|
@ -130,22 +130,24 @@ export default createReactClass({
|
|||
},
|
||||
|
||||
onViewSourceClick: function() {
|
||||
const ev = this.props.mxEvent.replacingEvent() || this.props.mxEvent;
|
||||
const ViewSource = sdk.getComponent('structures.ViewSource');
|
||||
Modal.createTrackedDialog('View Event Source', '', ViewSource, {
|
||||
roomId: this.props.mxEvent.getRoomId(),
|
||||
eventId: this.props.mxEvent.getId(),
|
||||
content: this.props.mxEvent.event,
|
||||
roomId: ev.getRoomId(),
|
||||
eventId: ev.getId(),
|
||||
content: ev.event,
|
||||
}, 'mx_Dialog_viewsource');
|
||||
this.closeMenu();
|
||||
},
|
||||
|
||||
onViewClearSourceClick: function() {
|
||||
const ev = this.props.mxEvent.replacingEvent() || this.props.mxEvent;
|
||||
const ViewSource = sdk.getComponent('structures.ViewSource');
|
||||
Modal.createTrackedDialog('View Clear Event Source', '', ViewSource, {
|
||||
roomId: this.props.mxEvent.getRoomId(),
|
||||
eventId: this.props.mxEvent.getId(),
|
||||
roomId: ev.getRoomId(),
|
||||
eventId: ev.getId(),
|
||||
// FIXME: _clearEvent is private
|
||||
content: this.props.mxEvent._clearEvent,
|
||||
content: ev._clearEvent,
|
||||
}, 'mx_Dialog_viewsource');
|
||||
this.closeMenu();
|
||||
},
|
||||
|
|
|
@ -30,12 +30,13 @@ export default createReactClass({
|
|||
displayName: 'CreateRoomDialog',
|
||||
propTypes: {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
defaultPublic: PropTypes.bool,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
const config = SdkConfig.get();
|
||||
return {
|
||||
isPublic: false,
|
||||
isPublic: this.props.defaultPublic || false,
|
||||
isEncrypted: true,
|
||||
name: "",
|
||||
topic: "",
|
||||
|
|
|
@ -25,6 +25,7 @@ import { _t } from '../../../languageHandler';
|
|||
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import {ERROR_USER_CANCELLED} from "../../structures/InteractiveAuth";
|
||||
import {SSOAuthEntry} from "../auth/InteractiveAuthEntryComponents";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'InteractiveAuthDialog',
|
||||
|
@ -66,6 +67,8 @@ export default createReactClass({
|
|||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Default is defined in _getDefaultDialogAesthetics()
|
||||
aestheticsForStagePhases: PropTypes.object,
|
||||
},
|
||||
|
||||
|
@ -79,6 +82,28 @@ export default createReactClass({
|
|||
};
|
||||
},
|
||||
|
||||
_getDefaultDialogAesthetics: function() {
|
||||
const ssoAesthetics = {
|
||||
[SSOAuthEntry.PHASE_PREAUTH]: {
|
||||
title: _t("Use Single Sign On to continue"),
|
||||
body: _t("To continue, use Single Sign On to prove your identity."),
|
||||
continueText: _t("Single Sign On"),
|
||||
continueKind: "primary",
|
||||
},
|
||||
[SSOAuthEntry.PHASE_POSTAUTH]: {
|
||||
title: _t("Confirm to continue"),
|
||||
body: _t("Click the button below to confirm your identity."),
|
||||
continueText: _t("Confirm"),
|
||||
continueKind: "primary",
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
[SSOAuthEntry.LOGIN_TYPE]: ssoAesthetics,
|
||||
[SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: ssoAesthetics,
|
||||
};
|
||||
},
|
||||
|
||||
_onAuthFinished: function(success, result) {
|
||||
if (success) {
|
||||
this.props.onFinished(true, result);
|
||||
|
@ -113,9 +138,10 @@ export default createReactClass({
|
|||
let body = this.state.authError ? null : this.props.body;
|
||||
let continueText = null;
|
||||
let continueKind = null;
|
||||
if (!this.state.authError && this.props.aestheticsForStagePhases) {
|
||||
if (this.props.aestheticsForStagePhases[this.state.uiaStage]) {
|
||||
const aesthetics = this.props.aestheticsForStagePhases[this.state.uiaStage][this.state.uiaStagePhase];
|
||||
const dialogAesthetics = this.props.aestheticsForStagePhases || this._getDefaultDialogAesthetics();
|
||||
if (!this.state.authError && dialogAesthetics) {
|
||||
if (dialogAesthetics[this.state.uiaStage]) {
|
||||
const aesthetics = dialogAesthetics[this.state.uiaStage][this.state.uiaStagePhase];
|
||||
if (aesthetics && aesthetics.title) title = aesthetics.title;
|
||||
if (aesthetics && aesthetics.body) body = aesthetics.body;
|
||||
if (aesthetics && aesthetics.continueText) continueText = aesthetics.continueText;
|
||||
|
|
|
@ -23,6 +23,7 @@ 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;
|
||||
|
@ -89,14 +90,21 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
|
||||
_onResetRecoveryClick = () => {
|
||||
this.props.onFinished(false);
|
||||
Modal.createTrackedDialogAsync('Key Backup', 'Key Backup',
|
||||
import('../../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'),
|
||||
{
|
||||
onFinished: () => {
|
||||
this._loadBackupStatus();
|
||||
},
|
||||
}, null, /* priority = */ false, /* static = */ true,
|
||||
);
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_onRecoveryKeyChange = (e) => {
|
||||
|
|
|
@ -424,13 +424,13 @@ export default class AppTile extends React.Component {
|
|||
_setupWidgetMessaging() {
|
||||
// FIXME: There's probably no reason to do this here: it should probably be done entirely
|
||||
// in ActiveWidgetStore.
|
||||
|
||||
// We use the app's URL over the rendered URL so that anything the widget does which could
|
||||
// lead to requesting a "security key" will pass accordingly. The only other thing this URL
|
||||
// is used for is to determine the origin we're talking to, and therefore we don't need the
|
||||
// fully templated URL.
|
||||
const widgetMessaging = new WidgetMessaging(
|
||||
this.props.app.id, this.props.app.url, this.props.userWidget, this._appFrame.current.contentWindow);
|
||||
this.props.app.id,
|
||||
this.props.app.url,
|
||||
this._getRenderedUrl(),
|
||||
this.props.userWidget,
|
||||
this._appFrame.current.contentWindow,
|
||||
);
|
||||
ActiveWidgetStore.setWidgetMessaging(this.props.app.id, widgetMessaging);
|
||||
widgetMessaging.getCapabilities().then((requestedCapabilities) => {
|
||||
console.log(`Widget ${this.props.app.id} requested capabilities: ` + requestedCapabilities);
|
||||
|
|
|
@ -66,12 +66,12 @@ const EventListSummary = ({events, children, threshold=3, onToggle, startExpande
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="mx_EventListSummary" data-scroll-tokens={eventIds}>
|
||||
<li className="mx_EventListSummary" data-scroll-tokens={eventIds}>
|
||||
<AccessibleButton className="mx_EventListSummary_toggle" onClick={toggleExpanded} aria-expanded={expanded}>
|
||||
{ expanded ? _t('collapse') : _t('expand') }
|
||||
</AccessibleButton>
|
||||
{ body }
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -90,7 +90,7 @@ EventListSummary.propTypes = {
|
|||
// The list of room members for which to show avatars next to the summary
|
||||
summaryMembers: PropTypes.arrayOf(PropTypes.instanceOf(RoomMember)),
|
||||
// The text to show as the summary of this event list
|
||||
summaryText: PropTypes.string.isRequired,
|
||||
summaryText: PropTypes.string,
|
||||
};
|
||||
|
||||
export default EventListSummary;
|
||||
|
|
|
@ -73,6 +73,7 @@ export default class ImageView extends React.Component {
|
|||
Modal.createTrackedDialog('Confirm Redact Dialog', 'Image View', ConfirmRedactDialog, {
|
||||
onFinished: (proceed) => {
|
||||
if (!proceed) return;
|
||||
this.props.onFinished();
|
||||
MatrixClientPeg.get().redactEvent(
|
||||
this.props.mxEvent.getRoomId(), this.props.mxEvent.getId(),
|
||||
).catch(function(e) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import * as sdk from '../../../index';
|
|||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import Modal from '../../../Modal';
|
||||
import classNames from 'classnames';
|
||||
import RedactedBody from "./RedactedBody";
|
||||
|
||||
function getReplacedContent(event) {
|
||||
const originalContent = event.getOriginalContent();
|
||||
|
@ -132,8 +133,7 @@ export default class EditHistoryMessage extends React.PureComponent {
|
|||
const content = getReplacedContent(mxEvent);
|
||||
let contentContainer;
|
||||
if (mxEvent.isRedacted()) {
|
||||
const UnknownBody = sdk.getComponent('messages.UnknownBody');
|
||||
contentContainer = <UnknownBody mxEvent={this.props.mxEvent} />;
|
||||
contentContainer = <RedactedBody mxEvent={this.props.mxEvent} />;
|
||||
} else {
|
||||
let contentElements;
|
||||
if (this.props.previousEdit) {
|
||||
|
@ -158,7 +158,6 @@ export default class EditHistoryMessage extends React.PureComponent {
|
|||
const isSending = (['sending', 'queued', 'encrypting'].indexOf(this.state.sendStatus) !== -1);
|
||||
const classes = classNames({
|
||||
"mx_EventTile": true,
|
||||
"mx_EventTile_redacted": mxEvent.isRedacted(),
|
||||
"mx_EventTile_sending": isSending,
|
||||
"mx_EventTile_notSent": this.state.sendStatus === 'not_sent',
|
||||
});
|
||||
|
|
|
@ -20,6 +20,8 @@ import createReactClass from 'create-react-class';
|
|||
import * as sdk from '../../../index';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {Mjolnir} from "../../../mjolnir/Mjolnir";
|
||||
import RedactedBody from "./RedactedBody";
|
||||
import UnknownBody from "./UnknownBody";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'MessageEvent',
|
||||
|
@ -61,8 +63,6 @@ export default createReactClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
const UnknownBody = sdk.getComponent('messages.UnknownBody');
|
||||
|
||||
const bodyTypes = {
|
||||
'm.text': sdk.getComponent('messages.TextualBody'),
|
||||
'm.notice': sdk.getComponent('messages.TextualBody'),
|
||||
|
@ -79,7 +79,7 @@ export default createReactClass({
|
|||
const content = this.props.mxEvent.getContent();
|
||||
const type = this.props.mxEvent.getType();
|
||||
const msgtype = content.msgtype;
|
||||
let BodyType = UnknownBody;
|
||||
let BodyType = RedactedBody;
|
||||
if (!this.props.mxEvent.isRedacted()) {
|
||||
// only resolve BodyType if event is not redacted
|
||||
if (type && evTypes[type]) {
|
||||
|
@ -89,6 +89,9 @@ export default createReactClass({
|
|||
} else if (content.url) {
|
||||
// Fallback to MFileBody if there's a content URL
|
||||
BodyType = bodyTypes['m.file'];
|
||||
} else {
|
||||
// Fallback to UnknownBody otherwise if not redacted
|
||||
BodyType = UnknownBody;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
46
src/components/views/messages/RedactedBody.tsx
Normal file
46
src/components/views/messages/RedactedBody.tsx
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
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 React, {useContext} from "react";
|
||||
import {MatrixClient} from "matrix-js-sdk/src/client";
|
||||
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
|
||||
interface IProps {
|
||||
mxEvent: MatrixEvent;
|
||||
}
|
||||
|
||||
const RedactedBody = React.forwardRef<any, IProps>(({mxEvent}, ref) => {
|
||||
const cli: MatrixClient = useContext(MatrixClientContext);
|
||||
|
||||
let text = _t("Message deleted");
|
||||
const unsigned = mxEvent.getUnsigned();
|
||||
const redactedBecauseUserId = unsigned && unsigned.redacted_because && unsigned.redacted_because.sender;
|
||||
if (redactedBecauseUserId && redactedBecauseUserId !== mxEvent.getSender()) {
|
||||
const room = cli.getRoom(mxEvent.getRoomId());
|
||||
const sender = room && room.getMember(redactedBecauseUserId);
|
||||
text = _t("Message deleted by %(name)s", { name: sender ? sender.name : redactedBecauseUserId });
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="mx_RedactedBody" ref={ref}>
|
||||
{ text }
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
export default RedactedBody;
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -14,27 +15,13 @@ 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 { _t } from '../../../languageHandler';
|
||||
import React from "react";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'UnknownBody',
|
||||
|
||||
render: function() {
|
||||
let tooltip = _t("Removed or unknown message type");
|
||||
if (this.props.mxEvent.isRedacted()) {
|
||||
const redactedBecauseUserId = this.props.mxEvent.getUnsigned().redacted_because.sender;
|
||||
tooltip = redactedBecauseUserId ?
|
||||
_t("Message removed by %(userId)s", { userId: redactedBecauseUserId }) :
|
||||
_t("Message removed");
|
||||
}
|
||||
|
||||
const text = this.props.mxEvent.getContent().body;
|
||||
return (
|
||||
<span className="mx_UnknownBody" title={tooltip}>
|
||||
{ text }
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
export default ({mxEvent}) => {
|
||||
const text = mxEvent.getContent().body;
|
||||
return (
|
||||
<span className="mx_UnknownBody">
|
||||
{ text }
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -45,9 +45,6 @@ const EncryptionPanel = (props) => {
|
|||
}
|
||||
}, [verificationRequest]);
|
||||
|
||||
const deviceId = request && request.channel.deviceId;
|
||||
const device = MatrixClientPeg.get().getStoredDevice(MatrixClientPeg.get().getUserId(), deviceId);
|
||||
|
||||
useEffect(() => {
|
||||
async function awaitPromise() {
|
||||
setRequesting(true);
|
||||
|
@ -143,7 +140,7 @@ const EncryptionPanel = (props) => {
|
|||
key={request.channel.transactionId}
|
||||
inDialog={layout === "dialog"}
|
||||
phase={phase}
|
||||
device={device} />
|
||||
/>
|
||||
</React.Fragment>);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -557,7 +557,7 @@ const RedactMessagesButton = ({member}) => {
|
|||
let eventsToRedact = [];
|
||||
while (timeline) {
|
||||
eventsToRedact = timeline.getEvents().reduce((events, event) => {
|
||||
if (event.getSender() === userId && !event.isRedacted()) {
|
||||
if (event.getSender() === userId && !event.isRedacted() && !event.isRedaction()) {
|
||||
return events.concat(event);
|
||||
} else {
|
||||
return events;
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||
import * as sdk from '../../../index';
|
||||
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
|
||||
import {SCAN_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
|
||||
|
@ -161,6 +162,11 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
this.state.reciprocateQREvent.cancel();
|
||||
};
|
||||
|
||||
_getDevice() {
|
||||
const deviceId = this.props.request && this.props.request.channel.deviceId;
|
||||
return MatrixClientPeg.get().getStoredDevice(MatrixClientPeg.get().getUserId(), deviceId);
|
||||
}
|
||||
|
||||
renderQRReciprocatePhase() {
|
||||
const {member, request} = this.props;
|
||||
let Button;
|
||||
|
@ -217,16 +223,27 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
const description = request.isSelfVerification ?
|
||||
_t("You've successfully verified %(deviceName)s (%(deviceId)s)!", {
|
||||
deviceName: this.props.device.getDisplayName(),
|
||||
deviceId: this.props.device.deviceId,
|
||||
}):
|
||||
_t("You've successfully verified %(displayName)s!", {
|
||||
let description;
|
||||
if (request.isSelfVerification) {
|
||||
const device = this._getDevice();
|
||||
if (!device) {
|
||||
// This can happen if the device is logged out while we're still showing verification
|
||||
// UI for it.
|
||||
console.warn("Verified device we don't know about: " + this.props.request.channel.deviceId);
|
||||
description = _t("You've successfully verified your device!");
|
||||
} else {
|
||||
description = _t("You've successfully verified %(deviceName)s (%(deviceId)s)!", {
|
||||
deviceName: device ? device.getDisplayName() : '',
|
||||
deviceId: this.props.request.channel.deviceId,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
description = _t("You've successfully verified %(displayName)s!", {
|
||||
displayName: member.displayName || member.name || member.userId,
|
||||
});
|
||||
}
|
||||
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
return (
|
||||
<div className="mx_UserInfo_container mx_VerificationPanel_verified_section">
|
||||
<h3>{_t("Verified")}</h3>
|
||||
|
@ -297,12 +314,12 @@ export default class VerificationPanel extends React.PureComponent {
|
|||
const emojis = this.state.sasEvent ?
|
||||
<VerificationShowSas
|
||||
displayName={displayName}
|
||||
device={this._getDevice()}
|
||||
sas={this.state.sasEvent.sas}
|
||||
onCancel={this._onSasMismatchesClick}
|
||||
onDone={this._onSasMatchesClick}
|
||||
inDialog={this.props.inDialog}
|
||||
isSelf={request.isSelfVerification}
|
||||
device={this.props.device}
|
||||
/> : <Spinner />;
|
||||
return <div className="mx_UserInfo_container">
|
||||
<h3>{_t("Compare emoji")}</h3>
|
||||
|
|
|
@ -673,7 +673,6 @@ export default createReactClass({
|
|||
mx_EventTile_unknown: !isBubbleMessage && this.state.verified === E2E_STATE.UNKNOWN,
|
||||
mx_EventTile_bad: isEncryptionFailure,
|
||||
mx_EventTile_emote: msgtype === 'm.emote',
|
||||
mx_EventTile_redacted: isRedacted,
|
||||
});
|
||||
|
||||
let permalink = "#";
|
||||
|
|
|
@ -364,7 +364,7 @@ export default createReactClass({
|
|||
let eventsToRedact = [];
|
||||
for (const timeline of timelineSet.getTimelines()) {
|
||||
eventsToRedact = timeline.getEvents().reduce((events, event) => {
|
||||
if (event.getSender() === userId && !event.isRedacted()) {
|
||||
if (event.getSender() === userId && !event.isRedacted() && !event.isRedaction()) {
|
||||
return events.concat(event);
|
||||
} else {
|
||||
return events;
|
||||
|
|
|
@ -266,9 +266,9 @@ export default createReactClass({
|
|||
params: {
|
||||
email: this.props.invitedEmail,
|
||||
signurl: this.props.signUrl,
|
||||
room_name: this.props.oobData.room_name,
|
||||
room_avatar_url: this.props.oobData.avatarUrl,
|
||||
inviter_name: this.props.oobData.inviterName,
|
||||
room_name: this.props.oobData ? this.props.oobData.room_name : null,
|
||||
room_avatar_url: this.props.oobData ? this.props.oobData.avatarUrl : null,
|
||||
inviter_name: this.props.oobData ? this.props.oobData.inviterName : null,
|
||||
}
|
||||
};
|
||||
},
|
||||
|
|
|
@ -131,8 +131,8 @@ export default class CrossSigningPanel extends React.PureComponent {
|
|||
}
|
||||
|
||||
_destroySecureSecretStorage = () => {
|
||||
const ConfirmDestoryCrossSigningDialog = sdk.getComponent("dialogs.ConfirmDestroyCrossSigningDialog");
|
||||
Modal.createDialog(ConfirmDestoryCrossSigningDialog, {
|
||||
const ConfirmDestroyCrossSigningDialog = sdk.getComponent("dialogs.ConfirmDestroyCrossSigningDialog");
|
||||
Modal.createDialog(ConfirmDestroyCrossSigningDialog, {
|
||||
onFinished: this.onDestroyStorage,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -112,7 +112,10 @@ export default class SecurityUserSettingsTab extends React.Component {
|
|||
};
|
||||
|
||||
_onGoToUserProfileClick = () => {
|
||||
window.location.href = "#/user/" + MatrixClientPeg.get().getUserId();
|
||||
dis.dispatch({
|
||||
action: 'view_user_info',
|
||||
userId: MatrixClientPeg.get().getUserId(),
|
||||
});
|
||||
this.props.closeSettingsFn();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ export default class VerificationShowSas extends React.Component {
|
|||
static propTypes = {
|
||||
pending: PropTypes.bool,
|
||||
displayName: PropTypes.string, // required if pending is true
|
||||
device: PropTypes.object,
|
||||
onDone: PropTypes.func.isRequired,
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
sas: PropTypes.object.isRequired,
|
||||
|
@ -116,10 +117,16 @@ export default class VerificationShowSas extends React.Component {
|
|||
let text;
|
||||
if (this.state.pending) {
|
||||
if (this.props.isSelf) {
|
||||
text = _t("Waiting for your other session, %(deviceName)s (%(deviceId)s), to verify…", {
|
||||
deviceName: this.props.device.getDisplayName(),
|
||||
deviceId: this.props.device.deviceId,
|
||||
});
|
||||
// device shouldn't be null in this situation but it can be, eg. if the device is
|
||||
// logged out during verification
|
||||
if (this.props.device) {
|
||||
text = _t("Waiting for your other session, %(deviceName)s (%(deviceId)s), to verify…", {
|
||||
deviceName: this.props.device ? this.props.device.getDisplayName() : '',
|
||||
deviceId: this.props.device ? this.props.device.deviceId : '',
|
||||
});
|
||||
} else {
|
||||
text = _t("Waiting for your other session to verify…");
|
||||
}
|
||||
} else {
|
||||
const {displayName} = this.props;
|
||||
text = _t("Waiting for %(displayName)s to verify…", {displayName});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue