MatrixChat: Replace state.{loading,loggedIn,loggingIn} with views

MatrixChat is essentially a glorified state machine, with its states partially
determined by the loading, loggedIn and loggingIn state flags. If we actually
make each of the states implied by those flags an explicit 'view', then
everything gets much clearer.
pull/21833/head
Richard van der Hoff 2017-06-14 20:04:55 +01:00
parent ce42a9a06f
commit 90213ce72e
1 changed files with 51 additions and 26 deletions

View File

@ -45,14 +45,38 @@ import { _t, getCurrentLanguage } from '../../languageHandler';
/** constants for MatrixChat.state.view */
const VIEWS = {
DEFAULT: 0,
// a special initial state which is only used at startup, while we are
// trying to re-animate a matrix client or register as a guest.
LOADING: 0,
// we are showing the login view
LOGIN: 1,
// we are showing the registration view
REGISTER: 2,
// completeing the registration flow
POST_REGISTRATION: 3,
// showing the 'forgot password' view
FORGOT_PASSWORD: 4,
// we have valid matrix credentials (either via an explicit login, via the
// initial re-animation/guest registration, or via a registration), and are
// now setting up a matrixclient to talk to it. This isn't an instant
// process because (a) we need to clear out indexeddb, and (b) we need to
LOGGING_IN: 5,
// we are logged in with an active matrix client.
LOGGED_IN: 6,
};
module.exports = React.createClass({
// we export this so that the integration tests can use it :-S
statics: {
VIEWS: VIEWS,
},
displayName: 'MatrixChat',
propTypes: {
@ -102,10 +126,8 @@ module.exports = React.createClass({
getInitialState: function() {
const s = {
loading: true,
// the master view we are showing.
view: VIEWS.DEFAULT,
view: VIEWS.LOADING,
// a thing to call showScreen with once login completes.
screenAfterLogin: this.props.initialScreenAfterLogin,
@ -126,8 +148,6 @@ module.exports = React.createClass({
// If we're trying to just view a user ID (i.e. /user URL), this is it
viewUserId: null,
loggedIn: false,
loggingIn: false,
collapse_lhs: false,
collapse_rhs: false,
ready: false,
@ -289,7 +309,6 @@ module.exports = React.createClass({
firstScreen === 'register' ||
firstScreen === 'forgot_password') {
this.props.onLoadCompleted();
this.setState({loading: false});
this._showScreenAfterLogin();
return;
}
@ -331,17 +350,18 @@ module.exports = React.createClass({
},
setStateForNewView: function(state) {
if (state.view === undefined) {
throw new Error("setStateForNewView with no view!");
}
const newState = {
view: VIEWS.DEFAULT,
viewUserId: null,
loggedIn: false,
ready: false,
};
Object.assign(newState, state);
this.setState(newState);
},
onAction: function(payload) {
// console.log(`MatrixClientPeg.onAction: ${payload.action}`);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
@ -366,7 +386,7 @@ module.exports = React.createClass({
this.notifyNewScreen('login');
break;
case 'start_post_registration':
this.setState({ // don't clobber loggedIn status
this.setState({
view: VIEWS.POST_REGISTRATION,
});
break;
@ -516,7 +536,10 @@ module.exports = React.createClass({
// and also that we're not ready (we'll be marked as logged
// in once the login completes, then ready once the sync
// completes).
this.setState({loggingIn: true, ready: false});
this.setStateForNewView({
view: VIEWS.LOGGING_IN,
ready: false,
});
break;
case 'on_logged_in':
this._onLoggedIn(payload.teamToken);
@ -864,7 +887,12 @@ module.exports = React.createClass({
*/
_onLoadCompleted: function() {
this.props.onLoadCompleted();
this.setState({loading: false});
// if we've got this far without leaving the 'loading' view, then
// login must have failed, so start the login process
if (this.state.view === VIEWS.LOADING) {
dis.dispatch({action: "start_login"});
}
},
/**
@ -919,9 +947,8 @@ module.exports = React.createClass({
*/
_onLoggedIn: function(teamToken) {
this.setState({
view: VIEWS.LOGGED_IN,
guestCreds: null,
loggedIn: true,
loggingIn: false,
});
if (teamToken) {
@ -983,7 +1010,7 @@ module.exports = React.createClass({
_onLoggedOut: function() {
this.notifyNewScreen('login');
this.setStateForNewView({
loggedIn: false,
view: VIEWS.LOGIN,
ready: false,
collapse_lhs: false,
collapse_rhs: false,
@ -1146,7 +1173,7 @@ module.exports = React.createClass({
// we can't view a room unless we're logged in
// (a guest account is fine)
if (this.state.loggedIn) {
if (this.state.view === VIEWS.LOGGED_IN) {
dis.dispatch(payload);
}
} else if (screen.indexOf('user/') == 0) {
@ -1266,7 +1293,7 @@ module.exports = React.createClass({
onFinishPostRegistration: function() {
// Don't confuse this with "PageType" which is the middle window to show
this.setState({
view: VIEWS.DEFAULT,
view: VIEWS.LOGGED_IN,
});
this.showScreen("settings");
},
@ -1334,11 +1361,9 @@ module.exports = React.createClass({
},
render: function() {
// `loading` might be set to false before `loggedIn = true`, causing the default
// (`<Login>`) to be visible for a few MS (say, whilst a request is in-flight to
// the RTS). So in the meantime, use `loggingIn`, which is true between
// actions `on_logging_in` and `on_logged_in`.
if (this.state.loading || this.state.loggingIn) {
// console.log(`Rendering MatrixChat with view ${this.state.view}`);
if (this.state.view === VIEWS.LOADING || this.state.view === VIEWS.LOGGING_IN) {
const Spinner = sdk.getComponent('elements.Spinner');
return (
<div className="mx_MatrixChat_splash">
@ -1356,10 +1381,10 @@ module.exports = React.createClass({
);
}
// `ready` and `loggedIn` may be set before `page_type` (because the
// `ready` and `view==LOGGED_IN` may be set before `page_type` (because the
// latter is set via the dispatcher). If we don't yet have a `page_type`,
// keep showing the spinner for now.
if (this.state.loggedIn && this.state.ready && this.state.page_type) {
if (this.state.view === VIEWS.LOGGED_IN && this.state.ready && this.state.page_type) {
/* for now, we stuff the entirety of our props and state into the LoggedInView.
* we should go through and figure out what we actually need to pass down, as well
* as using something like redux to avoid having a billion bits of state kicking around.
@ -1376,7 +1401,7 @@ module.exports = React.createClass({
{...this.state}
/>
);
} else if (this.state.loggedIn) {
} else if (this.state.view === VIEWS.LOGGED_IN) {
// we think we are logged in, but are still waiting for the /sync to complete
const Spinner = sdk.getComponent('elements.Spinner');
return (