diff --git a/src/components/alerts/alert.jsx b/src/components/alerts/alert.jsx index facbc2303c6a8f1cfd84a52edc4248651da1a704..64b931171afe61ffaec61b410f3bdfcf917567ba 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 b2b14a6384c207b825c5361f3b00b20204619e27..21d6c01c45a7960ba64ed1c6f6d414287aab79b8 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 3aefc70b22aba900b013e64b5a8bfe75dc7e852c..08909a7b7d5dff889950b31caf89a783415b9e3d 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 66474b8c8480e0f702d45e53dd5eb73a53640482..dda178e1fd5c779240fd8e963d4487e532285c65 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 3fbbad36129765a87910a5048600007f4064a668..d23e60fa6a77b206b2fdec5627a529d8fc382510 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 5b0fbd09ab09bd4583bf5c09351c6a569b91e5da..e38b591a4555e6151005f2148f2a8718adc1c7af 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, {