upload, download, compile and reset blocks
This commit is contained in:
		
							parent
							
								
									8060927cc6
								
							
						
					
					
						commit
						4550977471
					
				| @ -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", | ||||
|  | ||||
| @ -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 ( | ||||
|       <ListItem button onClick={() => {this.clearWorkspace(); this.props.onClick();}}> | ||||
|         <ListItemIcon><FontAwesomeIcon icon={faTrashRestore} /></ListItemIcon> | ||||
|         <ListItemText primary='Zurücksetzen' /> | ||||
|       </ListItem> | ||||
|     ); | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| ClearWorkspace.propTypes = { | ||||
|   clearStats: PropTypes.func.isRequired, | ||||
|   onChangeCode: PropTypes.func.isRequired | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| export default connect(null, { clearStats, onChangeCode })(ClearWorkspace); | ||||
| @ -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 ( | ||||
|       <div style={{display: 'inline'}}> | ||||
|         <Button style={{ float: 'right', color: 'white' }} variant="contained" color="primary" onClick={() => this.compile()}> | ||||
|           Kompilieren | ||||
|         </Button> | ||||
|       <div style={{}}> | ||||
|         {this.props.iconButton ? | ||||
|           <Tooltip title='Blöcke kompilieren' arrow style={{marginRight: '5px'}}> | ||||
|             <IconButton | ||||
|               className={this.props.classes.button} | ||||
|               onClick={() => this.compile()} | ||||
|             > | ||||
|               <FontAwesomeIcon icon={faPlay} size="xs"/> | ||||
|             </IconButton> | ||||
|           </Tooltip> | ||||
|          : | ||||
|           <Button style={{ float: 'right', color: 'white' }} variant="contained" color="primary" onClick={() => this.compile()}> | ||||
|             Kompilieren | ||||
|           </Button> | ||||
|         } | ||||
|         <Backdrop className={this.props.classes.backdrop} open={this.state.progress}> | ||||
|           <CircularProgress color="inherit" /> | ||||
|         </Backdrop> | ||||
|  | ||||
| @ -72,7 +72,8 @@ class Home extends Component { | ||||
|   render() { | ||||
|     return ( | ||||
|       <div> | ||||
|         <WorkspaceStats /> | ||||
|         <div style={{float: 'left'}}><WorkspaceStats /></div> | ||||
|         <div style={{float: 'right'}}><WorkspaceFunc /></div> | ||||
|         <Grid container spacing={2}> | ||||
|           <Grid item xs={12} md={this.state.codeOn ? 6 : 12} style={{ position: 'relative' }}> | ||||
|             <Tooltip title={this.state.codeOn ? 'Code ausblenden' : 'Code anzeigen'} > | ||||
| @ -93,7 +94,6 @@ class Home extends Component { | ||||
|             </Grid> | ||||
|             : null} | ||||
|         </Grid> | ||||
|         <WorkspaceFunc /> | ||||
|       </div> | ||||
|     ); | ||||
|   }; | ||||
|  | ||||
| @ -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 { | ||||
|                 </ListItem> | ||||
|               </Link> | ||||
|             ))} | ||||
|             <ClearWorkspace onClick={this.toggleDrawer}/> | ||||
|           </List> | ||||
|           <Divider classes={{root: this.props.classes.appBarColor}} style={{marginTop: 'auto'}}/> | ||||
|           <List> | ||||
|  | ||||
| @ -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 ( | ||||
|       <div style={{ marginTop: '20px' }}> | ||||
|       <div style={{ marginBottom: '20px', width: 'max-content', display: 'flex'}}> | ||||
|         <Compile iconButton /> | ||||
|         <Tooltip title='Blöcke speichern' arrow style={{marginRight: '5px'}}> | ||||
|           <IconButton | ||||
|             className={this.props.classes.button} | ||||
|             onClick={() => this.saveXmlFile(this.props.xml)} | ||||
|           > | ||||
|             <FontAwesomeIcon icon={faSave} size="xs"/> | ||||
|           </IconButton> | ||||
|         </Tooltip> | ||||
|         <div ref={this.inputRef} style={{width: 'max-content', height: '40px', marginRight: '5px'}}> | ||||
|           <input | ||||
|             style={{display: 'none'}} | ||||
|             accept="text/xml" | ||||
|             onChange={(e) => {this.uploadXmlFile(e.target.files[0])}} | ||||
|             id="open-blocks" | ||||
|             type="file" | ||||
|           /> | ||||
|           <label htmlFor="open-blocks"> | ||||
|             <Tooltip title='Blöcke öffnen' arrow style={{marginRight: '5px'}}> | ||||
|               <div className={this.props.classes.button} style={{borderRadius: '50%', cursor: 'pointer', display: 'table-cell', | ||||
|               verticalAlign: 'middle', | ||||
|               textAlign: 'center'}}> | ||||
|                 <FontAwesomeIcon icon={faUpload} style={{width: '18px', height: '18px'}}/> | ||||
|               </div> | ||||
|             </Tooltip> | ||||
|           </label> | ||||
|         </div> | ||||
|         <Tooltip title='Workspace zurücksetzen' arrow> | ||||
|           <IconButton | ||||
|             className={this.props.classes.button} | ||||
|             onClick={() => this.resetWorkspace()} | ||||
|           > | ||||
|             <FontAwesomeIcon icon={faShare} size="xs" flip='horizontal'/> | ||||
|           </IconButton> | ||||
|         </Tooltip> | ||||
|         <Dialog onClose={this.toggleDialog} open={this.state.open}> | ||||
|           <DialogTitle>{this.state.title}</DialogTitle> | ||||
|           <DialogContent dividers> | ||||
| @ -45,14 +149,6 @@ class WorkspaceFunc extends Component { | ||||
|             </Button> | ||||
|           </DialogActions> | ||||
|         </Dialog> | ||||
|         <Button style={{ marginRight: '10px', color: 'white' }} variant="contained" color="primary" onClick={() => this.getArduinoCode()}> | ||||
|           Get Adurino Code | ||||
|         </Button> | ||||
|         <Button style={{ marginRight: '10px', color: 'white' }} variant="contained" color="primary" onClick={() => this.getXMLCode()}> | ||||
|           Get XML Code | ||||
|         </Button> | ||||
|         <MaxBlocks /> | ||||
|         <Compile /> | ||||
|       </div> | ||||
|     ); | ||||
|   }; | ||||
| @ -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)); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user