From 8991e2b40b9327e947f15ce6762768bbf1a615e9 Mon Sep 17 00:00:00 2001 From: Delucse <46593742+Delucse@users.noreply.github.com> Date: Tue, 29 Sep 2020 20:12:33 +0200 Subject: [PATCH 1/5] possibility to add pictures to the instructions --- public/media/tutorial/block_en.svg | 1 + src/actions/tutorialBuilderActions.js | 24 +++- src/components/Tutorial/Builder/Builder.js | 9 +- src/components/Tutorial/Builder/Picture.js | 132 +++++++++++++++++++++ src/components/Tutorial/Builder/Step.js | 4 + src/data/tutorials.json | 4 +- 6 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 public/media/tutorial/block_en.svg create mode 100644 src/components/Tutorial/Builder/Picture.js diff --git a/public/media/tutorial/block_en.svg b/public/media/tutorial/block_en.svg new file mode 100644 index 0000000..13b559a --- /dev/null +++ b/public/media/tutorial/block_en.svg @@ -0,0 +1 @@ + diff --git a/src/actions/tutorialBuilderActions.js b/src/actions/tutorialBuilderActions.js index f200cf0..ec61f1a 100644 --- a/src/actions/tutorialBuilderActions.js +++ b/src/actions/tutorialBuilderActions.js @@ -170,6 +170,8 @@ export const setSubmitError = () => (dispatch, getState) => { dispatch(setError(undefined, 'title')); } var type = builder.steps.map((step, i) => { + // picture and xml are directly checked for errors in their components and + // therefore do not have to be checked again step.id = i+1; if(i === 0){ if(step.requirements && step.requirements.length > 0){ @@ -255,9 +257,29 @@ export const readJSON = (json) => (dispatch, getState) => { steps: json.steps.map(() => {return {};}) } }); + // accept only valid attributes + var steps = json.steps.map((step, i) => { + var object = { + id: step.id, + type: step.type, + headline: step.headline, + text: step.text + }; + if(i === 0){ + object.hardware = step.hardware; + object.requirements = step.requirements; + } + if(step.xml){ + object.xml = step.xml; + } + if(step.picture && step.type === 'instruction'){ + object.picture = step.picture; + } + return object; + }); dispatch(tutorialTitle(json.title)); dispatch(tutorialId(json.id)); - dispatch(tutorialSteps(json.steps)); + dispatch(tutorialSteps(steps)); dispatch(setSubmitError()); dispatch(progress(false)); }; diff --git a/src/components/Tutorial/Builder/Builder.js b/src/components/Tutorial/Builder/Builder.js index 1c6c819..2a177f2 100644 --- a/src/components/Tutorial/Builder/Builder.js +++ b/src/components/Tutorial/Builder/Builder.js @@ -58,10 +58,17 @@ class Builder extends Component { window.scrollTo(0, 0); } else{ + // export steps without attribute 'url' + var steps = this.props.steps.map(step => { + if(step.url){ + delete step.url; + } + return step; + }); var tutorial = { id: this.props.id, title: this.props.title, - steps: this.props.steps + steps: steps } var blob = new Blob([JSON.stringify(tutorial)], { type: 'text/json' }); saveAs(blob, `${detectWhitespacesAndReturnReadableResult(tutorial.title)}.json`); diff --git a/src/components/Tutorial/Builder/Picture.js b/src/components/Tutorial/Builder/Picture.js new file mode 100644 index 0000000..6c23f30 --- /dev/null +++ b/src/components/Tutorial/Builder/Picture.js @@ -0,0 +1,132 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { changeContent, deleteProperty, setError, deleteError } from '../../../actions/tutorialBuilderActions'; + +import { withStyles } from '@material-ui/core/styles'; +import Switch from '@material-ui/core/Switch'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import Button from '@material-ui/core/Button'; + +const styles = (theme) => ({ + errorColor: { + color: theme.palette.error.dark + }, + errorBorder: { + border: `1px solid ${theme.palette.error.dark}` + }, + errorButton: { + marginTop: '5px', + height: '40px', + backgroundColor: theme.palette.error.dark, + '&:hover':{ + backgroundColor: theme.palette.error.dark + } + } +}); + +class Picture extends Component { + + constructor(props){ + super(props); + this.state={ + checked: props.value ? true : false, + error: false + }; + } + + componentDidUpdate(props){ + if(props.value !== this.props.value){ + this.setState({ checked: this.props.value ? true : false }); + } + } + + onChange = (value) => { + var oldValue = this.state.checked; + this.setState({checked: value}); + if(oldValue !== value){ + if(value){ + this.props.setError(this.props.index, 'picture'); + } else { + this.props.deleteError(this.props.index, 'picture'); + this.props.deleteProperty(this.props.index, 'picture'); + this.props.deleteProperty(this.props.index, 'url'); + this.setState({ error: false}); + } + } + } + + uploadPicture = (pic) => { + if(!(/^image\/.*/.test(pic.type))){ + this.props.setError(this.props.index, 'picture'); + this.setState({ error: true }); + this.props.deleteProperty(this.props.index, 'url'); + } + else { + this.props.deleteError(this.props.index, 'picture'); + this.setState({ error: false }); + this.props.changeContent(this.props.index, 'url', URL.createObjectURL(pic)); + } + this.props.changeContent(this.props.index, 'picture', pic.name); + } + + render() { + return ( +