Start to factor out session-loading magic

Take some of the magic out of MatrixChat.componentDidMount() into a new
component.

Also delete the MatrixChat test. It wasn't really doing much, is broken by the
change, and I am replacing it with (better) app-level tests in the vector
project.
pull/21833/head
Richard van der Hoff 2016-08-10 00:03:29 +01:00
parent 0351ab0a3d
commit 24841cc5c4
5 changed files with 200 additions and 109 deletions

View File

@ -26,7 +26,9 @@ import dis from './dispatcher';
*/
function setLoggedIn(credentials) {
credentials.guest = Boolean(credentials.guest);
console.log("onLoggedIn => %s (guest=%s)", credentials.userId, credentials.guest);
console.log("onLoggedIn => %s (guest=%s) hs=%s",
credentials.userId, credentials.guest,
credentials.homeserverUrl);
MatrixClientPeg.replaceUsingCreds(credentials);
dis.dispatch({action: 'on_logged_in'});

View File

@ -39,6 +39,7 @@ module.exports.components['structures.login.ForgotPassword'] = require('./compon
module.exports.components['structures.login.Login'] = require('./components/structures/login/Login');
module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration');
module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration');
module.exports.components['structures.login.SessionLoader'] = require('./components/structures/login/SessionLoader');
module.exports.components['views.avatars.BaseAvatar'] = require('./components/views/avatars/BaseAvatar');
module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar');
module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar');

View File

@ -26,6 +26,7 @@ var UserActivity = require("../../UserActivity");
var Presence = require("../../Presence");
var dis = require("../../dispatcher");
var SessionLoader = require("./login/SessionLoader");
var Login = require("./login/Login");
var Registration = require("./login/Registration");
var PostRegistration = require("./login/PostRegistration");
@ -65,6 +66,9 @@ module.exports = React.createClass({
getInitialState: function() {
var s = {
loading: true,
screen: undefined,
// If we are viewing a room by alias, this contains the alias
currentRoomAlias: null,
@ -72,7 +76,7 @@ module.exports = React.createClass({
// in the case where we view a room by ID or by RoomView when it resolves
// what ID an alias points at.
currentRoomId: null,
logged_in: !!(MatrixClientPeg.get() && MatrixClientPeg.get().credentials),
logged_in: false,
collapse_lhs: false,
collapse_rhs: false,
ready: false,
@ -80,15 +84,6 @@ module.exports = React.createClass({
sideOpacity: 1.0,
middleOpacity: 1.0,
};
if (s.logged_in) {
if (MatrixClientPeg.get().getRooms().length) {
s.page_type = this.PageTypes.RoomView;
} else {
// we don't need to default to the directoy here
// as we'll go there anyway after syncing
// s.page_type = this.PageTypes.RoomDirectory;
}
}
return s;
},
@ -150,47 +145,14 @@ module.exports = React.createClass({
if (this.props.config.sync_timeline_limit) {
MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit;
}
// register our dispatcher listener here rather than in
// componentDidMount so that we hear about any actions raised during
// the loading process.
this.dispatcherRef = dis.register(this.onAction);
},
componentDidMount: function() {
let clientStarted = false;
this._autoRegisterAsGuest = false;
if (this.props.enableGuest) {
if (!this.getCurrentHsUrl()) {
console.error("Cannot enable guest access: can't determine HS URL to use");
}
else if (this.props.startingQueryParams.client_secret && this.props.startingQueryParams.sid) {
console.log("Not registering as guest; registration.");
this._autoRegisterAsGuest = false;
}
else if (this.props.startingQueryParams.guest_user_id &&
this.props.startingQueryParams.guest_access_token)
{
this._autoRegisterAsGuest = false;
Lifecycle.setLoggedIn({
userId: this.props.startingQueryParams.guest_user_id,
accessToken: this.props.startingQueryParams.guest_access_token,
homeserverUrl: this.getDefaultHsUrl(),
identityServerUrl: this.getDefaultIsUrl(),
guest: true
});
clientStarted = true;
}
else {
this._autoRegisterAsGuest = true;
}
}
this.dispatcherRef = dis.register(this.onAction);
if (this.state.logged_in) {
// Don't auto-register as a guest. This applies if you refresh the page on a
// logged in client THEN hit the Sign Out button.
this._autoRegisterAsGuest = false;
if (!clientStarted) {
Lifecycle.startMatrixClient();
}
}
this.focusComposer = false;
// scrollStateMap is a map from room id to the scroll state returned by
// RoomView.getScrollState()
@ -198,14 +160,6 @@ module.exports = React.createClass({
document.addEventListener("keydown", this.onKeyDown);
window.addEventListener("focus", this.onFocus);
if (this.state.logged_in) {
this.notifyNewScreen('');
} else if (this._autoRegisterAsGuest) {
this._registerAsGuest();
} else {
this.notifyNewScreen('login');
}
// this can technically be done anywhere but doing this here keeps all
// the routing url path logic together.
if (this.onAliasClick) {
@ -243,7 +197,6 @@ module.exports = React.createClass({
MatrixClientPeg.replaceUsingUrls(hsUrl, isUrl);
MatrixClientPeg.get().registerGuest().done(function(creds) {
console.log("Registered as guest: %s", creds.user_id);
self._setAutoRegisterAsGuest(false);
Lifecycle.setLoggedIn({
userId: creds.user_id,
accessToken: creds.access_token,
@ -260,15 +213,9 @@ module.exports = React.createClass({
});
}
console.error("Failed to register as guest: " + err + " " + err.data);
self._setAutoRegisterAsGuest(false);
});
},
_setAutoRegisterAsGuest: function(shouldAutoRegister) {
this._autoRegisterAsGuest = shouldAutoRegister;
this.forceUpdate();
},
onAction: function(payload) {
var roomIndexDelta = 1;
@ -479,6 +426,9 @@ module.exports = React.createClass({
case 'will_start_client':
this._onWillStartClient();
break;
case 'load_completed':
this._onLoadCompleted();
break;
}
},
@ -589,6 +539,13 @@ module.exports = React.createClass({
this.scrollStateMap[roomId] = state;
},
/**
* Called when the sessionloader has finished
*/
_onLoadCompleted: function() {
this.setState({loading: false});
},
/**
* Called when a new logged in session has started
*/
@ -1029,8 +986,26 @@ module.exports = React.createClass({
// work out the HS URL prompts we should show for
// console.log("rendering; loading="+this.state.loading+"; screen="+this.state.screen +
// "; logged_in="+this.state.logged_in+"; ready="+this.state.ready);
if (this.state.loading) {
return (
<SessionLoader
queryParams={this.props.startingQueryParams}
enableGuest={this.props.enableGuest}
hsUrl={this.getCurrentHsUrl()}
isUrl={this.getCurrentIsUrl()}
onLoggedIn={Lifecycle.setLoggedIn}
// stuff this through the dispatcher so that it happens
// after the on_logged_in action.
onComplete={()=>{dis.dispatch({action: 'load_completed'});}}
/>
);
}
// needs to be before normal PageTypes as you are logged in technically
if (this.state.screen == 'post_registration') {
else if (this.state.screen == 'post_registration') {
return (
<PostRegistration
onComplete={this.onFinishPostRegistration} />
@ -1101,20 +1076,15 @@ module.exports = React.createClass({
</div>
</div>
);
} else if (this.state.logged_in || (!this.state.logged_in && this._autoRegisterAsGuest)) {
} else if (this.state.logged_in) {
// we think we are logged in, but are still waiting for the /sync to complete
var Spinner = sdk.getComponent('elements.Spinner');
var logoutLink;
if (this.state.logged_in) {
logoutLink = (
<a href="#" className="mx_MatrixChat_splashButtons" onClick={ this.onLogoutClick }>
Logout
</a>
);
}
return (
<div className="mx_MatrixChat_splash">
<Spinner />
{logoutLink}
<a href="#" className="mx_MatrixChat_splashButtons" onClick={ this.onLogoutClick }>
Logout
</a>
</div>
);
} else if (this.state.screen == 'register') {

View File

@ -0,0 +1,152 @@
/*
Copyright 2016 OpenMarket Ltd
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 from 'react';
import q from 'q';
import dis from '../../../dispatcher';
import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Lifecycle from '../../../Lifecycle';
/**
* A react component which is only used when the application first starts.
*
* Its job is to attempt to build a logged-in Matrix session. It tries a number
* of things:
*
* 0. if it looks like we are in the middle of a registration process, it does
* nothing.
*
* 1. if we have a guest access token in the query params, it uses that.
*
* 2. if an access token is stored in local storage (from a previous session),
* it uses that.
*
* 3. it attempts to auto-register as a guest user.
*
* If any of steps 1-3 are successful, it will call onLoggedIn (which is
* typically Lifecycle.setLoggedIn, which in turn will raise on_logged_in and
* will_start_client events).
*
* Finally, it calls onComplete, which makes MatrixChat move into its normal processing.
*/
export default class SessionLoader extends React.Component {
constructor(props, context) {
super(props, context);
}
componentDidMount() {
this._loadSession().done(() => {
this.props.onComplete();
});
}
componentWillReceiveProps(nextProps) {
if (nextProps.hsUrl != this.props.hsUrl ||
nextProps.isUrl != this.props.isUrl
) {
throw new Error("changing servers on a SessionLoader is not supported");
};
}
_loadSession() {
if (this.props.queryParams.client_secret && this.props.queryParams.sid) {
// this happens during email validation: the email contains a link to the
// IS, which in turn redirects back to vector. We let MatrixChat create a
// Registration component which completes the next stage of registration.
console.log("Not registering as guest: registration already in progress.");
return q();
}
let enableGuest = false;
if (this.props.enableGuest) {
if (!this.props.hsUrl) {
console.warn("Cannot enable guest access: can't determine HS URL to use");
}
else {
enableGuest = true;
}
}
if (enableGuest &&
this.props.queryParams.guest_user_id &&
this.props.queryParams.guest_access_token
) {
console.log("Using guest access credentials");
this.props.onLoggedIn({
userId: this.props.queryParams.guest_user_id,
accessToken: this.props.queryParams.guest_access_token,
homeserverUrl: this.props.hsUrl,
identityServerUrl: this.props.isUrl,
guest: true,
});
return q();
}
if (MatrixClientPeg.get() && MatrixClientPeg.get().credentials) {
console.log("Using existing credentials");
this.props.onLoggedIn(MatrixClientPeg.getCredentials());
return q();
}
if (enableGuest) {
return this._registerAsGuest();
}
// fall back to login screen
return q();
}
_registerAsGuest() {
var hsUrl = this.props.hsUrl;
var isUrl = this.props.isUrl;
console.log("Doing guest login on %s", hsUrl);
MatrixClientPeg.replaceUsingUrls(hsUrl, isUrl);
return MatrixClientPeg.get().registerGuest().then((creds) => {
console.log("Registered as guest: %s", creds.user_id);
this.props.onLoggedIn({
userId: creds.user_id,
accessToken: creds.access_token,
homeserverUrl: hsUrl,
identityServerUrl: isUrl,
guest: true,
});
}, (err) => {
console.error("Failed to register as guest: " + err + " " + err.data);
});
}
render() {
const Spinner = sdk.getComponent('elements.Spinner');
return (
<div className="mx_MatrixChat_splash">
<Spinner />
</div>
);
}
}
SessionLoader.propTypes = {
queryParams: React.PropTypes.object.isRequired,
enableGuest: React.PropTypes.bool,
hsUrl: React.PropTypes.string,
isUrl: React.PropTypes.string,
onLoggedIn: React.PropTypes.func.isRequired,
onComplete: React.PropTypes.func.isRequired,
};

View File

@ -1,34 +0,0 @@
var React = require('react');
var TestUtils = require('react-addons-test-utils');
var expect = require('expect');
var sdk = require('matrix-react-sdk');
var MatrixChat = sdk.getComponent('structures.MatrixChat');
var peg = require('../../../src/MatrixClientPeg');
var test_utils = require('../../test-utils');
var q = require('q');
describe('MatrixChat', function () {
var sandbox;
beforeEach(function() {
sandbox = test_utils.stubClient();
});
afterEach(function() {
sandbox.restore();
});
it('gives a login panel by default', function () {
peg.get().loginFlows.returns(q({flows:[]}));
var res = TestUtils.renderIntoDocument(
<MatrixChat config={{}}/>
);
// we expect a single <Login> component
TestUtils.findRenderedComponentWithType(
res, sdk.getComponent('structures.login.Login'));
});
});