diff --git a/client/constants.js b/client/constants.js index 12769f4a..c2e6c5be 100644 --- a/client/constants.js +++ b/client/constants.js @@ -78,6 +78,9 @@ export const SHOW_EDITOR_OPTIONS = 'SHOW_EDITOR_OPTIONS'; export const CLOSE_EDITOR_OPTIONS = 'CLOSE_EDITOR_OPTIONS'; export const SHOW_KEYBOARD_SHORTCUT_MODAL = 'SHOW_KEYBOARD_SHORTCUT_MODAL'; export const CLOSE_KEYBOARD_SHORTCUT_MODAL = 'CLOSE_KEYBOARD_SHORTCUT_MODAL'; +export const SHOW_TOAST = 'SHOW_TOAST'; +export const HIDE_TOAST = 'HIDE_TOAST'; +export const SET_TOAST_TEXT = 'SET_TOAST_TEXT'; // eventually, handle errors more specifically and better export const ERROR = 'ERROR'; diff --git a/client/modules/IDE/actions/project.js b/client/modules/IDE/actions/project.js index c6358397..18ad523d 100644 --- a/client/modules/IDE/actions/project.js +++ b/client/modules/IDE/actions/project.js @@ -5,6 +5,7 @@ import JSZip from 'jszip'; import JSZipUtils from 'jszip-utils'; import { saveAs } from 'file-saver'; import { getBlobUrl } from './files'; +import { showToast, setToastText } from './toast'; const ROOT_URL = location.href.indexOf('localhost') > 0 ? 'http://localhost:8000/api' : '/api'; @@ -60,6 +61,8 @@ export function saveProject() { dispatch({ type: ActionTypes.PROJECT_SAVE_SUCCESS }); + dispatch(showToast()); + dispatch(setToastText('Project saved.')); }) .catch((response) => dispatch({ type: ActionTypes.PROJECT_SAVE_FAIL, @@ -76,6 +79,8 @@ export function saveProject() { owner: response.data.user, files: response.data.files }); + dispatch(showToast()); + dispatch(setToastText('Project saved.')); }) .catch(response => dispatch({ type: ActionTypes.PROJECT_SAVE_FAIL, diff --git a/client/modules/IDE/actions/toast.js b/client/modules/IDE/actions/toast.js new file mode 100644 index 00000000..d39ed79a --- /dev/null +++ b/client/modules/IDE/actions/toast.js @@ -0,0 +1,23 @@ +import * as ActionTypes from '../../../constants'; + +export function hideToast() { + return { + type: ActionTypes.HIDE_TOAST + }; +} + +export function showToast() { + return (dispatch) => { + dispatch({ + type: ActionTypes.SHOW_TOAST + }); + setTimeout(() => dispatch(hideToast()), 1500); + }; +} + +export function setToastText(text) { + return { + type: ActionTypes.SET_TOAST_TEXT, + text + }; +} diff --git a/client/modules/IDE/components/Toast.js b/client/modules/IDE/components/Toast.js new file mode 100644 index 00000000..8b56c5fd --- /dev/null +++ b/client/modules/IDE/components/Toast.js @@ -0,0 +1,34 @@ +import React, { PropTypes } from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import InlineSVG from 'react-inlinesvg'; +const exitUrl = require('../../../images/exit.svg'); +import * as ToastActions from '../actions/toast'; + +function Toast(props) { + return ( +
+

+ {props.text} +

+ +
+ ); +} + +Toast.propTypes = { + text: PropTypes.string.isRequired, + hideToast: PropTypes.func.isRequired +}; + +function mapStateToProps(state) { + return state.toast; +} + +function mapDispatchToProps(dispatch) { + return bindActionCreators(ToastActions, dispatch); +} + +export default connect(mapStateToProps, mapDispatchToProps)(Toast); diff --git a/client/modules/IDE/pages/IDEView.js b/client/modules/IDE/pages/IDEView.js index 1fcb6148..04b36d06 100644 --- a/client/modules/IDE/pages/IDEView.js +++ b/client/modules/IDE/pages/IDEView.js @@ -11,6 +11,7 @@ import ShareModal from '../components/ShareModal'; import KeyboardShortcutModal from '../components/KeyboardShortcutModal'; import Nav from '../../../components/Nav'; import Console from '../components/Console'; +import Toast from '../components/Toast'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import * as FileActions from '../actions/files'; @@ -19,6 +20,7 @@ import * as ProjectActions from '../actions/project'; import * as EditorAccessibilityActions from '../actions/editorAccessibility'; import * as PreferencesActions from '../actions/preferences'; import * as UserActions from '../../User/actions'; +import * as ToastActions from '../actions/toast'; import { getHTMLFile, getJSFiles, getCSSFiles } from '../reducers/files'; import SplitPane from 'react-split-pane'; import Overlay from '../../App/components/Overlay'; @@ -114,6 +116,7 @@ class IDEView extends React.Component { render() { return (
+ {this.props.toast.isVisible && }