Don't force-logout the user if reading localstorage fails

Give them a modal dialog to give them a chance to abort.
pull/21833/head
Richard van der Hoff 2017-02-15 18:44:15 +00:00
parent a5325ee14a
commit bdb8f9d052
3 changed files with 133 additions and 25 deletions

View File

@ -1,5 +1,6 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with 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 dis from './dispatcher';
import DMRoomMap from './utils/DMRoomMap'; import DMRoomMap from './utils/DMRoomMap';
import RtsClient from './RtsClient'; 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 * Called at startup, to attempt to build a logged-in Matrix session. It tries
@ -109,16 +112,17 @@ export function loadSession(opts) {
return q(); return q();
} }
if (_restoreFromLocalStorage()) { return _restoreFromLocalStorage().then((success) => {
return q(); if (success) {
} return;
}
if (enableGuest) { if (enableGuest) {
return _registerAsGuest(guestHsUrl, guestIsUrl, defaultDeviceDisplayName); return _registerAsGuest(guestHsUrl, guestIsUrl, defaultDeviceDisplayName);
} }
// fall back to login screen // fall back to login screen
return q(); });
} }
function _loginWithToken(queryParams, defaultDeviceDisplayName) { 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() { function _restoreFromLocalStorage() {
if (!localStorage) { if (!localStorage) {
return false; return q(false);
} }
const hs_url = localStorage.getItem("mx_hs_url"); const hs_url = localStorage.getItem("mx_hs_url");
const is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org'; const is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org';
@ -208,28 +213,55 @@ function _restoreFromLocalStorage() {
identityServerUrl: is_url, identityServerUrl: is_url,
guest: is_guest, guest: is_guest,
}); });
return true; return q(true);
} catch (e) { } catch (e) {
console.log("Unable to restore session", e); return _handleRestoreFailure(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);
} }
} else { } else {
console.log("No previous session found."); 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; let rtsClient = null;
export function initRtsClient(url) { export function initRtsClient(url) {
rtsClient = new RtsClient(url); rtsClient = new RtsClient(url);

View File

@ -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); views$dialogs$NeedToRegisterDialog && (module.exports.components['views.dialogs.NeedToRegisterDialog'] = views$dialogs$NeedToRegisterDialog);
import views$dialogs$QuestionDialog from './components/views/dialogs/QuestionDialog'; import views$dialogs$QuestionDialog from './components/views/dialogs/QuestionDialog';
views$dialogs$QuestionDialog && (module.exports.components['views.dialogs.QuestionDialog'] = 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'; import views$dialogs$SetDisplayNameDialog from './components/views/dialogs/SetDisplayNameDialog';
views$dialogs$SetDisplayNameDialog && (module.exports.components['views.dialogs.SetDisplayNameDialog'] = views$dialogs$SetDisplayNameDialog); views$dialogs$SetDisplayNameDialog && (module.exports.components['views.dialogs.SetDisplayNameDialog'] = views$dialogs$SetDisplayNameDialog);
import views$dialogs$TextInputDialog from './components/views/dialogs/TextInputDialog'; import views$dialogs$TextInputDialog from './components/views/dialogs/TextInputDialog';

View File

@ -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 = (
<p>Otherwise, <a onClick={this._sendBugReport} href='#'>
click here</a> to send a bug report.
</p>
);
}
return (
<BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished}
title='Unable to restore session'>
<div className="mx_Dialog_content">
<p>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.</p>
<p>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.</p>
{bugreport}
</div>
<div className="mx_Dialog_buttons">
<button className="mx_Dialog_primary" onClick={this._continueClicked}>
Continue anyway
</button>
</div>
</BaseDialog>
);
},
});