diff --git a/src/actions/projectActions.js b/src/actions/projectActions.js index eb7bdd1..7709ab1 100644 --- a/src/actions/projectActions.js +++ b/src/actions/projectActions.js @@ -83,6 +83,27 @@ export const updateProject = () => (dispatch, getState) => { }); } +export const deleteProject = () => (dispatch, getState) => { + var project = getState().project; + var id = project.projects[0]._id; + var type = project.type; + axios.delete(`${process.env.REACT_APP_BLOCKLY_API}/${type}/${id}`) + .then(res => { + dispatch({type: GET_PROJECTS, payload: []}); + if(type === 'project'){ + dispatch(returnSuccess(res.data.message, res.status, 'PROJECT_DELETE_SUCCESS')); + } else { + dispatch(returnSuccess(res.data.message, res.status, 'GALLERY_DELETE_SUCCESS')); + } + }) + .catch(err => { + if(err.response){ + dispatch(returnErrors(err.response.data.message, err.response.status, 'PROJECT_DELETE_FAIL')); + } + }); +} + + export const resetProject = () => (dispatch) => { dispatch({ type: GET_PROJECTS, diff --git a/src/components/Home.js b/src/components/Home.js index 3bce82c..38368d8 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -12,6 +12,7 @@ import BlocklyWindow from './Blockly/BlocklyWindow'; import CodeViewer from './CodeViewer'; import TrashcanButtons from './TrashcanButtons'; import HintTutorialExists from './Tutorial/HintTutorialExists'; +import Snackbar from './Snackbar'; import Grid from '@material-ui/core/Grid'; import IconButton from '@material-ui/core/IconButton'; @@ -47,7 +48,11 @@ class Home extends Component { state = { codeOn: false, - stats: window.localStorage.getItem('stats') + stats: window.localStorage.getItem('stats'), + snackbar: false, + type: '', + key: '', + message: '' } componentDidMount() { @@ -55,6 +60,9 @@ class Home extends Component { if(!this.props.project){ this.props.workspaceName(createNameId()); } + if(this.props.message && this.props.message.id === 'GET_SHARE_FAIL'){ + this.setState({ snackbar: true, key: Date.now(), message: `Das angefragte geteilte Projekt konnte nicht gefunden werden.`, type: 'error' }); + } } componentDidUpdate(props) { @@ -112,6 +120,12 @@ class Home extends Component { : null} + ); }; @@ -119,8 +133,13 @@ class Home extends Component { Home.propTypes = { clearStats: PropTypes.func.isRequired, - workspaceName: PropTypes.func.isRequired + workspaceName: PropTypes.func.isRequired, + message: PropTypes.object.isRequired }; +const mapStateToProps = state => ({ + message: state.message +}); -export default connect(null, { clearStats, workspaceName })(withStyles(styles, { withTheme: true })(Home)); + +export default connect(mapStateToProps, { clearStats, workspaceName })(withStyles(styles, { withTheme: true })(Home)); diff --git a/src/components/Project/Project.js b/src/components/Project/Project.js index 3e7244d..8a72713 100644 --- a/src/components/Project/Project.js +++ b/src/components/Project/Project.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { workspaceName } from '../../actions/workspaceActions'; import { getProject, resetProject } from '../../actions/projectActions'; -import { clearMessages } from '../../actions/messageActions'; +import { clearMessages, returnErrors } from '../../actions/messageActions'; import axios from 'axios'; import { createNameId } from 'mnemonic-id'; @@ -18,6 +18,7 @@ import CircularProgress from '@material-ui/core/CircularProgress'; class Project extends Component { componentDidMount() { + this.props.resetProject(); this.getProject(); } @@ -31,8 +32,13 @@ class Project extends Component { } if(this.props.message !== props.message){ if(this.props.message.id === 'PROJECT_EMPTY' || this.props.message.id === 'GET_PROJECT_FAIL'){ - this.props.workspaceName(createNameId()); - this.props.history.push('/'); + if(this.props.type!=='share'){ + this.props.returnErrors('', 404, 'GET_PROJECT_FAIL'); + this.props.history.push(`/${this.props.type}`); + } else { + this.props.history.push('/'); + this.props.returnErrors('', 404, 'GET_SHARE_FAIL'); + } } if(this.props.message.id === 'GET_PROJECT_SUCCESS'){ this.props.workspaceName(this.props.project.title); @@ -43,9 +49,6 @@ class Project extends Component { componentWillUnmount() { this.props.resetProject(); this.props.workspaceName(null); - if(this.props.message.msg){ - this.props.clearMessages(); - } } getProject = () => { @@ -77,6 +80,7 @@ Project.propTypes = { getProject: PropTypes.func.isRequired, resetProject: PropTypes.func.isRequired, clearMessages: PropTypes.func.isRequired, + returnErrors: PropTypes.func.isRequired, project: PropTypes.object.isRequired, type: PropTypes.string.isRequired, message: PropTypes.object.isRequired, @@ -90,4 +94,4 @@ const mapStateToProps = state => ({ message: state.message }); -export default connect(mapStateToProps, { workspaceName, getProject, resetProject, clearMessages })(Project); +export default connect(mapStateToProps, { workspaceName, getProject, resetProject, clearMessages, returnErrors })(Project); diff --git a/src/components/Project/ProjectHome.js b/src/components/Project/ProjectHome.js index 907b846..2cd32c9 100644 --- a/src/components/Project/ProjectHome.js +++ b/src/components/Project/ProjectHome.js @@ -2,36 +2,60 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { getProjects, resetProject } from '../../actions/projectActions'; +import { clearMessages } from '../../actions/messageActions'; import axios from 'axios'; import { Link } from 'react-router-dom'; import Breadcrumbs from '../Breadcrumbs'; import BlocklyWindow from '../Blockly/BlocklyWindow'; +import Snackbar from '../Snackbar'; import Grid from '@material-ui/core/Grid'; import Paper from '@material-ui/core/Paper'; import Divider from '@material-ui/core/Divider'; import Typography from '@material-ui/core/Typography'; +import Backdrop from '@material-ui/core/Backdrop'; +import CircularProgress from '@material-ui/core/CircularProgress'; class ProjectHome extends Component { + state = { + snackbar: false, + type: '', + key: '', + message: '' + } + componentDidMount() { - this.props.getProjects(this.props.match.path.replace('/','')); + var type = this.props.match.path.replace('/',''); + this.props.getProjects(type); + if(this.props.message){ + if(this.props.message.id === 'PROJECT_DELETE_SUCCESS'){ + this.setState({ snackbar: true, key: Date.now(), message: `Dein Projekt wurde erfolgreich gelöscht.`, type: 'success' }); + } + else if(this.props.message.id === 'GALLERY_DELETE_SUCCESS'){ + this.setState({ snackbar: true, key: Date.now(), message: `Dein Galerie-Projekt wurde erfolgreich gelöscht.`, type: 'success' }); + } + else if(this.props.message.id === 'GET_PROJECT_FAIL'){ + this.setState({ snackbar: true, key: Date.now(), message: `Dein angefragtes ${type === 'gallery' ? 'Galerie-':''}Projekt konnte nicht gefunden werden.`, type: 'error' }); + } + } } componentDidUpdate(props) { if(props.match.path !== this.props.match.path){ + this.setState({snackbar: false}); this.props.getProjects(this.props.match.path.replace('/','')); } } componentWillUnmount() { this.props.resetProject(); + this.props.clearMessages(); } - render() { var data = this.props.match.path === '/project' ? 'Projekte' : 'Galerie'; return ( @@ -39,7 +63,11 @@ class ProjectHome extends Component {

{data}

- {this.props.progress ? null : + {this.props.progress ? + + + + : {this.props.projects.map((project, i) => { return ( @@ -60,6 +88,12 @@ class ProjectHome extends Component { ) })} } + ); }; @@ -68,14 +102,17 @@ class ProjectHome extends Component { ProjectHome.propTypes = { getProjects: PropTypes.func.isRequired, resetProject: PropTypes.func.isRequired, + clearMessages: PropTypes.func.isRequired, projects: PropTypes.array.isRequired, - progress: PropTypes.bool.isRequired + progress: PropTypes.bool.isRequired, + message: PropTypes.object.isRequired }; const mapStateToProps = state => ({ projects: state.project.projects, - progress: state.project.progress + progress: state.project.progress, + message: state.message }); -export default connect(mapStateToProps, { getProjects, resetProject })(ProjectHome); +export default connect(mapStateToProps, { getProjects, resetProject, clearMessages })(ProjectHome); diff --git a/src/components/Routes.js b/src/components/Routes.js index 4dd5acc..54b4ac7 100644 --- a/src/components/Routes.js +++ b/src/components/Routes.js @@ -30,8 +30,8 @@ class Routes extends Component { // Tutorials - + // Sharing // Gallery-Projects diff --git a/src/components/Snackbar.js b/src/components/Snackbar.js index 16fd4c2..55e7429 100644 --- a/src/components/Snackbar.js +++ b/src/components/Snackbar.js @@ -35,6 +35,12 @@ class Snackbar extends Component { } } + componentDidUpdate(){ + if(!this.state.open){ + clearTimeout(this.timeout); + } + } + componentWillUnmount(){ if(this.state.open){ clearTimeout(this.timeout); diff --git a/src/components/WorkspaceFunc.js b/src/components/WorkspaceFunc.js index bc0879e..54013d8 100644 --- a/src/components/WorkspaceFunc.js +++ b/src/components/WorkspaceFunc.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { clearStats, onChangeCode, workspaceName } from '../actions/workspaceActions'; -import { updateProject } from '../actions/projectActions'; +import { updateProject, deleteProject } from '../actions/projectActions'; import * as Blockly from 'blockly/core'; @@ -28,7 +28,7 @@ import Tooltip from '@material-ui/core/Tooltip'; import TextField from '@material-ui/core/TextField'; import Typography from '@material-ui/core/Typography'; -import { faPen, faSave, faUpload, faFileDownload, faCamera, faShare, faShareAlt, faCopy } from "@fortawesome/free-solid-svg-icons"; +import { faPen, faSave, faUpload, faFileDownload, faTrashAlt, faCamera, faShare, faShareAlt, faCopy } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; const styles = (theme) => ({ @@ -51,6 +51,16 @@ const styles = (theme) => ({ color: theme.palette.primary.main, } }, + buttonTrash: { + backgroundColor: theme.palette.error.dark, + color: theme.palette.primary.contrastText, + width: '40px', + height: '40px', + '&:hover': { + backgroundColor: theme.palette.error.dark, + color: theme.palette.primary.contrastText, + } + }, link: { color: theme.palette.primary.main, textDecoration: 'none', @@ -92,9 +102,15 @@ class WorkspaceFunc extends Component { if(this.props.message.id === 'PROJECT_UPDATE_SUCCESS'){ this.setState({ snackbar: true, key: Date.now(), message: `Das Projekt wurde erfolgreich aktualisiert.`, type: 'success' }); } - else if (this.props.message.id === 'PROJECT_UPDATE_FAIL'){ + else if(this.props.message.id === 'PROJECT_DELETE_SUCCESS'){ + this.props.history.push(`/${this.props.projectType}`); + } + else if(this.props.message.id === 'PROJECT_UPDATE_FAIL'){ this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Aktualisieren des Projektes. Versuche es noch einmal.`, type: 'error' }); } + else if(this.props.message.id === 'PROJECT_DELETE_FAIL'){ + this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Löschen des Projektes. Versuche es noch einmal.`, type: 'error' }); + } } } @@ -239,7 +255,11 @@ class WorkspaceFunc extends Component { renameWorkspace = () => { this.props.workspaceName(this.state.name); this.toggleDialog(); - this.setState({ snackbar: true, type: 'success', key: Date.now(), message: `Das Projekt wurde erfolgreich in '${this.state.name}' umbenannt.` }); + if(this.props.projectType === 'project'){ + this.props.updateProject(); + } else { + this.setState({ snackbar: true, type: 'success', key: Date.now(), message: `Das Projekt wurde erfolgreich in '${this.state.name}' umbenannt.` }); + } } resetWorkspace = () => { @@ -264,9 +284,9 @@ class WorkspaceFunc extends Component { return (
{!this.props.assessment ? - +
{ this.setState({ file: true, open: true, saveFile: false, title: 'Projekt benennen', content: 'Bitte gib einen Namen für das Projekt ein und bestätige diesen mit einem Klick auf \'Eingabe\'.' }) }}> - {this.props.name && !isWidthDown('xs', this.props.width) ? {this.props.name} : null} + {this.props.name && !isWidthDown(this.props.projectType === 'project' || this.props.projectType === 'gallery' ? 'xl':'xs', this.props.width) ? {this.props.name} : null}
@@ -322,7 +342,17 @@ class WorkspaceFunc extends Component { : null} - + {!this.props.assessment? + + this.shareBlocks()} + > + + + + :null} + this.resetWorkspace()} @@ -330,13 +360,13 @@ class WorkspaceFunc extends Component { - {!this.props.assessment? - + {!this.props.assessment && (this.props.projectType === 'project' || this.props.projectType === 'gallery') ? + this.shareBlocks()} + className={this.props.classes.buttonTrash} + onClick={() => this.props.deleteProject()} > - + :null} @@ -351,7 +381,7 @@ class WorkspaceFunc extends Component { > {this.state.file ?
- +
: this.state.share ? @@ -389,6 +419,7 @@ WorkspaceFunc.propTypes = { onChangeCode: PropTypes.func.isRequired, workspaceName: PropTypes.func.isRequired, updateProject: PropTypes.func.isRequired, + deleteProject: PropTypes.func.isRequired, arduino: PropTypes.string.isRequired, xml: PropTypes.string.isRequired, name: PropTypes.string, @@ -404,4 +435,4 @@ const mapStateToProps = state => ({ message: state.message }); -export default connect(mapStateToProps, { clearStats, onChangeCode, workspaceName, updateProject })(withStyles(styles, { withTheme: true })(withWidth()(withRouter(WorkspaceFunc)))); +export default connect(mapStateToProps, { clearStats, onChangeCode, workspaceName, updateProject, deleteProject })(withStyles(styles, { withTheme: true })(withWidth()(withRouter(WorkspaceFunc))));