diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 23bf5f60a5..60da4feb06 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -14,19 +14,122 @@ See the License for the specific language governing permissions and limitations under the License. */ +import q from 'q'; + import MatrixClientPeg from './MatrixClientPeg'; import Notifier from './Notifier' import UserActivity from './UserActivity'; import Presence from './Presence'; import dis from './dispatcher'; +/** + * Called at startup, 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 {setLoggedIn}, which in + * turn will raise on_logged_in and will_start_client events. + * + * It returns a promise which resolves when the above process completes. + * + * @param {object} opts.queryParams: string->string map of the query-parameters + * extracted from the #-fragment of the starting URI. + * + * @param {boolean} opts.enableGuest: set to true to enable guest access tokens + * and auto-guest registrations. + * + * @params {string} opts.hsUrl: homeserver URL. Only used if enableGuest is + * true; defines the HS to register against. + * + * @params {string} opts.isUrl: homeserver URL. Only used if enableGuest is + * true; defines the IS to use. + * + */ +export function loadSession(opts) { + const queryParams = opts.queryParams || {}; + let enableGuest = opts.enableGuest || false; + const hsUrl = opts.hsUrl; + const isUrl = opts.isUrl; + + if (queryParams.client_secret && 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(); + } + + if (!hsUrl) { + console.warn("Cannot enable guest access: can't determine HS URL to use"); + enableGuest = false; + } + + if (enableGuest && + queryParams.guest_user_id && + queryParams.guest_access_token + ) { + console.log("Using guest access credentials"); + setLoggedIn({ + userId: queryParams.guest_user_id, + accessToken: queryParams.guest_access_token, + homeserverUrl: hsUrl, + identityServerUrl: isUrl, + guest: true, + }); + return q(); + } + + if (MatrixClientPeg.get() && MatrixClientPeg.get().credentials) { + console.log("Using existing credentials"); + setLoggedIn(MatrixClientPeg.getCredentials()); + return q(); + } + + if (enableGuest) { + return _registerAsGuest(hsUrl, isUrl); + } + + // fall back to login screen + return q(); +} + +function _registerAsGuest(hsUrl, 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); + setLoggedIn({ + 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); + }); +} + + /** * Transitions to a logged-in state using the given credentials * @param {MatrixClientCreds} credentials The credentials to use */ -function setLoggedIn(credentials) { +export function setLoggedIn(credentials) { credentials.guest = Boolean(credentials.guest); - console.log("onLoggedIn => %s (guest=%s)", credentials.userId, credentials.guest); + console.log("setLoggedIn => %s (guest=%s) hs=%s", + credentials.userId, credentials.guest, + credentials.homeserverUrl); MatrixClientPeg.replaceUsingCreds(credentials); dis.dispatch({action: 'on_logged_in'}); @@ -37,7 +140,7 @@ function setLoggedIn(credentials) { /** * Logs the current session out and transitions to the logged-out state */ -function logout() { +export function logout() { if (MatrixClientPeg.get().isGuest()) { // logout doesn't work for guest sessions // Also we sometimes want to re-log in a guest session @@ -65,7 +168,7 @@ function logout() { * Starts the matrix client and all other react-sdk services that * listen for events while a session is logged in. */ -function startMatrixClient() { +export function startMatrixClient() { // 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 @@ -83,7 +186,7 @@ function startMatrixClient() { * Stops a running client and all related services, used after * a session has been logged out / ended. */ -function onLoggedOut() { +export function onLoggedOut() { if (window.localStorage) { const hsUrl = window.localStorage.getItem("mx_hs_url"); const isUrl = window.localStorage.getItem("mx_is_url"); @@ -110,7 +213,3 @@ function _stopMatrixClient() { MatrixClientPeg.get().removeAllListeners(); MatrixClientPeg.unset(); } - -module.exports = { - setLoggedIn, logout, startMatrixClient, onLoggedOut -}; diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 4924c06320..b69537d006 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -66,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, @@ -73,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, @@ -81,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; }, @@ -155,44 +149,8 @@ module.exports = React.createClass({ }, 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() @@ -200,14 +158,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) { @@ -219,6 +169,17 @@ module.exports = React.createClass({ window.addEventListener('resize', this.handleResize); this.handleResize(); + + Lifecycle.loadSession({ + queryParams: this.props.startingQueryParams, + enableGuest: this.props.enableGuest, + hsUrl: this.getDefaultHsUrl(), + isUrl: this.getDefaultIsUrl(), + }).done(()=>{ + // stuff this through the dispatcher so that it happens + // after the on_logged_in action. + dis.dispatch({action: 'load_completed'}); + }); }, componentWillUnmount: function() { @@ -245,7 +206,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, @@ -262,15 +222,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; @@ -481,6 +435,9 @@ module.exports = React.createClass({ case 'will_start_client': this._onWillStartClient(); break; + case 'load_completed': + this._onLoadCompleted(); + break; } }, @@ -591,6 +548,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 */ @@ -1031,8 +995,19 @@ 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) { + var Spinner = sdk.getComponent('elements.Spinner'); + return ( +
+ +
+ ); + } // 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 ( @@ -1108,20 +1083,15 @@ module.exports = React.createClass({ ); - } 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 = ( - - Logout - - ); - } return (
- {logoutLink} + + Logout +
); } else if (this.state.screen == 'register') { diff --git a/test/components/structures/MatrixChat-test.js b/test/components/structures/MatrixChat-test.js deleted file mode 100644 index 68bc85dd27..0000000000 --- a/test/components/structures/MatrixChat-test.js +++ /dev/null @@ -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( - - ); - - // we expect a single component - TestUtils.findRenderedComponentWithType( - res, sdk.getComponent('structures.login.Login')); - }); -});