add block sharing

This commit is contained in:
Mario 2020-10-16 22:33:02 +02:00
parent 0cb7bba521
commit e647d6e58e
7 changed files with 194 additions and 54 deletions

1
.env
View File

@ -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
View File

@ -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": {

View File

@ -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",

View File

@ -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' }}>

View File

@ -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>

View File

@ -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} />

View File

@ -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}