possibility to add pictures to the instructions
This commit is contained in:
		
							parent
							
								
									3a92a6a08c
								
							
						
					
					
						commit
						8991e2b40b
					
				
							
								
								
									
										1
									
								
								public/media/tutorial/block_en.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								public/media/tutorial/block_en.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 9.4 KiB | 
| @ -170,6 +170,8 @@ export const setSubmitError = () => (dispatch, getState) => { | |||||||
|     dispatch(setError(undefined, 'title')); |     dispatch(setError(undefined, 'title')); | ||||||
|   } |   } | ||||||
|   var type = builder.steps.map((step, i) => { |   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; |     step.id = i+1; | ||||||
|     if(i === 0){ |     if(i === 0){ | ||||||
|       if(step.requirements && step.requirements.length > 0){ |       if(step.requirements && step.requirements.length > 0){ | ||||||
| @ -255,9 +257,29 @@ export const readJSON = (json) => (dispatch, getState) => { | |||||||
|       steps: json.steps.map(() => {return {};}) |       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(tutorialTitle(json.title)); | ||||||
|   dispatch(tutorialId(json.id)); |   dispatch(tutorialId(json.id)); | ||||||
|   dispatch(tutorialSteps(json.steps)); |   dispatch(tutorialSteps(steps)); | ||||||
|   dispatch(setSubmitError()); |   dispatch(setSubmitError()); | ||||||
|   dispatch(progress(false)); |   dispatch(progress(false)); | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -58,10 +58,17 @@ class Builder extends Component { | |||||||
|       window.scrollTo(0, 0); |       window.scrollTo(0, 0); | ||||||
|     } |     } | ||||||
|     else{ |     else{ | ||||||
|  |       // export steps without attribute 'url'
 | ||||||
|  |       var steps = this.props.steps.map(step => { | ||||||
|  |         if(step.url){ | ||||||
|  |           delete step.url; | ||||||
|  |         } | ||||||
|  |         return step; | ||||||
|  |       }); | ||||||
|       var tutorial = { |       var tutorial = { | ||||||
|         id: this.props.id, |         id: this.props.id, | ||||||
|         title: this.props.title, |         title: this.props.title, | ||||||
|         steps: this.props.steps |         steps: steps | ||||||
|       } |       } | ||||||
|       var blob = new Blob([JSON.stringify(tutorial)], { type: 'text/json' }); |       var blob = new Blob([JSON.stringify(tutorial)], { type: 'text/json' }); | ||||||
|       saveAs(blob, `${detectWhitespacesAndReturnReadableResult(tutorial.title)}.json`); |       saveAs(blob, `${detectWhitespacesAndReturnReadableResult(tutorial.title)}.json`); | ||||||
|  | |||||||
							
								
								
									
										132
									
								
								src/components/Tutorial/Builder/Picture.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/components/Tutorial/Builder/Picture.js
									
									
									
									
									
										Normal file
									
								
							| @ -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 ( | ||||||
|  |       <div style={{marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)'}}> | ||||||
|  |         <FormControlLabel | ||||||
|  |           labelPlacement="end" | ||||||
|  |           label={"Bild"} | ||||||
|  |           control={ | ||||||
|  |             <Switch | ||||||
|  |               checked={this.state.checked} | ||||||
|  |               onChange={(e) => this.onChange(e.target.checked)} | ||||||
|  |               color="primary" | ||||||
|  |             /> | ||||||
|  |           } | ||||||
|  |         /> | ||||||
|  |         {this.state.checked ? | ||||||
|  |           <div> | ||||||
|  |             {!this.props.error ? | ||||||
|  |               <div> | ||||||
|  |                 <FormHelperText style={{lineHeight: 'initial', marginBottom: '10px'}}>{`Beachte, dass das Foto zusätzlich in den Ordner public/media/tutorial unter dem Namen '${this.props.value}' abgespeichert werden muss.`}</FormHelperText> | ||||||
|  |                 <img src={this.props.url ? this.props.url : `/media/tutorial/${this.props.value}`} alt={this.props.url ? '' : `Das Bild '${this.props.value}' konnte nicht im Ordner public/media/tutorial gefunden werden und kann daher nicht angezeigt werden.`} style={{maxHeight: '180px', maxWidth: '360px', marginBottom: '5px'}}/> | ||||||
|  |               </div> | ||||||
|  |             : <div | ||||||
|  |                 style={{height: '150px', maxWidth: '250px', marginBottom: '5px', justifyContent: "center", alignItems: "center", display:"flex", padding: '20px'}} | ||||||
|  |                 className={this.props.error ? this.props.classes.errorBorder : null} > | ||||||
|  |                 {this.props.error ? | ||||||
|  |                   this.state.error ? | ||||||
|  |                     <FormHelperText style={{lineHeight: 'initial', textAlign: 'center'}} className={this.props.classes.errorColor}>{`Die übergebene Datei entspricht nicht dem geforderten Bild-Format. Überprüfe, ob es sich um ein Bild handelt und versuche es nochmal.`}</FormHelperText> | ||||||
|  |                   : <FormHelperText style={{lineHeight: 'initial', textAlign: 'center'}} className={this.props.classes.errorColor}>{`Wähle ein Bild aus.`}</FormHelperText> | ||||||
|  |                 : null} | ||||||
|  |               </div>} | ||||||
|  |             {/*upload picture*/} | ||||||
|  |             <div ref={this.inputRef}> | ||||||
|  |               <input | ||||||
|  |                 style={{display: 'none'}} | ||||||
|  |                 accept="image/*" | ||||||
|  |                 onChange={(e) => {this.uploadPicture(e.target.files[0])}} | ||||||
|  |                 id="picture" | ||||||
|  |                 type="file" | ||||||
|  |               /> | ||||||
|  |               <label htmlFor="picture"> | ||||||
|  |                 <Button component="span" className={this.props.error ? this.props.classes.errorButton : null} style={{marginRight: '10px', marginBottom: '10px'}} variant='contained' color='primary'>Bild auswählen</Button> | ||||||
|  |               </label> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         : null} | ||||||
|  |       </div> | ||||||
|  |     ); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Picture.propTypes = { | ||||||
|  |   changeContent: PropTypes.func.isRequired, | ||||||
|  |   deleteProperty: PropTypes.func.isRequired, | ||||||
|  |   setError: PropTypes.func.isRequired, | ||||||
|  |   deleteError: PropTypes.func.isRequired, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export default connect(null, { changeContent, deleteProperty, setError, deleteError })(withStyles(styles, {withTheme: true})(Picture)); | ||||||
| @ -10,6 +10,7 @@ import StepType from './StepType'; | |||||||
| import BlocklyExample from './BlocklyExample'; | import BlocklyExample from './BlocklyExample'; | ||||||
| import Requirements from './Requirements'; | import Requirements from './Requirements'; | ||||||
| import Hardware from './Hardware'; | import Hardware from './Hardware'; | ||||||
|  | import Picture from './Picture'; | ||||||
| 
 | 
 | ||||||
| import { withStyles } from '@material-ui/core/styles'; | import { withStyles } from '@material-ui/core/styles'; | ||||||
| import Typography from '@material-ui/core/Typography'; | import Typography from '@material-ui/core/Typography'; | ||||||
| @ -103,6 +104,9 @@ class Step extends Component { | |||||||
|                 <Hardware value={this.props.step.hardware ? this.props.step.hardware : []} index={index} error={this.props.error.steps[index].hardware}/> |                 <Hardware value={this.props.step.hardware ? this.props.step.hardware : []} index={index} error={this.props.error.steps[index].hardware}/> | ||||||
|               </div> |               </div> | ||||||
|             : null} |             : null} | ||||||
|  |             {this.props.step.type === 'instruction' ? | ||||||
|  |               <Picture value={this.props.step.picture} url={this.props.step.url} index={index} error={this.props.error.steps[index].picture} /> | ||||||
|  |             : null} | ||||||
|             <BlocklyExample value={this.props.step.xml} index={index} task={this.props.step.type === 'task'} error={this.props.error.steps[index].xml ? true : false}/> |             <BlocklyExample value={this.props.step.xml} index={index} task={this.props.step.type === 'task'} error={this.props.error.steps[index].xml ? true : false}/> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  | |||||||
| @ -63,8 +63,8 @@ | |||||||
|                 "id": 3, |                 "id": 3, | ||||||
|                 "type": "instruction", |                 "type": "instruction", | ||||||
|                 "headline": "Block richtig einbinden", |                 "headline": "Block richtig einbinden", | ||||||
|                 "text": "", |                 "text": "Dies ist ein Test.", | ||||||
|                 "xml": "<xml xmlns='https://developers.google.com/blockly/xml'><block type='arduino_functions' id='QWW|$jB8+*EL;}|#uA' deletable='false' x='27' y='16'><statement name='SETUP_FUNC'><block type='sensebox_wifi' id='W}P2Y^g,muH@]|@anou}'><field name='SSID'>SSID</field><field name='Password'>Password</field></block></statement></block></xml>" |                 "picture": "block_en.svg" | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 "id": 4, |                 "id": 4, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user