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
parent
0351ab0a3d
commit
24841cc5c4
|
@ -26,7 +26,9 @@ import dis from './dispatcher';
|
||||||
*/
|
*/
|
||||||
function setLoggedIn(credentials) {
|
function setLoggedIn(credentials) {
|
||||||
credentials.guest = Boolean(credentials.guest);
|
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);
|
MatrixClientPeg.replaceUsingCreds(credentials);
|
||||||
|
|
||||||
dis.dispatch({action: 'on_logged_in'});
|
dis.dispatch({action: 'on_logged_in'});
|
||||||
|
|
|
@ -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.Login'] = require('./components/structures/login/Login');
|
||||||
module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration');
|
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.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.BaseAvatar'] = require('./components/views/avatars/BaseAvatar');
|
||||||
module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar');
|
module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar');
|
||||||
module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar');
|
module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar');
|
||||||
|
|
|
@ -26,6 +26,7 @@ var UserActivity = require("../../UserActivity");
|
||||||
var Presence = require("../../Presence");
|
var Presence = require("../../Presence");
|
||||||
var dis = require("../../dispatcher");
|
var dis = require("../../dispatcher");
|
||||||
|
|
||||||
|
var SessionLoader = require("./login/SessionLoader");
|
||||||
var Login = require("./login/Login");
|
var Login = require("./login/Login");
|
||||||
var Registration = require("./login/Registration");
|
var Registration = require("./login/Registration");
|
||||||
var PostRegistration = require("./login/PostRegistration");
|
var PostRegistration = require("./login/PostRegistration");
|
||||||
|
@ -65,6 +66,9 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
var s = {
|
var s = {
|
||||||
|
loading: true,
|
||||||
|
screen: undefined,
|
||||||
|
|
||||||
// If we are viewing a room by alias, this contains the alias
|
// If we are viewing a room by alias, this contains the alias
|
||||||
currentRoomAlias: null,
|
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
|
// in the case where we view a room by ID or by RoomView when it resolves
|
||||||
// what ID an alias points at.
|
// what ID an alias points at.
|
||||||
currentRoomId: null,
|
currentRoomId: null,
|
||||||
logged_in: !!(MatrixClientPeg.get() && MatrixClientPeg.get().credentials),
|
logged_in: false,
|
||||||
collapse_lhs: false,
|
collapse_lhs: false,
|
||||||
collapse_rhs: false,
|
collapse_rhs: false,
|
||||||
ready: false,
|
ready: false,
|
||||||
|
@ -80,15 +84,6 @@ module.exports = React.createClass({
|
||||||
sideOpacity: 1.0,
|
sideOpacity: 1.0,
|
||||||
middleOpacity: 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;
|
return s;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -150,47 +145,14 @@ module.exports = React.createClass({
|
||||||
if (this.props.config.sync_timeline_limit) {
|
if (this.props.config.sync_timeline_limit) {
|
||||||
MatrixClientPeg.opts.initialSyncLimit = 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() {
|
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;
|
this.focusComposer = false;
|
||||||
// scrollStateMap is a map from room id to the scroll state returned by
|
// scrollStateMap is a map from room id to the scroll state returned by
|
||||||
// RoomView.getScrollState()
|
// RoomView.getScrollState()
|
||||||
|
@ -198,14 +160,6 @@ module.exports = React.createClass({
|
||||||
document.addEventListener("keydown", this.onKeyDown);
|
document.addEventListener("keydown", this.onKeyDown);
|
||||||
window.addEventListener("focus", this.onFocus);
|
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
|
// this can technically be done anywhere but doing this here keeps all
|
||||||
// the routing url path logic together.
|
// the routing url path logic together.
|
||||||
if (this.onAliasClick) {
|
if (this.onAliasClick) {
|
||||||
|
@ -243,7 +197,6 @@ module.exports = React.createClass({
|
||||||
MatrixClientPeg.replaceUsingUrls(hsUrl, isUrl);
|
MatrixClientPeg.replaceUsingUrls(hsUrl, isUrl);
|
||||||
MatrixClientPeg.get().registerGuest().done(function(creds) {
|
MatrixClientPeg.get().registerGuest().done(function(creds) {
|
||||||
console.log("Registered as guest: %s", creds.user_id);
|
console.log("Registered as guest: %s", creds.user_id);
|
||||||
self._setAutoRegisterAsGuest(false);
|
|
||||||
Lifecycle.setLoggedIn({
|
Lifecycle.setLoggedIn({
|
||||||
userId: creds.user_id,
|
userId: creds.user_id,
|
||||||
accessToken: creds.access_token,
|
accessToken: creds.access_token,
|
||||||
|
@ -260,15 +213,9 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.error("Failed to register as guest: " + err + " " + err.data);
|
console.error("Failed to register as guest: " + err + " " + err.data);
|
||||||
self._setAutoRegisterAsGuest(false);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_setAutoRegisterAsGuest: function(shouldAutoRegister) {
|
|
||||||
this._autoRegisterAsGuest = shouldAutoRegister;
|
|
||||||
this.forceUpdate();
|
|
||||||
},
|
|
||||||
|
|
||||||
onAction: function(payload) {
|
onAction: function(payload) {
|
||||||
var roomIndexDelta = 1;
|
var roomIndexDelta = 1;
|
||||||
|
|
||||||
|
@ -479,6 +426,9 @@ module.exports = React.createClass({
|
||||||
case 'will_start_client':
|
case 'will_start_client':
|
||||||
this._onWillStartClient();
|
this._onWillStartClient();
|
||||||
break;
|
break;
|
||||||
|
case 'load_completed':
|
||||||
|
this._onLoadCompleted();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -589,6 +539,13 @@ module.exports = React.createClass({
|
||||||
this.scrollStateMap[roomId] = state;
|
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
|
* 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
|
// 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
|
// 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 (
|
return (
|
||||||
<PostRegistration
|
<PostRegistration
|
||||||
onComplete={this.onFinishPostRegistration} />
|
onComplete={this.onFinishPostRegistration} />
|
||||||
|
@ -1101,20 +1076,15 @@ module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
</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 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 (
|
return (
|
||||||
<div className="mx_MatrixChat_splash">
|
<div className="mx_MatrixChat_splash">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{logoutLink}
|
<a href="#" className="mx_MatrixChat_splashButtons" onClick={ this.onLogoutClick }>
|
||||||
|
Logout
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (this.state.screen == 'register') {
|
} else if (this.state.screen == 'register') {
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
|
@ -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'));
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue