Call the logout API when we log out

Also try to refactor some of the login/logout code out of MatrixChat and into a separate Lifecycle.js. This still isn't great, but it at least gets some code out of MatrixClient.
pull/21833/head
David Baker 2016-08-02 14:04:20 +01:00
parent 215b32b377
commit db9750a7e3
4 changed files with 176 additions and 72 deletions

View File

@ -181,11 +181,11 @@ function _onAction(payload) {
console.error("Unknown conf call type: %s", payload.type); console.error("Unknown conf call type: %s", payload.type);
} }
} }
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
switch (payload.action) { switch (payload.action) {
case 'place_call': case 'place_call':
if (module.exports.getAnyActiveCall()) { if (module.exports.getAnyActiveCall()) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: "Existing Call", title: "Existing Call",
description: "You are already in a call." description: "You are already in a call."
@ -195,6 +195,7 @@ function _onAction(payload) {
// if the runtime env doesn't do VoIP, whine. // if the runtime env doesn't do VoIP, whine.
if (!MatrixClientPeg.get().supportsVoip()) { if (!MatrixClientPeg.get().supportsVoip()) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: "VoIP is unsupported", title: "VoIP is unsupported",
description: "You cannot place VoIP calls in this browser." description: "You cannot place VoIP calls in this browser."
@ -210,7 +211,7 @@ function _onAction(payload) {
var members = room.getJoinedMembers(); var members = room.getJoinedMembers();
if (members.length <= 1) { if (members.length <= 1) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
description: "You cannot place a call with yourself." description: "You cannot place a call with yourself."
}); });
@ -236,11 +237,13 @@ function _onAction(payload) {
case 'place_conference_call': case 'place_conference_call':
console.log("Place conference call in %s", payload.room_id); console.log("Place conference call in %s", payload.room_id);
if (!ConferenceHandler) { if (!ConferenceHandler) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
description: "Conference calls are not supported in this client" description: "Conference calls are not supported in this client"
}); });
} }
else if (!MatrixClientPeg.get().supportsVoip()) { else if (!MatrixClientPeg.get().supportsVoip()) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: "VoIP is unsupported", title: "VoIP is unsupported",
description: "You cannot place VoIP calls in this browser." description: "You cannot place VoIP calls in this browser."

98
src/Lifecycle.js Normal file
View File

@ -0,0 +1,98 @@
/*
Copyright 2015, 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 MatrixClientPeg from './MatrixClientPeg';
import Notifier from './Notifier'
import UserActivity from './UserActivity';
import Presence from './Presence';
import dis from './dispatcher';
function login(credentials, options) {
credentials.guest = Boolean(credentials.guest);
console.log("onLoggedIn => %s (guest=%s)", credentials.userId, credentials.guest);
MatrixClientPeg.replaceUsingAccessToken(
credentials.homeserverUrl, credentials.identityServerUrl,
credentials.userId, credentials.accessToken, credentials.guest
);
dis.dispatch({action: 'on_logged_in'});
startMatrixClient(options);
}
function logout() {
if (MatrixClientPeg.get().isGuest()) {
// logout doesn't work for guest sessions
// Also we sometimes want to re-log in a guest session
// if we abort the login
_onLoggedOut();
return;
}
return MatrixClientPeg.get().logout().then(_onLoggedOut,
// Just throwing an error here is going to be very unhelpful
// if you're trying to log out because your server's down and
// you want to log into a different server, so just forget the
// access token. It's annoying that this will leave the access
// token still valid, but we should fix this by having access
// tokens expire (and if you really think you've been compromised,
// change your password).
_onLoggedOut
);
}
function startMatrixClient(options) {
// dispatch this before starting the matrix client: it's used
// to add listeners for the 'sync' event so otherwise we'd have
// a race condition (and we need to dispatch synchronously for this
// to work).
dis.dispatch({action: 'will_start_client'}, true);
Notifier.start();
UserActivity.start();
Presence.start();
MatrixClientPeg.get().startClient(MatrixClientPeg.opts);
}
function _onLoggedOut() {
if (window.localStorage) {
const hsUrl = window.localStorage.getItem("mx_hs_url");
const isUrl = window.localStorage.getItem("mx_is_url");
window.localStorage.clear();
// preserve our HS & IS URLs for convenience
// N.B. we cache them in hsUrl/isUrl and can't really inline them
// as getCurrentHsUrl() may call through to localStorage.
if (hsUrl) window.localStorage.setItem("mx_hs_url", hsUrl);
if (isUrl) window.localStorage.setItem("mx_is_url", isUrl);
}
_stopMatrixClient();
dis.dispatch({action: 'on_logged_out'});
}
// stop all the background processes related to the current client
function _stopMatrixClient() {
Notifier.stop();
UserActivity.stop();
Presence.stop();
MatrixClientPeg.get().stopClient();
MatrixClientPeg.get().removeAllListeners();
MatrixClientPeg.unset();
}
module.exports = {
login, logout, startMatrixClient
};

View File

@ -40,6 +40,14 @@ function deviceId() {
class MatrixClientPeg { class MatrixClientPeg {
constructor() { constructor() {
this.matrixClient = null; this.matrixClient = null;
// These are the default options used when Lifecycle.js
// starts the client. These can be altered when the
// 'will_start_client' event is dispatched.
this.opts = {
pendingEventOrdering: "detached",
initialSyncLimit: 20,
};
} }
get(): MatrixClient { get(): MatrixClient {
@ -96,13 +104,13 @@ class MatrixClientPeg {
} }
getCredentials() { getCredentials() {
return [ return {
this.matrixClient.baseUrl, homeserverUrl: this.matrixClient.baseUrl,
this.matrixClient.idBaseUrl, identityServerUrl: this.matrixClient.idBaseUrl,
this.matrixClient.credentials.userId, userId: this.matrixClient.credentials.userId,
this.matrixClient.getAccessToken(), accessToken: this.matrixClient.getAccessToken(),
this.matrixClient.isGuest(), guest: this.matrixClient.isGuest(),
]; };
} }
tryRestore() { tryRestore() {

View File

@ -36,6 +36,7 @@ var sdk = require('../../index');
var MatrixTools = require('../../MatrixTools'); var MatrixTools = require('../../MatrixTools');
var linkifyMatrix = require("../../linkify-matrix"); var linkifyMatrix = require("../../linkify-matrix");
var KeyCode = require('../../KeyCode'); var KeyCode = require('../../KeyCode');
var Lifecycle = require('../../Lifecycle');
var createRoom = require("../../createRoom"); var createRoom = require("../../createRoom");
@ -140,6 +141,7 @@ module.exports = React.createClass({
componentWillMount: function() { componentWillMount: function() {
this.favicon = new Favico({animation: 'none'}); this.favicon = new Favico({animation: 'none'});
this.guestCreds = null;
}, },
componentDidMount: function() { componentDidMount: function() {
@ -156,7 +158,7 @@ module.exports = React.createClass({
this.props.startingQueryParams.guest_access_token) this.props.startingQueryParams.guest_access_token)
{ {
this._autoRegisterAsGuest = false; this._autoRegisterAsGuest = false;
this.onLoggedIn({ this._onHaveCredentials({
userId: this.props.startingQueryParams.guest_user_id, userId: this.props.startingQueryParams.guest_user_id,
accessToken: this.props.startingQueryParams.guest_access_token, accessToken: this.props.startingQueryParams.guest_access_token,
homeserverUrl: this.getDefaultHsUrl(), homeserverUrl: this.getDefaultHsUrl(),
@ -174,7 +176,7 @@ module.exports = React.createClass({
// Don't auto-register as a guest. This applies if you refresh the page on a // 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. // logged in client THEN hit the Sign Out button.
this._autoRegisterAsGuest = false; this._autoRegisterAsGuest = false;
this.startMatrixClient(); 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
@ -229,7 +231,7 @@ module.exports = React.createClass({
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); self._setAutoRegisterAsGuest(false);
self.onLoggedIn({ self._onHaveCredentials({
userId: creds.user_id, userId: creds.user_id,
accessToken: creds.access_token, accessToken: creds.access_token,
homeserverUrl: hsUrl, homeserverUrl: hsUrl,
@ -260,34 +262,10 @@ module.exports = React.createClass({
var self = this; var self = this;
switch (payload.action) { switch (payload.action) {
case 'logout': case 'logout':
var guestCreds;
if (MatrixClientPeg.get().isGuest()) { if (MatrixClientPeg.get().isGuest()) {
guestCreds = { // stash our guest creds so we can backout if needed this.guestCreds = MatrixClientPeg.getCredentials();
userId: MatrixClientPeg.get().credentials.userId,
accessToken: MatrixClientPeg.get().getAccessToken(),
homeserverUrl: MatrixClientPeg.get().getHomeserverUrl(),
identityServerUrl: MatrixClientPeg.get().getIdentityServerUrl(),
guest: true
}
} }
Lifecycle.logout();
if (window.localStorage) {
var hsUrl = this.getCurrentHsUrl();
var isUrl = this.getCurrentIsUrl();
window.localStorage.clear();
// preserve our HS & IS URLs for convenience
// N.B. we cache them in hsUrl/isUrl and can't really inline them
// as getCurrentHsUrl() may call through to localStorage.
window.localStorage.setItem("mx_hs_url", hsUrl);
window.localStorage.setItem("mx_is_url", isUrl);
}
this._stopMatrixClient();
this.notifyNewScreen('login');
this.replaceState({
logged_in: false,
ready: false,
guestCreds: guestCreds,
});
break; break;
case 'start_registration': case 'start_registration':
var newState = payload.params || {}; var newState = payload.params || {};
@ -313,7 +291,6 @@ module.exports = React.createClass({
if (this.state.logged_in) return; if (this.state.logged_in) return;
this.replaceState({ this.replaceState({
screen: 'login', screen: 'login',
guestCreds: this.state.guestCreds,
}); });
this.notifyNewScreen('login'); this.notifyNewScreen('login');
break; break;
@ -323,17 +300,14 @@ module.exports = React.createClass({
}); });
break; break;
case 'start_upgrade_registration': case 'start_upgrade_registration':
// stash our guest creds so we can backout if needed
if (MatrixClientPeg.get().isGuest()) {
this.guestCreds = MatrixClientPeg.getCredentials();
}
this.replaceState({ this.replaceState({
screen: "register", screen: "register",
upgradeUsername: MatrixClientPeg.get().getUserIdLocalpart(), upgradeUsername: MatrixClientPeg.get().getUserIdLocalpart(),
guestAccessToken: MatrixClientPeg.get().getAccessToken(), guestAccessToken: MatrixClientPeg.get().getAccessToken(),
guestCreds: { // stash our guest creds so we can backout if needed
userId: MatrixClientPeg.get().credentials.userId,
accessToken: MatrixClientPeg.get().getAccessToken(),
homeserverUrl: MatrixClientPeg.get().getHomeserverUrl(),
identityServerUrl: MatrixClientPeg.get().getIdentityServerUrl(),
guest: true
}
}); });
this.notifyNewScreen('register'); this.notifyNewScreen('register');
break; break;
@ -482,6 +456,15 @@ module.exports = React.createClass({
middleOpacity: payload.middleOpacity, middleOpacity: payload.middleOpacity,
}); });
break; break;
case 'on_logged_in':
this._onLoggedIn();
break;
case 'on_logged_out':
this._onLoggedOut();
break;
case 'will_start_client':
this._onWillStartClient();
break;
} }
}, },
@ -592,23 +575,40 @@ module.exports = React.createClass({
this.scrollStateMap[roomId] = state; this.scrollStateMap[roomId] = state;
}, },
onLoggedIn: function(credentials) { _doLogin(creds) {
credentials.guest = Boolean(credentials.guest); Lifecycle.login(creds, {
console.log("onLoggedIn => %s (guest=%s)", credentials.userId, credentials.guest); syncTimelineLimit: this.props.config.sync_timeline_limit,
MatrixClientPeg.replaceUsingAccessToken(
credentials.homeserverUrl, credentials.identityServerUrl,
credentials.userId, credentials.accessToken, credentials.guest
);
this.setState({
screen: undefined,
logged_in: true
}); });
this.startMatrixClient();
this.notifyNewScreen('');
}, },
startMatrixClient: function() { _onHaveCredentials: function(credentials) {
credentials.guest = Boolean(credentials.guest);
Lifecycle.login(credentials);
},
_onLoggedIn: function(credentials) {
this.guestCreds = null;
this.setState({
screen: undefined,
logged_in: true,
});
},
_onLoggedOut: function() {
this.notifyNewScreen('login');
this.replaceState({
logged_in: false,
ready: false,
});
},
_onWillStartClient() {
var cli = MatrixClientPeg.get(); var cli = MatrixClientPeg.get();
if (this.props.config.sync_timeline_limit) {
MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit;
}
var self = this; var self = this;
cli.on('sync', function(state, prevState) { cli.on('sync', function(state, prevState) {
self.updateFavicon(state, prevState); self.updateFavicon(state, prevState);
@ -675,13 +675,6 @@ module.exports = React.createClass({
action: 'logout' action: 'logout'
}); });
}); });
Notifier.start();
UserActivity.start();
Presence.start();
cli.startClient({
pendingEventOrdering: "detached",
initialSyncLimit: this.props.config.sync_timeline_limit || 20,
});
}, },
// stop all the background processes related to the current client // stop all the background processes related to the current client
@ -919,12 +912,14 @@ module.exports = React.createClass({
onReturnToGuestClick: function() { onReturnToGuestClick: function() {
// reanimate our guest login // reanimate our guest login
this.onLoggedIn(this.state.guestCreds); if (this.guestCreds) {
this.setState({ guestCreds: null }); this._onHaveCredentials(this.guestCreds);
this.guestCreds = null;
}
}, },
onRegistered: function(credentials) { onRegistered: function(credentials) {
this.onLoggedIn(credentials); this._onHaveCredentials(credentials);
// do post-registration stuff // do post-registration stuff
// This now goes straight to user settings // This now goes straight to user settings
// We use _setPage since if we wait for // We use _setPage since if we wait for
@ -1130,7 +1125,7 @@ module.exports = React.createClass({
onLoggedIn={this.onRegistered} onLoggedIn={this.onRegistered}
onLoginClick={this.onLoginClick} onLoginClick={this.onLoginClick}
onRegisterClick={this.onRegisterClick} onRegisterClick={this.onRegisterClick}
onCancelClick={ this.state.guestCreds ? this.onReturnToGuestClick : null } onCancelClick={this.guestCreds ? this.onReturnToGuestClick : null}
/> />
); );
} else if (this.state.screen == 'forgot_password') { } else if (this.state.screen == 'forgot_password') {
@ -1146,7 +1141,7 @@ module.exports = React.createClass({
} else { } else {
return ( return (
<Login <Login
onLoggedIn={this.onLoggedIn} onLoggedIn={this._onHaveCredentials}
onRegisterClick={this.onRegisterClick} onRegisterClick={this.onRegisterClick}
defaultHsUrl={this.getDefaultHsUrl()} defaultHsUrl={this.getDefaultHsUrl()}
defaultIsUrl={this.getDefaultIsUrl()} defaultIsUrl={this.getDefaultIsUrl()}
@ -1155,7 +1150,7 @@ module.exports = React.createClass({
fallbackHsUrl={this.getFallbackHsUrl()} fallbackHsUrl={this.getFallbackHsUrl()}
onForgotPasswordClick={this.onForgotPasswordClick} onForgotPasswordClick={this.onForgotPasswordClick}
onLoginAsGuestClick={this.props.enableGuest && this.props.config && this._registerAsGuest.bind(this, true)} onLoginAsGuestClick={this.props.enableGuest && this.props.config && this._registerAsGuest.bind(this, true)}
onCancelClick={ this.state.guestCreds ? this.onReturnToGuestClick : null } onCancelClick={this.guestCreds ? this.onReturnToGuestClick : null}
/> />
); );
} }