diff --git a/src/components/Tutorial/Assessment.js b/src/components/Tutorial/Assessment.js
index be845ee..75e180d 100644
--- a/src/components/Tutorial/Assessment.js
+++ b/src/components/Tutorial/Assessment.js
@@ -5,7 +5,7 @@ import { workspaceName } from '../../actions/workspaceActions';
import BlocklyWindow from '../Blockly/BlocklyWindow';
import CodeViewer from '../CodeViewer';
-import WorkspaceFunc from '../WorkspaceFunc';
+import WorkspaceFunc from '../Workspace/WorkspaceFunc';
import withWidth, { isWidthDown } from '@material-ui/core/withWidth';
import Grid from '@material-ui/core/Grid';
diff --git a/src/components/Tutorial/SolutionCheck.js b/src/components/Tutorial/SolutionCheck.js
index 37cafa7..5728ef1 100644
--- a/src/components/Tutorial/SolutionCheck.js
+++ b/src/components/Tutorial/SolutionCheck.js
@@ -5,7 +5,7 @@ import { tutorialCheck, tutorialStep } from '../../actions/tutorialActions';
import { withRouter } from 'react-router-dom';
-import Compile from '../Compile';
+import Compile from '../Workspace/Compile';
import Dialog from '../Dialog';
import { checkXml } from '../../helpers/compareXml';
diff --git a/src/components/Compile.js b/src/components/Workspace/Compile.js
similarity index 96%
rename from src/components/Compile.js
rename to src/components/Workspace/Compile.js
index 7fe5932..89b081e 100644
--- a/src/components/Compile.js
+++ b/src/components/Workspace/Compile.js
@@ -1,11 +1,11 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
-import { workspaceName } from '../actions/workspaceActions';
+import { workspaceName } from '../../actions/workspaceActions';
-import { detectWhitespacesAndReturnReadableResult } from '../helpers/whitespace';
+import { detectWhitespacesAndReturnReadableResult } from '../../helpers/whitespace';
-import Dialog from './Dialog';
+import Dialog from '../Dialog';
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
diff --git a/src/components/Workspace/DeleteProject.js b/src/components/Workspace/DeleteProject.js
new file mode 100644
index 0000000..70b617a
--- /dev/null
+++ b/src/components/Workspace/DeleteProject.js
@@ -0,0 +1,89 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { deleteProject } from '../../actions/projectActions';
+
+import { withRouter } from 'react-router-dom';
+
+import Snackbar from '../Snackbar';
+
+import { withStyles } from '@material-ui/core/styles';
+import IconButton from '@material-ui/core/IconButton';
+import Tooltip from '@material-ui/core/Tooltip';
+
+import { faTrashAlt } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+const styles = (theme) => ({
+ 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,
+ }
+ }
+});
+
+
+
+class DeleteProject extends Component {
+
+ constructor(props) {
+ super(props);
+ this.inputRef = React.createRef();
+ this.state = {
+ snackbar: false,
+ type: '',
+ key: '',
+ message: ''
+ };
+ }
+
+ componentDidUpdate(props) {
+ if(this.props.message !== props.message){
+ if(this.props.message.id === 'PROJECT_DELETE_SUCCESS'){
+ this.props.history.push(`/${this.props.projectType}`);
+ }
+ 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' });
+ }
+ }
+ }
+
+ render() {
+ return (
+
+
+ this.props.deleteProject(this.props.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id)}
+ >
+
+
+
+
+
+
+ );
+ };
+}
+
+DeleteProject.propTypes = {
+ deleteProject: PropTypes.func.isRequired,
+ message: PropTypes.string.isRequired
+};
+
+const mapStateToProps = state => ({
+ message: state.message,
+});
+
+
+export default connect(mapStateToProps, { deleteProject })(withStyles(styles, { withTheme: true })(withRouter(DeleteProject)));
diff --git a/src/components/Workspace/DownloadProject.js b/src/components/Workspace/DownloadProject.js
new file mode 100644
index 0000000..9b30273
--- /dev/null
+++ b/src/components/Workspace/DownloadProject.js
@@ -0,0 +1,66 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+
+import { saveAs } from 'file-saver';
+
+import { detectWhitespacesAndReturnReadableResult } from '../../helpers/whitespace';
+
+import { withStyles } from '@material-ui/core/styles';
+import IconButton from '@material-ui/core/IconButton';
+import Tooltip from '@material-ui/core/Tooltip';
+
+import { faFileDownload } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+const styles = (theme) => ({
+ button: {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ width: '40px',
+ height: '40px',
+ '&:hover': {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ }
+ }
+});
+
+
+class DownloadProject extends Component {
+
+ downloadXmlFile = () => {
+ var code = this.props.xml;
+ var fileName = detectWhitespacesAndReturnReadableResult(this.props.name);
+ fileName = `${fileName}.xml`
+ var blob = new Blob([code], { type: 'text/xml' });
+ saveAs(blob, fileName);
+ }
+
+ render() {
+ return (
+
+
+ this.downloadXmlFile()}
+ >
+
+
+
+
+ );
+ };
+}
+
+DownloadProject.propTypes = {
+ xml: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+};
+
+const mapStateToProps = state => ({
+ xml: state.workspace.code.xml,
+ name: state.workspace.name
+});
+
+export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(DownloadProject));
diff --git a/src/components/Workspace/OpenProject.js b/src/components/Workspace/OpenProject.js
new file mode 100644
index 0000000..6038a49
--- /dev/null
+++ b/src/components/Workspace/OpenProject.js
@@ -0,0 +1,143 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { clearStats, workspaceName } from '../../actions/workspaceActions';
+
+import * as Blockly from 'blockly/core';
+
+import Snackbar from '../Snackbar';
+import Dialog from '../Dialog';
+
+import { withStyles } from '@material-ui/core/styles';
+import Button from '@material-ui/core/Button';
+import IconButton from '@material-ui/core/IconButton';
+import Tooltip from '@material-ui/core/Tooltip';
+import Typography from '@material-ui/core/Typography';
+
+import { faUpload } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+const styles = (theme) => ({
+ button: {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ width: '40px',
+ height: '40px',
+ '&:hover': {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ }
+ }
+});
+
+
+class OpenProject extends Component {
+
+ constructor(props) {
+ super(props);
+ this.inputRef = React.createRef();
+ this.state = {
+ title: '',
+ content: '',
+ open: false,
+ snackbar: false,
+ type: '',
+ key: '',
+ message: ''
+ };
+ }
+
+ toggleDialog = () => {
+ this.setState({ open: !this.state, title: '', content: '' });
+ }
+
+ uploadXmlFile = (xmlFile) => {
+ if (xmlFile.type !== 'text/xml') {
+ this.setState({ open: true, title: 'Unzulässiger Dateityp', content: 'Die übergebene Datei entsprach nicht dem geforderten Format. Es sind nur XML-Dateien zulässig.' });
+ }
+ else {
+ var reader = new FileReader();
+ reader.readAsText(xmlFile);
+ reader.onloadend = () => {
+ var xmlDom = null;
+ try {
+ xmlDom = Blockly.Xml.textToDom(reader.result);
+ const workspace = Blockly.getMainWorkspace();
+ var xmlBefore = this.props.xml;
+ workspace.clear();
+ this.props.clearStats();
+ Blockly.Xml.domToWorkspace(xmlDom, workspace);
+ if (workspace.getAllBlocks().length < 1) {
+ Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xmlBefore), workspace)
+ this.setState({ open: true, title: 'Keine Blöcke', content: 'Es wurden keine Blöcke detektiert. Bitte überprüfe den XML-Code und versuche es erneut.' });
+ }
+ else {
+ if (!this.props.assessment) {
+ var extensionPosition = xmlFile.name.lastIndexOf('.');
+ this.props.workspaceName(xmlFile.name.substr(0, extensionPosition));
+ }
+ this.setState({ snackbar: true, type: 'success', key: Date.now(), message: 'Das Projekt aus gegebener XML-Datei wurde erfolgreich eingefügt.' });
+ }
+ } catch (err) {
+ this.setState({ open: true, title: 'Ungültige XML', content: 'Die XML-Datei konnte nicht in Blöcke zerlegt werden. Bitte überprüfe den XML-Code und versuche es erneut.' });
+ }
+ };
+ }
+ }
+
+ render() {
+ return (
+
+
+
{ this.uploadXmlFile(e.target.files[0]) }}
+ id="open-blocks"
+ type="file"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ };
+}
+
+OpenProject.propTypes = {
+ clearStats: PropTypes.func.isRequired,
+ workspaceName: PropTypes.func.isRequired,
+ xml: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired
+};
+
+const mapStateToProps = state => ({
+ xml: state.workspace.code.xml,
+ name: state.workspace.name
+});
+
+export default connect(mapStateToProps, { clearStats, workspaceName })(withStyles(styles, { withTheme: true })(OpenProject));
diff --git a/src/components/Workspace/ResetWorkspace.js b/src/components/Workspace/ResetWorkspace.js
new file mode 100644
index 0000000..0c36299
--- /dev/null
+++ b/src/components/Workspace/ResetWorkspace.js
@@ -0,0 +1,95 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { clearStats, onChangeCode, workspaceName } from '../../actions/workspaceActions';
+
+import * as Blockly from 'blockly/core';
+
+import { createNameId } from 'mnemonic-id';
+import { initialXml } from '../Blockly/initialXml.js';
+
+import Snackbar from '../Snackbar';
+
+import { withStyles } from '@material-ui/core/styles';
+import IconButton from '@material-ui/core/IconButton';
+import Tooltip from '@material-ui/core/Tooltip';
+
+import { faShare } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+const styles = (theme) => ({
+ button: {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ width: '40px',
+ height: '40px',
+ '&:hover': {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ }
+ }
+});
+
+
+
+class ResetWorkspace extends Component {
+
+ constructor(props) {
+ super(props);
+ this.inputRef = React.createRef();
+ this.state = {
+ snackbar: false,
+ type: '',
+ key: '',
+ message: '',
+ };
+ }
+
+ resetWorkspace = () => {
+ const workspace = Blockly.getMainWorkspace();
+ Blockly.Events.disable(); // https://groups.google.com/forum/#!topic/blockly/m7e3g0TC75Y
+ // if events are disabled, then the workspace will be cleared AND the blocks are not in the trashcan
+ const xmlDom = Blockly.Xml.textToDom(initialXml)
+ Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom, workspace);
+ Blockly.Events.enable();
+ workspace.options.maxBlocks = Infinity;
+ this.props.onChangeCode();
+ this.props.clearStats();
+ if (!this.props.assessment) {
+ this.props.workspaceName(createNameId());
+ }
+ this.setState({ snackbar: true, type: 'success', key: Date.now(), message: 'Das Projekt wurde erfolgreich zurückgesetzt.' });
+ }
+
+
+
+ render() {
+ return (
+
+
+ this.resetWorkspace()}
+ >
+
+
+
+
+
+
+ );
+ };
+}
+
+ResetWorkspace.propTypes = {
+ clearStats: PropTypes.func.isRequired,
+ onChangeCode: PropTypes.func.isRequired,
+ workspaceName: PropTypes.func.isRequired
+};
+
+export default connect(null, { clearStats, onChangeCode, workspaceName })(withStyles(styles, { withTheme: true })(ResetWorkspace));
diff --git a/src/components/Workspace/SaveProject.js b/src/components/Workspace/SaveProject.js
new file mode 100644
index 0000000..91bc17a
--- /dev/null
+++ b/src/components/Workspace/SaveProject.js
@@ -0,0 +1,200 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { updateProject, setDescription } from '../../actions/projectActions';
+
+import axios from 'axios';
+import { withRouter } from 'react-router-dom';
+
+import Snackbar from '../Snackbar';
+import Dialog from '../Dialog';
+
+import { withStyles } from '@material-ui/core/styles';
+import Button from '@material-ui/core/Button';
+import IconButton from '@material-ui/core/IconButton';
+import Tooltip from '@material-ui/core/Tooltip';
+import TextField from '@material-ui/core/TextField';
+import Typography from '@material-ui/core/Typography';
+import Menu from '@material-ui/core/Menu';
+import MenuItem from '@material-ui/core/MenuItem';
+
+import { faSave } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+const styles = (theme) => ({
+ button: {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ width: '40px',
+ height: '40px',
+ '&:hover': {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ }
+ }
+});
+
+
+
+class SaveProject extends Component {
+
+ constructor(props) {
+ super(props);
+ this.inputRef = React.createRef();
+ this.state = {
+ title: '',
+ content: '',
+ open: false,
+ description: props.description,
+ snackbar: false,
+ type: '',
+ key: '',
+ message: '',
+ menuOpen: false,
+ anchor: '',
+ projectType: props.projectType
+ };
+ }
+
+ componentDidUpdate(props) {
+ if (props.projectType !== this.props.projectType) {
+ this.setState({ projectType: this.props.projectType });
+ }
+ if (props.description !== this.props.description) {
+ this.setState({ description: this.props.description });
+ }
+ if(this.props.message !== props.message){
+ 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 === 'GALLERY_UPDATE_SUCCESS'){
+ this.setState({ snackbar: true, key: Date.now(), message: `Das Galerie-Projekt wurde erfolgreich aktualisiert.`, type: 'success' });
+ }
+ 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 === 'GALLERY_UPDATE_FAIL'){
+ this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Aktualisieren des Galerie-Projektes. Versuche es noch einmal.`, type: 'error' });
+ }
+ }
+ }
+
+ toggleMenu = (e) => {
+ this.setState({ menuOpen: !this.state.menuOpen, anchor: e.currentTarget });
+ };
+
+ toggleDialog = () => {
+ this.setState({ open: !this.state, title: '', content: '' });
+ }
+
+ saveProject = () => {
+ var body = {
+ xml: this.props.xml,
+ title: this.props.name
+ };
+ if(this.state.projectType === 'gallery'){
+ body.description = this.state.description;
+ }
+ axios.post(`${process.env.REACT_APP_BLOCKLY_API}/${this.state.projectType}`, body)
+ .then(res => {
+ var project = res.data[this.state.projectType];
+ this.props.history.push(`/${this.state.projectType}/${project._id}`);
+ })
+ .catch(err => {
+ this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Speichern des ${this.state.projectType === 'gallery' ? 'Galerie-':''}Projektes. Versuche es noch einmal.`, type: 'error' });
+ window.scrollTo(0, 0);
+ });
+ }
+
+ setDescription = (e) => {
+ this.setState({ description: e.target.value });
+ }
+
+ workspaceDescription = () => {
+ this.props.setDescription(this.state.description);
+ this.setState({projectType: 'gallery'},
+ () => this.saveProject()
+ );
+ }
+
+ render() {
+ console.log(1, this.props);
+ return (
+
+
+ this.toggleMenu(e) : this.state.projectType === 'project' ? () => this.props.updateProject(this.state.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id) : () => {this.setState({projectType: 'project'}, () => this.saveProject())}}
+ >
+
+
+
+
+ {this.toggleMenu(e); this.props.updateProject(this.state.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id)} : (e) => {this.toggleMenu(e); this.setState({projectType: 'project'}, () => this.saveProject())}}
+ >
+ {this.state.projectType === 'project' ? 'Projekt aktualisieren' : 'Projekt erstellen'}
+
+ {this.toggleMenu(e); this.props.updateProject(this.state.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id)} : (e) => {this.toggleMenu(e); this.setState({ open: true, title: 'Projekbeschreibung ergänzen', content: 'Bitte gib eine Beschreibung für das Galerie-Projekt ein und bestätige deine Angabe mit einem Klick auf \'Eingabe\'.'});}}
+ >
+ {this.state.projectType === 'gallery' ? 'Galerie-Projekt aktualisieren' : 'Galerie-Projekt erstellen'}
+
+
+
+
+
{this.toggleDialog(); this.setState({ description: this.props.description });}}
+ onClick={() => {this.toggleDialog(); this.setState({ description: this.props.description });}}
+ button={'Abbrechen'}
+ >
+
+
+ {this.workspaceDescription(); this.toggleDialog();}}>Eingabe
+
+
+
+ );
+ };
+}
+
+SaveProject.propTypes = {
+ updateProject: PropTypes.func.isRequired,
+ setDescription: PropTypes.func.isRequired,
+ name: PropTypes.string.isRequired,
+ description: PropTypes.string.isRequired,
+ xml: PropTypes.string.isRequired,
+ message: PropTypes.object.isRequired,
+ user: PropTypes.object
+};
+
+const mapStateToProps = state => ({
+ name: state.workspace.name,
+ description: state.project.description,
+ xml: state.workspace.code.xml,
+ message: state.message,
+ user: state.auth.user
+});
+
+export default connect(mapStateToProps, { updateProject, setDescription })(withStyles(styles, { withTheme: true })(withRouter(SaveProject)));
diff --git a/src/components/Workspace/Screenshot.js b/src/components/Workspace/Screenshot.js
new file mode 100644
index 0000000..c150db2
--- /dev/null
+++ b/src/components/Workspace/Screenshot.js
@@ -0,0 +1,97 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+
+import * as Blockly from 'blockly/core';
+
+import { saveAs } from 'file-saver';
+
+import { detectWhitespacesAndReturnReadableResult } from '../../helpers/whitespace';
+
+import { withStyles } from '@material-ui/core/styles';
+import IconButton from '@material-ui/core/IconButton';
+import Tooltip from '@material-ui/core/Tooltip';
+
+import { faCamera } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+const styles = (theme) => ({
+ button: {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ width: '40px',
+ height: '40px',
+ '&:hover': {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ }
+ }
+});
+
+
+class Screenshot extends Component {
+
+ getSvg = () => {
+ const workspace = Blockly.getMainWorkspace();
+ var canvas = workspace.svgBlockCanvas_.cloneNode(true);
+
+ if (canvas.children[0] !== undefined) {
+ canvas.removeAttribute("transform");
+ // does not work in react
+ // var cssContent = Blockly.Css.CONTENT.join('');
+ var cssContent = '';
+ for (var i = 0; i < document.getElementsByTagName('style').length; i++) {
+ if (/^blockly.*$/.test(document.getElementsByTagName('style')[i].id)) {
+ cssContent += document.getElementsByTagName('style')[i].firstChild.data.replace(/\..* \./g, '.');
+ }
+ }
+ // ensure that fill-opacity is 1, because there cannot be a replacing
+ // https://github.com/google/blockly/pull/3431/files#diff-00254795773903d3c0430915a68c9521R328
+ cssContent += `.blocklyPath {
+ fill-opacity: 1;
+ }
+ .blocklyPathDark {
+ display: flex;
+ }
+ .blocklyPathLight {
+ display: flex;
+ } `;
+ var css = '
';
+ var bbox = document.getElementsByClassName("blocklyBlockCanvas")[0].getBBox();
+ var content = new XMLSerializer().serializeToString(canvas);
+ var xml = `
+ ${css}">${content} `;
+ var fileName = detectWhitespacesAndReturnReadableResult(this.props.name);
+ // this.props.workspaceName(this.state.name);
+ fileName = `${fileName}.svg`
+ var blob = new Blob([xml], { type: 'image/svg+xml;base64' });
+ saveAs(blob, fileName);
+ }
+ }
+
+ render() {
+ return (
+
+
+ this.getSvg()}
+ >
+
+
+
+
+ );
+ };
+}
+
+Screenshot.propTypes = {
+ name: PropTypes.string.isRequired,
+};
+
+const mapStateToProps = state => ({
+ name: state.workspace.name,
+});
+
+export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(Screenshot));
diff --git a/src/components/Workspace/ShareProject.js b/src/components/Workspace/ShareProject.js
new file mode 100644
index 0000000..f4ca60d
--- /dev/null
+++ b/src/components/Workspace/ShareProject.js
@@ -0,0 +1,154 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { shareProject } from '../../actions/projectActions';
+
+import moment from 'moment';
+
+import Dialog from '../Dialog';
+import Snackbar from '../Snackbar';
+
+import { Link } from 'react-router-dom';
+
+import { withStyles } from '@material-ui/core/styles';
+import IconButton from '@material-ui/core/IconButton';
+import Tooltip from '@material-ui/core/Tooltip';
+import Typography from '@material-ui/core/Typography';
+
+import { faShareAlt, faCopy } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+const styles = (theme) => ({
+ button: {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ width: '40px',
+ height: '40px',
+ '&:hover': {
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ }
+ },
+ link: {
+ color: theme.palette.primary.main,
+ textDecoration: 'none',
+ '&:hover': {
+ color: theme.palette.primary.main,
+ textDecoration: 'underline'
+ }
+ }
+});
+
+
+class WorkspaceFunc extends Component {
+
+ constructor(props) {
+ super(props);
+ this.inputRef = React.createRef();
+ this.state = {
+ snackbar: false,
+ type: '',
+ key: '',
+ message: '',
+ title: '',
+ content: '',
+ open: false,
+ id: '',
+ };
+ }
+
+ componentDidUpdate(props) {
+ if(this.props.message !== props.message){
+ if(this.props.message.id === 'SHARE_SUCCESS' && (!this.props.multiple ||
+ (this.props.message.status === this.props.project._id || this.props.message.status === this.props.project._id._id))){
+ this.setState({ share: true, open: true, title: 'Programm teilen', id: this.props.message.status });
+ }
+ else if(this.props.message.id === 'SHARE_FAIL' && (!this.props.multiple ||
+ (this.props.message.status === this.props.project._id || this.props.message.status === this.props.project._id._id))){
+ this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Erstellen eines Links zum Teilen deines Programmes. Versuche es noch einmal.`, type: 'error' });
+ window.scrollTo(0, 0);
+ }
+ }
+ }
+
+ toggleDialog = () => {
+ this.setState({ open: !this.state, title: '', content: '' });
+ }
+
+
+ shareBlocks = () => {
+ if(this.props.projectType === 'project' && this.props.project._id._id){
+ // project is already shared
+ this.setState({ open: true, title: 'Programm teilen', id: this.props.project._id._id });
+ }
+ else {
+ this.props.shareProject(this.props.name || this.props.project.title, this.props.projectType, this.props.project ? this.props.project._id._id ? this.props.project._id._id : this.props.project._id : undefined);
+ }
+ }
+
+ render() {
+ return (
+
+
+ this.shareBlocks()}
+ >
+
+
+
+
+
+
+
+ Über den folgenden Link kannst du dein Programm teilen:
+ this.toggleDialog()} className={this.props.classes.link}>{`${window.location.origin}/share/${this.state.id}`}
+
+ {
+ navigator.clipboard.writeText(`${window.location.origin}/share/${this.state.id}`);
+ this.setState({ snackbar: true, key: Date.now(), message: 'Link erfolgreich in Zwischenablage gespeichert.', type: 'success' });
+ }}
+ >
+
+
+
+ {this.props.project && this.props.project._id._id ?
+ {`Das Projekt wurde bereits geteilt. Der Link ist noch mindestens ${
+ moment(this.props.project._id.expiresAt).diff(moment().utc(), 'days') === 0 ?
+ moment(this.props.project._id.expiresAt).diff(moment().utc(), 'hours') === 0 ?
+ `${moment(this.props.project._id.expiresAt).diff(moment().utc(), 'minutes')} Minuten`
+ : `${moment(this.props.project._id.expiresAt).diff(moment().utc(), 'hours')} Stunden`
+ : `${moment(this.props.project._id.expiresAt).diff(moment().utc(), 'days')} Tage`} gültig.`}
+ : {`Der Link ist nun ${process.env.REACT_APP_SHARE_LINK_EXPIRES} Tage gültig.`} }
+
+
+
+ );
+ };
+}
+
+WorkspaceFunc.propTypes = {
+ shareProject: PropTypes.func.isRequired,
+ name: PropTypes.string.isRequired,
+ message: PropTypes.object.isRequired
+};
+
+const mapStateToProps = state => ({
+ name: state.workspace.name,
+ message: state.message
+});
+
+export default connect(mapStateToProps, { shareProject })(withStyles(styles, { withTheme: true })(WorkspaceFunc));
diff --git a/src/components/TrashcanButtons.js b/src/components/Workspace/TrashcanButtons.js
similarity index 100%
rename from src/components/TrashcanButtons.js
rename to src/components/Workspace/TrashcanButtons.js
diff --git a/src/components/Workspace/WorkspaceFunc.js b/src/components/Workspace/WorkspaceFunc.js
new file mode 100644
index 0000000..8c8ae31
--- /dev/null
+++ b/src/components/Workspace/WorkspaceFunc.js
@@ -0,0 +1,94 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+
+import WorkspaceName from './WorkspaceName';
+import SaveProject from './SaveProject';
+import Compile from './Compile';
+import SolutionCheck from '../Tutorial/SolutionCheck';
+import DownloadProject from './DownloadProject';
+import OpenProject from './OpenProject';
+import Screenshot from './Screenshot';
+import ShareProject from './ShareProject';
+import ResetWorkspace from './ResetWorkspace';
+import DeleteProject from './DeleteProject';
+
+class WorkspaceFunc extends Component {
+
+ render() {
+ return (
+
+
+ {!this.props.assessment ?
+
+ : null}
+
+ {this.props.assessment ?
+
+ : !this.props.multiple ?
+
+ : null}
+
+ {this.props.user && !this.props.multiple?
+
+ : null}
+
+ {!this.props.multiple ?
+
+ : null}
+
+ {!this.props.assessment && !this.props.multiple?
+
+ : null}
+
+ {!this.props.assessment && !this.props.multiple?
+
+ : null}
+
+ {this.props.projectType !== 'gallery' && !this.props.assessment ?
+
+ :null}
+
+ {!this.props.multiple ?
+
+ : null}
+
+ {!this.props.assessment && (this.props.projectType === 'project' || this.props.projectType === 'gallery') && this.props.user && this.props.user.email === this.props.project.creator ?
+
+ :null}
+
+
+ );
+ };
+}
+
+WorkspaceFunc.propTypes = {
+ user: PropTypes.object
+};
+
+const mapStateToProps = state => ({
+ user: state.auth.user
+});
+
+export default connect(mapStateToProps, null)(WorkspaceFunc);
diff --git a/src/components/Workspace/WorkspaceName.js b/src/components/Workspace/WorkspaceName.js
new file mode 100644
index 0000000..33d94ab
--- /dev/null
+++ b/src/components/Workspace/WorkspaceName.js
@@ -0,0 +1,150 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { workspaceName } from '../../actions/workspaceActions';
+import { setDescription, updateProject } from '../../actions/projectActions';
+
+import Snackbar from '../Snackbar';
+import Dialog from '../Dialog';
+
+import withWidth, { isWidthDown } from '@material-ui/core/withWidth';
+import { withStyles } from '@material-ui/core/styles';
+import Button from '@material-ui/core/Button';
+import Tooltip from '@material-ui/core/Tooltip';
+import TextField from '@material-ui/core/TextField';
+import Typography from '@material-ui/core/Typography';
+
+import { faPen } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+const styles = (theme) => ({
+ workspaceName: {
+ backgroundColor: theme.palette.secondary.main,
+ borderRadius: '25px',
+ display: 'inline-flex',
+ cursor: 'pointer',
+ '&:hover': {
+ color: theme.palette.primary.main,
+ }
+ }
+});
+
+
+class WorkspaceName extends Component {
+
+ constructor(props) {
+ super(props);
+ this.inputRef = React.createRef();
+ this.state = {
+ title: '',
+ content: '',
+ open: false,
+ name: props.name,
+ description: props.description,
+ snackbar: false,
+ type: '',
+ key: '',
+ message: ''
+ };
+ }
+
+ componentDidUpdate(props) {
+ if (props.name !== this.props.name) {
+ this.setState({ name: this.props.name });
+ }
+ if (props.description !== this.props.description) {
+ this.setState({ description: this.props.description });
+ }
+ }
+
+ toggleDialog = () => {
+ this.setState({ open: !this.state, title: '', content: '' });
+ }
+
+ setFileName = (e) => {
+ this.setState({ name: e.target.value });
+ }
+
+ setDescription = (e) => {
+ this.setState({ description: e.target.value });
+ }
+
+ renameWorkspace = () => {
+ this.props.workspaceName(this.state.name);
+ this.toggleDialog();
+ if(this.props.projectType === 'project' || this.props.projectType === 'gallery' || this.state.projectType === 'gallery'){
+ if(this.props.projectType === 'gallery' || this.state.projectType === 'gallery'){
+ this.props.setDescription(this.state.description);
+ }
+ if(this.state.projectType === 'gallery'){
+ this.saveGallery();
+ } else {
+ this.props.updateProject(this.props.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id);
+ }
+ } else {
+ this.setState({ snackbar: true, type: 'success', key: Date.now(), message: `Das Projekt wurde erfolgreich in '${this.state.name}' umbenannt.` });
+ }
+ }
+
+ render() {
+ return (
+
+
+ {if(this.props.multiple){this.props.workspaceName(this.props.project.title);if(this.props.projectType === 'gallery'){this.props.setDescription(this.props.project.description);}} this.setState({ open: true, title: this.props.projectType === 'gallery' ? 'Projektdaten ändern': this.props.projectType === 'project' ? 'Projekt umbenennen' : 'Projekt benennen', content: this.props.projectType === 'gallery' ? 'Bitte gib einen Titel und eine Beschreibung für das Galerie-Projekt ein und bestätige die Angaben mit einem Klick auf \'Eingabe\'.':'Bitte gib einen Namen für das Projekt ein und bestätige diesen mit einem Klick auf \'Eingabe\'.' }) }}
+ >
+ {this.props.name && !isWidthDown(this.props.projectType === 'project' || this.props.projectType === 'gallery' ? 'xl':'xs', this.props.width) ?
+
{this.props.name}
+ : null}
+
+
+
+
+
+
+
+
{this.toggleDialog(); this.setState({ name: this.props.name, description: this.props.description });}}
+ onClick={() => {this.toggleDialog(); this.setState({ name: this.props.name, description: this.props.description });}}
+ button={'Abbrechen'}
+ >
+
+ {this.props.projectType === 'gallery' || this.state.projectType === 'gallery' ?
+
+
+
+
+ :
}
+
{ this.renameWorkspace(); this.toggleDialog(); }}>Eingabe
+
+
+
+ );
+ };
+}
+
+WorkspaceName.propTypes = {
+ workspaceName: PropTypes.func.isRequired,
+ setDescription: PropTypes.func.isRequired,
+ updateProject: PropTypes.func.isRequired,
+ name: PropTypes.string.isRequired,
+ description: PropTypes.string.isRequired,
+ message: PropTypes.object.isRequired,
+};
+
+const mapStateToProps = state => ({
+ name: state.workspace.name,
+ description: state.project.description,
+ message: state.message,
+});
+
+export default connect(mapStateToProps, { workspaceName, setDescription, updateProject })(withStyles(styles, { withTheme: true })(withWidth()(WorkspaceName)));
diff --git a/src/components/WorkspaceStats.js b/src/components/Workspace/WorkspaceStats.js
similarity index 100%
rename from src/components/WorkspaceStats.js
rename to src/components/Workspace/WorkspaceStats.js
diff --git a/src/components/WorkspaceFunc.js b/src/components/WorkspaceFunc.js
deleted file mode 100644
index f11e620..0000000
--- a/src/components/WorkspaceFunc.js
+++ /dev/null
@@ -1,480 +0,0 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { connect } from 'react-redux';
-import { clearStats, onChangeCode, workspaceName } from '../actions/workspaceActions';
-import { updateProject, deleteProject, shareProject, setDescription } from '../actions/projectActions';
-
-import * as Blockly from 'blockly/core';
-
-import { withRouter } from 'react-router-dom';
-import axios from 'axios';
-import moment from 'moment';
-import { saveAs } from 'file-saver';
-
-import { detectWhitespacesAndReturnReadableResult } from '../helpers/whitespace';
-import { initialXml } from './Blockly/initialXml.js';
-
-import Compile from './Compile';
-import SolutionCheck from './Tutorial/SolutionCheck';
-import Snackbar from './Snackbar';
-import Dialog from './Dialog';
-
-import { Link } from 'react-router-dom';
-
-import withWidth, { isWidthDown } from '@material-ui/core/withWidth';
-import { withStyles } from '@material-ui/core/styles';
-import Button from '@material-ui/core/Button';
-import IconButton from '@material-ui/core/IconButton';
-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, faTrashAlt, faCamera, faShare, faShareAlt, faCopy } from "@fortawesome/free-solid-svg-icons";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-
-const styles = (theme) => ({
- button: {
- backgroundColor: theme.palette.primary.main,
- color: theme.palette.primary.contrastText,
- width: '40px',
- height: '40px',
- '&:hover': {
- backgroundColor: theme.palette.primary.main,
- color: theme.palette.primary.contrastText,
- }
- },
- workspaceName: {
- backgroundColor: theme.palette.secondary.main,
- borderRadius: '25px',
- display: 'inline-flex',
- cursor: 'pointer',
- '&:hover': {
- 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',
- '&:hover': {
- color: theme.palette.primary.main,
- textDecoration: 'underline'
- }
- }
-});
-
-
-
-class WorkspaceFunc extends Component {
-
- constructor(props) {
- super(props);
- this.inputRef = React.createRef();
- this.state = {
- title: '',
- content: '',
- open: false,
- file: false,
- saveFile: false,
- share: false,
- name: props.name,
- description: props.description,
- snackbar: false,
- type: '',
- key: '',
- message: '',
- id: ''
- };
- }
-
- componentDidUpdate(props) {
- if (props.name !== this.props.name) {
- this.setState({ name: this.props.name });
- }
- if (props.description !== this.props.description) {
- this.setState({ description: this.props.description });
- }
- if(this.props.message !== props.message){
- if(this.props.message.id === 'PROJECT_UPDATE_SUCCESS'){
- this.setState({ snackbar: true, key: Date.now(), message: `Das Projekt wurde erfolgreich aktualisiert.`, type: 'success' });
- }
- if(this.props.message.id === 'GALLERY_UPDATE_SUCCESS'){
- this.setState({ snackbar: true, key: Date.now(), message: `Das Galerie-Projekt wurde erfolgreich aktualisiert.`, type: 'success' });
- }
- 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' });
- }
- else if(this.props.message.id === 'SHARE_SUCCESS' && (!this.props.multiple ||
- (this.props.message.status === this.props.project._id || this.props.message.status === this.props.project._id._id))){
- this.setState({ share: true, open: true, title: 'Programm teilen', id: this.props.message.status });
- }
- else if(this.props.message.id === 'SHARE_FAIL' && (!this.props.multiple ||
- (this.props.message.status === this.props.project._id || this.props.message.status === this.props.project._id._id))){
- this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Erstellen eines Links zum Teilen deines Programmes. Versuche es noch einmal.`, type: 'error' });
- window.scrollTo(0, 0);
- }
- }
- }
-
- toggleDialog = () => {
- this.setState({ open: !this.state, share: false, file: false, saveFile: false, title: '', content: '' });
- }
-
- saveProject = () => {
- var body = {
- xml: this.props.xml,
- title: this.props.name
- };
- axios.post(`${process.env.REACT_APP_BLOCKLY_API}/project`, body)
- .then(res => {
- var project = res.data.project;
- this.props.history.push(`/project/${project._id}`);
- })
- .catch(err => {
- this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Speichern des Projektes. Versuche es noch einmal.`, type: 'error' });
- window.scrollTo(0, 0);
- });
- }
-
- downloadXmlFile = () => {
- var code = this.props.xml;
- this.toggleDialog();
- var fileName = detectWhitespacesAndReturnReadableResult(this.state.name);
- this.props.workspaceName(this.state.name);
- fileName = `${fileName}.xml`
- var blob = new Blob([code], { type: 'text/xml' });
- saveAs(blob, fileName);
- }
-
- shareBlocks = () => {
- if(this.props.projectType === 'project' && this.props.project._id._id){
- // project is already shared
- this.setState({ share: true, open: true, title: 'Programm teilen', id: this.props.project._id._id });
- }
- else {
- this.props.shareProject(this.state.name || this.props.project.title, this.props.projectType, this.props.project ? this.props.project._id._id ? this.props.project._id._id : this.props.project._id : undefined);
- }
- }
-
- getSvg = () => {
- const workspace = Blockly.getMainWorkspace();
- var canvas = workspace.svgBlockCanvas_.cloneNode(true);
-
- if (canvas.children[0] !== undefined) {
- canvas.removeAttribute("transform");
-
- // does not work in react
- // var cssContent = Blockly.Css.CONTENT.join('');
- var cssContent = '';
- for (var i = 0; i < document.getElementsByTagName('style').length; i++) {
- if (/^blockly.*$/.test(document.getElementsByTagName('style')[i].id)) {
- cssContent += document.getElementsByTagName('style')[i].firstChild.data.replace(/\..* \./g, '.');
- }
- }
- // ensure that fill-opacity is 1, because there cannot be a replacing
- // https://github.com/google/blockly/pull/3431/files#diff-00254795773903d3c0430915a68c9521R328
- cssContent += `.blocklyPath {
- fill-opacity: 1;
- }
- .blocklyPathDark {
- display: flex;
- }
- .blocklyPathLight {
- display: flex;
- } `;
-
- var css = '
';
-
- var bbox = document.getElementsByClassName("blocklyBlockCanvas")[0].getBBox();
- var content = new XMLSerializer().serializeToString(canvas);
-
- var xml = `
- ${css}">${content} `;
- var fileName = detectWhitespacesAndReturnReadableResult(this.state.name);
- this.props.workspaceName(this.state.name);
- fileName = `${fileName}.svg`
- var blob = new Blob([xml], { type: 'image/svg+xml;base64' });
- saveAs(blob, fileName);
- }
- }
-
- createFileName = (filetype) => {
- this.setState({ file: filetype }, () => {
- if (this.state.name) {
- this.state.file === 'xml' ? this.downloadXmlFile() : this.getSvg()
- }
- else {
- this.setState({ saveFile: true, file: filetype, open: true, title: this.state.file === 'xml' ? 'Projekt herunterladen' : 'Screenshot erstellen', content: `Bitte gib einen Namen für die Bennenung der ${this.state.file === 'xml' ? 'XML' : 'SVG'}-Datei ein und bestätige diesen mit einem Klick auf 'Eingabe'.` });
- }
- });
- }
-
- setFileName = (e) => {
- this.setState({ name: e.target.value });
- }
-
- setDescription = (e) => {
- this.setState({ description: e.target.value });
- }
-
- uploadXmlFile = (xmlFile) => {
- if (xmlFile.type !== 'text/xml') {
- this.setState({ open: true, file: false, title: 'Unzulässiger Dateityp', content: 'Die übergebene Datei entsprach nicht dem geforderten Format. Es sind nur XML-Dateien zulässig.' });
- }
- else {
- var reader = new FileReader();
- reader.readAsText(xmlFile);
- reader.onloadend = () => {
- var xmlDom = null;
- try {
- xmlDom = Blockly.Xml.textToDom(reader.result);
- const workspace = Blockly.getMainWorkspace();
- var xmlBefore = this.props.xml;
- workspace.clear();
- this.props.clearStats();
- Blockly.Xml.domToWorkspace(xmlDom, workspace);
- if (workspace.getAllBlocks().length < 1) {
- Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xmlBefore), workspace)
- this.setState({ open: true, file: false, title: 'Keine Blöcke', content: 'Es wurden keine Blöcke detektiert. Bitte überprüfe den XML-Code und versuche es erneut.' });
- }
- else {
- if (!this.props.assessment) {
- var extensionPosition = xmlFile.name.lastIndexOf('.');
- this.props.workspaceName(xmlFile.name.substr(0, extensionPosition));
- }
- this.setState({ snackbar: true, type: 'success', key: Date.now(), message: 'Das Projekt aus gegebener XML-Datei wurde erfolgreich eingefügt.' });
- }
- } catch (err) {
- this.setState({ open: true, file: false, title: 'Ungültige XML', content: 'Die XML-Datei konnte nicht in Blöcke zerlegt werden. Bitte überprüfe den XML-Code und versuche es erneut.' });
- }
- };
- }
- }
-
- renameWorkspace = () => {
- this.props.workspaceName(this.state.name);
- this.toggleDialog();
- console.log(this.props.projectType);
- if(this.props.projectType === 'project' || this.props.projectType === 'gallery'){
- if(this.props.projectType === 'gallery'){
- this.props.setDescription(this.state.description);
- }
- this.props.updateProject(this.props.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id);
- } else {
- this.setState({ snackbar: true, type: 'success', key: Date.now(), message: `Das Projekt wurde erfolgreich in '${this.state.name}' umbenannt.` });
- }
- }
-
- resetWorkspace = () => {
- const workspace = Blockly.getMainWorkspace();
- Blockly.Events.disable(); // https://groups.google.com/forum/#!topic/blockly/m7e3g0TC75Y
- // if events are disabled, then the workspace will be cleared AND the blocks are not in the trashcan
- const xmlDom = Blockly.Xml.textToDom(initialXml)
- Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom, workspace);
- Blockly.Events.enable();
- workspace.options.maxBlocks = Infinity;
- this.props.onChangeCode();
- this.props.clearStats();
- if (!this.props.assessment) {
- this.props.workspaceName(null);
- }
- this.setState({ snackbar: true, type: 'success', key: Date.now(), message: 'Das Projekt wurde erfolgreich zurückgesetzt.' });
- }
-
-
-
- render() {
- return (
-
- {!this.props.assessment ?
-
- {if(this.props.multiple){this.props.workspaceName(this.props.project.title);if(this.props.projectType === 'gallery'){this.props.setDescription(this.props.project.description);}} this.setState({ file: true, open: true, saveFile: false, title: this.props.projectType === 'gallery' ? 'Projektdaten eintragen':'Projekt benennen', content: this.props.projectType === 'gallery' ? 'Bitte gib einen Titel und eine Beschreibung für das Galerie-Projekt ein und bestätige die Angaben mit einem Klick auf \'Eingabe\'.':'Bitte gib einen Namen für das Projekt ein und bestätige diesen mit einem Klick auf \'Eingabe\'.' }) }}>
- {this.props.name && !isWidthDown(this.props.projectType === 'project' || this.props.projectType === 'gallery' ? 'xl':'xs', this.props.width) ?
{this.props.name} : null}
-
-
-
-
-
- : null}
- {this.props.assessment ?
: !this.props.multiple ?
: null}
- {this.props.user && !this.props.multiple?
-
- this.props.updateProject(this.props.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id) : () => this.saveProject()}
- >
-
-
-
- : null}
- {!this.props.multiple ?
-
- { this.createFileName('xml'); }}
- >
-
-
-
- : null}
- {!this.props.assessment && !this.props.multiple?
-
-
{ this.uploadXmlFile(e.target.files[0]) }}
- id="open-blocks"
- type="file"
- />
-
-
-
-
-
-
-
-
- : null}
- {!this.props.assessment && !this.props.multiple?
-
- { this.createFileName('svg'); }}
- >
-
-
-
- : null}
- {this.props.projectType !== 'gallery' && !this.props.assessment ?
-
- this.shareBlocks()}
- >
-
-
-
- :null}
- {!this.props.multiple ?
-
- this.resetWorkspace()}
- >
-
-
-
- : null}
- {!this.props.assessment && (this.props.projectType === 'project' || this.props.projectType === 'gallery') ?
-
- this.props.deleteProject(this.props.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id)}
- >
-
-
-
- :null}
-
-
{ this.toggleDialog(); this.setState({ name: this.props.name }) } : this.toggleDialog}
- button={this.state.file ? 'Abbrechen' : 'Schließen'}
- >
- {this.state.file ?
-
- {this.props.projectType === 'gallery' ?
-
-
-
-
- :
}
-
{ this.state.saveFile ? this.state.file === 'xml' ? this.downloadXmlFile() : this.getSvg() : this.renameWorkspace(); this.toggleDialog(); }}>Eingabe
-
- : this.state.share ?
-
- Über den folgenden Link kannst du dein Programm teilen:
- this.toggleDialog()} className={this.props.classes.link}>{`${window.location.origin}/share/${this.state.id}`}
-
- {
- navigator.clipboard.writeText(`${window.location.origin}/share/${this.state.id}`);
- this.setState({ snackbar: true, key: Date.now(), message: 'Link erfolgreich in Zwischenablage gespeichert.', type: 'success' });
- }}
- >
-
-
-
- {this.props.project && this.props.project._id._id ?
- {`Das Projekt wurde bereits geteilt. Der Link ist noch mindestens ${
- moment(this.props.project._id.expiresAt).diff(moment().utc(), 'days') === 0 ?
- moment(this.props.project._id.expiresAt).diff(moment().utc(), 'hours') === 0 ?
- `${moment(this.props.project._id.expiresAt).diff(moment().utc(), 'minutes')} Minuten`
- : `${moment(this.props.project._id.expiresAt).diff(moment().utc(), 'hours')} Stunden`
- : `${moment(this.props.project._id.expiresAt).diff(moment().utc(), 'days')} Tage`} gültig.`}
- : {`Der Link ist nun ${process.env.REACT_APP_SHARE_LINK_EXPIRES} Tage gültig.`} }
-
- : null}
-
-
-
-
-
- );
- };
-}
-
-WorkspaceFunc.propTypes = {
- clearStats: PropTypes.func.isRequired,
- onChangeCode: PropTypes.func.isRequired,
- workspaceName: PropTypes.func.isRequired,
- updateProject: PropTypes.func.isRequired,
- shareProject: PropTypes.func.isRequired,
- deleteProject: PropTypes.func.isRequired,
- setDescription: PropTypes.func.isRequired,
- arduino: PropTypes.string.isRequired,
- xml: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- description: PropTypes.string.isRequired,
- message: PropTypes.object.isRequired,
- user: PropTypes.object
-};
-
-const mapStateToProps = state => ({
- arduino: state.workspace.code.arduino,
- xml: state.workspace.code.xml,
- name: state.workspace.name,
- description: state.project.description,
- message: state.message,
- user: state.auth.user
-});
-
-export default connect(mapStateToProps, { clearStats, onChangeCode, workspaceName, updateProject, shareProject, deleteProject, setDescription })(withStyles(styles, { withTheme: true })(withWidth()(withRouter(WorkspaceFunc))));
From 3cf7f10d34d44a50d206b70d7d8e37f15eb34624 Mon Sep 17 00:00:00 2001
From: Delucse <46593742+Delucse@users.noreply.github.com>
Date: Tue, 8 Dec 2020 15:33:03 +0100
Subject: [PATCH 26/32] delete tutorial button at tutorial builder
---
src/actions/tutorialActions.js | 22 ++++++
src/components/Tutorial/Builder/Builder.js | 83 ++++++++++++++++------
2 files changed, 85 insertions(+), 20 deletions(-)
diff --git a/src/actions/tutorialActions.js b/src/actions/tutorialActions.js
index ec0e67b..bffabfa 100644
--- a/src/actions/tutorialActions.js
+++ b/src/actions/tutorialActions.js
@@ -56,6 +56,28 @@ export const getTutorials = () => (dispatch, getState) => {
});
};
+export const deleteTutorial = (id) => (dispatch, getState) => {
+ var tutorial = getState().tutorial;
+ var id = getState().builder.id;
+ axios.delete(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/${id}`)
+ .then(res => {
+ var tutorials = tutorial.tutorials;
+ var index = tutorials.findIndex(res => res._id === id);
+ tutorials.splice(index, 1)
+ dispatch({
+ type: GET_TUTORIALS,
+ payload: tutorials
+ });
+ dispatch(returnSuccess(res.data.message, res.status, 'TUTORIAL_DELETE_SUCCESS'));
+ })
+ .catch(err => {
+ if(err.response){
+ dispatch(returnErrors(err.response.data.message, err.response.status, 'TUTORIAL_DELETE_FAIL'));
+ }
+ });
+};
+
+
export const resetTutorial = () => (dispatch) => {
dispatch({
type: GET_TUTORIALS,
diff --git a/src/components/Tutorial/Builder/Builder.js b/src/components/Tutorial/Builder/Builder.js
index 753edd0..9656d00 100644
--- a/src/components/Tutorial/Builder/Builder.js
+++ b/src/components/Tutorial/Builder/Builder.js
@@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { checkError, readJSON, jsonString, progress, tutorialId, resetTutorial as resetTutorialBuilder} from '../../../actions/tutorialBuilderActions';
-import { getTutorials, resetTutorial} from '../../../actions/tutorialActions';
+import { getTutorials, resetTutorial, deleteTutorial } from '../../../actions/tutorialActions';
import { clearMessages } from '../../../actions/messageActions';
import axios from 'axios';
@@ -38,9 +38,18 @@ const styles = (theme) => ({
},
errorColor: {
color: theme.palette.error.dark
+ },
+ errorButton: {
+ marginTop: '5px',
+ height: '40px',
+ backgroundColor: theme.palette.error.dark,
+ '&:hover':{
+ backgroundColor: theme.palette.error.dark
+ }
}
});
+
class Builder extends Component {
constructor(props) {
@@ -63,9 +72,18 @@ class Builder extends Component {
}
componentDidUpdate(props, state) {
- if(this.props.message.id === 'GET_TUTORIALS_FAIL'){
- alert(this.props.message.msg);
- this.props.clearMessages();
+ if(props.message !== this.props.message){
+ if(this.props.message.id === 'GET_TUTORIALS_FAIL'){
+ // alert(this.props.message.msg);
+ this.props.clearMessages();
+ }
+ else if(this.props.message.id === 'TUTORIAL_DELETE_SUCCESS'){
+ this.onChange('new');
+ this.setState({ snackbar: true, key: Date.now(), message: `Das Tutorial wurde erfolgreich gelöscht.`, type: 'success' });
+ }
+ else if(this.props.message.id === 'TUTORIAL_DELETE_FAIL'){
+ this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Löschen des Tutorials. Versuche es noch einmal.`, type: 'error' });
+ }
}
}
@@ -130,10 +148,12 @@ class Builder extends Component {
onChangeId = (value) => {
this.props.tutorialId(value);
- this.props.progress(true);
- var tutorial = this.props.tutorials.filter(tutorial => tutorial._id === value)[0];
- this.props.readJSON(tutorial);
- this.setState({ snackbar: true, key: Date.now(), message: `Das ausgewählte Tutorial "${tutorial.title}" wurde erfolgreich übernommen.`, type: 'success' });
+ if(this.state.tutorial === 'change'){
+ this.props.progress(true);
+ var tutorial = this.props.tutorials.filter(tutorial => tutorial._id === value)[0];
+ this.props.readJSON(tutorial);
+ this.setState({ snackbar: true, key: Date.now(), message: `Das ausgewählte Tutorial "${tutorial.title}" wurde erfolgreich übernommen.`, type: 'success' });
+ }
}
resetFull = () => {
@@ -222,6 +242,7 @@ class Builder extends Component {
}
render() {
+ var filteredTutorials = this.props.tutorials.filter(tutorial => tutorial.creator === this.props.user.email);
return (
@@ -235,13 +256,24 @@ class Builder extends Component {
label="neues Tutorial erstellen"
labelPlacement="end"
/>
-
}
- label="bestehendes Tutorial ändern"
- labelPlacement="end"
- />
+ {filteredTutorials.length > 0 ?
+
+ }
+ label="bestehendes Tutorial ändern"
+ labelPlacement="end"
+ />
+ }
+ label="bestehendes Tutorial löschen"
+ labelPlacement="end"
+ />
+
+ : null}
@@ -266,11 +298,11 @@ class Builder extends Component {
this.onChangeId(e.target.value)}
label="Tutorial"
>
- {this.props.tutorials.map(tutorial =>
+ {filteredTutorials.map(tutorial =>
{tutorial.title}
)}
@@ -311,6 +343,14 @@ class Builder extends Component {
: null}
+ {this.state.tutorial === 'delete' && this.props.id !== '' ?
+
this.props.deleteTutorial()}>Tutorial löschen
+ : null}
+
({
@@ -376,7 +418,8 @@ const mapStateToProps = state => ({
json: state.builder.json,
isProgress: state.builder.progress,
tutorials: state.tutorial.tutorials,
- message: state.message
+ message: state.message,
+ user: state.auth.user,
});
-export default connect(mapStateToProps, { checkError, readJSON, jsonString, progress, tutorialId, resetTutorialBuilder, getTutorials, resetTutorial, clearMessages })(withStyles(styles, { withTheme: true })(withRouter(Builder)));
+export default connect(mapStateToProps, { checkError, readJSON, jsonString, progress, tutorialId, resetTutorialBuilder, getTutorials, resetTutorial, clearMessages, deleteTutorial })(withStyles(styles, { withTheme: true })(withRouter(Builder)));
From f53baf25360b07035c8438c89700bc75969dadbb Mon Sep 17 00:00:00 2001
From: Mario
Date: Tue, 8 Dec 2020 20:06:51 +0100
Subject: [PATCH 27/32] update compile button color
---
src/App.js | 5 ++++-
src/components/Tutorial/SolutionCheck.js | 8 ++++----
src/components/Workspace/Compile.js | 10 +++++-----
3 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/src/App.js b/src/App.js
index db72ff0..1eccac4 100644
--- a/src/App.js
+++ b/src/App.js
@@ -24,13 +24,16 @@ const theme = createMuiTheme({
},
secondary: {
main: '#DDDDDD'
+ },
+ button: {
+ compile: '#e27136'
}
}
});
class App extends Component {
- componentDidMount(){
+ componentDidMount() {
store.dispatch(loadUser());
}
diff --git a/src/components/Tutorial/SolutionCheck.js b/src/components/Tutorial/SolutionCheck.js
index 5728ef1..f8c1383 100644
--- a/src/components/Tutorial/SolutionCheck.js
+++ b/src/components/Tutorial/SolutionCheck.js
@@ -15,15 +15,15 @@ import IconButton from '@material-ui/core/IconButton';
import Tooltip from '@material-ui/core/Tooltip';
import Button from '@material-ui/core/Button';
-import { faPlay } from "@fortawesome/free-solid-svg-icons";
+import { faClipboardCheck } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const styles = (theme) => ({
compile: {
- backgroundColor: theme.palette.primary.main,
+ backgroundColor: theme.palette.button.compile,
color: theme.palette.primary.contrastText,
'&:hover': {
- backgroundColor: theme.palette.primary.main,
+ backgroundColor: theme.palette.button.compile,
color: theme.palette.primary.contrastText,
}
}
@@ -63,7 +63,7 @@ class SolutionCheck extends Component {
style={{ width: '40px', height: '40px', marginRight: '5px' }}
onClick={() => this.check()}
>
-
+
diff --git a/src/components/Workspace/Compile.js b/src/components/Workspace/Compile.js
index 89b081e..e2e4768 100644
--- a/src/components/Workspace/Compile.js
+++ b/src/components/Workspace/Compile.js
@@ -15,7 +15,7 @@ import IconButton from '@material-ui/core/IconButton';
import Tooltip from '@material-ui/core/Tooltip';
import TextField from '@material-ui/core/TextField';
-import { faCogs } from "@fortawesome/free-solid-svg-icons";
+import { faClipboardCheck } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const styles = (theme) => ({
@@ -24,12 +24,12 @@ const styles = (theme) => ({
color: '#fff',
},
button: {
- backgroundColor: theme.palette.primary.main,
+ backgroundColor: theme.palette.button.compile,
color: theme.palette.primary.contrastText,
width: '40px',
height: '40px',
'&:hover': {
- backgroundColor: theme.palette.primary.main,
+ backgroundColor: theme.palette.button.compile,
color: theme.palette.primary.contrastText,
}
}
@@ -116,12 +116,12 @@ class Compile extends Component {
className={this.props.classes.button}
onClick={() => this.compile()}
>
-
+
:
this.compile()}>
- Kompilieren
+ Kompilieren
}
From a4cf0d32b07d1cde6457032c79245b22f7dc96ac Mon Sep 17 00:00:00 2001
From: Delucse <46593742+Delucse@users.noreply.github.com>
Date: Tue, 8 Dec 2020 20:15:54 +0100
Subject: [PATCH 28/32] adjustment of the project object property 'shared'
---
src/actions/projectActions.js | 7 ++---
src/components/Project/ProjectHome.js | 2 +-
src/components/Workspace/DeleteProject.js | 2 +-
src/components/Workspace/SaveProject.js | 6 ++---
src/components/Workspace/ShareProject.js | 33 ++++++++++++-----------
src/components/Workspace/WorkspaceName.js | 2 +-
6 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/src/actions/projectActions.js b/src/actions/projectActions.js
index 344140b..8ab01cf 100644
--- a/src/actions/projectActions.js
+++ b/src/actions/projectActions.js
@@ -113,7 +113,7 @@ export const deleteProject = (type, id) => (dispatch, getState) => {
axios.delete(`${process.env.REACT_APP_BLOCKLY_API}/${type}/${id}`)
.then(res => {
var projects = getState().project.projects;
- var index = projects.findIndex(res => res._id === id || res._id._id === id);
+ var index = projects.findIndex(res => res._id === id);
projects.splice(index, 1)
dispatch({
type: GET_PROJECTS,
@@ -148,10 +148,7 @@ export const shareProject = (title, type, id) => (dispatch, getState) => {
if(body.projectId){
var projects = getState().project.projects;
var index = projects.findIndex(res => res._id === id);
- projects[index]._id = {
- _id: shareContent._id,
- expiresAt: shareContent.expiresAt
- };
+ projects[index].shared = shareContent.expiresAt;
dispatch({
type: GET_PROJECTS,
payload: projects
diff --git a/src/components/Project/ProjectHome.js b/src/components/Project/ProjectHome.js
index 66f8f0a..07e559b 100644
--- a/src/components/Project/ProjectHome.js
+++ b/src/components/Project/ProjectHome.js
@@ -96,7 +96,7 @@ class ProjectHome extends Component {
return (
-
+
{project.title}
this.props.deleteProject(this.props.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id)}
+ onClick={() => this.props.deleteProject(this.props.projectType, this.props.project._id)}
>
diff --git a/src/components/Workspace/SaveProject.js b/src/components/Workspace/SaveProject.js
index 91bc17a..0a46cf0 100644
--- a/src/components/Workspace/SaveProject.js
+++ b/src/components/Workspace/SaveProject.js
@@ -124,7 +124,7 @@ class SaveProject extends Component {
this.toggleMenu(e) : this.state.projectType === 'project' ? () => this.props.updateProject(this.state.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id) : () => {this.setState({projectType: 'project'}, () => this.saveProject())}}
+ onClick={this.props.user.blocklyRole !== 'user' && (!this.props.project || this.props.user.email === this.props.project.creator) ? (e) => this.toggleMenu(e) : this.state.projectType === 'project' ? () => this.props.updateProject(this.state.projectType, this.props.project._id) : () => {this.setState({projectType: 'project'}, () => this.saveProject())}}
>
@@ -144,12 +144,12 @@ class SaveProject extends Component {
onClose={this.toggleMenu}
>
{this.toggleMenu(e); this.props.updateProject(this.state.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id)} : (e) => {this.toggleMenu(e); this.setState({projectType: 'project'}, () => this.saveProject())}}
+ onClick={this.state.projectType === 'project' ? (e) => {this.toggleMenu(e); this.props.updateProject(this.state.projectType, this.props.project._id)} : (e) => {this.toggleMenu(e); this.setState({projectType: 'project'}, () => this.saveProject())}}
>
{this.state.projectType === 'project' ? 'Projekt aktualisieren' : 'Projekt erstellen'}
{this.toggleMenu(e); this.props.updateProject(this.state.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id)} : (e) => {this.toggleMenu(e); this.setState({ open: true, title: 'Projekbeschreibung ergänzen', content: 'Bitte gib eine Beschreibung für das Galerie-Projekt ein und bestätige deine Angabe mit einem Klick auf \'Eingabe\'.'});}}
+ onClick={this.state.projectType === 'gallery' ? (e) => {this.toggleMenu(e); this.props.updateProject(this.state.projectType, this.props.project._id)} : (e) => {this.toggleMenu(e); this.setState({ open: true, title: 'Projekbeschreibung ergänzen', content: 'Bitte gib eine Beschreibung für das Galerie-Projekt ein und bestätige deine Angabe mit einem Klick auf \'Eingabe\'.'});}}
>
{this.state.projectType === 'gallery' ? 'Galerie-Projekt aktualisieren' : 'Galerie-Projekt erstellen'}
diff --git a/src/components/Workspace/ShareProject.js b/src/components/Workspace/ShareProject.js
index f4ca60d..060e0e9 100644
--- a/src/components/Workspace/ShareProject.js
+++ b/src/components/Workspace/ShareProject.js
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { shareProject } from '../../actions/projectActions';
+import { clearMessages } from '../../actions/messageActions';
import moment from 'moment';
@@ -59,30 +60,31 @@ class WorkspaceFunc extends Component {
componentDidUpdate(props) {
if(this.props.message !== props.message){
- if(this.props.message.id === 'SHARE_SUCCESS' && (!this.props.multiple ||
- (this.props.message.status === this.props.project._id || this.props.message.status === this.props.project._id._id))){
+ if(this.props.message.id === 'SHARE_SUCCESS' && (!this.props.multiple || this.props.message.status === this.props.project._id)){
this.setState({ share: true, open: true, title: 'Programm teilen', id: this.props.message.status });
}
- else if(this.props.message.id === 'SHARE_FAIL' && (!this.props.multiple ||
- (this.props.message.status === this.props.project._id || this.props.message.status === this.props.project._id._id))){
+ else if(this.props.message.id === 'SHARE_FAIL' && (!this.props.multiple || this.props.message.status === this.props.project._id)){
this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Erstellen eines Links zum Teilen deines Programmes. Versuche es noch einmal.`, type: 'error' });
window.scrollTo(0, 0);
}
}
}
+ componentWillUnmount(){
+ this.props.clearMessages();
+ }
+
toggleDialog = () => {
this.setState({ open: !this.state, title: '', content: '' });
}
-
shareBlocks = () => {
- if(this.props.projectType === 'project' && this.props.project._id._id){
+ if(this.props.projectType === 'project' && this.props.project.shared){
// project is already shared
- this.setState({ open: true, title: 'Programm teilen', id: this.props.project._id._id });
+ this.setState({ open: true, title: 'Programm teilen', id: this.props.project._id });
}
else {
- this.props.shareProject(this.props.name || this.props.project.title, this.props.projectType, this.props.project ? this.props.project._id._id ? this.props.project._id._id : this.props.project._id : undefined);
+ this.props.shareProject(this.props.name || this.props.project.title, this.props.projectType, this.props.project ? this.props.project._id : undefined);
}
}
@@ -125,13 +127,13 @@ class WorkspaceFunc extends Component {
- {this.props.project && this.props.project._id._id ?
+ {this.props.project && this.props.project.shared && this.props.message.id !== 'SHARE_SUCCESS' ?
{`Das Projekt wurde bereits geteilt. Der Link ist noch mindestens ${
- moment(this.props.project._id.expiresAt).diff(moment().utc(), 'days') === 0 ?
- moment(this.props.project._id.expiresAt).diff(moment().utc(), 'hours') === 0 ?
- `${moment(this.props.project._id.expiresAt).diff(moment().utc(), 'minutes')} Minuten`
- : `${moment(this.props.project._id.expiresAt).diff(moment().utc(), 'hours')} Stunden`
- : `${moment(this.props.project._id.expiresAt).diff(moment().utc(), 'days')} Tage`} gültig.`}
+ moment(this.props.project.shared).diff(moment().utc(), 'days') === 0 ?
+ moment(this.props.project.shared).diff(moment().utc(), 'hours') === 0 ?
+ `${moment(this.props.project.shared).diff(moment().utc(), 'minutes')} Minuten`
+ : `${moment(this.props.project.shared).diff(moment().utc(), 'hours')} Stunden`
+ : `${moment(this.props.project.shared).diff(moment().utc(), 'days')} Tage`} gültig.`}
: {`Der Link ist nun ${process.env.REACT_APP_SHARE_LINK_EXPIRES} Tage gültig.`} }
@@ -142,6 +144,7 @@ class WorkspaceFunc extends Component {
WorkspaceFunc.propTypes = {
shareProject: PropTypes.func.isRequired,
+ clearMessages: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
message: PropTypes.object.isRequired
};
@@ -151,4 +154,4 @@ const mapStateToProps = state => ({
message: state.message
});
-export default connect(mapStateToProps, { shareProject })(withStyles(styles, { withTheme: true })(WorkspaceFunc));
+export default connect(mapStateToProps, { shareProject, clearMessages })(withStyles(styles, { withTheme: true })(WorkspaceFunc));
diff --git a/src/components/Workspace/WorkspaceName.js b/src/components/Workspace/WorkspaceName.js
index 33d94ab..9efe915 100644
--- a/src/components/Workspace/WorkspaceName.js
+++ b/src/components/Workspace/WorkspaceName.js
@@ -79,7 +79,7 @@ class WorkspaceName extends Component {
if(this.state.projectType === 'gallery'){
this.saveGallery();
} else {
- this.props.updateProject(this.props.projectType, this.props.project._id._id ? this.props.project._id._id : this.props.project._id);
+ this.props.updateProject(this.props.projectType, this.props.project._id);
}
} else {
this.setState({ snackbar: true, type: 'success', key: Date.now(), message: `Das Projekt wurde erfolgreich in '${this.state.name}' umbenannt.` });
From c47d98d2ebfc47dcf9c173e610773532ab188207 Mon Sep 17 00:00:00 2001
From: Delucse <46593742+Delucse@users.noreply.github.com>
Date: Wed, 9 Dec 2020 09:34:33 +0100
Subject: [PATCH 29/32] compile button
---
src/components/Workspace/Compile.js | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/src/components/Workspace/Compile.js b/src/components/Workspace/Compile.js
index e2e4768..f8a204a 100644
--- a/src/components/Workspace/Compile.js
+++ b/src/components/Workspace/Compile.js
@@ -23,7 +23,7 @@ const styles = (theme) => ({
zIndex: theme.zIndex.drawer + 1,
color: '#fff',
},
- button: {
+ iconButton: {
backgroundColor: theme.palette.button.compile,
color: theme.palette.primary.contrastText,
width: '40px',
@@ -32,6 +32,14 @@ const styles = (theme) => ({
backgroundColor: theme.palette.button.compile,
color: theme.palette.primary.contrastText,
}
+ },
+ button: {
+ backgroundColor: theme.palette.button.compile,
+ color: theme.palette.primary.contrastText,
+ '&:hover': {
+ backgroundColor: theme.palette.button.compile,
+ color: theme.palette.primary.contrastText,
+ }
}
});
@@ -113,14 +121,14 @@ class Compile extends Component {
{this.props.iconButton ?