UI fixes in SessionRestoreErrorDialog
* Make the 'delete my data' button not the default * Make it red * Give it a confirmation dialog * Remove the 'cancel' button: what does it mean to cancel an error? In this case, it tried again and almost certainly got the same error. * Remove the top-right 'x' and don't cancel on esc for the same reason. * Move 'send bug report' to a button rather than a 'click here' link * Add a 'refresh' button which, even if it's no more likely to work, will at least look like it's doing something (it's mostly so if you don't have a bug report endpoint, there's still a button other than the one that deletes all your data).pull/21833/head
							parent
							
								
									0323f8ed0c
								
							
						
					
					
						commit
						6d9e07580b
					
				|  | @ -250,6 +250,7 @@ textarea { | |||
| .mx_Dialog button.danger, .mx_Dialog input[type="submit"].danger { | ||||
|     background-color: $warning-color; | ||||
|     border: solid 1px $warning-color; | ||||
|     color: $accent-fg-color; | ||||
| } | ||||
| 
 | ||||
| .mx_Dialog button:disabled, .mx_Dialog input[type="submit"]:disabled { | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| /* | ||||
| Copyright 2017 Vector Creations Ltd | ||||
| Copyright 2018 New Vector Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
|  | @ -41,6 +42,13 @@ export default React.createClass({ | |||
|         // cancelled (from BaseDialog, this is always false).
 | ||||
|         onFinished: PropTypes.func.isRequired, | ||||
| 
 | ||||
|         // Whether the dialog should have a 'close' button that will
 | ||||
|         // cause the dialog to be cancelled. This should only be set
 | ||||
|         // to true if there is nothing the app can sensibly do if the
 | ||||
|         // dialog is cancelled, eg. "We can't restore your session and
 | ||||
|         // the app cannot work".
 | ||||
|         hasCancel: PropTypes.bool, | ||||
| 
 | ||||
|         // called when a key is pressed
 | ||||
|         onKeyDown: PropTypes.func, | ||||
| 
 | ||||
|  | @ -59,6 +67,12 @@ export default React.createClass({ | |||
|         contentId: React.PropTypes.string, | ||||
|     }, | ||||
| 
 | ||||
|     getDefaultProps: function() { | ||||
|         return { | ||||
|             hasCancel: true, | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     childContextTypes: { | ||||
|         matrixClient: PropTypes.instanceOf(MatrixClient), | ||||
|     }, | ||||
|  | @ -77,7 +91,7 @@ export default React.createClass({ | |||
|         if (this.props.onKeyDown) { | ||||
|             this.props.onKeyDown(e); | ||||
|         } | ||||
|         if (e.keyCode === KeyCode.ESCAPE) { | ||||
|         if (this.props.hasCancel && e.keyCode === KeyCode.ESCAPE) { | ||||
|             e.stopPropagation(); | ||||
|             e.preventDefault(); | ||||
|             this.props.onFinished(false); | ||||
|  | @ -104,11 +118,11 @@ export default React.createClass({ | |||
|                 // AT users can skip its presentation.
 | ||||
|                 aria-describedby={this.props.contentId} | ||||
|             > | ||||
|                 <AccessibleButton onClick={this._onCancelClick} | ||||
|                 { this.props.hasCancel ? <AccessibleButton onClick={this._onCancelClick} | ||||
|                     className="mx_Dialog_cancelButton" | ||||
|                 > | ||||
|                     <TintableSvg src="img/icons-close-button.svg" width="35" height="35" /> | ||||
|                 </AccessibleButton> | ||||
|                 </AccessibleButton> : null } | ||||
|                 <div className={'mx_Dialog_title ' + this.props.titleClass} id='mx_BaseDialog_title'> | ||||
|                     { this.props.title } | ||||
|                 </div> | ||||
|  |  | |||
|  | @ -31,63 +31,73 @@ export default React.createClass({ | |||
|         onFinished: PropTypes.func.isRequired, | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         if (this.refs.bugreportLink) { | ||||
|             this.refs.bugreportLink.focus(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     _sendBugReport: function() { | ||||
|         const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog"); | ||||
|         Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, {}); | ||||
|     }, | ||||
| 
 | ||||
|     _onContinueClick: function() { | ||||
|         this.props.onFinished(true); | ||||
|     _onClearStorageClick: function() { | ||||
|         const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); | ||||
|         Modal.createTrackedDialog('Session Restore Confirm Logout', '', QuestionDialog, { | ||||
|             title: _t("Sign out"), | ||||
|             description: | ||||
|                 <div>{ _t("Log out and remove encryption keys?") }</div>, | ||||
|             button: _t("Sign out"), | ||||
|             danger: true, | ||||
|             onFinished: this.props.onFinished, | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     _onCancelClick: function() { | ||||
|         this.props.onFinished(false); | ||||
|     _onRefreshClick: function() { | ||||
|         // Is this likely to help? Probably not, but giving only one button
 | ||||
|         // that clears your storage seems awful.
 | ||||
|         window.location.reload(true); | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); | ||||
|         const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); | ||||
|         let bugreport; | ||||
| 
 | ||||
|         let dialogButtons; | ||||
|         if (SdkConfig.get().bug_report_endpoint_url) { | ||||
|             bugreport = ( | ||||
|                 <p> | ||||
|                 { _t( | ||||
|                     "Otherwise, <a>click here</a> to send a bug report.", | ||||
|                     {}, | ||||
|                     { 'a': (sub) => <a ref="bugreportLink" onClick={this._sendBugReport} | ||||
|                     key="bugreport" href='#'>{ sub }</a> }, | ||||
|                 ) } | ||||
|                 </p> | ||||
|             ); | ||||
|             dialogButtons = <DialogButtons primaryButton={_t("Send Logs")} | ||||
|                 onPrimaryButtonClick={this._sendBugReport} | ||||
|                 focus={true} | ||||
|                 hasCancel={false} | ||||
|             > | ||||
|                 <button onClick={this._onClearStorageClick} className="danger"> | ||||
|                     { _t("Clear Storage and Sign Out") } | ||||
|                 </button> | ||||
|             </DialogButtons> | ||||
|         } else { | ||||
|             dialogButtons = <DialogButtons primaryButton={_t("Refresh")} | ||||
|                 onPrimaryButtonClick={this._onRefreshClick} | ||||
|                 focus={true} | ||||
|                 hasCancel={false} | ||||
|             > | ||||
|                 <button onClick={this._onClearStorageClick} className="danger"> | ||||
|                     { _t("Clear Storage and Sign Out") } | ||||
|                 </button> | ||||
|             </DialogButtons> | ||||
|         } | ||||
|         const shouldFocusContinueButton =!(bugreport==true); | ||||
| 
 | ||||
|         return ( | ||||
|             <BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished} | ||||
|                     title={_t('Unable to restore session')} | ||||
|                 title={_t('Unable to restore session')} | ||||
|                 contentId='mx_Dialog_content' | ||||
|                 hasCancel={false} | ||||
|             > | ||||
|                 <div className="mx_Dialog_content" id='mx_Dialog_content'> | ||||
|                     <p>{ _t("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>{ _t("We encountered an error trying to restore your previous session.") }</p> | ||||
| 
 | ||||
|                     <p>{ _t("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 } | ||||
|                     <p>{ _t("Clearing your browser's storage may fix the problem, but will sign you " + | ||||
|                     "out and cause any encrypted chat history to become unreadable.") }</p> | ||||
|                 </div> | ||||
|                 <DialogButtons primaryButton={_t("Continue anyway")} | ||||
|                     onPrimaryButtonClick={this._onContinueClick} focus={shouldFocusContinueButton} | ||||
|                     onCancel={this._onCancelClick} /> | ||||
|                 { dialogButtons } | ||||
|             </BaseDialog> | ||||
|         ); | ||||
|     }, | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| /* | ||||
| Copyright 2017 Aidan Gauland | ||||
| Copyright 2018 New Vector Ltd. | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
|  | @ -14,8 +15,6 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| import React from "react"; | ||||
| import PropTypes from "prop-types"; | ||||
| import { _t } from '../../../languageHandler'; | ||||
|  | @ -33,12 +32,21 @@ module.exports = React.createClass({ | |||
|         // onClick handler for the primary button.
 | ||||
|         onPrimaryButtonClick: PropTypes.func.isRequired, | ||||
| 
 | ||||
|         // should there be a cancel button? default: true
 | ||||
|         hasCancel: PropTypes.bool, | ||||
| 
 | ||||
|         // onClick handler for the cancel button.
 | ||||
|         onCancel: PropTypes.func.isRequired, | ||||
|         onCancel: PropTypes.func, | ||||
| 
 | ||||
|         focus: PropTypes.bool, | ||||
|     }, | ||||
| 
 | ||||
|     getDefaultProps: function() { | ||||
|         return { | ||||
|             hasCancel: true, | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     _onCancelClick: function() { | ||||
|         this.props.onCancel(); | ||||
|     }, | ||||
|  | @ -57,9 +65,9 @@ module.exports = React.createClass({ | |||
|                     { this.props.primaryButton } | ||||
|                 </button> | ||||
|                 { this.props.children } | ||||
|                 <button onClick={this._onCancelClick}> | ||||
|                 { this.props.hasCancel ? <button onClick={this._onCancelClick}> | ||||
|                     { _t("Cancel") } | ||||
|                 </button> | ||||
|                 </button> : null } | ||||
|             </div> | ||||
|         ); | ||||
|     }, | ||||
|  |  | |||
|  | @ -103,7 +103,6 @@ | |||
|     "You need to be logged in.": "You need to be logged in.", | ||||
|     "You need to be able to invite users to do that.": "You need to be able to invite users to do that.", | ||||
|     "Unable to create widget.": "Unable to create widget.", | ||||
|     "Popout widget": "Popout widget", | ||||
|     "Missing roomId.": "Missing roomId.", | ||||
|     "Failed to send request.": "Failed to send request.", | ||||
|     "This room is not recognised.": "This room is not recognised.", | ||||
|  | @ -655,6 +654,7 @@ | |||
|     "Delete widget": "Delete widget", | ||||
|     "Revoke widget access": "Revoke widget access", | ||||
|     "Minimize apps": "Minimize apps", | ||||
|     "Popout widget": "Popout widget", | ||||
|     "Picture": "Picture", | ||||
|     "Edit": "Edit", | ||||
|     "Create new room": "Create new room", | ||||
|  | @ -807,11 +807,15 @@ | |||
|     "Ignore request": "Ignore request", | ||||
|     "Loading device info...": "Loading device info...", | ||||
|     "Encryption key request": "Encryption key request", | ||||
|     "Otherwise, <a>click here</a> to send a bug report.": "Otherwise, <a>click here</a> to send a bug report.", | ||||
|     "Sign out": "Sign out", | ||||
|     "Log out and remove encryption keys?": "Log out and remove encryption keys?", | ||||
|     "Send Logs": "Send Logs", | ||||
|     "Clear Storage and Sign Out": "Clear Storage and Sign Out", | ||||
|     "Refresh": "Refresh", | ||||
|     "Unable to restore session": "Unable to restore session", | ||||
|     "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.": "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.", | ||||
|     "We encountered an error trying to restore your previous session.": "We encountered an error trying to restore your previous session.", | ||||
|     "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.": "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.", | ||||
|     "Continue anyway": "Continue anyway", | ||||
|     "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.", | ||||
|     "Invalid Email Address": "Invalid Email Address", | ||||
|     "This doesn't appear to be a valid email address": "This doesn't appear to be a valid email address", | ||||
|     "Verification Pending": "Verification Pending", | ||||
|  | @ -1015,7 +1019,6 @@ | |||
|     "Status.im theme": "Status.im theme", | ||||
|     "Can't load user settings": "Can't load user settings", | ||||
|     "Server may be unavailable or overloaded": "Server may be unavailable or overloaded", | ||||
|     "Sign out": "Sign out", | ||||
|     "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.", | ||||
|     "Success": "Success", | ||||
|     "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them", | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 David Baker
						David Baker