diff --git a/package.json b/package.json index ff3634c..9f78c9a 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", "blockly": "^3.20200625.2", + "file-saver": "^2.0.2", "prismjs": "^1.20.0", "react": "^16.13.1", "react-dom": "^16.13.1", diff --git a/src/components/ClearWorkspace.js b/src/components/ClearWorkspace.js deleted file mode 100644 index 2e31b01..0000000 --- a/src/components/ClearWorkspace.js +++ /dev/null @@ -1,46 +0,0 @@ -import React, {Component} from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { clearStats, onChangeCode } from '../actions/workspaceActions'; -import { initialXml } from './Blockly/initialXml.js'; - -import * as Blockly from 'blockly/core'; - -import ListItem from '@material-ui/core/ListItem'; -import ListItemIcon from '@material-ui/core/ListItemIcon'; -import ListItemText from '@material-ui/core/ListItemText'; - -import { faTrashRestore } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - -class ClearWorkspace extends Component { - - clearWorkspace = () => { - 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(); - } - - render() { - return ( - {this.clearWorkspace(); this.props.onClick();}}> - - - - ); - }; -} - -ClearWorkspace.propTypes = { - clearStats: PropTypes.func.isRequired, - onChangeCode: PropTypes.func.isRequired -}; - - -export default connect(null, { clearStats, onChangeCode })(ClearWorkspace); diff --git a/src/components/Compile.js b/src/components/Compile.js index a6c86c2..e42f5cf 100644 --- a/src/components/Compile.js +++ b/src/components/Compile.js @@ -10,11 +10,26 @@ import DialogTitle from '@material-ui/core/DialogTitle'; import DialogContent from '@material-ui/core/DialogContent'; import DialogActions from '@material-ui/core/DialogActions'; import Dialog from '@material-ui/core/Dialog'; +import IconButton from '@material-ui/core/IconButton'; +import Tooltip from '@material-ui/core/Tooltip'; + +import { faPlay } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; const styles = (theme) => ({ backdrop: { zIndex: theme.zIndex.drawer + 1, color: '#fff', + }, + 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, + } } }); @@ -60,10 +75,21 @@ class Compile extends Component { render() { return ( -
- +
+ {this.props.iconButton ? + + this.compile()} + > + + + + : + + } diff --git a/src/components/Home.js b/src/components/Home.js index 5b6f71f..47555f9 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -72,7 +72,8 @@ class Home extends Component { render() { return (
- +
+
@@ -93,7 +94,6 @@ class Home extends Component { : null} -
); }; diff --git a/src/components/Navbar.js b/src/components/Navbar.js index fb5365f..55db003 100644 --- a/src/components/Navbar.js +++ b/src/components/Navbar.js @@ -1,7 +1,6 @@ import React, { Component } from 'react'; import { Link } from 'react-router-dom'; -import ClearWorkspace from './ClearWorkspace'; import senseboxLogo from './sensebox_logo.svg'; import { withRouter } from 'react-router-dom'; @@ -105,7 +104,6 @@ class Navbar extends Component { ))} - diff --git a/src/components/WorkspaceFunc.js b/src/components/WorkspaceFunc.js index 8c4826c..117bf68 100644 --- a/src/components/WorkspaceFunc.js +++ b/src/components/WorkspaceFunc.js @@ -1,39 +1,143 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; +import { clearStats, onChangeCode } from '../actions/workspaceActions'; + +import * as Blockly from 'blockly/core'; + +import { saveAs } from 'file-saver'; + +import { initialXml } from './Blockly/initialXml.js'; -import MaxBlocks from './MaxBlocks'; import Compile from './Compile'; +import { withStyles } from '@material-ui/core/styles'; import Button from '@material-ui/core/Button'; import DialogTitle from '@material-ui/core/DialogTitle'; import DialogContent from '@material-ui/core/DialogContent'; import DialogActions from '@material-ui/core/DialogActions'; import Dialog from '@material-ui/core/Dialog'; +import IconButton from '@material-ui/core/IconButton'; +import Tooltip from '@material-ui/core/Tooltip'; + +import { faSave, faUpload, 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 WorkspaceFunc extends Component { - state = { - title: '', - content: '', - open: false - } - - getArduinoCode = () => { - this.setState({ title: 'Adurino Code', content: this.props.arduino, open: true }); - } - - getXMLCode = () => { - this.setState({ title: 'XML Code', content: this.props.xml, open: true }); + constructor(props){ + super(props); + this.inputRef = React.createRef(); + this.state = { + title: '', + content: '', + open: false + }; } toggleDialog = () => { this.setState({ open: !this.state }); } + saveXmlFile = (code) => { + // saveTextFileAs + var fileName = 'todo.xml' + var blob = new Blob([code], { type: 'text/xml' }); + saveAs(blob, fileName); + } + + uploadXmlFile = (xmlFile) => { + console.log(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.' }); + } + } 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.' }); + } + }; + } + } + + 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(); + } + render() { return ( -
+
+ + + this.saveXmlFile(this.props.xml)} + > + + + +
+ {this.uploadXmlFile(e.target.files[0])}} + id="open-blocks" + type="file" + /> + +
+ + this.resetWorkspace()} + > + + + {this.state.title} @@ -45,14 +149,6 @@ class WorkspaceFunc extends Component { - - - -
); }; @@ -60,7 +156,9 @@ class WorkspaceFunc extends Component { WorkspaceFunc.propTypes = { arduino: PropTypes.string.isRequired, - xml: PropTypes.string.isRequired + xml: PropTypes.string.isRequired, + clearStats: PropTypes.func.isRequired, + onChangeCode: PropTypes.func.isRequired }; const mapStateToProps = state => ({ @@ -68,4 +166,4 @@ const mapStateToProps = state => ({ xml: state.workspace.code.xml }); -export default connect(mapStateToProps, null)(WorkspaceFunc); +export default connect(mapStateToProps, { clearStats, onChangeCode })(withStyles(styles, {withTheme: true})(WorkspaceFunc));