Merge branch 'develop' into travis/cross-sign-setting
This commit is contained in:
commit
db749519e7
18 changed files with 226 additions and 176 deletions
|
@ -59,17 +59,17 @@ export default class CompleteSecurity extends React.Component {
|
|||
let title;
|
||||
|
||||
if (phase === PHASE_INTRO) {
|
||||
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning"></span>;
|
||||
title = _t("Complete security");
|
||||
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
|
||||
title = _t("Verify this session");
|
||||
} else if (phase === PHASE_DONE) {
|
||||
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_verified"></span>;
|
||||
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_verified" />;
|
||||
title = _t("Session verified");
|
||||
} else if (phase === PHASE_CONFIRM_SKIP) {
|
||||
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning"></span>;
|
||||
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
|
||||
title = _t("Are you sure?");
|
||||
} else if (phase === PHASE_BUSY) {
|
||||
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning"></span>;
|
||||
title = _t("Complete security");
|
||||
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
|
||||
title = _t("Verify this session");
|
||||
} else {
|
||||
throw new Error(`Unknown phase ${phase}`);
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ export default class SetupEncryptionBody extends React.Component {
|
|||
"granting it access to encrypted messages.",
|
||||
)}</p>
|
||||
<p>{_t(
|
||||
"If you can’t access one, <button>use your recovery key or passphrase.</button>",
|
||||
"If you can’t access one, <button>use your recovery key or recovery passphrase.</button>",
|
||||
{}, {
|
||||
button: sub => <AccessibleButton element="span"
|
||||
className="mx_linkButton"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2018 New Vector 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,15 +15,20 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, {createRef} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Room, User, Group, RoomMember, MatrixEvent} from 'matrix-js-sdk';
|
||||
import * as React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import {Room} from "matrix-js-sdk/src/models/room";
|
||||
import {User} from "matrix-js-sdk/src/models/user";
|
||||
import {Group} from "matrix-js-sdk/src/models/group";
|
||||
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
|
||||
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import QRCode from 'qrcode-react';
|
||||
import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../utils/permalinks/Permalinks";
|
||||
import * as ContextMenu from "../../structures/ContextMenu";
|
||||
import {toRightOf} from "../../structures/ContextMenu";
|
||||
import {copyPlaintext, selectText} from "../../../utils/strings";
|
||||
|
||||
const socials = [
|
||||
{
|
||||
|
@ -52,7 +58,18 @@ const socials = [
|
|||
},
|
||||
];
|
||||
|
||||
export default class ShareDialog extends React.Component {
|
||||
interface IProps {
|
||||
onFinished: () => void;
|
||||
target: Room | User | Group | RoomMember | MatrixEvent;
|
||||
permalinkCreator: RoomPermalinkCreator;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
linkSpecificEvent: boolean;
|
||||
permalinkCreator: RoomPermalinkCreator;
|
||||
}
|
||||
|
||||
export default class ShareDialog extends React.PureComponent<IProps, IState> {
|
||||
static propTypes = {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
target: PropTypes.oneOfType([
|
||||
|
@ -64,6 +81,8 @@ export default class ShareDialog extends React.Component {
|
|||
]).isRequired,
|
||||
};
|
||||
|
||||
protected closeCopiedTooltip: () => void;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
@ -81,45 +100,26 @@ export default class ShareDialog extends React.Component {
|
|||
linkSpecificEvent: this.props.target instanceof MatrixEvent,
|
||||
permalinkCreator,
|
||||
};
|
||||
|
||||
this._link = createRef();
|
||||
}
|
||||
|
||||
static _selectText(target) {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(target);
|
||||
|
||||
const selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
|
||||
static onLinkClick(e) {
|
||||
e.preventDefault();
|
||||
const {target} = e;
|
||||
ShareDialog._selectText(target);
|
||||
selectText(e.target);
|
||||
}
|
||||
|
||||
onCopyClick(e) {
|
||||
async onCopyClick(e) {
|
||||
e.preventDefault();
|
||||
const target = e.target; // copy target before we go async and React throws it away
|
||||
|
||||
ShareDialog._selectText(this._link.current);
|
||||
|
||||
let successful;
|
||||
try {
|
||||
successful = document.execCommand('copy');
|
||||
} catch (err) {
|
||||
console.error('Failed to copy: ', err);
|
||||
}
|
||||
|
||||
const buttonRect = e.target.getBoundingClientRect();
|
||||
const successful = await copyPlaintext(this.getUrl());
|
||||
const buttonRect = target.getBoundingClientRect();
|
||||
const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
|
||||
const {close} = ContextMenu.createMenu(GenericTextContextMenu, {
|
||||
...toRightOf(buttonRect, 2),
|
||||
message: successful ? _t('Copied!') : _t('Failed to copy'),
|
||||
});
|
||||
// Drop a reference to this close handler for componentWillUnmount
|
||||
this.closeCopiedTooltip = e.target.onmouseleave = close;
|
||||
this.closeCopiedTooltip = target.onmouseleave = close;
|
||||
}
|
||||
|
||||
onLinkSpecificEventCheckboxClick() {
|
||||
|
@ -134,10 +134,32 @@ export default class ShareDialog extends React.Component {
|
|||
if (this.closeCopiedTooltip) this.closeCopiedTooltip();
|
||||
}
|
||||
|
||||
render() {
|
||||
let title;
|
||||
getUrl() {
|
||||
let matrixToUrl;
|
||||
|
||||
if (this.props.target instanceof Room) {
|
||||
if (this.state.linkSpecificEvent) {
|
||||
const events = this.props.target.getLiveTimeline().getEvents();
|
||||
matrixToUrl = this.state.permalinkCreator.forEvent(events[events.length - 1].getId());
|
||||
} else {
|
||||
matrixToUrl = this.state.permalinkCreator.forRoom();
|
||||
}
|
||||
} else if (this.props.target instanceof User || this.props.target instanceof RoomMember) {
|
||||
matrixToUrl = makeUserPermalink(this.props.target.userId);
|
||||
} else if (this.props.target instanceof Group) {
|
||||
matrixToUrl = makeGroupPermalink(this.props.target.groupId);
|
||||
} else if (this.props.target instanceof MatrixEvent) {
|
||||
if (this.state.linkSpecificEvent) {
|
||||
matrixToUrl = this.props.permalinkCreator.forEvent(this.props.target.getId());
|
||||
} else {
|
||||
matrixToUrl = this.props.permalinkCreator.forRoom();
|
||||
}
|
||||
}
|
||||
return matrixToUrl;
|
||||
}
|
||||
|
||||
render() {
|
||||
let title;
|
||||
let checkbox;
|
||||
|
||||
if (this.props.target instanceof Room) {
|
||||
|
@ -155,18 +177,10 @@ export default class ShareDialog extends React.Component {
|
|||
</label>
|
||||
</div>;
|
||||
}
|
||||
|
||||
if (this.state.linkSpecificEvent) {
|
||||
matrixToUrl = this.state.permalinkCreator.forEvent(events[events.length - 1].getId());
|
||||
} else {
|
||||
matrixToUrl = this.state.permalinkCreator.forRoom();
|
||||
}
|
||||
} else if (this.props.target instanceof User || this.props.target instanceof RoomMember) {
|
||||
title = _t('Share User');
|
||||
matrixToUrl = makeUserPermalink(this.props.target.userId);
|
||||
} else if (this.props.target instanceof Group) {
|
||||
title = _t('Share Community');
|
||||
matrixToUrl = makeGroupPermalink(this.props.target.groupId);
|
||||
} else if (this.props.target instanceof MatrixEvent) {
|
||||
title = _t('Share Room Message');
|
||||
checkbox = <div>
|
||||
|
@ -178,14 +192,9 @@ export default class ShareDialog extends React.Component {
|
|||
{ _t('Link to selected message') }
|
||||
</label>
|
||||
</div>;
|
||||
|
||||
if (this.state.linkSpecificEvent) {
|
||||
matrixToUrl = this.props.permalinkCreator.forEvent(this.props.target.getId());
|
||||
} else {
|
||||
matrixToUrl = this.props.permalinkCreator.forRoom();
|
||||
}
|
||||
}
|
||||
|
||||
const matrixToUrl = this.getUrl();
|
||||
const encodedUrl = encodeURIComponent(matrixToUrl);
|
||||
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
|
@ -196,8 +205,7 @@ export default class ShareDialog extends React.Component {
|
|||
>
|
||||
<div className="mx_ShareDialog_content">
|
||||
<div className="mx_ShareDialog_matrixto">
|
||||
<a ref={this._link}
|
||||
href={matrixToUrl}
|
||||
<a href={matrixToUrl}
|
||||
onClick={ShareDialog.onLinkClick}
|
||||
className="mx_ShareDialog_matrixto_link"
|
||||
>
|
||||
|
@ -216,17 +224,18 @@ export default class ShareDialog extends React.Component {
|
|||
<QRCode value={matrixToUrl} size={256} logoWidth={48} logo={require("../../../../res/img/matrix-m.svg")} />
|
||||
</div>
|
||||
<div className="mx_ShareDialog_social_container">
|
||||
{
|
||||
socials.map((social) => <a rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
key={social.name}
|
||||
name={social.name}
|
||||
href={social.url(encodedUrl)}
|
||||
className="mx_ShareDialog_social_icon"
|
||||
{ socials.map((social) => (
|
||||
<a
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
key={social.name}
|
||||
title={social.name}
|
||||
href={social.url(encodedUrl)}
|
||||
className="mx_ShareDialog_social_icon"
|
||||
>
|
||||
<img src={social.img} alt={social.name} height={64} width={64} />
|
||||
</a>)
|
||||
}
|
||||
</a>
|
||||
)) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -48,7 +48,7 @@ export default class VerificationRequestDialog extends React.Component {
|
|||
const member = this.props.member ||
|
||||
otherUserId && MatrixClientPeg.get().getUser(otherUserId);
|
||||
const title = request && request.isSelfVerification ?
|
||||
_t("Verify this session") : _t("Verification Request");
|
||||
_t("Verify other session") : _t("Verification Request");
|
||||
|
||||
return <BaseDialog className="mx_InfoDialog" onFinished={this.onFinished}
|
||||
contentId="mx_Dialog_content"
|
||||
|
|
|
@ -283,7 +283,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
title = _t("Recovery key mismatch");
|
||||
content = <div>
|
||||
<p>{_t(
|
||||
"Backup could not be decrypted with this key: " +
|
||||
"Backup could not be decrypted with this recovery key: " +
|
||||
"please verify that you entered the correct recovery key.",
|
||||
)}</p>
|
||||
</div>;
|
||||
|
@ -291,7 +291,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
title = _t("Incorrect recovery passphrase");
|
||||
content = <div>
|
||||
<p>{_t(
|
||||
"Backup could not be decrypted with this passphrase: " +
|
||||
"Backup could not be decrypted with this recovery passphrase: " +
|
||||
"please verify that you entered the correct recovery passphrase.",
|
||||
)}</p>
|
||||
</div>;
|
||||
|
|
|
@ -119,14 +119,14 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
if (hasPassphrase && !this.state.forceRecoveryKey) {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
title = _t("Enter secret storage passphrase");
|
||||
title = _t("Enter recovery passphrase");
|
||||
|
||||
let keyStatus;
|
||||
if (this.state.keyMatches === false) {
|
||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
|
||||
{"\uD83D\uDC4E "}{_t(
|
||||
"Unable to access secret storage. Please verify that you " +
|
||||
"entered the correct passphrase.",
|
||||
"Unable to access secret storage. " +
|
||||
"Please verify that you entered the correct recovery passphrase.",
|
||||
)}
|
||||
</div>;
|
||||
} else {
|
||||
|
@ -135,13 +135,12 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
|
||||
content = <div>
|
||||
<p>{_t(
|
||||
"<b>Warning</b>: You should only access secret storage " +
|
||||
"from a trusted computer.", {},
|
||||
"<b>Warning</b>: You should only do this on a trusted computer.", {},
|
||||
{ b: sub => <b>{sub}</b> },
|
||||
)}</p>
|
||||
<p>{_t(
|
||||
"Access your secure message history and your cross-signing " +
|
||||
"identity for verifying other sessions by entering your passphrase.",
|
||||
"identity for verifying other sessions by entering your recovery passphrase.",
|
||||
)}</p>
|
||||
|
||||
<form className="mx_AccessSecretStorageDialog_primaryContainer" onSubmit={this._onPassPhraseNext}>
|
||||
|
@ -164,7 +163,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
/>
|
||||
</form>
|
||||
{_t(
|
||||
"If you've forgotten your passphrase you can "+
|
||||
"If you've forgotten your recovery passphrase you can "+
|
||||
"<button1>use your recovery key</button1> or " +
|
||||
"<button2>set up new recovery options</button2>."
|
||||
, {}, {
|
||||
|
@ -183,7 +182,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
})}
|
||||
</div>;
|
||||
} else {
|
||||
title = _t("Enter secret storage recovery key");
|
||||
title = _t("Enter recovery key");
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
||||
|
@ -193,8 +192,8 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
} else if (this.state.keyMatches === false) {
|
||||
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
|
||||
{"\uD83D\uDC4E "}{_t(
|
||||
"Unable to access secret storage. Please verify that you " +
|
||||
"entered the correct recovery key.",
|
||||
"Unable to access secret storage. " +
|
||||
"Please verify that you entered the correct recovery key.",
|
||||
)}
|
||||
</div>;
|
||||
} else if (this.state.recoveryKeyValid) {
|
||||
|
@ -209,8 +208,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
|
|||
|
||||
content = <div>
|
||||
<p>{_t(
|
||||
"<b>Warning</b>: You should only access secret storage " +
|
||||
"from a trusted computer.", {},
|
||||
"<b>Warning</b>: You should only do this on a trusted computer.", {},
|
||||
{ b: sub => <b>{sub}</b> },
|
||||
)}</p>
|
||||
<p>{_t(
|
||||
|
|
|
@ -34,6 +34,7 @@ import {pillifyLinks, unmountPills} from '../../../utils/pillify';
|
|||
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||
import {isPermalinkHost} from "../../../utils/permalinks/Permalinks";
|
||||
import {toRightOf} from "../../structures/ContextMenu";
|
||||
import {copyPlaintext} from "../../../utils/strings";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'TextualBody',
|
||||
|
@ -69,23 +70,6 @@ export default createReactClass({
|
|||
};
|
||||
},
|
||||
|
||||
copyToClipboard: function(text) {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
|
||||
let successful = false;
|
||||
try {
|
||||
successful = document.execCommand('copy');
|
||||
} catch (err) {
|
||||
console.log('Unable to copy');
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
return successful;
|
||||
},
|
||||
|
||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._content = createRef();
|
||||
|
@ -277,17 +261,17 @@ export default createReactClass({
|
|||
Array.from(ReactDOM.findDOMNode(this).querySelectorAll('.mx_EventTile_body pre')).forEach((p) => {
|
||||
const button = document.createElement("span");
|
||||
button.className = "mx_EventTile_copyButton";
|
||||
button.onclick = (e) => {
|
||||
button.onclick = async () => {
|
||||
const copyCode = button.parentNode.getElementsByTagName("pre")[0];
|
||||
const successful = this.copyToClipboard(copyCode.textContent);
|
||||
const successful = await copyPlaintext(copyCode.textContent);
|
||||
|
||||
const buttonRect = e.target.getBoundingClientRect();
|
||||
const buttonRect = button.getBoundingClientRect();
|
||||
const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
|
||||
const {close} = ContextMenu.createMenu(GenericTextContextMenu, {
|
||||
...toRightOf(buttonRect, 2),
|
||||
message: successful ? _t('Copied!') : _t('Failed to copy'),
|
||||
});
|
||||
e.target.onmouseleave = close;
|
||||
button.onmouseleave = close;
|
||||
};
|
||||
|
||||
// Wrap a div around <pre> so that the copy button can be correctly positioned
|
||||
|
|
|
@ -18,7 +18,9 @@ import React from 'react';
|
|||
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
||||
import {_t} from "../../../languageHandler";
|
||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||
|
||||
@replaceableComponent("views.room_settings.RoomPublishSetting")
|
||||
export default class RoomPublishSetting extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
|
@ -20,6 +20,7 @@ import { _t, _td } from '../../../languageHandler';
|
|||
import {PendingActionSpinner} from "../right_panel/EncryptionInfo";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import { fixupColorFonts } from '../../../utils/FontManager';
|
||||
|
||||
function capFirst(s) {
|
||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||
|
@ -44,6 +45,13 @@ export default class VerificationShowSas extends React.Component {
|
|||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
// As this component is also used before login (during complete security),
|
||||
// also make sure we have a working emoji font to display the SAS emojis here.
|
||||
// This is also done from LoggedInView.
|
||||
fixupColorFonts();
|
||||
}
|
||||
|
||||
onMatchClick = () => {
|
||||
this.setState({ pending: true });
|
||||
this.props.onDone();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue