Merge pull request #1102 from matrix-org/rav/refactor_matrixclient_states
Refactor the state machine in MatrixChatpull/21833/head
commit
115a3deed9
|
@ -62,6 +62,8 @@ import { _t } from './languageHandler';
|
||||||
* true; defines the IS to use.
|
* true; defines the IS to use.
|
||||||
*
|
*
|
||||||
* @returns {Promise} a promise which resolves when the above process completes.
|
* @returns {Promise} a promise which resolves when the above process completes.
|
||||||
|
* Resolves to `true` if we ended up starting a session, or `false` if we
|
||||||
|
* failed.
|
||||||
*/
|
*/
|
||||||
export function loadSession(opts) {
|
export function loadSession(opts) {
|
||||||
const fragmentQueryParams = opts.fragmentQueryParams || {};
|
const fragmentQueryParams = opts.fragmentQueryParams || {};
|
||||||
|
@ -86,12 +88,12 @@ export function loadSession(opts) {
|
||||||
homeserverUrl: guestHsUrl,
|
homeserverUrl: guestHsUrl,
|
||||||
identityServerUrl: guestIsUrl,
|
identityServerUrl: guestIsUrl,
|
||||||
guest: true,
|
guest: true,
|
||||||
}, true);
|
}, true).then(() => true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _restoreFromLocalStorage().then((success) => {
|
return _restoreFromLocalStorage().then((success) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableGuest) {
|
if (enableGuest) {
|
||||||
|
@ -99,6 +101,7 @@ export function loadSession(opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// fall back to login screen
|
// fall back to login screen
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,9 +179,10 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) {
|
||||||
homeserverUrl: hsUrl,
|
homeserverUrl: hsUrl,
|
||||||
identityServerUrl: isUrl,
|
identityServerUrl: isUrl,
|
||||||
guest: true,
|
guest: true,
|
||||||
}, true);
|
}, true).then(() => true);
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
console.error("Failed to register as guest: " + err + " " + err.data);
|
console.error("Failed to register as guest: " + err + " " + err.data);
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,41 @@ import createRoom from "../../createRoom";
|
||||||
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
|
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
|
||||||
import { _t, getCurrentLanguage } from '../../languageHandler';
|
import { _t, getCurrentLanguage } from '../../languageHandler';
|
||||||
|
|
||||||
|
/** constants for MatrixChat.state.view */
|
||||||
|
const VIEWS = {
|
||||||
|
// 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
|
||||||
|
// talk to the team server; while it is going on we show a big spinner.
|
||||||
|
LOGGING_IN: 5,
|
||||||
|
|
||||||
|
// we are logged in with an active matrix client.
|
||||||
|
LOGGED_IN: 6,
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
|
// we export this so that the integration tests can use it :-S
|
||||||
|
statics: {
|
||||||
|
VIEWS: VIEWS,
|
||||||
|
},
|
||||||
|
|
||||||
displayName: 'MatrixChat',
|
displayName: 'MatrixChat',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
@ -93,8 +127,10 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
const s = {
|
const s = {
|
||||||
loading: true,
|
// the master view we are showing.
|
||||||
screen: undefined,
|
view: VIEWS.LOADING,
|
||||||
|
|
||||||
|
// a thing to call showScreen with once login completes.
|
||||||
screenAfterLogin: this.props.initialScreenAfterLogin,
|
screenAfterLogin: this.props.initialScreenAfterLogin,
|
||||||
|
|
||||||
// Stashed guest credentials if the user logs out
|
// Stashed guest credentials if the user logs out
|
||||||
|
@ -113,8 +149,6 @@ module.exports = React.createClass({
|
||||||
// If we're trying to just view a user ID (i.e. /user URL), this is it
|
// If we're trying to just view a user ID (i.e. /user URL), this is it
|
||||||
viewUserId: null,
|
viewUserId: null,
|
||||||
|
|
||||||
loggedIn: false,
|
|
||||||
loggingIn: false,
|
|
||||||
collapse_lhs: false,
|
collapse_lhs: false,
|
||||||
collapse_rhs: false,
|
collapse_rhs: false,
|
||||||
ready: false,
|
ready: false,
|
||||||
|
@ -301,10 +335,12 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
console.error("Unable to load session", e);
|
console.error("Unable to load session", e);
|
||||||
}).then(()=>{
|
return false;
|
||||||
// stuff this through the dispatcher so that it happens
|
}).then((loadedSession) => {
|
||||||
// after the on_logged_in action.
|
if (!loadedSession) {
|
||||||
dis.dispatch({action: 'load_completed'});
|
// fall back to showing the login screen
|
||||||
|
dis.dispatch({action: "start_login"});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}).done();
|
}).done();
|
||||||
},
|
},
|
||||||
|
@ -325,18 +361,19 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setStateForNewScreen: function(state) {
|
setStateForNewView: function(state) {
|
||||||
|
if (state.view === undefined) {
|
||||||
|
throw new Error("setStateForNewView with no view!");
|
||||||
|
}
|
||||||
const newState = {
|
const newState = {
|
||||||
screen: undefined,
|
|
||||||
viewUserId: null,
|
viewUserId: null,
|
||||||
loggedIn: false,
|
|
||||||
ready: false,
|
|
||||||
};
|
};
|
||||||
Object.assign(newState, state);
|
Object.assign(newState, state);
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
},
|
},
|
||||||
|
|
||||||
onAction: function(payload) {
|
onAction: function(payload) {
|
||||||
|
// console.log(`MatrixClientPeg.onAction: ${payload.action}`);
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
|
|
||||||
|
@ -355,19 +392,19 @@ module.exports = React.createClass({
|
||||||
guestCreds: MatrixClientPeg.getCredentials(),
|
guestCreds: MatrixClientPeg.getCredentials(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.setStateForNewScreen({
|
this.setStateForNewView({
|
||||||
screen: 'login',
|
view: VIEWS.LOGIN,
|
||||||
});
|
});
|
||||||
this.notifyNewScreen('login');
|
this.notifyNewScreen('login');
|
||||||
break;
|
break;
|
||||||
case 'start_post_registration':
|
case 'start_post_registration':
|
||||||
this.setState({ // don't clobber loggedIn status
|
this.setState({
|
||||||
screen: 'post_registration',
|
view: VIEWS.POST_REGISTRATION,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'start_password_recovery':
|
case 'start_password_recovery':
|
||||||
this.setStateForNewScreen({
|
this.setStateForNewView({
|
||||||
screen: 'forgot_password',
|
view: VIEWS.FORGOT_PASSWORD,
|
||||||
});
|
});
|
||||||
this.notifyNewScreen('forgot_password');
|
this.notifyNewScreen('forgot_password');
|
||||||
break;
|
break;
|
||||||
|
@ -511,7 +548,10 @@ module.exports = React.createClass({
|
||||||
// and also that we're not ready (we'll be marked as logged
|
// and also that we're not ready (we'll be marked as logged
|
||||||
// in once the login completes, then ready once the sync
|
// in once the login completes, then ready once the sync
|
||||||
// completes).
|
// completes).
|
||||||
this.setState({loggingIn: true, ready: false});
|
this.setStateForNewView({
|
||||||
|
view: VIEWS.LOGGING_IN,
|
||||||
|
ready: false,
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case 'on_logged_in':
|
case 'on_logged_in':
|
||||||
this._onLoggedIn(payload.teamToken);
|
this._onLoggedIn(payload.teamToken);
|
||||||
|
@ -522,9 +562,6 @@ module.exports = React.createClass({
|
||||||
case 'will_start_client':
|
case 'will_start_client':
|
||||||
this._onWillStartClient();
|
this._onWillStartClient();
|
||||||
break;
|
break;
|
||||||
case 'load_completed':
|
|
||||||
this._onLoadCompleted();
|
|
||||||
break;
|
|
||||||
case 'new_version':
|
case 'new_version':
|
||||||
this.onVersion(
|
this.onVersion(
|
||||||
payload.currentVersion, payload.newVersion,
|
payload.currentVersion, payload.newVersion,
|
||||||
|
@ -548,8 +585,8 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_startRegistration: function(params) {
|
_startRegistration: function(params) {
|
||||||
this.setStateForNewScreen({
|
this.setStateForNewView({
|
||||||
screen: 'register',
|
view: VIEWS.REGISTER,
|
||||||
// these params may be undefined, but if they are,
|
// these params may be undefined, but if they are,
|
||||||
// unset them from our state: we don't want to
|
// unset them from our state: we don't want to
|
||||||
// resume a previous registration session if the
|
// resume a previous registration session if the
|
||||||
|
@ -857,13 +894,6 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the sessionloader has finished
|
|
||||||
*/
|
|
||||||
_onLoadCompleted: function() {
|
|
||||||
this.setState({loading: false});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called whenever someone changes the theme
|
* Called whenever someone changes the theme
|
||||||
*
|
*
|
||||||
|
@ -916,9 +946,8 @@ module.exports = React.createClass({
|
||||||
*/
|
*/
|
||||||
_onLoggedIn: function(teamToken) {
|
_onLoggedIn: function(teamToken) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
view: VIEWS.LOGGED_IN,
|
||||||
guestCreds: null,
|
guestCreds: null,
|
||||||
loggedIn: true,
|
|
||||||
loggingIn: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (teamToken) {
|
if (teamToken) {
|
||||||
|
@ -979,8 +1008,8 @@ module.exports = React.createClass({
|
||||||
*/
|
*/
|
||||||
_onLoggedOut: function() {
|
_onLoggedOut: function() {
|
||||||
this.notifyNewScreen('login');
|
this.notifyNewScreen('login');
|
||||||
this.setStateForNewScreen({
|
this.setStateForNewView({
|
||||||
loggedIn: false,
|
view: VIEWS.LOGIN,
|
||||||
ready: false,
|
ready: false,
|
||||||
collapse_lhs: false,
|
collapse_lhs: false,
|
||||||
collapse_rhs: false,
|
collapse_rhs: false,
|
||||||
|
@ -1143,7 +1172,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
// we can't view a room unless we're logged in
|
// we can't view a room unless we're logged in
|
||||||
// (a guest account is fine)
|
// (a guest account is fine)
|
||||||
if (this.state.loggedIn) {
|
if (this.state.view === VIEWS.LOGGED_IN) {
|
||||||
dis.dispatch(payload);
|
dis.dispatch(payload);
|
||||||
}
|
}
|
||||||
} else if (screen.indexOf('user/') == 0) {
|
} else if (screen.indexOf('user/') == 0) {
|
||||||
|
@ -1263,7 +1292,7 @@ module.exports = React.createClass({
|
||||||
onFinishPostRegistration: function() {
|
onFinishPostRegistration: function() {
|
||||||
// Don't confuse this with "PageType" which is the middle window to show
|
// Don't confuse this with "PageType" which is the middle window to show
|
||||||
this.setState({
|
this.setState({
|
||||||
screen: undefined,
|
view: VIEWS.LOGGED_IN,
|
||||||
});
|
});
|
||||||
this.showScreen("settings");
|
this.showScreen("settings");
|
||||||
},
|
},
|
||||||
|
@ -1352,11 +1381,9 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
// `loading` might be set to false before `loggedIn = true`, causing the default
|
// console.log(`Rendering MatrixChat with view ${this.state.view}`);
|
||||||
// (`<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
|
if (this.state.view === VIEWS.LOADING || this.state.view === VIEWS.LOGGING_IN) {
|
||||||
// actions `on_logging_in` and `on_logged_in`.
|
|
||||||
if (this.state.loading || this.state.loggingIn) {
|
|
||||||
const Spinner = sdk.getComponent('elements.Spinner');
|
const Spinner = sdk.getComponent('elements.Spinner');
|
||||||
return (
|
return (
|
||||||
<div className="mx_MatrixChat_splash">
|
<div className="mx_MatrixChat_splash">
|
||||||
|
@ -1366,7 +1393,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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') {
|
if (this.state.view === VIEWS.POST_REGISTRATION) {
|
||||||
const PostRegistration = sdk.getComponent('structures.login.PostRegistration');
|
const PostRegistration = sdk.getComponent('structures.login.PostRegistration');
|
||||||
return (
|
return (
|
||||||
<PostRegistration
|
<PostRegistration
|
||||||
|
@ -1374,38 +1401,42 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// `ready` and `loggedIn` may be set before `page_type` (because the
|
if (this.state.view === VIEWS.LOGGED_IN) {
|
||||||
// latter is set via the dispatcher). If we don't yet have a `page_type`,
|
// `ready` and `view==LOGGED_IN` may be set before `page_type` (because the
|
||||||
// keep showing the spinner for now.
|
// latter is set via the dispatcher). If we don't yet have a `page_type`,
|
||||||
if (this.state.loggedIn && this.state.ready && this.state.page_type) {
|
// keep showing the spinner for now.
|
||||||
/* for now, we stuff the entirety of our props and state into the LoggedInView.
|
if (this.state.ready && this.state.page_type) {
|
||||||
* we should go through and figure out what we actually need to pass down, as well
|
/* for now, we stuff the entirety of our props and state into the LoggedInView.
|
||||||
* as using something like redux to avoid having a billion bits of state kicking around.
|
* 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.
|
||||||
const LoggedInView = sdk.getComponent('structures.LoggedInView');
|
*/
|
||||||
return (
|
const LoggedInView = sdk.getComponent('structures.LoggedInView');
|
||||||
<LoggedInView ref="loggedInView" matrixClient={MatrixClientPeg.get()}
|
return (
|
||||||
onRoomCreated={this.onRoomCreated}
|
<LoggedInView ref="loggedInView" matrixClient={MatrixClientPeg.get()}
|
||||||
onUserSettingsClose={this.onUserSettingsClose}
|
onRoomCreated={this.onRoomCreated}
|
||||||
onRegistered={this.onRegistered}
|
onUserSettingsClose={this.onUserSettingsClose}
|
||||||
currentRoomId={this.state.currentRoomId}
|
onRegistered={this.onRegistered}
|
||||||
teamToken={this._teamToken}
|
currentRoomId={this.state.currentRoomId}
|
||||||
{...this.props}
|
teamToken={this._teamToken}
|
||||||
{...this.state}
|
{...this.props}
|
||||||
/>
|
{...this.state}
|
||||||
);
|
/>
|
||||||
} else if (this.state.loggedIn) {
|
);
|
||||||
// we think we are logged in, but are still waiting for the /sync to complete
|
} else {
|
||||||
const Spinner = sdk.getComponent('elements.Spinner');
|
// we think we are logged in, but are still waiting for the /sync to complete
|
||||||
return (
|
const Spinner = sdk.getComponent('elements.Spinner');
|
||||||
<div className="mx_MatrixChat_splash">
|
return (
|
||||||
<Spinner />
|
<div className="mx_MatrixChat_splash">
|
||||||
<a href="#" className="mx_MatrixChat_splashButtons" onClick={ this.onLogoutClick }>
|
<Spinner />
|
||||||
{ _t('Logout') }
|
<a href="#" className="mx_MatrixChat_splashButtons" onClick={ this.onLogoutClick }>
|
||||||
</a>
|
{ _t('Logout') }
|
||||||
</div>
|
</a>
|
||||||
);
|
</div>
|
||||||
} else if (this.state.screen == 'register') {
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.view === VIEWS.REGISTER) {
|
||||||
const Registration = sdk.getComponent('structures.login.Registration');
|
const Registration = sdk.getComponent('structures.login.Registration');
|
||||||
return (
|
return (
|
||||||
<Registration
|
<Registration
|
||||||
|
@ -1428,7 +1459,10 @@ module.exports = React.createClass({
|
||||||
onCancelClick={this.state.guestCreds ? this.onReturnToGuestClick : null}
|
onCancelClick={this.state.guestCreds ? this.onReturnToGuestClick : null}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (this.state.screen == 'forgot_password') {
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (this.state.view === VIEWS.FORGOT_PASSWORD) {
|
||||||
const ForgotPassword = sdk.getComponent('structures.login.ForgotPassword');
|
const ForgotPassword = sdk.getComponent('structures.login.ForgotPassword');
|
||||||
return (
|
return (
|
||||||
<ForgotPassword
|
<ForgotPassword
|
||||||
|
@ -1440,7 +1474,9 @@ module.exports = React.createClass({
|
||||||
onRegisterClick={this.onRegisterClick}
|
onRegisterClick={this.onRegisterClick}
|
||||||
onLoginClick={this.onLoginClick} />
|
onLoginClick={this.onLoginClick} />
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (this.state.view === VIEWS.LOGIN) {
|
||||||
const Login = sdk.getComponent('structures.login.Login');
|
const Login = sdk.getComponent('structures.login.Login');
|
||||||
return (
|
return (
|
||||||
<Login
|
<Login
|
||||||
|
@ -1458,5 +1494,7 @@ module.exports = React.createClass({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.error(`Unknown view ${this.state.view}`);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue