Merge pull request #4413 from matrix-org/t3chguy/wait4initialsync
Login block on initialSync with spinners
This commit is contained in:
commit
f4c4fe7d12
10 changed files with 103 additions and 32 deletions
|
@ -89,3 +89,13 @@ limitations under the License.
|
||||||
.mx_Login_underlinedServerName {
|
.mx_Login_underlinedServerName {
|
||||||
border-bottom: 1px dashed $accent-color;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -119,6 +119,24 @@ limitations under the License.
|
||||||
margin-right: 0;
|
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 {
|
.mx_AuthBody_changeFlow {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -33,6 +33,10 @@ limitations under the License.
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_Dropdown_input.mx_AccessibleButton_disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_Dropdown_input:focus {
|
.mx_Dropdown_input:focus {
|
||||||
border-color: $input-focused-border-color;
|
border-color: $input-focused-border-color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1902,27 +1902,22 @@ export default createReactClass({
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
// We're checking `isCryptoAvailable` here instead of `isCryptoEnabled`
|
// We're checking `isCryptoAvailable` here instead of `isCryptoEnabled`
|
||||||
// because the client hasn't been started yet.
|
// because the client hasn't been started yet.
|
||||||
if (!isCryptoAvailable()) {
|
const cryptoAvailable = isCryptoAvailable();
|
||||||
|
if (!cryptoAvailable) {
|
||||||
this._onLoggedIn();
|
this._onLoggedIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test for the master cross-signing key in SSSS as a quick proxy for
|
this.setState({ pendingInitialSync: true });
|
||||||
// whether cross-signing has been set up on the account. We can't
|
await this.firstSyncPromise.promise;
|
||||||
// really continue until we know whether it's there or not so retry
|
|
||||||
// if this fails.
|
if (!cryptoAvailable) {
|
||||||
let masterKeyInStorage;
|
this.setState({ pendingInitialSync: false });
|
||||||
while (masterKeyInStorage === undefined) {
|
return setLoggedInPromise;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
if (masterKeyInStorage) {
|
||||||
// Auto-enable cross-signing for the new session when key found in
|
// Auto-enable cross-signing for the new session when key found in
|
||||||
// secret storage.
|
// secret storage.
|
||||||
|
@ -1939,6 +1934,7 @@ export default createReactClass({
|
||||||
} else {
|
} else {
|
||||||
this._onLoggedIn();
|
this._onLoggedIn();
|
||||||
}
|
}
|
||||||
|
this.setState({ pendingInitialSync: false });
|
||||||
|
|
||||||
return setLoggedInPromise;
|
return setLoggedInPromise;
|
||||||
},
|
},
|
||||||
|
@ -2060,6 +2056,7 @@ export default createReactClass({
|
||||||
const Login = sdk.getComponent('structures.auth.Login');
|
const Login = sdk.getComponent('structures.auth.Login');
|
||||||
view = (
|
view = (
|
||||||
<Login
|
<Login
|
||||||
|
isSyncing={this.state.pendingInitialSync}
|
||||||
onLoggedIn={this.onUserCompletedLoginFlow}
|
onLoggedIn={this.onUserCompletedLoginFlow}
|
||||||
onRegisterClick={this.onRegisterClick}
|
onRegisterClick={this.onRegisterClick}
|
||||||
fallbackHsUrl={this.getFallbackHsUrl()}
|
fallbackHsUrl={this.getFallbackHsUrl()}
|
||||||
|
|
|
@ -84,11 +84,13 @@ export default createReactClass({
|
||||||
onServerConfigChange: PropTypes.func.isRequired,
|
onServerConfigChange: PropTypes.func.isRequired,
|
||||||
|
|
||||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||||
|
isSyncing: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
busy: false,
|
busy: false,
|
||||||
|
busyLoggingIn: null,
|
||||||
errorText: null,
|
errorText: null,
|
||||||
loginIncorrect: false,
|
loginIncorrect: false,
|
||||||
canTryLogin: true, // can we attempt to log in or are there validation errors?
|
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);
|
const componentState = AutoDiscoveryUtils.authComponentStateForError(e);
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: false,
|
busy: false,
|
||||||
|
busyLoggingIn: false,
|
||||||
...componentState,
|
...componentState,
|
||||||
});
|
});
|
||||||
aliveAgain = !componentState.serverErrorIsFatal;
|
aliveAgain = !componentState.serverErrorIsFatal;
|
||||||
|
@ -182,6 +185,7 @@ export default createReactClass({
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
|
busyLoggingIn: true,
|
||||||
errorText: null,
|
errorText: null,
|
||||||
loginIncorrect: false,
|
loginIncorrect: false,
|
||||||
});
|
});
|
||||||
|
@ -250,6 +254,7 @@ export default createReactClass({
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: false,
|
busy: false,
|
||||||
|
busyLoggingIn: false,
|
||||||
errorText: errorText,
|
errorText: errorText,
|
||||||
// 401 would be the sensible status code for 'incorrect password'
|
// 401 would be the sensible status code for 'incorrect password'
|
||||||
// but the login API gives a 403 https://matrix.org/jira/browse/SYN-744
|
// 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}
|
loginIncorrect={this.state.loginIncorrect}
|
||||||
serverConfig={this.props.serverConfig}
|
serverConfig={this.props.serverConfig}
|
||||||
disableSubmit={this.isBusy()}
|
disableSubmit={this.isBusy()}
|
||||||
|
busy={this.props.isSyncing || this.state.busyLoggingIn}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -629,9 +635,11 @@ export default createReactClass({
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
|
const InlineSpinner = sdk.getComponent("elements.InlineSpinner");
|
||||||
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
||||||
const AuthBody = sdk.getComponent("auth.AuthBody");
|
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;
|
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 (
|
return (
|
||||||
<AuthPage>
|
<AuthPage>
|
||||||
<AuthHeader />
|
<AuthHeader disableLanguageSelector={this.props.isSyncing || this.state.busyLoggingIn} />
|
||||||
<AuthBody>
|
<AuthBody>
|
||||||
<h2>
|
<h2>
|
||||||
{_t('Sign in')}
|
{_t('Sign in')}
|
||||||
|
@ -670,9 +697,7 @@ export default createReactClass({
|
||||||
{ serverDeadSection }
|
{ serverDeadSection }
|
||||||
{ this.renderServerComponent() }
|
{ this.renderServerComponent() }
|
||||||
{ this.renderLoginComponentForStep() }
|
{ this.renderLoginComponentForStep() }
|
||||||
<a className="mx_AuthBody_changeFlow" onClick={this.onTryRegisterClick} href="#">
|
{ footer }
|
||||||
{ _t('Create account') }
|
|
||||||
</a>
|
|
||||||
</AuthBody>
|
</AuthBody>
|
||||||
</AuthPage>
|
</AuthPage>
|
||||||
);
|
);
|
||||||
|
|
|
@ -16,12 +16,17 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
import createReactClass from 'create-react-class';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
|
||||||
export default createReactClass({
|
export default createReactClass({
|
||||||
displayName: 'AuthHeader',
|
displayName: 'AuthHeader',
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
disableLanguageSelector: PropTypes.bool,
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const AuthHeaderLogo = sdk.getComponent('auth.AuthHeaderLogo');
|
const AuthHeaderLogo = sdk.getComponent('auth.AuthHeaderLogo');
|
||||||
const LanguageSelector = sdk.getComponent('views.auth.LanguageSelector');
|
const LanguageSelector = sdk.getComponent('views.auth.LanguageSelector');
|
||||||
|
@ -29,7 +34,7 @@ export default createReactClass({
|
||||||
return (
|
return (
|
||||||
<div className="mx_AuthHeader">
|
<div className="mx_AuthHeader">
|
||||||
<AuthHeaderLogo />
|
<AuthHeaderLogo />
|
||||||
<LanguageSelector />
|
<LanguageSelector disabled={this.props.disableLanguageSelector} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -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 />;
|
if (SdkConfig.get()['disable_login_language_selector']) return <div />;
|
||||||
|
|
||||||
const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown');
|
const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown');
|
||||||
return <LanguageDropdown className="mx_AuthBody_language"
|
return <LanguageDropdown
|
||||||
|
className="mx_AuthBody_language"
|
||||||
onOptionChange={onChange}
|
onOptionChange={onChange}
|
||||||
value={getCurrentLanguage()}
|
value={getCurrentLanguage()}
|
||||||
|
disabled={disabled}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
||||||
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pure UI component which displays a username/password form.
|
* A pure UI component which displays a username/password form.
|
||||||
|
@ -44,6 +45,7 @@ export default class PasswordLogin extends React.Component {
|
||||||
loginIncorrect: PropTypes.bool,
|
loginIncorrect: PropTypes.bool,
|
||||||
disableSubmit: PropTypes.bool,
|
disableSubmit: PropTypes.bool,
|
||||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||||
|
busy: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -265,12 +267,16 @@ export default class PasswordLogin extends React.Component {
|
||||||
if (this.props.onForgotPasswordClick) {
|
if (this.props.onForgotPasswordClick) {
|
||||||
forgotPasswordJsx = <span>
|
forgotPasswordJsx = <span>
|
||||||
{_t('Not sure of your password? <a>Set a new one</a>', {}, {
|
{_t('Not sure of your password? <a>Set a new one</a>', {}, {
|
||||||
a: sub => <a className="mx_Login_forgot"
|
a: sub => (
|
||||||
onClick={this.onForgotPasswordClick}
|
<AccessibleButton
|
||||||
href="#"
|
className="mx_Login_forgot"
|
||||||
>
|
disabled={this.props.busy}
|
||||||
{sub}
|
kind="link"
|
||||||
</a>,
|
onClick={this.onForgotPasswordClick}
|
||||||
|
>
|
||||||
|
{sub}
|
||||||
|
</AccessibleButton>
|
||||||
|
),
|
||||||
})}
|
})}
|
||||||
</span>;
|
</span>;
|
||||||
}
|
}
|
||||||
|
@ -332,11 +338,11 @@ export default class PasswordLogin extends React.Component {
|
||||||
disabled={this.props.disableSubmit}
|
disabled={this.props.disableSubmit}
|
||||||
/>
|
/>
|
||||||
{forgotPasswordJsx}
|
{forgotPasswordJsx}
|
||||||
<input className="mx_Login_submit"
|
{ !this.props.busy && <input className="mx_Login_submit"
|
||||||
type="submit"
|
type="submit"
|
||||||
value={_t('Sign in')}
|
value={_t('Sign in')}
|
||||||
disabled={this.props.disableSubmit}
|
disabled={this.props.disableSubmit}
|
||||||
/>
|
/> }
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -114,6 +114,7 @@ export default class LanguageDropdown extends React.Component {
|
||||||
searchEnabled={true}
|
searchEnabled={true}
|
||||||
value={value}
|
value={value}
|
||||||
label={_t("Language Dropdown")}
|
label={_t("Language Dropdown")}
|
||||||
|
disabled={this.props.disabled}
|
||||||
>
|
>
|
||||||
{ options }
|
{ options }
|
||||||
</Dropdown>;
|
</Dropdown>;
|
||||||
|
|
|
@ -2102,6 +2102,9 @@
|
||||||
"Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.",
|
"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 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.",
|
"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",
|
"Create account": "Create account",
|
||||||
"Failed to fetch avatar URL": "Failed to fetch avatar URL",
|
"Failed to fetch avatar URL": "Failed to fetch avatar URL",
|
||||||
"Set a display name:": "Set a display name:",
|
"Set a display name:": "Set a display name:",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue