From c00c0949e4d2c076994d5f4bd90a34d60b5500cd Mon Sep 17 00:00:00 2001 From: Delucse <46593742+Delucse@users.noreply.github.com> Date: Fri, 4 Sep 2020 11:50:04 +0200 Subject: [PATCH 1/4] clear workspace after changing tutorial --- src/components/Tutorial/Tutorial.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/Tutorial/Tutorial.js b/src/components/Tutorial/Tutorial.js index caebc9d..1102f2a 100644 --- a/src/components/Tutorial/Tutorial.js +++ b/src/components/Tutorial/Tutorial.js @@ -1,5 +1,7 @@ import React, { Component } from 'react'; +import * as Blockly from 'blockly/core'; + import Breadcrumbs from '../Breadcrumbs'; import StepperHorizontal from './StepperHorizontal'; import StepperVertical from './StepperVertical'; @@ -8,6 +10,7 @@ import CodeViewer from '../CodeViewer'; import NotFound from '../NotFound'; import tutorials from './tutorials.json'; +import { initialXml } from '../Blockly/initialXml.js'; import Tabs from '@material-ui/core/Tabs'; import Tab from '@material-ui/core/Tab'; @@ -23,7 +26,14 @@ class Tutorial extends Component { componentDidUpdate(props, state){ if(state.tutorialId !== Number(this.props.match.params.tutorialId)){ - this.setState({tutorialId: Number(this.props.match.params.tutorialId)}) + this.setState({tutorialId: Number(this.props.match.params.tutorialId)}); + // clear workspace + const workspace = Blockly.getMainWorkspace(); + Blockly.Events.disable(); // https://groups.google.com/forum/#!topic/blockly/m7e3g0TC75Y + // if events are disabled, then the workspace will be cleared AND the blocks are not in the trashcan + const xmlDom = Blockly.Xml.textToDom(initialXml) + Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom, workspace); + Blockly.Events.enable(); } } From 1586e74cdca7673b6c708b3f89820a9c6b3fe822 Mon Sep 17 00:00:00 2001 From: Delucse <46593742+Delucse@users.noreply.github.com> Date: Fri, 4 Sep 2020 15:06:55 +0200 Subject: [PATCH 2/4] checked if wifi is initialized --- src/components/Compile.js | 4 +- src/components/Tutorial/SolutionCheck.js | 98 ++++++++++++++ src/components/Tutorial/StepperHorizontal.js | 2 +- src/components/Tutorial/StepperVertical.js | 4 +- src/components/Tutorial/Tutorial.js | 6 +- src/components/Tutorial/TutorialHome.js | 2 +- src/components/Tutorial/tutorials.js | 128 +++++++++++++++++++ src/components/Tutorial/tutorials.json | 74 ----------- 8 files changed, 236 insertions(+), 82 deletions(-) create mode 100644 src/components/Tutorial/SolutionCheck.js create mode 100644 src/components/Tutorial/tutorials.js delete mode 100644 src/components/Tutorial/tutorials.json diff --git a/src/components/Compile.js b/src/components/Compile.js index 1d0f15c..a6c86c2 100644 --- a/src/components/Compile.js +++ b/src/components/Compile.js @@ -62,7 +62,7 @@ class Compile extends Component { return (
@@ -70,7 +70,7 @@ class Compile extends Component { Fehler - Etwas ist beim Compilieren schief gelaufen. Versuche es nochmal. + Etwas ist beim Kompilieren schief gelaufen. Versuche es nochmal. +
+ : null} + + + + + + + : null + ); + }; +} + +export default withRouter(withStyles(styles, {withTheme: true})(SolutionCheck)); diff --git a/src/components/Tutorial/StepperHorizontal.js b/src/components/Tutorial/StepperHorizontal.js index 510d326..6dc9dfc 100644 --- a/src/components/Tutorial/StepperHorizontal.js +++ b/src/components/Tutorial/StepperHorizontal.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import { withRouter } from 'react-router-dom'; -import tutorials from './tutorials.json'; +import { tutorials } from './tutorials'; import { fade } from '@material-ui/core/styles/colorManipulator'; import { withStyles } from '@material-ui/core/styles'; diff --git a/src/components/Tutorial/StepperVertical.js b/src/components/Tutorial/StepperVertical.js index 6d25d1b..c8772a9 100644 --- a/src/components/Tutorial/StepperVertical.js +++ b/src/components/Tutorial/StepperVertical.js @@ -4,7 +4,7 @@ import { withRouter, Link } from 'react-router-dom'; import clsx from 'clsx'; -import tutorials from './tutorials.json'; +import { tutorials } from './tutorials'; import { fade } from '@material-ui/core/styles/colorManipulator'; import { withStyles } from '@material-ui/core/styles'; @@ -123,7 +123,7 @@ class StepperVertical extends Component { > {'<'} -
+
- + + diff --git a/src/components/Tutorial/TutorialHome.js b/src/components/Tutorial/TutorialHome.js index 323f5f1..7c6c1f7 100644 --- a/src/components/Tutorial/TutorialHome.js +++ b/src/components/Tutorial/TutorialHome.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import Breadcrumbs from '../Breadcrumbs'; -import tutorials from './tutorials.json'; +import { tutorials } from './tutorials'; import { Link } from 'react-router-dom'; diff --git a/src/components/Tutorial/tutorials.js b/src/components/Tutorial/tutorials.js new file mode 100644 index 0000000..988c150 --- /dev/null +++ b/src/components/Tutorial/tutorials.js @@ -0,0 +1,128 @@ +export const tutorials = [ + { + "title": "erste Schritte" + }, + { + "title": "WLAN", + "test": function(workspace){ + var wifi = workspace.getBlocksByType('sensebox_wifi'); // result is an array with Blocks as objects + if(wifi.length > 0){ + var wifiBlock = wifi[wifi.length-1] // first block is probably overwritten + if(wifiBlock.getRootBlock().type === 'sensebox_wifi'){ + return {text: 'Block, um eine WLAN-Verbindung herzustellen, ist nicht verbunden.', type: 'error'} + } + if(!wifiBlock.getFieldValue('SSID')){ + return {text: 'Die SSID-Angabe fehlt.', type: 'error'} + } + if(!wifiBlock.getFieldValue('Password')){ + return {text: 'Die Angabe des Passworts fehlt.', type: 'error'} + } + return {text: 'Super. Alles richtig!', type: 'success'} + } + else { + return {text: 'Der Block, um eine WLAN-Verbindung herzustellen, fehlt.', type: 'error'} + } + } + }, + { + "title": "spezifisches WLAN", + "test": function(workspace){ + var wifi = workspace.getBlocksByType('sensebox_wifi'); // result is an array with Blocks as objects + if(wifi.length > 0){ + var wifiBlock = wifi[wifi.length-1] // first block is probably overwritten + if(wifiBlock.getRootBlock().type === 'sensebox_wifi'){ + return {text: 'Block, um eine WLAN-Verbindung herzustellen, ist nicht verbunden.', type: 'error'} + } + var ssid = wifiBlock.getFieldValue('SSID'); + if(ssid){ + if(ssid !== 'SSID'){ + return {text: 'SSID muss als Angabe "SSID" haben.', type: 'error'} + } + } + else{ + return {text: 'Die SSID-Angabe fehlt.', type: 'error'} + } + var password = wifiBlock.getFieldValue('Password') + if(password !== 'Passwort'){ + return {text: 'Password muss als Angabe "Passwort" haben.', type: 'error'} + } + else{ + return {text: 'Die Angabe des Passworts fehlt.', type: 'error'} + } + return {text: 'Super. Alles richtig!', type: 'success'} + } + else { + return {text: 'Der Block, um eine WLAN-Verbindung herzustellen, fehlt.', type: 'error'} + } + } + }, + { + "title": "if-Bedingung" + }, + { + "title": "for-Schleife" + }, + { + "title": "erste Schritte" + }, + { + "title": "if-Bedingung" + }, + { + "title": "for-Schleife" + }, + { + "title": "erste Schritte" + }, + { + "title": "if-Bedingung" + }, + { + "title": "for-Schleife" + }, + { + "title": "erste Schritte" + }, + { + "title": "if-Bedingung" + }, + { + "title": "for-Schleife" + }, + { + "title": "erste Schritte" + }, + { + "title": "if-Bedingung" + }, + { + "title": "for-Schleife" + }, + { + "title": "erste Schritte" + }, + { + "title": "if-Bedingung" + }, + { + "title": "for-Schleife" + }, + { + "title": "erste Schritte" + }, + { + "title": "if-Bedingung" + }, + { + "title": "for-Schleife" + }, + { + "title": "erste Schritte" + }, + { + "title": "if-Bedingung" + }, + { + "title": "for-Schleife" + } +] diff --git a/src/components/Tutorial/tutorials.json b/src/components/Tutorial/tutorials.json deleted file mode 100644 index 04cf6cc..0000000 --- a/src/components/Tutorial/tutorials.json +++ /dev/null @@ -1,74 +0,0 @@ -[ - { - "title": "erste Schritte" - }, - { - "title": "if-Bedingung" - }, - { - "title": "for-Schleife" - }, - { - "title": "erste Schritte" - }, - { - "title": "if-Bedingung" - }, - { - "title": "for-Schleife" - }, - { - "title": "erste Schritte" - }, - { - "title": "if-Bedingung" - }, - { - "title": "for-Schleife" - }, - { - "title": "erste Schritte" - }, - { - "title": "if-Bedingung" - }, - { - "title": "for-Schleife" - }, - { - "title": "erste Schritte" - }, - { - "title": "if-Bedingung" - }, - { - "title": "for-Schleife" - }, - { - "title": "erste Schritte" - }, - { - "title": "if-Bedingung" - }, - { - "title": "for-Schleife" - }, - { - "title": "erste Schritte" - }, - { - "title": "if-Bedingung" - }, - { - "title": "for-Schleife" - }, - { - "title": "erste Schritte" - }, - { - "title": "if-Bedingung" - }, - { - "title": "for-Schleife" - } -] From 0e18c0bd23fe97b8a56ff4d2f2a3565672ce0c54 Mon Sep 17 00:00:00 2001 From: Delucse <46593742+Delucse@users.noreply.github.com> Date: Fri, 4 Sep 2020 15:15:22 +0200 Subject: [PATCH 3/4] elimination of password-bug --- src/components/Tutorial/tutorials.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Tutorial/tutorials.js b/src/components/Tutorial/tutorials.js index 988c150..d2d2a38 100644 --- a/src/components/Tutorial/tutorials.js +++ b/src/components/Tutorial/tutorials.js @@ -43,8 +43,10 @@ export const tutorials = [ return {text: 'Die SSID-Angabe fehlt.', type: 'error'} } var password = wifiBlock.getFieldValue('Password') - if(password !== 'Passwort'){ - return {text: 'Password muss als Angabe "Passwort" haben.', type: 'error'} + if(password){ + if(password !== 'Passwort'){ + return {text: 'Password muss als Angabe "Passwort" haben.', type: 'error'} + } } else{ return {text: 'Die Angabe des Passworts fehlt.', type: 'error'} From 4c66ab9173058966aedf6b394f8c7ffe41b58e1d Mon Sep 17 00:00:00 2001 From: Delucse <46593742+Delucse@users.noreply.github.com> Date: Mon, 7 Sep 2020 15:57:09 +0200 Subject: [PATCH 4/4] displaying the status of the tutorial --- src/actions/tutorialActions.js | 24 ++++++ src/actions/types.js | 5 ++ src/components/Tutorial/SolutionCheck.js | 10 ++- src/components/Tutorial/StepperHorizontal.js | 41 +++++++++- src/components/Tutorial/StepperVertical.js | 85 +++++++++++++------- src/components/Tutorial/Tutorial.js | 12 +-- src/components/Tutorial/TutorialHome.js | 77 ++++++++++++++++-- src/reducers/index.js | 4 +- src/reducers/tutorialReducer.js | 25 ++++++ 9 files changed, 229 insertions(+), 54 deletions(-) create mode 100644 src/actions/tutorialActions.js create mode 100644 src/reducers/tutorialReducer.js diff --git a/src/actions/tutorialActions.js b/src/actions/tutorialActions.js new file mode 100644 index 0000000..dd0a5d4 --- /dev/null +++ b/src/actions/tutorialActions.js @@ -0,0 +1,24 @@ +import { TUTORIAL_SUCCESS, TUTORIAL_ERROR, TUTORIAL_CHANGE } from './types'; + +import { tutorials } from '../components/Tutorial/tutorials'; + +export const tutorialChange = () => (dispatch) => { + dispatch({ + type: TUTORIAL_CHANGE + }); +}; + +export const tutorialCheck = (id, status) => (dispatch, getState) => { + var tutorialsStatus = getState().tutorial.status ? + getState().tutorial.status + : new Array(tutorials.length).fill({}); + tutorialsStatus[id].status = status; + console.log(tutorials); + dispatch({ + type: status === 'success' ? TUTORIAL_SUCCESS : TUTORIAL_ERROR, + payload: tutorialsStatus + }); + dispatch(tutorialChange()); + // update locale storage - sync with redux store + window.localStorage.setItem('tutorial', JSON.stringify(tutorialsStatus)); +}; diff --git a/src/actions/types.js b/src/actions/types.js index c0be6c2..28e5e33 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -5,3 +5,8 @@ export const MOVE_BLOCK = 'MOVE_BLOCK'; export const CHANGE_BLOCK = 'CHANGE_BLOCK'; export const DELETE_BLOCK = 'DELETE_BLOCK'; export const CLEAR_STATS = 'CLEAR_STATS'; + + +export const TUTORIAL_SUCCESS = 'TUTORIAL_SUCCESS'; +export const TUTORIAL_ERROR = 'TUTORIAL_ERROR'; +export const TUTORIAL_CHANGE = 'TUTORIAL_CHANGE'; diff --git a/src/components/Tutorial/SolutionCheck.js b/src/components/Tutorial/SolutionCheck.js index 2228570..01a9380 100644 --- a/src/components/Tutorial/SolutionCheck.js +++ b/src/components/Tutorial/SolutionCheck.js @@ -1,4 +1,7 @@ import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { tutorialCheck } from '../../actions/tutorialActions'; import * as Blockly from 'blockly/core'; @@ -49,6 +52,7 @@ class SolutionCheck extends Component { check = () => { const workspace = Blockly.getMainWorkspace(); var msg = tutorials[this.props.tutorial].test(workspace); + this.props.tutorialCheck(this.props.tutorial, msg.type); this.setState({ msg, open: true }); } @@ -95,4 +99,8 @@ class SolutionCheck extends Component { }; } -export default withRouter(withStyles(styles, {withTheme: true})(SolutionCheck)); +SolutionCheck.propTypes = { + tutorialCheck: PropTypes.func.isRequired +}; + +export default connect(null, { tutorialCheck })(withRouter(withStyles(styles, {withTheme: true})(SolutionCheck))); diff --git a/src/components/Tutorial/StepperHorizontal.js b/src/components/Tutorial/StepperHorizontal.js index 6dc9dfc..7197103 100644 --- a/src/components/Tutorial/StepperHorizontal.js +++ b/src/components/Tutorial/StepperHorizontal.js @@ -1,7 +1,11 @@ import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; +import clsx from 'clsx'; + import { tutorials } from './tutorials'; import { fade } from '@material-ui/core/styles/colorManipulator'; @@ -11,9 +15,11 @@ import Stepper from '@material-ui/core/Stepper'; import Step from '@material-ui/core/Step'; import StepLabel from '@material-ui/core/StepLabel'; +import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + const styles = (theme) => ({ stepper: { - backgroundColor: fade(theme.palette.primary.main, 0.6), width: 'calc(100% - 40px)', borderRadius: '25px', padding: '0 20px', @@ -21,8 +27,23 @@ const styles = (theme) => ({ display: 'flex', justifyContent: 'space-between' }, + stepperSuccess: { + backgroundColor: fade(theme.palette.primary.main, 0.6), + }, + stepperError: { + backgroundColor: fade(theme.palette.error.dark, 0.6), + }, + stepperOther: { + backgroundColor: fade(theme.palette.secondary.main, 0.6), + }, color: { backgroundColor: 'transparent ' + }, + iconDivSuccess: { + color: theme.palette.primary.main + }, + iconDivError: { + color: theme.palette.error.dark } }); @@ -40,8 +61,10 @@ class StepperHorizontal extends Component { render() { var tutorialId = this.state.tutorialId; + var tutorialStatus = this.props.status[tutorialId-1].status === 'success' ? 'Success' : + this.props.status[tutorialId-1].status === 'error' ? 'Error' : 'Other'; return ( -
+
: ''}>

{tutorials[tutorialId-1].title}

@@ -67,4 +90,14 @@ class StepperHorizontal extends Component { }; } -export default withRouter(withStyles(styles, {withTheme: true})(StepperHorizontal)); +StepperHorizontal.propTypes = { + status: PropTypes.array.isRequired, + change: PropTypes.number.isRequired, +}; + +const mapStateToProps = state => ({ + change: state.tutorial.change, + status: state.tutorial.status +}); + +export default connect(mapStateToProps, null)(withRouter(withStyles(styles, {withTheme: true})(StepperHorizontal))); diff --git a/src/components/Tutorial/StepperVertical.js b/src/components/Tutorial/StepperVertical.js index 34c94a8..48ea972 100644 --- a/src/components/Tutorial/StepperVertical.js +++ b/src/components/Tutorial/StepperVertical.js @@ -1,4 +1,6 @@ import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; import { withRouter, Link } from 'react-router-dom'; @@ -20,33 +22,44 @@ const styles = (theme) => ({ padding: 0, width: '30px', }, - stepIconSmall: { - border: `2px solid ${theme.palette.primary.main}`, + stepIcon: { + borderStyle: `solid`, + borderWith: '2px', borderRadius: '50%', width: '12px', height: '12px', margin: '0 auto' }, stepIconMedium: { - border: `2px solid ${theme.palette.primary.main}`, - borderRadius: '50%', width: '18px', height: '18px', - margin: '0 auto' }, stepIconLarge: { - border: `2px solid ${theme.palette.primary.main}`, - borderRadius: '50%', width: '24px', height: '24px' }, stepIconTransparent: { - border: `2px solid transparent`, + borderColor: `transparent`, cursor: 'default' }, - stepIconActive: { + stepIconSuccess: { + borderColor: theme.palette.primary.main, + }, + stepIconError: { + borderColor: theme.palette.error.dark, + }, + stepIconOther: { + borderColor: theme.palette.secondary.main, + }, + stepIconActiveSuccess: { backgroundColor: fade(theme.palette.primary.main, 0.6) }, + stepIconActiveError: { + backgroundColor: fade(theme.palette.error.dark, 0.6) + }, + stepIconActiveOther: { + backgroundColor: fade(theme.palette.secondary.main, 0.6) + }, progress: { position: 'absolute', top: 0, @@ -76,7 +89,7 @@ class StepperVertical extends Component { tutorials.slice(Number(this.props.match.params.tutorialId)-3-1, Number(this.props.match.params.tutorialId)+3) : tutorials.slice(Number(this.props.match.params.tutorialId)-2-1,Number(this.props.match.params.tutorialId)+2), tutorialId: Number(this.props.match.params.tutorialId), - verticalTutorialId: Number(this.props.match.params.tutorialId) + selectedVerticalTutorialId: Number(this.props.match.params.tutorialId) } componentDidUpdate(props, state){ @@ -92,13 +105,13 @@ class StepperVertical extends Component { tutorials.slice(Number(this.props.match.params.tutorialId)-3-1, Number(this.props.match.params.tutorialId)+3) : tutorials.slice(Number(this.props.match.params.tutorialId)-2-1,Number(this.props.match.params.tutorialId)+2), tutorialId: Number(this.props.match.params.tutorialId), - verticalTutorialId: Number(this.props.match.params.tutorialId) + selectedVerticalTutorialId: Number(this.props.match.params.tutorialId) }) } } verticalStepper = (step) => { - var newTutorialId = this.state.verticalTutorialId + step; + var newTutorialId = this.state.selectedVerticalTutorialId + step; var tutorialArray = Number(newTutorialId) === 1 ? tutorials.slice(newTutorialId-1, newTutorialId+4) : newTutorialId === 2 ? @@ -108,18 +121,18 @@ class StepperVertical extends Component { : newTutorialId === tutorials.length-1 ? tutorials.slice(newTutorialId-3-1, newTutorialId+3) : tutorials.slice(newTutorialId-2-1, newTutorialId+2); - this.setState({ tutorialArray: tutorialArray, verticalTutorialId: newTutorialId }); + this.setState({ tutorialArray: tutorialArray, selectedVerticalTutorialId: newTutorialId }); } render() { var tutorialId = this.state.tutorialId; - var verticalTutorialId = this.state.verticalTutorialId; + var selectedVerticalTutorialId = this.state.selectedVerticalTutorialId; return ( isWidthUp('sm', this.props.width) ?