Merge branch 'develop' into travis/dispatcher-types
This commit is contained in:
@@ -412,14 +412,14 @@ export const EmailIdentityAuthEntry = createReactClass({
|
||||
this.props.onPhaseChange(DEFAULT_PHASE);
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
requestingToken: false,
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.state.requestingToken) {
|
||||
// This component is now only displayed once the token has been requested,
|
||||
// so we know the email has been sent. It can also get loaded after the user
|
||||
// has clicked the validation link if the server takes a while to propagate
|
||||
// the validation internally. If we're in the session spawned from clicking
|
||||
// the validation link, we won't know the email address, so if we don't have it,
|
||||
// assume that the link has been clicked and the server will realise when we poll.
|
||||
if (this.props.inputs.emailAddress === undefined) {
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
return <Loader />;
|
||||
} else {
|
||||
|
||||
@@ -45,7 +45,8 @@ export default class Welcome extends React.PureComponent {
|
||||
idBaseUrl: isUrl,
|
||||
});
|
||||
const plaf = PlatformPeg.get();
|
||||
const callbackUrl = plaf.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl());
|
||||
const callbackUrl = plaf.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl(),
|
||||
this.props.fragmentAfterLogin);
|
||||
|
||||
return (
|
||||
<AuthPage>
|
||||
|
||||
@@ -34,6 +34,7 @@ export default class DeactivateAccountDialog extends React.Component {
|
||||
shouldErase: false,
|
||||
errStr: null,
|
||||
authData: null, // for UIA
|
||||
authEnabled: true, // see usages for information
|
||||
|
||||
// A few strings that are passed to InteractiveAuth for design or are displayed
|
||||
// next to the InteractiveAuth component.
|
||||
@@ -42,21 +43,7 @@ export default class DeactivateAccountDialog extends React.Component {
|
||||
continueKind: null,
|
||||
};
|
||||
|
||||
MatrixClientPeg.get().deactivateAccount(null, false).then(r => {
|
||||
// If we got here, oops. The server didn't require any auth.
|
||||
// Our application lifecycle will catch the error and do the logout bits.
|
||||
// We'll try to log something in an vain attempt to record what happened (storage
|
||||
// is also obliterated on logout).
|
||||
console.warn("User's account got deactivated without confirmation: Server had no auth");
|
||||
this.setState({errStr: _t("Server did not require any authentication")});
|
||||
}).catch(e => {
|
||||
if (e && e.httpStatus === 401 && e.data) {
|
||||
// Valid UIA response
|
||||
this.setState({authData: e.data});
|
||||
} else {
|
||||
this.setState({errStr: _t("Server did not return valid authentication information.")});
|
||||
}
|
||||
});
|
||||
this._initAuth(/* shouldErase= */false);
|
||||
}
|
||||
|
||||
_onStagePhaseChange = (stage, phase) => {
|
||||
@@ -124,13 +111,40 @@ export default class DeactivateAccountDialog extends React.Component {
|
||||
_onEraseFieldChange = (ev) => {
|
||||
this.setState({
|
||||
shouldErase: ev.target.checked,
|
||||
|
||||
// Disable the auth form because we're going to have to reinitialize the auth
|
||||
// information. We do this because we can't modify the parameters in the UIA
|
||||
// session, and the user will have selected something which changes the request.
|
||||
// Therefore, we throw away the last auth session and try a new one.
|
||||
authEnabled: false,
|
||||
});
|
||||
|
||||
// As mentioned above, set up for auth again to get updated UIA session info
|
||||
this._initAuth(/* shouldErase= */ev.target.checked);
|
||||
};
|
||||
|
||||
_onCancel() {
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
|
||||
_initAuth(shouldErase) {
|
||||
MatrixClientPeg.get().deactivateAccount(null, shouldErase).then(r => {
|
||||
// If we got here, oops. The server didn't require any auth.
|
||||
// Our application lifecycle will catch the error and do the logout bits.
|
||||
// We'll try to log something in an vain attempt to record what happened (storage
|
||||
// is also obliterated on logout).
|
||||
console.warn("User's account got deactivated without confirmation: Server had no auth");
|
||||
this.setState({errStr: _t("Server did not require any authentication")});
|
||||
}).catch(e => {
|
||||
if (e && e.httpStatus === 401 && e.data) {
|
||||
// Valid UIA response
|
||||
this.setState({authData: e.data, authEnabled: true});
|
||||
} else {
|
||||
this.setState({errStr: _t("Server did not return valid authentication information.")});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
|
||||
@@ -142,7 +156,7 @@ export default class DeactivateAccountDialog extends React.Component {
|
||||
}
|
||||
|
||||
let auth = <div>{_t("Loading...")}</div>;
|
||||
if (this.state.authData) {
|
||||
if (this.state.authData && this.state.authEnabled) {
|
||||
auth = (
|
||||
<div>
|
||||
{this.state.bodyText}
|
||||
|
||||
@@ -24,7 +24,7 @@ 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 QRCode from "../elements/QRCode";
|
||||
import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../utils/permalinks/Permalinks";
|
||||
import * as ContextMenu from "../../structures/ContextMenu";
|
||||
import {toRightOf} from "../../structures/ContextMenu";
|
||||
@@ -221,7 +221,7 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
|
||||
|
||||
<div className="mx_ShareDialog_split">
|
||||
<div className="mx_ShareDialog_qrcode_container">
|
||||
<QRCode value={matrixToUrl} size={256} logoWidth={48} logo={require("../../../../res/img/matrix-m.svg")} />
|
||||
<QRCode data={matrixToUrl} width={256} />
|
||||
</div>
|
||||
<div className="mx_ShareDialog_social_container">
|
||||
{ socials.map((social) => (
|
||||
|
||||
51
src/components/views/elements/QRCode.tsx
Normal file
51
src/components/views/elements/QRCode.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import {toDataURL, QRCodeSegment, QRCodeToDataURLOptions} from "qrcode";
|
||||
import classNames from "classnames";
|
||||
|
||||
import {_t} from "../../../languageHandler";
|
||||
import Spinner from "./Spinner";
|
||||
|
||||
interface IProps extends QRCodeToDataURLOptions {
|
||||
data: string | QRCodeSegment[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const defaultOptions: QRCodeToDataURLOptions = {
|
||||
errorCorrectionLevel: 'L', // we want it as trivial-looking as possible
|
||||
};
|
||||
|
||||
const QRCode: React.FC<IProps> = ({data, className, ...options}) => {
|
||||
const [dataUri, setUri] = React.useState<string>(null);
|
||||
React.useEffect(() => {
|
||||
let cancelled = false;
|
||||
toDataURL(data, {...defaultOptions, ...options}).then(uri => {
|
||||
if (cancelled) return;
|
||||
setUri(uri);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [JSON.stringify(data), options]);
|
||||
|
||||
return <div className={classNames("mx_QRCode", className)}>
|
||||
{ dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} /> : <Spinner /> }
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default QRCode;
|
||||
@@ -21,9 +21,9 @@ import PlatformPeg from "../../../PlatformPeg";
|
||||
import AccessibleButton from "./AccessibleButton";
|
||||
import {_t} from "../../../languageHandler";
|
||||
|
||||
const SSOButton = ({matrixClient, loginType, ...props}) => {
|
||||
const SSOButton = ({matrixClient, loginType, fragmentAfterLogin, ...props}) => {
|
||||
const onClick = () => {
|
||||
PlatformPeg.get().startSingleSignOn(matrixClient, loginType);
|
||||
PlatformPeg.get().startSingleSignOn(matrixClient, loginType, fragmentAfterLogin);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -36,6 +36,7 @@ const SSOButton = ({matrixClient, loginType, ...props}) => {
|
||||
SSOButton.propTypes = {
|
||||
matrixClient: PropTypes.object.isRequired, // does not use context as may use a temporary client
|
||||
loginType: PropTypes.oneOf(["sso", "cas"]), // defaults to "sso" in base-apis
|
||||
fragmentAfterLogin: PropTypes.string,
|
||||
};
|
||||
|
||||
export default SSOButton;
|
||||
|
||||
@@ -17,8 +17,7 @@ limitations under the License.
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {replaceableComponent} from "../../../../utils/replaceableComponent";
|
||||
import Spinner from "../Spinner";
|
||||
import * as QRCode from "qrcode";
|
||||
import QRCode from "../QRCode";
|
||||
|
||||
@replaceableComponent("views.elements.crypto.VerificationQRCode")
|
||||
export default class VerificationQRCode extends React.PureComponent {
|
||||
@@ -26,33 +25,12 @@ export default class VerificationQRCode extends React.PureComponent {
|
||||
qrCodeData: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
dataUri: null,
|
||||
};
|
||||
this.generateQrCode();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps): void {
|
||||
if (JSON.stringify(this.props) === JSON.stringify(prevProps)) return; // No prop change
|
||||
|
||||
this.generateQRCode();
|
||||
}
|
||||
|
||||
async generateQrCode() {
|
||||
// Now actually assemble the QR code's data URI
|
||||
const uri = await QRCode.toDataURL([{data: this.props.qrCodeData.buffer, mode: 'byte'}], {
|
||||
errorCorrectionLevel: 'L', // we want it as trivial-looking as possible
|
||||
});
|
||||
this.setState({dataUri: uri});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state.dataUri) {
|
||||
return <div className='mx_VerificationQRCode'><Spinner /></div>;
|
||||
}
|
||||
|
||||
return <img src={this.state.dataUri} className='mx_VerificationQRCode' />;
|
||||
return (
|
||||
<QRCode
|
||||
data={[{data: this.props.qrCodeData.buffer, mode: 'byte'}]}
|
||||
className="mx_VerificationQRCode"
|
||||
width={196} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,8 +114,19 @@ class UploadButton extends React.Component {
|
||||
this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this);
|
||||
|
||||
this._uploadInput = createRef();
|
||||
this._dispatcherRef = dis.register(this.onAction);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
dis.unregister(this._dispatcherRef);
|
||||
}
|
||||
|
||||
onAction = payload => {
|
||||
if (payload.action === "upload_file") {
|
||||
this.onUploadClick();
|
||||
}
|
||||
};
|
||||
|
||||
onUploadClick(ev) {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
dis.dispatch({action: 'require_registration'});
|
||||
@@ -128,7 +139,7 @@ class UploadButton extends React.Component {
|
||||
if (ev.target.files.length === 0) return;
|
||||
|
||||
// take a copy so we can safely reset the value of the form control
|
||||
// (Note it is a FileList: we can't use slice or sesnible iteration).
|
||||
// (Note it is a FileList: we can't use slice or sensible iteration).
|
||||
const tfiles = [];
|
||||
for (let i = 0; i < ev.target.files.length; ++i) {
|
||||
tfiles.push(ev.target.files[i]);
|
||||
|
||||
@@ -36,7 +36,6 @@ export default class CrossSigningPanel extends React.PureComponent {
|
||||
userSigningPrivateKeyCached: false,
|
||||
sessionBackupKeyCached: false,
|
||||
secretStorageKeyInAccount: false,
|
||||
secretStorageKeyNeedsUpgrade: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -88,7 +87,6 @@ export default class CrossSigningPanel extends React.PureComponent {
|
||||
const homeserverSupportsCrossSigning =
|
||||
await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");
|
||||
const crossSigningReady = await cli.isCrossSigningReady();
|
||||
const secretStorageKeyNeedsUpgrade = await cli.secretStorageKeyNeedsUpgrade();
|
||||
|
||||
this.setState({
|
||||
crossSigningPublicKeysOnDevice,
|
||||
@@ -100,7 +98,6 @@ export default class CrossSigningPanel extends React.PureComponent {
|
||||
secretStorageKeyInAccount,
|
||||
homeserverSupportsCrossSigning,
|
||||
crossSigningReady,
|
||||
secretStorageKeyNeedsUpgrade,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -150,7 +147,6 @@ export default class CrossSigningPanel extends React.PureComponent {
|
||||
secretStorageKeyInAccount,
|
||||
homeserverSupportsCrossSigning,
|
||||
crossSigningReady,
|
||||
secretStorageKeyNeedsUpgrade,
|
||||
} = this.state;
|
||||
|
||||
let errorSection;
|
||||
@@ -259,10 +255,6 @@ export default class CrossSigningPanel extends React.PureComponent {
|
||||
<td>{_t("Homeserver feature support:")}</td>
|
||||
<td>{homeserverSupportsCrossSigning ? _t("exists") : _t("not found")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{_t("Secret Storage key format:")}</td>
|
||||
<td>{secretStorageKeyNeedsUpgrade ? _t("outdated") : _t("up to date")}</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</details>
|
||||
{errorSection}
|
||||
|
||||
@@ -348,7 +348,7 @@ export default class GeneralUserSettingsTab extends React.Component {
|
||||
// For newer homeservers with separate 3PID add and bind methods (MSC2290),
|
||||
// there is no such concern, so we can always show the HS account 3PIDs.
|
||||
if (this.state.haveIdServer || this.state.serverSupportsSeparateAddAndBind === true) {
|
||||
const emails = this.state.loading3pids || true
|
||||
const emails = this.state.loading3pids
|
||||
? <Spinner />
|
||||
: <EmailAddresses
|
||||
emails={this.state.emails}
|
||||
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Modal from '../../../Modal';
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import * as sdk from "../../../index";
|
||||
import { _t } from '../../../languageHandler';
|
||||
import DeviceListener from '../../../DeviceListener';
|
||||
@@ -31,7 +30,6 @@ export default class SetupEncryptionToast extends React.PureComponent {
|
||||
'set_up_encryption',
|
||||
'verify_this_session',
|
||||
'upgrade_encryption',
|
||||
'upgrade_ssss',
|
||||
]).isRequired,
|
||||
};
|
||||
|
||||
@@ -39,24 +37,6 @@ export default class SetupEncryptionToast extends React.PureComponent {
|
||||
DeviceListener.sharedInstance().dismissEncryptionSetup();
|
||||
};
|
||||
|
||||
async _waitForCompletion() {
|
||||
if (this.props.kind === 'upgrade_ssss') {
|
||||
return new Promise(resolve => {
|
||||
const recheck = async () => {
|
||||
const needsUpgrade = await MatrixClientPeg.get().secretStorageKeyNeedsUpgrade();
|
||||
if (!needsUpgrade) {
|
||||
MatrixClientPeg.get().removeListener('accountData', recheck);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
MatrixClientPeg.get().on('accountData', recheck);
|
||||
recheck();
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_onSetupClick = async () => {
|
||||
if (this.props.kind === "verify_this_session") {
|
||||
Modal.createTrackedDialog('Verify session', 'Verify session', SetupEncryptionDialog,
|
||||
@@ -68,7 +48,6 @@ export default class SetupEncryptionToast extends React.PureComponent {
|
||||
);
|
||||
try {
|
||||
await accessSecretStorage();
|
||||
await this._waitForCompletion();
|
||||
} finally {
|
||||
modal.close();
|
||||
}
|
||||
@@ -82,8 +61,6 @@ export default class SetupEncryptionToast extends React.PureComponent {
|
||||
return _t('Verify yourself & others to keep your chats safe');
|
||||
case 'verify_this_session':
|
||||
return _t('Other users may not trust it');
|
||||
case 'upgrade_ssss':
|
||||
return _t('Update your secure storage');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +69,6 @@ export default class SetupEncryptionToast extends React.PureComponent {
|
||||
case 'set_up_encryption':
|
||||
return _t('Set up');
|
||||
case 'upgrade_encryption':
|
||||
case 'upgrade_ssss':
|
||||
return _t('Upgrade');
|
||||
case 'verify_this_session':
|
||||
return _t('Verify');
|
||||
|
||||
Reference in New Issue
Block a user