From e647d6e58e96870279ed57eea5d7a4b0ded7d973 Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 16 Oct 2020 22:33:02 +0200 Subject: [PATCH] add block sharing --- .env | 1 + package-lock.json | 32 ++++- package.json | 4 +- src/components/Gallery/GalleryHome.js | 20 ++- src/components/Home.js | 13 +- src/components/Routes.js | 1 + src/components/WorkspaceFunc.js | 177 +++++++++++++++++++------- 7 files changed, 194 insertions(+), 54 deletions(-) diff --git a/.env b/.env index 8a24119..a563d82 100644 --- a/.env +++ b/.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 diff --git a/package-lock.json b/package-lock.json index 6a8dc6d..50dcf2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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": { diff --git a/package.json b/package.json index a7a911e..67df513 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/Gallery/GalleryHome.js b/src/components/Gallery/GalleryHome.js index 6c68f34..4a1a83c 100644 --- a/src/components/Gallery/GalleryHome.js +++ b/src/components/Gallery/GalleryHome.js @@ -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 (
@@ -58,7 +74,7 @@ class GalleryHome extends Component {

Gallery

- {gallery.map((gallery, i) => { + {this.state.gallery.map((gallery, i) => { return ( diff --git a/src/components/Home.js b/src/components/Home.js index 861dbd1..3b8ed31 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -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 (
diff --git a/src/components/Routes.js b/src/components/Routes.js index 2993e7d..f312005 100644 --- a/src/components/Routes.js +++ b/src/components/Routes.js @@ -20,6 +20,7 @@ class Routes extends Component { + diff --git a/src/components/WorkspaceFunc.js b/src/components/WorkspaceFunc.js index 0b74f0c..7626bca 100644 --- a/src/components/WorkspaceFunc.js +++ b/src/components/WorkspaceFunc.js @@ -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,9 +59,10 @@ const styles = (theme) => ({ }); + class WorkspaceFunc extends Component { - constructor(props){ + constructor(props) { super(props); this.inputRef = React.createRef(); this.state = { @@ -60,21 +71,25 @@ 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}); + + + componentDidUpdate(props) { + if (props.name !== this.props.name) { + this.setState({ name: this.props.name }); } } 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); @@ -98,7 +148,7 @@ class WorkspaceFunc extends Component { // 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)){ + if (/^blockly.*$/.test(document.getElementsByTagName('style')[i].id)) { cssContent += document.getElementsByTagName('style')[i].firstChild.data.replace(/\..* \./g, '.'); } } @@ -125,22 +175,22 @@ class WorkspaceFunc extends Component { } createFileName = (filetype) => { - this.setState({file: filetype}, () => { - if(this.state.name){ + this.setState({ file: filetype }, () => { + if (this.state.name) { this.state.file === 'xml' ? this.saveXmlFile() : this.getSvg() } - else{ + else { this.setState({ saveFile: true, file: filetype, open: true, title: this.state.file === 'xml' ? 'Blöcke speichern' : '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}); + this.setState({ name: e.target.value }); } uploadXmlFile = (xmlFile) => { - if(xmlFile.type !== 'text/xml'){ + 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 { @@ -155,18 +205,18 @@ class WorkspaceFunc extends Component { workspace.clear(); this.props.clearStats(); Blockly.Xml.domToWorkspace(xmlDom, workspace); - if(workspace.getAllBlocks().length < 1){ + 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.solutionCheck){ + if (!this.props.solutionCheck) { var extensionPosition = xmlFile.name.lastIndexOf('.'); this.props.workspaceName(xmlFile.name.substr(0, extensionPosition)); } this.setState({ snackbar: true, key: Date.now(), message: 'Das Projekt aus gegebener XML-Datei wurde erfolgreich eingefügt.' }); } - } catch(err){ + } 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.' }); } }; @@ -189,56 +239,60 @@ class WorkspaceFunc extends Component { workspace.options.maxBlocks = Infinity; this.props.onChangeCode(); this.props.clearStats(); - if(!this.props.solutionCheck){ + if (!this.props.solutionCheck) { this.props.workspaceName(null); } this.setState({ snackbar: true, key: Date.now(), message: 'Das Projekt wurde erfolgreich zurückgesetzt.' }); } + + render() { return ( -
+
{!this.props.solutionCheck ? - -
{this.setState({file: true, open: true, saveFile: false, title: 'Projekt benennen', content: 'Bitte gib einen Namen für das Projekt ein und bestätige diesen mit einem Klick auf \'Eingabe\'.'})}}> - {this.props.name && !isWidthDown('xs', this.props.width) ? {this.props.name} : null} -
- + +
{ this.setState({ file: true, open: true, saveFile: false, title: 'Projekt benennen', content: 'Bitte gib einen Namen für das Projekt ein und bestätige diesen mit einem Klick auf \'Eingabe\'.' }) }}> + {this.props.name && !isWidthDown('xs', this.props.width) ? {this.props.name} : null} +
+ +
-
- : null} + : null} {this.props.solutionCheck ? : } - + {this.createFileName('xml');}} + onClick={() => { this.createFileName('xml'); }} > - + -
+
{this.uploadXmlFile(e.target.files[0])}} + onChange={(e) => { this.uploadXmlFile(e.target.files[0]) }} id="open-blocks" type="file" />