Merge pull request #4413 from matrix-org/t3chguy/wait4initialsync

Login block on initialSync with spinners
pull/21833/head
Michael Telatynski 2020-04-15 16:17:13 +01:00 committed by GitHub
commit f4c4fe7d12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 103 additions and 32 deletions

View File

@ -89,3 +89,13 @@ limitations under the License.
.mx_Login_underlinedServerName {
border-bottom: 1px dashed $accent-color;
}
div.mx_AccessibleButton_kind_link.mx_Login_forgot {
// style it as a link
font-size: inherit;
padding: 0;
&.mx_AccessibleButton_disabled {
cursor: not-allowed;
}
}

View File

@ -119,6 +119,24 @@ limitations under the License.
margin-right: 0;
}
.mx_AuthBody_paddedFooter {
height: 80px; // height of the submit button + register link
padding-top: 28px;
text-align: center;
.mx_AuthBody_paddedFooter_title {
margin-top: 16px;
font-size: $font-15px;
line-height: $font-24px;
}
.mx_AuthBody_paddedFooter_subtitle {
margin-top: 8px;
font-size: $font-10px;
line-height: $font-14px;
}
}
.mx_AuthBody_changeFlow {
display: block;
text-align: center;

View File

@ -33,6 +33,10 @@ limitations under the License.
user-select: none;
}
.mx_Dropdown_input.mx_AccessibleButton_disabled {
cursor: not-allowed;
}
.mx_Dropdown_input:focus {
border-color: $input-focused-border-color;
}

View File

@ -1902,27 +1902,22 @@ export default createReactClass({
const cli = MatrixClientPeg.get();
// We're checking `isCryptoAvailable` here instead of `isCryptoEnabled`
// because the client hasn't been started yet.
if (!isCryptoAvailable()) {
const cryptoAvailable = isCryptoAvailable();
if (!cryptoAvailable) {
this._onLoggedIn();
}
// Test for the master cross-signing key in SSSS as a quick proxy for
// whether cross-signing has been set up on the account. We can't
// really continue until we know whether it's there or not so retry
// if this fails.
let masterKeyInStorage;
while (masterKeyInStorage === undefined) {
try {
masterKeyInStorage = !!await cli.getAccountDataFromServer("m.cross_signing.master");
} catch (e) {
if (e.errcode === "M_NOT_FOUND") {
masterKeyInStorage = false;
} else {
console.warn("Secret storage account data check failed: retrying...", e);
}
}
this.setState({ pendingInitialSync: true });
await this.firstSyncPromise.promise;
if (!cryptoAvailable) {
this.setState({ pendingInitialSync: false });
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.
@ -1939,6 +1934,7 @@ export default createReactClass({
} else {
this._onLoggedIn();
}
this.setState({ pendingInitialSync: false });
return setLoggedInPromise;
},
@ -2060,6 +2056,7 @@ export default createReactClass({
const Login = sdk.getComponent('structures.auth.Login');
view = (
<Login
isSyncing={this.state.pendingInitialSync}
onLoggedIn={this.onUserCompletedLoginFlow}
onRegisterClick={this.onRegisterClick}
fallbackHsUrl={this.getFallbackHsUrl()}

View File

@ -84,11 +84,13 @@ export default createReactClass({
onServerConfigChange: PropTypes.func.isRequired,
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
isSyncing: PropTypes.bool,
},
getInitialState: function() {
return {
busy: false,
busyLoggingIn: null,
errorText: null,
loginIncorrect: false,
canTryLogin: true, // can we attempt to log in or are there validation errors?
@ -169,6 +171,7 @@ export default createReactClass({
const componentState = AutoDiscoveryUtils.authComponentStateForError(e);
this.setState({
busy: false,
busyLoggingIn: false,
...componentState,
});
aliveAgain = !componentState.serverErrorIsFatal;
@ -182,6 +185,7 @@ export default createReactClass({
this.setState({
busy: true,
busyLoggingIn: true,
errorText: null,
loginIncorrect: false,
});
@ -250,6 +254,7 @@ export default createReactClass({
this.setState({
busy: false,
busyLoggingIn: false,
errorText: errorText,
// 401 would be the sensible status code for 'incorrect password'
// but the login API gives a 403 https://matrix.org/jira/browse/SYN-744
@ -594,6 +599,7 @@ export default createReactClass({
loginIncorrect={this.state.loginIncorrect}
serverConfig={this.props.serverConfig}
disableSubmit={this.isBusy()}
busy={this.props.isSyncing || this.state.busyLoggingIn}
/>
);
},
@ -629,9 +635,11 @@ export default createReactClass({
render: function() {
const Loader = sdk.getComponent("elements.Spinner");
const InlineSpinner = sdk.getComponent("elements.InlineSpinner");
const AuthHeader = sdk.getComponent("auth.AuthHeader");
const AuthBody = sdk.getComponent("auth.AuthBody");
const loader = this.isBusy() ? <div className="mx_Login_loader"><Loader /></div> : null;
const loader = this.isBusy() && !this.state.busyLoggingIn ?
<div className="mx_Login_loader"><Loader /></div> : null;
const errorText = this.state.errorText;
@ -658,9 +666,28 @@ export default createReactClass({
);
}
let footer;
if (this.props.isSyncing || this.state.busyLoggingIn) {
footer = <div className="mx_AuthBody_paddedFooter">
<div className="mx_AuthBody_paddedFooter_title">
<InlineSpinner w={20} h={20} />
{ this.props.isSyncing ? _t("Syncing...") : _t("Signing In...") }
</div>
{ this.props.isSyncing && <div className="mx_AuthBody_paddedFooter_subtitle">
{_t("If you've joined lots of rooms, this might take a while")}
</div> }
</div>;
} else {
footer = (
<a className="mx_AuthBody_changeFlow" onClick={this.onTryRegisterClick} href="#">
{ _t('Create account') }
</a>
);
}
return (
<AuthPage>
<AuthHeader />
<AuthHeader disableLanguageSelector={this.props.isSyncing || this.state.busyLoggingIn} />
<AuthBody>
<h2>
{_t('Sign in')}
@ -670,9 +697,7 @@ export default createReactClass({
{ serverDeadSection }
{ this.renderServerComponent() }
{ this.renderLoginComponentForStep() }
<a className="mx_AuthBody_changeFlow" onClick={this.onTryRegisterClick} href="#">
{ _t('Create account') }
</a>
{ footer }
</AuthBody>
</AuthPage>
);

View File

@ -16,12 +16,17 @@ limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
export default createReactClass({
displayName: 'AuthHeader',
propTypes: {
disableLanguageSelector: PropTypes.bool,
},
render: function() {
const AuthHeaderLogo = sdk.getComponent('auth.AuthHeaderLogo');
const LanguageSelector = sdk.getComponent('views.auth.LanguageSelector');
@ -29,7 +34,7 @@ export default createReactClass({
return (
<div className="mx_AuthHeader">
<AuthHeaderLogo />
<LanguageSelector />
<LanguageSelector disabled={this.props.disableLanguageSelector} />
</div>
);
},

View File

@ -28,12 +28,14 @@ function onChange(newLang) {
}
}
export default function LanguageSelector() {
export default function LanguageSelector({disabled}) {
if (SdkConfig.get()['disable_login_language_selector']) return <div />;
const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown');
return <LanguageDropdown className="mx_AuthBody_language"
return <LanguageDropdown
className="mx_AuthBody_language"
onOptionChange={onChange}
value={getCurrentLanguage()}
disabled={disabled}
/>;
}

View File

@ -23,6 +23,7 @@ import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import SdkConfig from '../../../SdkConfig';
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
import AccessibleButton from "../elements/AccessibleButton";
/**
* A pure UI component which displays a username/password form.
@ -44,6 +45,7 @@ export default class PasswordLogin extends React.Component {
loginIncorrect: PropTypes.bool,
disableSubmit: PropTypes.bool,
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
busy: PropTypes.bool,
};
static defaultProps = {
@ -265,12 +267,16 @@ export default class PasswordLogin extends React.Component {
if (this.props.onForgotPasswordClick) {
forgotPasswordJsx = <span>
{_t('Not sure of your password? <a>Set a new one</a>', {}, {
a: sub => <a className="mx_Login_forgot"
onClick={this.onForgotPasswordClick}
href="#"
>
{sub}
</a>,
a: sub => (
<AccessibleButton
className="mx_Login_forgot"
disabled={this.props.busy}
kind="link"
onClick={this.onForgotPasswordClick}
>
{sub}
</AccessibleButton>
),
})}
</span>;
}
@ -332,11 +338,11 @@ export default class PasswordLogin extends React.Component {
disabled={this.props.disableSubmit}
/>
{forgotPasswordJsx}
<input className="mx_Login_submit"
{ !this.props.busy && <input className="mx_Login_submit"
type="submit"
value={_t('Sign in')}
disabled={this.props.disableSubmit}
/>
/> }
</form>
</div>
);

View File

@ -114,6 +114,7 @@ export default class LanguageDropdown extends React.Component {
searchEnabled={true}
value={value}
label={_t("Language Dropdown")}
disabled={this.props.disabled}
>
{ options }
</Dropdown>;

View File

@ -2102,6 +2102,9 @@
"Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.",
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.",
"Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.",
"Syncing...": "Syncing...",
"Signing In...": "Signing In...",
"If you've joined lots of rooms, this might take a while": "If you've joined lots of rooms, this might take a while",
"Create account": "Create account",
"Failed to fetch avatar URL": "Failed to fetch avatar URL",
"Set a display name:": "Set a display name:",