From bd6c65997dc29cdbc2c6c9573ab149583d5dbec9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan <pkaplan@media.mit.edu> Date: Wed, 28 Nov 2018 14:05:47 -0500 Subject: [PATCH] Add download and retry saving to saving error alert. I do not like how the alert component is hyper specialized for two distinct use cases. It would be much better to refactor the alerts so that the alerter can just fill the alert with whatever they want. But I do not feel comfortable making that refactor because I know ben and evelyn have gone back and forth about these alerts. --- src/components/alerts/alert.jsx | 34 ++++++++++++++++++++++- src/components/alerts/alerts.jsx | 2 ++ src/containers/alert.jsx | 46 +++++++++++++++++++++----------- src/lib/alerts/index.jsx | 12 +++++---- src/lib/project-saver-hoc.jsx | 7 +++-- src/reducers/alerts.js | 2 ++ 6 files changed, 78 insertions(+), 25 deletions(-) diff --git a/src/components/alerts/alert.jsx b/src/components/alerts/alert.jsx index facbc2303..64b931171 100644 --- a/src/components/alerts/alert.jsx +++ b/src/components/alerts/alert.jsx @@ -22,7 +22,11 @@ const AlertComponent = ({ iconSpinner, iconURL, level, + showDownload, + showSaveNow, onCloseAlert, + onDownload, + onSaveNow, onReconnect, showReconnect }) => ( @@ -53,6 +57,30 @@ const AlertComponent = ({ /> ) : content} </div> + {showSaveNow && ( + <button + className={styles.alertConnectionButton} + onClick={onSaveNow} + > + <FormattedMessage + defaultMessage="Try Again" // TODO control wrapping in css + description="Button to try saving again" + id="gui.alerts.tryAgain" + /> + </button> + )} + {showDownload && ( + <button + className={styles.alertConnectionButton} + onClick={onDownload} + > + <FormattedMessage + defaultMessage="Download" + description="Button to download project locally" + id="gui.alerts.download" + /> + </button> + )} {showReconnect && ( <button className={styles.alertConnectionButton} @@ -88,8 +116,12 @@ AlertComponent.propTypes = { iconURL: PropTypes.string, level: PropTypes.string, onCloseAlert: PropTypes.func.isRequired, + onDownload: PropTypes.func, onReconnect: PropTypes.func, - showReconnect: PropTypes.bool + onSaveNow: PropTypes.func, + showDownload: PropTypes.func, + showReconnect: PropTypes.bool, + showSaveNow: PropTypes.bool }; AlertComponent.defaultProps = { diff --git a/src/components/alerts/alerts.jsx b/src/components/alerts/alerts.jsx index b2b14a638..21d6c01c4 100644 --- a/src/components/alerts/alerts.jsx +++ b/src/components/alerts/alerts.jsx @@ -25,7 +25,9 @@ const AlertsComponent = ({ key={index} level={a.level} message={a.message} + showDownload={a.showDownload} showReconnect={a.showReconnect} + showSaveNow={a.showSaveNow} onCloseAlert={onCloseAlert} /> ))} diff --git a/src/containers/alert.jsx b/src/containers/alert.jsx index 3aefc70b2..08909a7b7 100644 --- a/src/containers/alert.jsx +++ b/src/containers/alert.jsx @@ -2,10 +2,11 @@ import React from 'react'; import bindAll from 'lodash.bindall'; import PropTypes from 'prop-types'; import {connect} from 'react-redux'; - +import SB3Downloader from './sb3-downloader.jsx'; import AlertComponent from '../components/alerts/alert.jsx'; import {openConnectionModal} from '../reducers/modals'; import {setConnectionModalExtensionId} from '../reducers/connection-modal'; +import {manualUpdateProject} from '../reducers/project-state'; class Alert extends React.Component { constructor (props) { @@ -32,21 +33,30 @@ class Alert extends React.Component { iconSpinner, iconURL, message, - showReconnect + onSaveNow, + showDownload, + showReconnect, + showSaveNow } = this.props; return ( - <AlertComponent - closeButton={closeButton} - content={content} - extensionName={extensionName} - iconSpinner={iconSpinner} - iconURL={iconURL} - level={level} - message={message} - showReconnect={showReconnect} - onCloseAlert={this.handleOnCloseAlert} - onReconnect={this.handleOnReconnect} - /> + <SB3Downloader>{(_, downloadProject) => ( + <AlertComponent + closeButton={closeButton} + content={content} + extensionName={extensionName} + iconSpinner={iconSpinner} + iconURL={iconURL} + level={level} + message={message} + showDownload={showDownload} + showReconnect={showReconnect} + showSaveNow={showSaveNow} + onCloseAlert={this.handleOnCloseAlert} + onDownload={downloadProject} + onReconnect={this.handleOnReconnect} + onSaveNow={onSaveNow} + /> + )}</SB3Downloader> ); } } @@ -57,6 +67,9 @@ const mapDispatchToProps = dispatch => ({ onOpenConnectionModal: id => { dispatch(setConnectionModalExtensionId(id)); dispatch(openConnectionModal()); + }, + onSaveNow: () => { + dispatch(manualUpdateProject()); } }); @@ -72,7 +85,10 @@ Alert.propTypes = { message: PropTypes.string, onCloseAlert: PropTypes.func.isRequired, onOpenConnectionModal: PropTypes.func, - showReconnect: PropTypes.bool + onSaveNow: PropTypes.func, + showDownload: PropTypes.bool, + showReconnect: PropTypes.bool, + showSaveNow: PropTypes.bool }; export default connect( diff --git a/src/lib/alerts/index.jsx b/src/lib/alerts/index.jsx index 66474b8c8..dda178e1f 100644 --- a/src/lib/alerts/index.jsx +++ b/src/lib/alerts/index.jsx @@ -61,11 +61,13 @@ const alerts = [ }, { alertId: 'savingError', - clearList: ['saving', 'saveSuccess'], - closeButton: true, + clearList: ['saving', 'saveSuccess', 'savingError'], + showDownload: true, + showSaveNow: true, + closeButton: false, content: ( <FormattedMessage - defaultMessage="Could not save the project. Please try again!" + defaultMessage="Project could not save." description="Message indicating that project could not be saved" id="gui.alerts.savingError" /> @@ -75,7 +77,7 @@ const alerts = [ { alertId: 'saveSuccess', alertType: AlertTypes.INLINE, - clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving'], + clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving', 'savingError'], content: ( <FormattedMessage defaultMessage="Successfully saved." @@ -90,7 +92,7 @@ const alerts = [ { alertId: 'saving', alertType: AlertTypes.INLINE, - clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving'], + clearList: ['createSuccess', 'creating', 'saveSuccess', 'saving', 'savingError'], content: ( <FormattedMessage defaultMessage="Saving..." diff --git a/src/lib/project-saver-hoc.jsx b/src/lib/project-saver-hoc.jsx index 3fbbad361..d23e60fa6 100644 --- a/src/lib/project-saver-hoc.jsx +++ b/src/lib/project-saver-hoc.jsx @@ -84,10 +84,9 @@ const ProjectSaverHOC = function (WrappedComponent) { this.props.onShowSaveSuccessAlert(); }) .catch(err => { - // NOTE: should throw up a notice for user - if (this.props.isManualUpdating) { - this.props.onShowAlert('savingError'); - } + // Always show the savingError alert because it gives the + // user the chance to download or retry the save manually. + this.props.onShowAlert('savingError'); this.props.onProjectError(err); }); } diff --git a/src/reducers/alerts.js b/src/reducers/alerts.js index 5b0fbd09a..e38b591a4 100644 --- a/src/reducers/alerts.js +++ b/src/reducers/alerts.js @@ -46,6 +46,8 @@ const reducer = function (state, action) { newAlert.iconURL = alertData.iconURL; newAlert.iconSpinner = alertData.iconSpinner; newAlert.level = alertData.level; + newAlert.showDownload = alertData.showDownload; + newAlert.showSaveNow = alertData.showSaveNow; newList.push(newAlert); return Object.assign({}, state, { -- GitLab