From bdb8f9d05292300501ae5516bde780be17ed00b5 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 15 Feb 2017 18:44:15 +0000 Subject: [PATCH] Don't force-logout the user if reading localstorage fails Give them a modal dialog to give them a chance to abort. --- src/Lifecycle.js | 82 +++++++++++++------ src/component-index.js | 2 + .../dialogs/SessionRestoreErrorDialog.js | 74 +++++++++++++++++ 3 files changed, 133 insertions(+), 25 deletions(-) create mode 100644 src/components/views/dialogs/SessionRestoreErrorDialog.js diff --git a/src/Lifecycle.js b/src/Lifecycle.js index e899ec6ad8..e891ab5984 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,6 +25,8 @@ import Presence from './Presence'; import dis from './dispatcher'; import DMRoomMap from './utils/DMRoomMap'; import RtsClient from './RtsClient'; +import Modal from './Modal'; +import sdk from './index'; /** * Called at startup, to attempt to build a logged-in Matrix session. It tries @@ -109,16 +112,17 @@ export function loadSession(opts) { return q(); } - if (_restoreFromLocalStorage()) { - return q(); - } + return _restoreFromLocalStorage().then((success) => { + if (success) { + return; + } - if (enableGuest) { - return _registerAsGuest(guestHsUrl, guestIsUrl, defaultDeviceDisplayName); - } + if (enableGuest) { + return _registerAsGuest(guestHsUrl, guestIsUrl, defaultDeviceDisplayName); + } - // fall back to login screen - return q(); + // fall back to login screen + }); } function _loginWithToken(queryParams, defaultDeviceDisplayName) { @@ -178,10 +182,11 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) { }); } -// returns true if a session is found in localstorage +// returns a promise which resolves to true if a session is found in +// localstorage function _restoreFromLocalStorage() { if (!localStorage) { - return false; + return q(false); } const hs_url = localStorage.getItem("mx_hs_url"); const is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org'; @@ -208,28 +213,55 @@ function _restoreFromLocalStorage() { identityServerUrl: is_url, guest: is_guest, }); - return true; + return q(true); } catch (e) { - console.log("Unable to restore session", e); - - var msg = e.message; - if (msg == "OLM.BAD_LEGACY_ACCOUNT_PICKLE") { - msg = "You need to log back in to generate end-to-end encryption keys " - + "for this device and submit the public key to your homeserver. " - + "This is a once off; sorry for the inconvenience."; - } - - // don't leak things into the new session - _clearLocalStorage(); - - throw new Error("Unable to restore previous session: " + msg); + return _handleRestoreFailure(e); } } else { console.log("No previous session found."); - return false; + return q(false); } } +function _handleRestoreFailure(e) { + console.log("Unable to restore session", e); + + let msg = e.message; + if (msg == "OLM.BAD_LEGACY_ACCOUNT_PICKLE") { + msg = "You need to log back in to generate end-to-end encryption keys " + + "for this device and submit the public key to your homeserver. " + + "This is a once off; sorry for the inconvenience."; + + _clearLocalStorage(); + + return q.reject(new Error( + "Unable to restore previous session: " + msg, + )); + } + + const def = q.defer(); + const SessionRestoreErrorDialog = + sdk.getComponent('views.dialogs.SessionRestoreErrorDialog'); + + Modal.createDialog(SessionRestoreErrorDialog, { + error: msg, + onFinished: (success) => { + def.resolve(success); + }, + }); + + return def.promise.then((success) => { + if (success) { + // user clicked continue. + _clearLocalStorage(); + return false; + } + + // try, try again + return _restoreFromLocalStorage(); + }); +} + let rtsClient = null; export function initRtsClient(url) { rtsClient = new RtsClient(url); diff --git a/src/component-index.js b/src/component-index.js index 62d26d4ce7..c705150e12 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -89,6 +89,8 @@ import views$dialogs$NeedToRegisterDialog from './components/views/dialogs/NeedT views$dialogs$NeedToRegisterDialog && (module.exports.components['views.dialogs.NeedToRegisterDialog'] = views$dialogs$NeedToRegisterDialog); import views$dialogs$QuestionDialog from './components/views/dialogs/QuestionDialog'; views$dialogs$QuestionDialog && (module.exports.components['views.dialogs.QuestionDialog'] = views$dialogs$QuestionDialog); +import views$dialogs$SessionRestoreErrorDialog from './components/views/dialogs/SessionRestoreErrorDialog'; +views$dialogs$SessionRestoreErrorDialog && (module.exports.components['views.dialogs.SessionRestoreErrorDialog'] = views$dialogs$SessionRestoreErrorDialog); import views$dialogs$SetDisplayNameDialog from './components/views/dialogs/SetDisplayNameDialog'; views$dialogs$SetDisplayNameDialog && (module.exports.components['views.dialogs.SetDisplayNameDialog'] = views$dialogs$SetDisplayNameDialog); import views$dialogs$TextInputDialog from './components/views/dialogs/TextInputDialog'; diff --git a/src/components/views/dialogs/SessionRestoreErrorDialog.js b/src/components/views/dialogs/SessionRestoreErrorDialog.js new file mode 100644 index 0000000000..358bbf1fec --- /dev/null +++ b/src/components/views/dialogs/SessionRestoreErrorDialog.js @@ -0,0 +1,74 @@ +/* +Copyright 2017 Vector Creations 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 sdk from '../../../index'; +import SdkConfig from '../../../SdkConfig'; +import Modal from '../../../Modal'; + + +export default React.createClass({ + displayName: 'SessionRestoreErrorDialog', + + propTypes: { + error: React.PropTypes.string.isRequired, + onFinished: React.PropTypes.func.isRequired, + }, + + _sendBugReport: function() { + const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog"); + Modal.createDialog(BugReportDialog, {}); + }, + + _continueClicked: function() { + this.props.onFinished(true); + }, + + render: function() { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + let bugreport; + + if (SdkConfig.get().bug_report_endpoint_url) { + bugreport = ( +

Otherwise, + click here to send a bug report. +

+ ); + } + + return ( + +
+

We encountered an error trying to restore your previous session. If + you continue, you will need to log in again, and encrypted chat + history will be unreadable.

+ +

If you have previously used a more recent version of Riot, your session + may be incompatible with this version. Close this window and return + to the more recent version.

+ + {bugreport} +
+
+ +
+
+ ); + }, +});