create and display content to share
This commit is contained in:
parent
d0dda4038e
commit
6db8c094ea
@ -5,6 +5,8 @@ import { clearStats, workspaceName } from '../actions/workspaceActions';
|
|||||||
|
|
||||||
import * as Blockly from 'blockly/core';
|
import * as Blockly from 'blockly/core';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
import WorkspaceStats from './WorkspaceStats';
|
import WorkspaceStats from './WorkspaceStats';
|
||||||
import WorkspaceFunc from './WorkspaceFunc';
|
import WorkspaceFunc from './WorkspaceFunc';
|
||||||
import BlocklyWindow from './Blockly/BlocklyWindow';
|
import BlocklyWindow from './Blockly/BlocklyWindow';
|
||||||
@ -17,6 +19,8 @@ import Grid from '@material-ui/core/Grid';
|
|||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import Backdrop from '@material-ui/core/Backdrop';
|
||||||
|
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||||
|
|
||||||
import { faCode } from "@fortawesome/free-solid-svg-icons";
|
import { faCode } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
@ -50,17 +54,28 @@ class Home extends Component {
|
|||||||
gallery: [],
|
gallery: [],
|
||||||
share: [],
|
share: [],
|
||||||
projectToLoad: undefined,
|
projectToLoad: undefined,
|
||||||
|
progress: false,
|
||||||
stats: window.localStorage.getItem('stats'),
|
stats: window.localStorage.getItem('stats'),
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.setState({ stats: window.localStorage.getItem('stats') })
|
this.setState({ stats: window.localStorage.getItem('stats') })
|
||||||
this.props.workspaceName(createNameId());
|
if(this.props.match.params.shareId || this.props.match.params.galleryId){
|
||||||
fetch(process.env.REACT_APP_BLOCKLY_API + this.props.location.pathname)
|
this.setState({progress: true});
|
||||||
.then(res => res.json())
|
axios.get(`${process.env.REACT_APP_BLOCKLY_API}${this.props.location.pathname}`)
|
||||||
.then((data) => {
|
.then(res => {
|
||||||
this.setState({ projectToLoad: data })
|
var shareContent = res.data.content;
|
||||||
})
|
this.props.workspaceName(res.data.content.name);
|
||||||
|
this.setState({ projectToLoad: res.data.content, progress: false });
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.setState({ progress: false, snackbar: true, key: Date.now(), message: `Fehler beim Erstellen eines Links zum Teilen deines Programmes. Versuche es noch einmal.`, type: 'error' });
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.props.workspaceName(createNameId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -91,35 +106,44 @@ class Home extends Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.state.stats ?
|
{this.state.progress ?
|
||||||
<div style={{ float: 'left', height: '40px', position: 'relative' }}><WorkspaceStats /></div>
|
<Backdrop open invisible>
|
||||||
: null
|
<CircularProgress color="primary" />
|
||||||
}
|
</Backdrop>
|
||||||
<div style={{ float: 'right', height: '40px', marginBottom: '20px' }}><WorkspaceFunc /></div>
|
:
|
||||||
<Grid container spacing={2}>
|
<div>
|
||||||
<Grid item xs={12} md={this.state.codeOn ? 8 : 12} style={{ position: 'relative' }}>
|
{this.state.stats ?
|
||||||
<Tooltip title={this.state.codeOn ? 'Code ausblenden' : 'Code anzeigen'} >
|
<div style={{ float: 'left', height: '40px', position: 'relative' }}><WorkspaceStats /></div>
|
||||||
<IconButton
|
: null
|
||||||
className={this.state.codeOn ? this.props.classes.codeOn : this.props.classes.codeOff}
|
}
|
||||||
style={{ width: '40px', height: '40px', position: 'absolute', top: -12, right: 8, zIndex: 21 }}
|
<div style={{ float: 'right', height: '40px', marginBottom: '20px' }}><WorkspaceFunc /></div>
|
||||||
onClick={() => this.onChange()}
|
<Grid container spacing={2}>
|
||||||
>
|
<Grid item xs={12} md={this.state.codeOn ? 8 : 12} style={{ position: 'relative' }}>
|
||||||
<FontAwesomeIcon icon={faCode} size="xs" />
|
<Tooltip title={this.state.codeOn ? 'Code ausblenden' : 'Code anzeigen'} >
|
||||||
</IconButton>
|
<IconButton
|
||||||
</Tooltip>
|
className={this.state.codeOn ? this.props.classes.codeOn : this.props.classes.codeOff}
|
||||||
<TrashcanButtons />
|
style={{ width: '40px', height: '40px', position: 'absolute', top: -12, right: 8, zIndex: 21 }}
|
||||||
{this.state.projectToLoad ?
|
onClick={() => this.onChange()}
|
||||||
< BlocklyWindow blocklyCSS={{ height: '80vH' }} initialXml={this.state.projectToLoad.xml} /> : < BlocklyWindow blocklyCSS={{ height: '80vH' }} />
|
>
|
||||||
}
|
<FontAwesomeIcon icon={faCode} size="xs" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<TrashcanButtons />
|
||||||
|
{this.state.projectToLoad ?
|
||||||
|
< BlocklyWindow blocklyCSS={{ height: '80vH' }} initialXml={this.state.projectToLoad.xml} />
|
||||||
|
: < BlocklyWindow blocklyCSS={{ height: '80vH' }} />
|
||||||
|
}
|
||||||
|
|
||||||
</Grid>
|
|
||||||
{this.state.codeOn ?
|
|
||||||
<Grid item xs={12} md={4}>
|
|
||||||
<CodeViewer />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
: null}
|
{this.state.codeOn ?
|
||||||
</Grid>
|
<Grid item xs={12} md={4}>
|
||||||
<HintTutorialExists />
|
<CodeViewer />
|
||||||
|
</Grid>
|
||||||
|
: null}
|
||||||
|
</Grid>
|
||||||
|
<HintTutorialExists />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,9 @@ import { clearStats, onChangeCode, workspaceName } from '../actions/workspaceAct
|
|||||||
|
|
||||||
import * as Blockly from 'blockly/core';
|
import * as Blockly from 'blockly/core';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
|
import { createId } from 'mnemonic-id';
|
||||||
|
|
||||||
import { detectWhitespacesAndReturnReadableResult } from '../helpers/whitespace';
|
import { detectWhitespacesAndReturnReadableResult } from '../helpers/whitespace';
|
||||||
import { initialXml } from './Blockly/initialXml.js';
|
import { initialXml } from './Blockly/initialXml.js';
|
||||||
@ -14,6 +16,8 @@ import Compile from './Compile';
|
|||||||
import SolutionCheck from './Tutorial/SolutionCheck';
|
import SolutionCheck from './Tutorial/SolutionCheck';
|
||||||
import Snackbar from './Snackbar';
|
import Snackbar from './Snackbar';
|
||||||
|
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import withWidth, { isWidthDown } from '@material-ui/core/withWidth';
|
import withWidth, { isWidthDown } from '@material-ui/core/withWidth';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
@ -21,7 +25,6 @@ import IconButton from '@material-ui/core/IconButton';
|
|||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
import TextField from '@material-ui/core/TextField';
|
import TextField from '@material-ui/core/TextField';
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import { createId } from 'mnemonic-id';
|
|
||||||
|
|
||||||
|
|
||||||
import Dialog from './Dialog';
|
import Dialog from './Dialog';
|
||||||
@ -33,7 +36,7 @@ import DialogTitle from '@material-ui/core/DialogTitle';
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { faPen, faSave, faUpload, faCamera, faShare, faShareAlt } from "@fortawesome/free-solid-svg-icons";
|
import { faPen, faSave, faUpload, faCamera, faShare, faShareAlt, faCopy } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
const styles = (theme) => ({
|
const styles = (theme) => ({
|
||||||
@ -55,6 +58,14 @@ const styles = (theme) => ({
|
|||||||
'&:hover': {
|
'&:hover': {
|
||||||
color: theme.palette.primary.main,
|
color: theme.palette.primary.main,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
textDecoration: 'none',
|
||||||
|
'&:hover': {
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
textDecoration: 'underline'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -74,14 +85,13 @@ class WorkspaceFunc extends Component {
|
|||||||
share: false,
|
share: false,
|
||||||
name: props.name,
|
name: props.name,
|
||||||
snackbar: false,
|
snackbar: false,
|
||||||
|
type: '',
|
||||||
key: '',
|
key: '',
|
||||||
message: '',
|
message: '',
|
||||||
id: ''
|
id: ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
componentDidUpdate(props) {
|
componentDidUpdate(props) {
|
||||||
if (props.name !== this.props.name) {
|
if (props.name !== this.props.name) {
|
||||||
this.setState({ name: this.props.name });
|
this.setState({ name: this.props.name });
|
||||||
@ -89,7 +99,7 @@ class WorkspaceFunc extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleDialog = () => {
|
toggleDialog = () => {
|
||||||
this.setState({ open: !this.state, share: false });
|
this.setState({ open: !this.state, share: false, file: false, saveFile: false, title: '', content: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
saveXmlFile = () => {
|
saveXmlFile = () => {
|
||||||
@ -103,38 +113,20 @@ class WorkspaceFunc extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shareBlocks = () => {
|
shareBlocks = () => {
|
||||||
let code = this.props.xml;
|
var body = {
|
||||||
let requestOptions = '';
|
_id: createId(10),
|
||||||
let id = '';
|
name: this.state.name,
|
||||||
if (this.state.id !== '') {
|
xml: this.props.xml
|
||||||
requestOptions = {
|
};
|
||||||
method: 'PUT',
|
axios.post(`${process.env.REACT_APP_BLOCKLY_API}/share`, body)
|
||||||
headers: { 'Content-Type': 'application/json' },
|
.then(res => {
|
||||||
body: JSON.stringify({
|
var shareContent = res.data.content;
|
||||||
id: this.state.id,
|
this.setState({ share: true, open: true, title: 'Programm teilen', id: shareContent._id });
|
||||||
name: this.state.name,
|
})
|
||||||
xml: code
|
.catch(err => {
|
||||||
})
|
this.setState({ snackbar: true, key: Date.now(), message: `Fehler beim Erstellen eines Links zum Teilen deines Programmes. Versuche es noch einmal.`, type: 'error' });
|
||||||
};
|
window.scrollTo(0, 0);
|
||||||
fetch(process.env.REACT_APP_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.REACT_APP_BLOCKLY_API + '/share', requestOptions)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => this.setState({ id: data.id, share: true }));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSvg = () => {
|
getSvg = () => {
|
||||||
@ -220,7 +212,7 @@ class WorkspaceFunc extends Component {
|
|||||||
var extensionPosition = xmlFile.name.lastIndexOf('.');
|
var extensionPosition = xmlFile.name.lastIndexOf('.');
|
||||||
this.props.workspaceName(xmlFile.name.substr(0, extensionPosition));
|
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.' });
|
this.setState({ snackbar: true, type: 'success', 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.' });
|
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.' });
|
||||||
@ -232,7 +224,7 @@ class WorkspaceFunc extends Component {
|
|||||||
renameWorkspace = () => {
|
renameWorkspace = () => {
|
||||||
this.props.workspaceName(this.state.name);
|
this.props.workspaceName(this.state.name);
|
||||||
this.toggleDialog();
|
this.toggleDialog();
|
||||||
this.setState({ snackbar: true, key: Date.now(), message: `Das Projekt wurde erfolgreich in '${this.state.name}' umbenannt.` });
|
this.setState({ snackbar: true, type: 'success', key: Date.now(), message: `Das Projekt wurde erfolgreich in '${this.state.name}' umbenannt.` });
|
||||||
}
|
}
|
||||||
|
|
||||||
resetWorkspace = () => {
|
resetWorkspace = () => {
|
||||||
@ -248,7 +240,7 @@ class WorkspaceFunc extends Component {
|
|||||||
if (!this.props.solutionCheck) {
|
if (!this.props.solutionCheck) {
|
||||||
this.props.workspaceName(null);
|
this.props.workspaceName(null);
|
||||||
}
|
}
|
||||||
this.setState({ snackbar: true, key: Date.now(), message: 'Das Projekt wurde erfolgreich zurückgesetzt.' });
|
this.setState({ snackbar: true, type: 'success', key: Date.now(), message: 'Das Projekt wurde erfolgreich zurückgesetzt.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -320,29 +312,6 @@ class WorkspaceFunc extends Component {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</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={window.location.origin + "/share/" + this.state.id}
|
|
||||||
label="url"
|
|
||||||
type="email"
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={this.toggleDialog} color="primary">
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
open={this.state.open}
|
open={this.state.open}
|
||||||
title={this.state.title}
|
title={this.state.title}
|
||||||
@ -356,13 +325,28 @@ class WorkspaceFunc extends Component {
|
|||||||
<TextField autoFocus placeholder={this.state.saveXml ? 'Dateiname' : 'Projektname'} value={this.state.name} onChange={this.setFileName} style={{ marginRight: '10px' }} />
|
<TextField autoFocus placeholder={this.state.saveXml ? 'Dateiname' : 'Projektname'} value={this.state.name} onChange={this.setFileName} style={{ marginRight: '10px' }} />
|
||||||
<Button disabled={!this.state.name} variant='contained' color='primary' onClick={() => { this.state.saveFile ? this.state.file === 'xml' ? this.saveXmlFile() : this.getSvg() : this.renameWorkspace(); this.toggleDialog(); }}>Eingabe</Button>
|
<Button disabled={!this.state.name} variant='contained' color='primary' onClick={() => { this.state.saveFile ? this.state.file === 'xml' ? this.saveXmlFile() : this.getSvg() : this.renameWorkspace(); this.toggleDialog(); }}>Eingabe</Button>
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: this.state.share ?
|
||||||
|
<div style={{ marginTop: '10px' }}>
|
||||||
|
<Typography>Über den folgenden Link kannst du dein Programm teilen:</Typography>
|
||||||
|
<Link to={`/share/${this.state.id}`} className={this.props.classes.link}>{`${window.location.origin}/share/${this.state.id}`}</Link>
|
||||||
|
<Tooltip title='Link kopieren' arrow style={{ marginRight: '5px' }}>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(`${window.location.origin}/share/${this.state.id}`);
|
||||||
|
this.setState({ snackbar: true, key: Date.now(), message: 'Link erfolgreich in Zwischenablage gespeichert.', type: 'success' });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCopy} size="xs" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<Snackbar
|
<Snackbar
|
||||||
open={this.state.snackbar}
|
open={this.state.snackbar}
|
||||||
message={this.state.message}
|
message={this.state.message}
|
||||||
type='success'
|
type={this.state.type}
|
||||||
key={this.state.key}
|
key={this.state.key}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user