add block sharing
This commit is contained in:
		
							parent
							
								
									0cb7bba521
								
							
						
					
					
						commit
						e647d6e58e
					
				
							
								
								
									
										1
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								.env
									
									
									
									
									
								
							| @ -1,2 +1,3 @@ | ||||
| REACT_APP_COMPILER_URL=https://compiler.sensebox.de | ||||
| REACT_APP_BOARD=sensebox-mcu | ||||
| REACT_APP_BLOCKLY_API=http://46.101.243.134:3000 | ||||
|  | ||||
							
								
								
									
										32
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										32
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -8720,6 +8720,11 @@ | ||||
|         "minimist": "^1.2.5" | ||||
|       } | ||||
|     }, | ||||
|     "mnemonic-id": { | ||||
|       "version": "3.2.7", | ||||
|       "resolved": "https://registry.npmjs.org/mnemonic-id/-/mnemonic-id-3.2.7.tgz", | ||||
|       "integrity": "sha512-kysx9gAGbvrzuFYxKkcRjnsg/NK61ovJOV4F1cHTRl9T5leg+bo6WI0pWIvOFh1Z/yDL0cjA5R3EEGPPLDv/XA==" | ||||
|     }, | ||||
|     "moment": { | ||||
|       "version": "2.29.0", | ||||
|       "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.0.tgz", | ||||
| @ -11394,6 +11399,13 @@ | ||||
|         "tough-cookie": "~2.5.0", | ||||
|         "tunnel-agent": "^0.6.0", | ||||
|         "uuid": "^3.3.2" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "uuid": { | ||||
|           "version": "3.4.0", | ||||
|           "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", | ||||
|           "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "request-promise-core": { | ||||
| @ -12125,6 +12137,13 @@ | ||||
|       "requires": { | ||||
|         "faye-websocket": "^0.10.0", | ||||
|         "uuid": "^3.0.1" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "uuid": { | ||||
|           "version": "3.4.0", | ||||
|           "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", | ||||
|           "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "sockjs-client": { | ||||
| @ -13269,9 +13288,9 @@ | ||||
|       "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" | ||||
|     }, | ||||
|     "uuid": { | ||||
|       "version": "3.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", | ||||
|       "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" | ||||
|       "version": "8.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", | ||||
|       "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==" | ||||
|     }, | ||||
|     "v8-compile-cache": { | ||||
|       "version": "2.1.1", | ||||
| @ -13929,6 +13948,13 @@ | ||||
|       "requires": { | ||||
|         "ansi-colors": "^3.0.0", | ||||
|         "uuid": "^3.3.2" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "uuid": { | ||||
|           "version": "3.4.0", | ||||
|           "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", | ||||
|           "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "webpack-manifest-plugin": { | ||||
|  | ||||
| @ -15,6 +15,7 @@ | ||||
|     "@testing-library/user-event": "^7.2.1", | ||||
|     "blockly": "^3.20200924.0", | ||||
|     "file-saver": "^2.0.2", | ||||
|     "mnemonic-id": "^3.2.7", | ||||
|     "moment": "^2.28.0", | ||||
|     "prismjs": "^1.20.0", | ||||
|     "react": "^16.13.1", | ||||
| @ -23,7 +24,8 @@ | ||||
|     "react-router-dom": "^5.2.0", | ||||
|     "react-scripts": "3.4.1", | ||||
|     "redux": "^4.0.5", | ||||
|     "redux-thunk": "^2.3.0" | ||||
|     "redux-thunk": "^2.3.0", | ||||
|     "uuid": "^8.3.1" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "start": "react-scripts start", | ||||
|  | ||||
| @ -6,7 +6,7 @@ import clsx from 'clsx'; | ||||
| 
 | ||||
| import Breadcrumbs from '../Breadcrumbs'; | ||||
| 
 | ||||
| import gallery from './gallery.json'; | ||||
| // import gallery from './gallery.json';
 | ||||
| // import tutorials from '../../data/tutorials.json';
 | ||||
| 
 | ||||
| import { Link } from 'react-router-dom'; | ||||
| @ -49,8 +49,24 @@ const styles = (theme) => ({ | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class GalleryHome extends Component { | ||||
| 
 | ||||
|     state = { | ||||
|         gallery: [] | ||||
|     } | ||||
| 
 | ||||
|     componentDidMount() { | ||||
|         console.log(process.env.REACT_APP_BLOCKLY_API) | ||||
|         fetch(process.env.REACT_APP_BLOCKLY_API + this.props.location.pathname) | ||||
|             .then(res => res.json()) | ||||
|             .then((data) => { | ||||
|                 this.setState({ gallery: data }) | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     render() { | ||||
|         return ( | ||||
|             <div> | ||||
| @ -58,7 +74,7 @@ class GalleryHome extends Component { | ||||
| 
 | ||||
|                 <h1>Gallery</h1> | ||||
|                 <Grid container spacing={2}> | ||||
|                     {gallery.map((gallery, i) => { | ||||
|                     {this.state.gallery.map((gallery, i) => { | ||||
|                         return ( | ||||
|                             <Grid item xs={12} sm={6} md={4} xl={3} key={i} style={{}}> | ||||
|                                 <Link to={`/gallery/${gallery.id}`} style={{ textDecoration: 'none', color: 'inherit' }}> | ||||
|  | ||||
| @ -10,6 +10,7 @@ import WorkspaceFunc from './WorkspaceFunc'; | ||||
| import BlocklyWindow from './Blockly/BlocklyWindow'; | ||||
| import CodeViewer from './CodeViewer'; | ||||
| import TrashcanButtons from './TrashcanButtons'; | ||||
| import { createNameId } from 'mnemonic-id'; | ||||
| 
 | ||||
| import Grid from '@material-ui/core/Grid'; | ||||
| import IconButton from '@material-ui/core/IconButton'; | ||||
| @ -18,7 +19,6 @@ import { withStyles } from '@material-ui/core/styles'; | ||||
| 
 | ||||
| import { faCode } from "@fortawesome/free-solid-svg-icons"; | ||||
| import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||||
| import gallery from './Gallery/gallery.json'; | ||||
| 
 | ||||
| const styles = (theme) => ({ | ||||
|   codeOn: { | ||||
| @ -46,11 +46,19 @@ class Home extends Component { | ||||
| 
 | ||||
|   state = { | ||||
|     codeOn: false, | ||||
|     gallery: [], | ||||
|     share: [], | ||||
|     projectToLoad: undefined | ||||
|   } | ||||
| 
 | ||||
|   componentDidMount() { | ||||
|     this.setState({ projectToLoad: gallery.find(project => project.id == this.props.match.params.galleryId) }) | ||||
| 
 | ||||
|     this.props.workspaceName(createNameId()); | ||||
|     fetch(process.env.BLOCKLY_API + this.props.location.pathname) | ||||
|       .then(res => res.json()) | ||||
|       .then((data) => { | ||||
|         this.setState({ projectToLoad: data }) | ||||
|       }) | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| @ -85,6 +93,7 @@ class Home extends Component { | ||||
|     if (this.state.projectToLoad) { | ||||
|       console.log(this.state.projectToLoad.xml) | ||||
|     } | ||||
|     console.log(this.props); | ||||
|     return ( | ||||
|       <div> | ||||
|         <div style={{ float: 'right', height: '40px', marginBottom: '20px' }}><WorkspaceFunc /></div> | ||||
|  | ||||
| @ -20,6 +20,7 @@ class Routes extends Component { | ||||
|           <Route path="/tutorial" exact component={TutorialHome} /> | ||||
|           <Route path="/gallery" exact component={GalleryHome} /> | ||||
|           <Route path="/gallery/:galleryId" exact component={Home} /> | ||||
|           <Route path="/share/:shareId" exact component={Home} /> | ||||
|           <Route path="/tutorial/builder" exact component={Builder} /> | ||||
|           <Route path="/tutorial/:tutorialId" exact component={Tutorial} /> | ||||
|           <Route component={NotFound} /> | ||||
|  | ||||
| @ -12,7 +12,6 @@ import { initialXml } from './Blockly/initialXml.js'; | ||||
| 
 | ||||
| import Compile from './Compile'; | ||||
| import SolutionCheck from './Tutorial/SolutionCheck'; | ||||
| import Dialog from './Dialog'; | ||||
| import Snackbar from './Snackbar'; | ||||
| 
 | ||||
| import withWidth, { isWidthDown } from '@material-ui/core/withWidth'; | ||||
| @ -22,8 +21,19 @@ 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 { createId } from 'mnemonic-id'; | ||||
| 
 | ||||
| import { faPen, faSave, faUpload, faCamera, faShare } from "@fortawesome/free-solid-svg-icons"; | ||||
| 
 | ||||
| import Dialog from './Dialog'; | ||||
| // import Dialog from '@material-ui/core/Dialog';
 | ||||
| import DialogActions from '@material-ui/core/DialogActions'; | ||||
| import DialogContent from '@material-ui/core/DialogContent'; | ||||
| import DialogContentText from '@material-ui/core/DialogContentText'; | ||||
| import DialogTitle from '@material-ui/core/DialogTitle'; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| import { faPen, faSave, faUpload, faCamera, faShare, faShareAlt } from "@fortawesome/free-solid-svg-icons"; | ||||
| import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||||
| 
 | ||||
| const styles = (theme) => ({ | ||||
| @ -49,6 +59,7 @@ const styles = (theme) => ({ | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class WorkspaceFunc extends Component { | ||||
| 
 | ||||
|   constructor(props) { | ||||
| @ -60,13 +71,17 @@ class WorkspaceFunc extends Component { | ||||
|       open: false, | ||||
|       file: false, | ||||
|       saveFile: false, | ||||
|       share: false, | ||||
|       name: props.name, | ||||
|       snackbar: false, | ||||
|       key: '', | ||||
|       message: '' | ||||
|       message: '', | ||||
|       id: '' | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|   componentDidUpdate(props) { | ||||
|     if (props.name !== this.props.name) { | ||||
|       this.setState({ name: this.props.name }); | ||||
| @ -74,7 +89,7 @@ class WorkspaceFunc extends Component { | ||||
|   } | ||||
| 
 | ||||
|   toggleDialog = () => { | ||||
|     this.setState({ open: !this.state }); | ||||
|     this.setState({ open: !this.state, share: false }); | ||||
|   } | ||||
| 
 | ||||
|   saveXmlFile = () => { | ||||
| @ -87,6 +102,41 @@ class WorkspaceFunc extends Component { | ||||
|     saveAs(blob, fileName); | ||||
|   } | ||||
| 
 | ||||
|   shareBlocks = () => { | ||||
|     let code = this.props.xml; | ||||
|     let requestOptions = ''; | ||||
|     let id = ''; | ||||
|     if (this.state.id !== '') { | ||||
|       requestOptions = { | ||||
|         method: 'PUT', | ||||
|         headers: { 'Content-Type': 'application/json' }, | ||||
|         body: JSON.stringify({ | ||||
|           id: this.state.id, | ||||
|           name: this.state.name, | ||||
|           xml: code | ||||
|         }) | ||||
|       }; | ||||
|       fetch(process.env.BLOCKLY_API + '/share' + this.state.id, requestOptions) | ||||
|         .then(response => response.json()) | ||||
|         .then(data => this.setState({ share: true })); | ||||
|     } | ||||
|     else { | ||||
|       id = createId(10); | ||||
|       requestOptions = { | ||||
|         method: 'POST', | ||||
|         headers: { 'Content-Type': 'application/json' }, | ||||
|         body: JSON.stringify({ | ||||
|           id: id, | ||||
|           name: this.state.name, | ||||
|           xml: code | ||||
|         }) | ||||
|       }; | ||||
|       fetch(process.env.BLOCKLY_API + '/share', requestOptions) | ||||
|         .then(response => response.json()) | ||||
|         .then(data => this.setState({ id: data.id, share: true })); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getSvg = () => { | ||||
|     const workspace = Blockly.getMainWorkspace(); | ||||
|     var canvas = workspace.svgBlockCanvas_.cloneNode(true); | ||||
| @ -195,6 +245,8 @@ class WorkspaceFunc extends Component { | ||||
|     this.setState({ snackbar: true, key: Date.now(), message: 'Das Projekt wurde erfolgreich zurückgesetzt.' }); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|   render() { | ||||
|     return ( | ||||
|       <div style={{ width: 'max-content', display: 'flex' }}> | ||||
| @ -227,9 +279,11 @@ class WorkspaceFunc extends Component { | ||||
|           /> | ||||
|           <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', | ||||
|               <div className={this.props.classes.button} style={{ | ||||
|                 borderRadius: '50%', cursor: 'pointer', display: 'table-cell', | ||||
|                 verticalAlign: 'middle', | ||||
|               textAlign: 'center'}}> | ||||
|                 textAlign: 'center' | ||||
|               }}> | ||||
|                 <FontAwesomeIcon icon={faUpload} style={{ width: '18px', height: '18px' }} /> | ||||
|               </div> | ||||
|             </Tooltip> | ||||
| @ -251,6 +305,37 @@ class WorkspaceFunc extends Component { | ||||
|             <FontAwesomeIcon icon={faShare} size="xs" flip='horizontal' /> | ||||
|           </IconButton> | ||||
|         </Tooltip> | ||||
|         <Tooltip title='Blöcke teilen' arrow> | ||||
|           <IconButton | ||||
|             className={this.props.classes.button} | ||||
|             onClick={() => this.shareBlocks()} | ||||
|           > | ||||
|             <FontAwesomeIcon icon={faShareAlt} size="xs" flip='horizontal' /> | ||||
|           </IconButton> | ||||
|         </Tooltip> | ||||
| 
 | ||||
|         <Dialog open={this.state.share} onClose={this.toggleDialog} aria-labelledby="form-dialog-title"> | ||||
|           <DialogTitle id="form-dialog-title">Dein Link wurde erstellt.</DialogTitle> | ||||
|           <DialogContent> | ||||
|             <DialogContentText> | ||||
|               Über den folgenden Link kannst du dein Programm teilen. | ||||
|           </DialogContentText> | ||||
|             <TextField | ||||
|               autoFocus | ||||
|               margin="dense" | ||||
|               id="name" | ||||
|               defaultValue={"http://localhost:3000/share/" + this.state.id} | ||||
|               label="url" | ||||
|               type="email" | ||||
|               fullWidth | ||||
|             /> | ||||
|           </DialogContent> | ||||
|           <DialogActions> | ||||
|             <Button onClick={this.toggleDialog} color="primary"> | ||||
|               Cancel | ||||
|           </Button> | ||||
|           </DialogActions> | ||||
|         </Dialog> | ||||
| 
 | ||||
|         <Dialog | ||||
|           open={this.state.open} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user