diff --git a/.env b/.env
index 9af8f29..044e061 100644
--- a/.env
+++ b/.env
@@ -2,5 +2,7 @@ REACT_APP_COMPILER_URL=https://compiler.sensebox.de
REACT_APP_BOARD=sensebox-mcu
REACT_APP_BLOCKLY_API=https://api.blockly.sensebox.de
+GENERATE_SOURCEMAP=false
+
# in days
REACT_APP_SHARE_LINK_EXPIRES=30
diff --git a/.gitignore b/.gitignore
index 273210c..f0c13ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,4 +22,4 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
-package-lock.json
+
diff --git a/package.json b/package.json
index 0a85d8e..418a0a5 100644
--- a/package.json
+++ b/package.json
@@ -1,45 +1,58 @@
{
"name": "blockly-react",
- "version": "0.1.0",
+ "version": "1.0.0",
"private": true,
"dependencies": {
- "@blockly/block-plus-minus": "^2.0.10",
- "@blockly/field-grid-dropdown": "^1.0.25",
- "@blockly/field-slider": "^2.1.1",
- "@blockly/plugin-scroll-options": "^1.0.2",
- "@blockly/plugin-typed-variable-modal": "^3.1.26",
- "@blockly/zoom-to-fit": "^2.0.7",
- "@fortawesome/fontawesome-svg-core": "^1.2.30",
- "@fortawesome/free-solid-svg-icons": "^5.14.0",
- "@fortawesome/react-fontawesome": "^0.1.11",
- "@material-ui/core": "^4.11.0",
- "@sentry/react": "^6.0.0",
- "@sentry/tracing": "^6.0.0",
+ "@blockly/block-plus-minus": "^3.0.5",
+ "@blockly/field-grid-dropdown": "^1.0.31",
+ "@blockly/field-slider": "^3.0.5",
+ "@blockly/plugin-scroll-options": "^2.0.5",
+ "@blockly/plugin-typed-variable-modal": "^4.0.5",
+ "@blockly/workspace-backpack": "^2.0.12",
+ "@blockly/zoom-to-fit": "^2.0.14",
+ "@fortawesome/fontawesome-svg-core": "^1.2.36",
+ "@fortawesome/free-solid-svg-icons": "^5.15.4",
+ "@fortawesome/react-fontawesome": "^0.1.19",
+ "@material-ui/core": "^4.12.4",
+ "@monaco-editor/react": "^4.3.1",
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^7.2.1",
"axios": "^0.22.0",
- "blockly": "^6.20210701.0",
- "file-saver": "^2.0.2",
+ "blockly": "^8.0.3",
+ "file-saver": "^2.0.5",
+ "markdown-it": "^12.3.2",
"mnemonic-id": "^3.2.7",
- "moment": "^2.28.0",
+ "moment": "^2.29.4",
"prismjs": "^1.27.0",
+ "qrcode.react": "^3.1.0",
"react": "^17.0.2",
- "react-cookie-consent": "^7.0.0",
+ "react-cookie-consent": "^7.2.1",
"react-dom": "^17.0.2",
- "react-markdown": "^5.0.2",
+ "react-markdown": "^8.0.0",
+ "react-markdown-editor-lite": "^1.3.3",
"react-mde": "^11.5.0",
- "react-redux": "^7.2.4",
- "react-router-dom": "^5.2.0",
- "react-scripts": "^4.0.3",
- "reactour": "^1.18.0",
- "redux": "^4.0.5",
- "redux-thunk": "^2.3.0",
+ "react-rating-stars-component": "^2.2.0",
+ "react-redux": "^7.2.9",
+ "react-router-dom": "^5.3.3",
+ "react-scripts": "^5.0.0",
+ "react-share": "^4.4.0",
+ "react-spinners": "^0.13.3",
+ "reactour": "^1.18.7",
+ "redux": "^4.2.0",
+ "redux-thunk": "^2.4.1",
+ "remark-gemoji": "^7.0.1",
+ "remark-gfm": "^3.0.1",
"styled-components": "^4.4.1",
- "uuid": "^8.3.1"
+ "uuid": "^8.3.1",
+ "watchpack": "^2.3.1"
+ },
+ "resolutions": {
+ "//": "See https://github.com/facebook/create-react-app/issues/11773",
+ "react-error-overlay": "6.0.9"
},
"scripts": {
- "start": "react-scripts start",
+ "start": "node_modules/react-scripts/bin/react-scripts.js start",
"dev": "set \"REACT_APP_BLOCKLY_API=http://localhost:8080\" && npm start",
"build": "react-scripts build",
"test": "react-scripts test",
@@ -48,16 +61,9 @@
"eslintConfig": {
"extends": "react-app"
},
- "browserslist": {
- "production": [
- ">0.2%",
- "not dead",
- "not op_mini all"
- ],
- "development": [
- "last 1 chrome version",
- "last 1 firefox version",
- "last 1 safari version"
- ]
- }
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ]
}
diff --git a/public/media/hardware/senseboxmcumini.png b/public/media/hardware/senseboxmcumini.png
new file mode 100644
index 0000000..220caa6
Binary files /dev/null and b/public/media/hardware/senseboxmcumini.png differ
diff --git a/src/App.css b/src/App.css
index 1a8a13d..8457170 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,51 +1,64 @@
.wrapper {
- min-height: calc(100vh - 60px); /* will cover the 100% of viewport - height of footer (padding-bottom) */
+ min-height: calc(
+ 100vh - 60px
+ ); /* will cover the 100% of viewport - height of footer (padding-bottom) */
overflow: hidden;
display: block;
position: relative;
padding-bottom: 60px; /* height of your footer + 30px*/
}
-
-.tutorial img{
+.tutorial img {
display: flex;
align-items: center;
- max-height: 40vH;
+ max-height: 40vh;
max-width: 100%;
margin: auto;
-}
-
-.news img{
- display: flex;
- align-items: center;
- max-height: 40vH;
- max-width: 100%;
- margin: auto;
-}
-
-.tutorial blockquote{
- background: #f9f9f9;
- border-left: 10px solid#4EAF47;
- margin: 1.5em 10px;
- padding: 0.5em 10px;
- quotes: "\201C""\201D""\2018""\2019";
- }
- blockquote:before {
- color:#4EAF47;
- content: open-quote;
- font-size: 4em;
- line-height: 0.1em;
- margin-right: 0.25em;
- vertical-align: -0.4em;
- }
- blockquote p {
- display: inline;
- }
-
-.overlay {
- display: flex;
- flex-direction: column;
- align-items: center;
}
-
+.news img {
+ display: flex;
+ align-items: center;
+ max-height: 40vh;
+ max-width: 100%;
+ margin: auto;
+}
+
+.tutorial blockquote {
+ background: #f9f9f9;
+ border-left: 10px solid#4EAF47;
+ margin: 1.5em 10px;
+ padding: 0.5em 10px;
+ quotes: "\201C""\201D""\2018""\2019";
+}
+blockquote:before {
+ color: #4eaf47;
+ content: open-quote;
+ font-size: 4em;
+ line-height: 0.1em;
+ margin-right: 0.25em;
+ vertical-align: -0.4em;
+}
+blockquote p {
+ display: inline;
+}
+
+.tutorial table,
+th,
+td {
+ border: 1px solid #ddd;
+}
+
+.tutorial th {
+ padding-top: 12px;
+ padding-bottom: 12px;
+ text-align: left;
+ background-color: #4eaf47;
+ color: white;
+}
+
+.overlay {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
diff --git a/src/App.js b/src/App.js
index ef9ca04..29289e5 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,35 +1,34 @@
-import React, { Component } from 'react';
+import React, { Component } from "react";
-import { BrowserRouter as Router } from 'react-router-dom';
+import { Router } from "react-router-dom";
import { createBrowserHistory } from "history";
-import { Provider } from 'react-redux';
-import store from './store';
-import { loadUser } from './actions/authActions';
+import { Provider } from "react-redux";
+import store from "./store";
+import { loadUser } from "./actions/authActions";
-import './App.css';
+import "./App.css";
-import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles';
+import { ThemeProvider, createTheme } from "@material-ui/core/styles";
-import Content from './components/Content';
+import Content from "./components/Content";
-const theme = createMuiTheme({
+const theme = createTheme({
palette: {
primary: {
- main: '#4EAF47',
- contrastText: '#ffffff'
+ main: "#4EAF47",
+ contrastText: "#ffffff",
},
secondary: {
- main: '#DDDDDD'
+ main: "#DDDDDD",
},
button: {
- compile: '#e27136'
- }
- }
+ compile: "#e27136",
+ },
+ },
});
class App extends Component {
-
componentDidMount() {
store.dispatch(loadUser());
}
diff --git a/src/actions/authActions.js b/src/actions/authActions.js
index 04a2265..086d703 100644
--- a/src/actions/authActions.js
+++ b/src/actions/authActions.js
@@ -63,63 +63,56 @@ export const loadUser = () => (dispatch) => {
});
};
-var logoutTimerId;
-const timeToLogout = 14.9 * 60 * 1000; // nearly 15 minutes corresponding to the API
-
// Login user
export const login =
({ email, password }) =>
- (dispatch) => {
- dispatch({
- type: USER_LOADING,
- });
- // Headers
- const config = {
- headers: {
- "Content-Type": "application/json",
- },
- };
- // Request Body
- const body = JSON.stringify({ email, password });
- axios
- .post(`${process.env.REACT_APP_BLOCKLY_API}/user`, body, config)
- .then((res) => {
- // Logout automatically if refreshToken "expired"
- const logoutTimer = () =>
- setTimeout(() => dispatch(logout()), timeToLogout);
- logoutTimerId = logoutTimer();
- dispatch(setLanguage(res.data.user.language));
- dispatch({
- type: LOGIN_SUCCESS,
- payload: res.data,
- });
- dispatch({
- type: GET_STATUS,
- payload: res.data.user.status,
- });
- dispatch(returnSuccess(res.data.message, res.status, "LOGIN_SUCCESS"));
- })
- .catch((err) => {
- dispatch(
- returnErrors(
- err.response.data.message,
- err.response.status,
- "LOGIN_FAIL"
- )
- );
- dispatch({
- type: LOGIN_FAIL,
- });
- var status = [];
- if (window.localStorage.getItem("status")) {
- status = JSON.parse(window.localStorage.getItem("status"));
- }
- dispatch({
- type: GET_STATUS,
- payload: status,
- });
+ (dispatch) => {
+ dispatch({
+ type: USER_LOADING,
});
- };
+ // Headers
+ const config = {
+ headers: {
+ "Content-Type": "application/json",
+ },
+ };
+ // Request Body
+ const body = JSON.stringify({ email, password });
+ axios
+ .post(`${process.env.REACT_APP_BLOCKLY_API}/user`, body, config)
+ .then((res) => {
+ dispatch(setLanguage(res.data.user.language));
+ dispatch({
+ type: LOGIN_SUCCESS,
+ payload: res.data,
+ });
+ dispatch({
+ type: GET_STATUS,
+ payload: res.data.user.status,
+ });
+ dispatch(returnSuccess(res.data.message, res.status, "LOGIN_SUCCESS"));
+ })
+ .catch((err) => {
+ dispatch(
+ returnErrors(
+ err.response.data.message,
+ err.response.status,
+ "LOGIN_FAIL"
+ )
+ );
+ dispatch({
+ type: LOGIN_FAIL,
+ });
+ var status = [];
+ if (window.localStorage.getItem("status")) {
+ status = JSON.parse(window.localStorage.getItem("status"));
+ }
+ dispatch({
+ type: GET_STATUS,
+ payload: status,
+ });
+ });
+ };
// Logout User
export const logout = () => (dispatch) => {
@@ -144,7 +137,6 @@ export const logout = () => (dispatch) => {
}
dispatch(setLanguage(locale));
dispatch(returnSuccess(res.data.message, res.status, "LOGOUT_SUCCESS"));
- clearTimeout(logoutTimerId);
},
error: (err) => {
dispatch(
@@ -165,7 +157,6 @@ export const logout = () => (dispatch) => {
type: GET_STATUS,
payload: status,
});
- clearTimeout(logoutTimerId);
},
};
axios
@@ -222,10 +213,6 @@ export const authInterceptor = () => (dispatch, getState) => {
})
.then((res) => {
if (res.status === 200) {
- clearTimeout(logoutTimerId);
- const logoutTimer = () =>
- setTimeout(() => dispatch(logout()), timeToLogout);
- logoutTimerId = logoutTimer();
dispatch({
type: REFRESH_TOKEN_SUCCESS,
payload: res.data,
diff --git a/src/actions/boardAction.js b/src/actions/boardAction.js
new file mode 100644
index 0000000..f78cb82
--- /dev/null
+++ b/src/actions/boardAction.js
@@ -0,0 +1,10 @@
+import {
+ BOARD,
+ } from "./types";
+
+export const setBoard = (board) => (dispatch) => {
+ dispatch({
+ type: BOARD,
+ payload: board,
+ });
+ };
\ No newline at end of file
diff --git a/src/actions/tutorialActions.js b/src/actions/tutorialActions.js
index 372697a..226bba6 100644
--- a/src/actions/tutorialActions.js
+++ b/src/actions/tutorialActions.js
@@ -84,6 +84,77 @@ export const getTutorials = () => (dispatch, getState) => {
});
};
+export const getAllTutorials = () => (dispatch, getState) => {
+ axios
+ .get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/getAllTutorials`)
+ .then((res) => {
+ var tutorials = res.data.tutorials;
+ existingTutorials(tutorials, getState().tutorial.status).then(
+ (status) => {
+ dispatch({
+ type: TUTORIAL_SUCCESS,
+ payload: status,
+ });
+ dispatch(updateStatus(status));
+ dispatch({
+ type: GET_TUTORIALS,
+ payload: tutorials,
+ });
+ dispatch({ type: TUTORIAL_PROGRESS });
+ dispatch(returnSuccess(res.data.message, res.status));
+ }
+ );
+ })
+ .catch((err) => {
+ if (err.response) {
+ dispatch(
+ returnErrors(
+ err.response.data.message,
+ err.response.status,
+ "GET_TUTORIALS_FAIL"
+ )
+ );
+ }
+ dispatch({ type: TUTORIAL_PROGRESS });
+ });
+};
+
+export const getUserTutorials = () => (dispatch, getState) => {
+ axios
+ .get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/getUserTutorials`)
+ .then((res) => {
+ var tutorials = res.data.tutorials;
+ existingTutorials(tutorials, getState().tutorial.status).then(
+ (status) => {
+ dispatch({
+ type: TUTORIAL_SUCCESS,
+ payload: status,
+ });
+ dispatch(updateStatus(status));
+ dispatch({
+ type: GET_TUTORIALS,
+ payload: tutorials,
+ });
+ dispatch({ type: TUTORIAL_PROGRESS });
+ dispatch(returnSuccess(res.data.message, res.status));
+ }
+ );
+ })
+ .catch((err) => {
+ console.log(err);
+ if (err.response) {
+ dispatch(
+ returnErrors(
+ err.response.data.message,
+ err.response.status,
+ "GET_TUTORIALS_FAIL"
+ )
+ );
+ }
+ dispatch({ type: TUTORIAL_PROGRESS });
+ });
+};
+
export const updateStatus = (status) => (dispatch, getState) => {
if (getState().auth.isAuthenticated) {
// update user account in database - sync with redux store
diff --git a/src/actions/tutorialBuilderActions.js b/src/actions/tutorialBuilderActions.js
index 72d7f3e..cb4e798 100644
--- a/src/actions/tutorialBuilderActions.js
+++ b/src/actions/tutorialBuilderActions.js
@@ -4,6 +4,9 @@ import {
BUILDER_CHANGE,
BUILDER_ERROR,
BUILDER_TITLE,
+ BUILDER_PUBLIC,
+ BUILDER_DIFFICULTY,
+ BUILDER_REVIEW,
BUILDER_ID,
BUILDER_ADD_STEP,
BUILDER_DELETE_STEP,
@@ -35,6 +38,30 @@ export const tutorialTitle = (title) => (dispatch) => {
dispatch(changeTutorialBuilder());
};
+export const tutorialPublic = (pub) => (dispatch) => {
+ dispatch({
+ type: BUILDER_PUBLIC,
+ payload: pub,
+ });
+ dispatch(changeTutorialBuilder());
+};
+
+export const tutorialDifficulty = (difficulty) => (dispatch) => {
+ dispatch({
+ type: BUILDER_DIFFICULTY,
+ payload: difficulty,
+ });
+ dispatch(changeTutorialBuilder());
+};
+
+export const tutorialReview = (review) => (dispatch) => {
+ dispatch({
+ type: BUILDER_REVIEW,
+ payload: review,
+ });
+ dispatch(changeTutorialBuilder());
+};
+
export const tutorialSteps = (steps) => (dispatch) => {
dispatch({
type: BUILDER_ADD_STEP,
@@ -320,6 +347,7 @@ export const readJSON = (json) => (dispatch, getState) => {
return object;
});
dispatch(tutorialTitle(json.title));
+ dispatch(tutorialDifficulty(json.difficulty));
dispatch(tutorialSteps(steps));
dispatch(setSubmitError());
dispatch(progress(false));
diff --git a/src/actions/types.js b/src/actions/types.js
index 6a10987..c67ad2a 100644
--- a/src/actions/types.js
+++ b/src/actions/types.js
@@ -21,6 +21,7 @@ export const NAME = "NAME";
export const TUTORIAL_PROGRESS = "TUTORIAL_PROGRESS";
export const GET_TUTORIAL = "GET_TUTORIAL";
export const GET_TUTORIALS = "GET_TUTORIALS";
+export const GET_USERTUTORIALS = "GET_USERTUTORIALS";
export const GET_STATUS = "GET_STATUS";
export const TUTORIAL_SUCCESS = "TUTORIAL_SUCCESS";
export const TUTORIAL_ERROR = "TUTORIAL_ERROR";
@@ -32,6 +33,9 @@ export const JSON_STRING = "JSON_STRING";
export const BUILDER_CHANGE = "BUILDER_CHANGE";
export const BUILDER_TITLE = "BUILDER_TITLE";
+export const BUILDER_DIFFICULTY = "BUILDER_DIFFICULTY";
+export const BUILDER_PUBLIC = "BUILDER_PUBLIC";
+export const BUILDER_REVIEW = "BUILDER_REVIEW";
export const BUILDER_ID = "BUILDER_ID";
export const BUILDER_ADD_STEP = "BUILDER_ADD_STEP";
export const BUILDER_DELETE_STEP = "BUILDER_DELETE_STEP";
@@ -59,3 +63,6 @@ export const GET_PROJECT = "GET_PROJECT";
export const GET_PROJECTS = "GET_PROJECTS";
export const PROJECT_TYPE = "PROJECT_TYPE";
export const PROJECT_DESCRIPTION = "PROJECT_DESCRIPTION";
+
+//board
+export const BOARD = "BOARD";
diff --git a/src/components/Alert.js b/src/components/Alert.js
index ce10c87..01bac66 100644
--- a/src/components/Alert.js
+++ b/src/components/Alert.js
@@ -1,34 +1,29 @@
-import React, { Component } from 'react';
+import React, { Component } from "react";
-import { withStyles } from '@material-ui/core/styles';
-import { fade } from '@material-ui/core/styles/colorManipulator';
+import { withStyles } from "@material-ui/core/styles";
+import { alpha } from "@material-ui/core/styles";
-import Typography from '@material-ui/core/Typography';
+import Typography from "@material-ui/core/Typography";
const styles = (theme) => ({
alert: {
- marginBottom: '20px',
+ marginBottom: "20px",
border: `1px solid ${theme.palette.primary.main}`,
- padding: '10px 20px',
- borderRadius: '4px',
- background: fade(theme.palette.primary.main, 0.3),
- color: 'rgb(70,70,70)'
- }
+ padding: "10px 20px",
+ borderRadius: "4px",
+ background: alpha(theme.palette.primary.main, 0.3),
+ color: "rgb(70,70,70)",
+ },
});
-
export class Alert extends Component {
-
- render(){
- return(
+ render() {
+ return (
-
- {this.props.children}
-
+ {this.props.children}
);
}
}
-
export default withStyles(styles, { withTheme: true })(Alert);
diff --git a/src/components/Blockly/BlocklyWindow.js b/src/components/Blockly/BlocklyWindow.js
index c69fc9d..bbab82d 100644
--- a/src/components/Blockly/BlocklyWindow.js
+++ b/src/components/Blockly/BlocklyWindow.js
@@ -12,6 +12,7 @@ import "./generator/index";
import { ZoomToFitControl } from "@blockly/zoom-to-fit";
import { initialXml } from "./initialXml.js";
import { getMaxInstances } from "./helpers/maxInstances";
+import { Backpack } from "@blockly/workspace-backpack";
class BlocklyWindow extends Component {
constructor(props) {
@@ -35,12 +36,23 @@ class BlocklyWindow extends Component {
Blockly.svgResize(workspace);
const zoomToFit = new ZoomToFitControl(workspace);
zoomToFit.init();
+ // Initialize plugin.
+ const backpack = new Backpack(workspace);
+ backpack.init();
}
componentDidUpdate(props) {
const workspace = Blockly.getMainWorkspace();
-
var xml = this.props.initialXml;
+ if (props.selectedBoard !== this.props.selectedBoard) {
+ // change board
+ if(!xml) xml = initialXml;
+ var xmlDom = Blockly.Xml.textToDom(xml);
+ Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom, workspace);
+ // var toolbox = workspace.getToolbox();
+ // workspace.updateToolbox(toolbox.toolboxDef_);
+ }
+
// if svg is true, then the update process is done in the BlocklySvg component
if (props.initialXml !== xml && !this.props.svg) {
// guarantees that the current xml-code (this.props.initialXml) is rendered
@@ -51,7 +63,7 @@ class BlocklyWindow extends Component {
if (props.language !== this.props.language) {
// change language
if (!xml) xml = initialXml;
- var xmlDom = Blockly.Xml.textToDom(xml);
+ xmlDom = Blockly.Xml.textToDom(xml);
Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom, workspace);
// var toolbox = workspace.getToolbox();
// workspace.updateToolbox(toolbox.toolboxDef_);
@@ -124,14 +136,16 @@ BlocklyWindow.propTypes = {
onChangeWorkspace: PropTypes.func.isRequired,
clearStats: PropTypes.func.isRequired,
renderer: PropTypes.string.isRequired,
- sounds: PropTypes.string.isRequired,
+ sounds: PropTypes.bool.isRequired,
language: PropTypes.string.isRequired,
+ selectedBoard: PropTypes.string.isRequired,
};
const mapStateToProps = (state) => ({
renderer: state.general.renderer,
sounds: state.general.sounds,
language: state.general.language,
+ selectedBoard: state.board.board,
});
export default connect(mapStateToProps, { onChangeWorkspace, clearStats })(
diff --git a/src/components/Blockly/blocks/sensebox-led.js b/src/components/Blockly/blocks/sensebox-led.js
index 981133e..6810fcb 100644
--- a/src/components/Blockly/blocks/sensebox-led.js
+++ b/src/components/Blockly/blocks/sensebox-led.js
@@ -45,15 +45,11 @@ Blockly.Blocks['sensebox_rgb_led'] = {
Blockly.Blocks['sensebox_ws2818_led_init'] = {
init: function () {
-
- var dropdownOptions = [[Blockly.Msg.senseBox_ultrasonic_port_A, '1'],
- [Blockly.Msg.senseBox_ultrasonic_port_B, '3'], [Blockly.Msg.senseBox_ultrasonic_port_C, '5']];
-
this.setColour(getColour().sensebox);
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_ws2818_rgb_led_init)
.appendField("Port:")
- .appendField(new Blockly.FieldDropdown(dropdownOptions), "Port")
+ .appendField(new Blockly.FieldDropdown(selectedBoard().digitalPinsRGB), "Port")
this.appendValueInput("BRIGHTNESS", "brightness")
.appendField((Blockly.Msg.senseBox_ws2818_rgb_led_brightness));
this.appendValueInput("NUMBER", "number")
@@ -66,15 +62,11 @@ Blockly.Blocks['sensebox_ws2818_led_init'] = {
Blockly.Blocks['sensebox_ws2818_led'] = {
init: function () {
-
- var dropdownOptions = [[Blockly.Msg.senseBox_ultrasonic_port_A, '1'],
- [Blockly.Msg.senseBox_ultrasonic_port_B, '3'], [Blockly.Msg.senseBox_ultrasonic_port_C, '5']];
-
this.setColour(getColour().sensebox);
this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_ws2818_rgb_led)
.appendField("Port:")
- .appendField(new Blockly.FieldDropdown(dropdownOptions), "Port")
+ .appendField(new Blockly.FieldDropdown(selectedBoard().digitalPinsRGB), "Port")
this.appendValueInput("POSITION", "position")
.appendField((Blockly.Msg.senseBox_ws2818_rgb_led_position));
this.appendValueInput("COLOR", 'Number')
diff --git a/src/components/Blockly/blocks/sensebox-sensors.js b/src/components/Blockly/blocks/sensebox-sensors.js
index 145a5e1..12929a7 100644
--- a/src/components/Blockly/blocks/sensebox-sensors.js
+++ b/src/components/Blockly/blocks/sensebox-sensors.js
@@ -114,10 +114,8 @@ Blockly.Blocks["sensebox_sensor_sds011"] = {
)
.appendField(Blockly.Msg.senseBox_sds011_dimension)
.appendField(
- new Blockly.FieldDropdown([
- [Blockly.Msg.senseBox_sds011_serial1, "Serial1"],
- [Blockly.Msg.senseBox_sds011_serial2, "Serial2"],
- ]),
+ new Blockly.FieldDropdown(
+ selectedBoard().serial),
"SERIAL"
);
this.setOutput(true, Types.DECIMAL.typeName);
diff --git a/src/components/Blockly/generator/generator.js b/src/components/Blockly/generator/generator.js
index 9217522..599f022 100644
--- a/src/components/Blockly/generator/generator.js
+++ b/src/components/Blockly/generator/generator.js
@@ -148,53 +148,7 @@ Blockly["Arduino"].init = function (workspace) {
// Blockly.Names.DEVELOPER_VARIABLE_TYPE));
// }
- const doubleVariables = workspace.getVariablesOfType("Number");
- let i = 0;
- let variableCode = "";
- for (i = 0; i < doubleVariables.length; i += 1) {
- variableCode +=
- "double " +
- Blockly["Arduino"].nameDB_.getName(
- doubleVariables[i].getId(),
- Blockly.Variables.NAME_TYPE
- ) +
- " = 0; \n\n";
- }
- const stringVariables = workspace.getVariablesOfType("String");
- for (i = 0; i < stringVariables.length; i += 1) {
- variableCode +=
- "String " +
- Blockly["Arduino"].nameDB_.getName(
- stringVariables[i].getId(),
- Blockly.Variables.NAME_TYPE
- ) +
- ' = ""; \n\n';
- }
-
- const booleanVariables = workspace.getVariablesOfType("Boolean");
- for (i = 0; i < booleanVariables.length; i += 1) {
- variableCode +=
- "boolean " +
- Blockly["Arduino"].nameDB_.getDistinctName(
- booleanVariables[i].getId(),
- Blockly.Variables.NAME_TYPE
- ) +
- " = false; \n\n";
- }
-
- const colourVariables = workspace.getVariablesOfType("Colour");
- for (i = 0; i < colourVariables.length; i += 1) {
- variableCode +=
- "RGB " +
- Blockly["Arduino"].nameDB_.getName(
- colourVariables[i].getId(),
- Blockly.Variables.NAME_TYPE
- ) +
- " = {0, 0, 0}; \n\n";
- }
-
- Blockly["Arduino"].variablesInitCode_ = variableCode;
};
/**
diff --git a/src/components/Blockly/generator/sensebox-ble.js b/src/components/Blockly/generator/sensebox-ble.js
index bd6ddd7..34a3a21 100644
--- a/src/components/Blockly/generator/sensebox-ble.js
+++ b/src/components/Blockly/generator/sensebox-ble.js
@@ -101,7 +101,6 @@ Blockly.Arduino.sensebox_phyphox_graph = function () {
Blockly.Arduino.sensebox_phyphox_experiment_send = function () {
var branch = Blockly.Arduino.statementToCode(this, "sendValues");
var blocks = this.getDescendants();
- console.log(blocks);
var count = 0;
if (blocks !== undefined) {
for (var i = 0; i < blocks.length; i++) {
@@ -115,7 +114,6 @@ Blockly.Arduino.sensebox_phyphox_experiment_send = function () {
var string = "";
for (var j = 1; j <= count; j++) {
- console.log("append");
if (string === "") {
string += `channel${j}`;
} else if (string !== "") {
diff --git a/src/components/Blockly/generator/sensebox-sd.js b/src/components/Blockly/generator/sensebox-sd.js
index ab54e47..419b76a 100644
--- a/src/components/Blockly/generator/sensebox-sd.js
+++ b/src/components/Blockly/generator/sensebox-sd.js
@@ -203,6 +203,5 @@ Blockly.Arduino.sensebox_sd_save_for_osem = function (block) {
Blockly.Arduino.definitions_["SENSOR_ID" + id + ""] =
"const char SENSOR_ID" + id + '[] PROGMEM = "' + sensor_id + '";';
code += "addMeasurement(SENSOR_ID" + id + "," + sensor_value + ");\n";
- console.log(code);
return code;
};
diff --git a/src/components/Blockly/generator/sensebox-web.js b/src/components/Blockly/generator/sensebox-web.js
index a2cae3b..3d94af1 100644
--- a/src/components/Blockly/generator/sensebox-web.js
+++ b/src/components/Blockly/generator/sensebox-web.js
@@ -1,4 +1,12 @@
import Blockly from "blockly";
+//import store from "../../../store";
+
+// preperations for the esp board
+// var selectedBoard = store.getState().board.board;
+// store.subscribe(() => {
+// selectedBoard = store.getState().board.board;
+// });
+
/* Wifi connection and openSenseMap Blocks*/
Blockly.Arduino.sensebox_wifi = function (block) {
@@ -110,3 +118,5 @@ Blockly.Arduino.sensebox_ethernetIp = function () {
var code = "Ethernet.localIP()";
return [code, Blockly.Arduino.ORDER_ATOMIC];
};
+
+
diff --git a/src/components/Blockly/generator/variables.js b/src/components/Blockly/generator/variables.js
index 7ee7be9..e8e4dd4 100644
--- a/src/components/Blockly/generator/variables.js
+++ b/src/components/Blockly/generator/variables.js
@@ -2,10 +2,17 @@ import Blockly from "blockly";
const setVariableFunction = function (defaultValue) {
return function (block) {
- const variableName = Blockly["Arduino"].nameDB_.getName(
- block.getFieldValue("VAR"),
- Blockly.Variables.NAME_TYPE
- );
+ var id = block.getFieldValue("VAR");
+
+ const variableName = Blockly.Variables.getVariable(
+ Blockly.getMainWorkspace(),
+ id
+ ).name;
+
+ // const variableName = Blockly["Arduino"].nameDB_.getName(
+ // id,
+ // Blockly.Variables.NAME_TYPE
+ // );
const variableValue = Blockly["Arduino"].valueToCode(
block,
"VALUE",
@@ -17,31 +24,10 @@ const setVariableFunction = function (defaultValue) {
.getAllVariables();
const myVar = allVars.filter((v) => v.name === variableName)[0];
var code = "";
-
- switch (myVar.type) {
- default:
- Blockly.Arduino.variables_[variableName + myVar.type] =
- myVar.type + " " + myVar.name + ";\n";
- code = variableName + " = " + (variableValue || defaultValue) + ";\n";
- break;
- case "Array":
- var arrayType;
- var number;
-
- if (this.getChildren().length > 0) {
- if (this.getChildren()[0].type === "lists_create_empty") {
- arrayType = this.getChildren()[0].getFieldValue("type");
- number = Blockly.Arduino.valueToCode(
- this.getChildren()[0],
- "NUMBER",
- Blockly["Arduino"].ORDER_ATOMIC
- );
- Blockly.Arduino.variables_[
- myVar + myVar.type
- ] = `${arrayType} ${myVar.name} [${number}];\n`;
- }
- }
- break;
+ if (myVar !== undefined) {
+ Blockly.Arduino.variables_[variableName + myVar.type] =
+ myVar.type + " " + myVar.name + ";\n";
+ code = variableName + " = " + (variableValue || defaultValue) + ";\n";
}
return code;
};
diff --git a/src/components/Blockly/helpers/board.js b/src/components/Blockly/helpers/board.js
index 16e0165..12dd18d 100644
--- a/src/components/Blockly/helpers/board.js
+++ b/src/components/Blockly/helpers/board.js
@@ -23,6 +23,11 @@ const sensebox_mcu = {
["C5", "5"],
["C6", "6"],
],
+ digitalPinsRGB: [
+ ["A", "1"],
+ ["B", "3"],
+ ["C", "5"],
+ ],
digitalPinsButton: [
["on Board", "0"],
["A1", "1"],
@@ -126,6 +131,124 @@ const sensebox_mcu = {
parseKey: "_*_",
};
-export const selectedBoard = () => {
- return sensebox_mcu;
+//senseBox MCU mini
+const sensebox_mini = {
+ description: "senseBox Mini",
+ compilerFlag: "arduino:samd",
+ digitalPins: [
+ ["IO1", "1"],
+ ["IO2", "2"],
+ ],
+ digitalPinsLED: [
+ ["BUILTIN_1", "7"],
+ ["BUILTIN_2", "8"],
+ ["IO1", "1"],
+ ["IO2", "2"],
+ ],
+ digitalPinsRGB: [
+ ["on Board", "6"],
+ ["IO1", "1"],
+ ["IO2", "2"],
+ ],
+ digitalPinsButton: [
+ ["on Board", "0"],
+ ["IO1", "1"],
+ ["IO2", "2"],
+
+ ],
+ pwmPins: [
+ ["IO1", "1"],
+ ["IO2", "2"],
+ ],
+ serial: [
+ ["serial", "SerialUSB"],
+ ["serial_1", "Serial1"],
+ ],
+ serialPins: {
+ SerialUSB: [
+ ["RX", ""],
+ ["TX", ""],
+ ],
+ Serial1: [
+ ["RX", "11"],
+ ["TX", "10"],
+ ],
+ Serial2: [
+ ["RX", "13"],
+ ["TX", "12"],
+ ],
+ },
+ serialSpeed: [
+ ["300", "300"],
+ ["600", "600"],
+ ["1200", "1200"],
+ ["2400", "2400"],
+ ["4800", "4800"],
+ ["9600", "9600"],
+ ["14400", "14400"],
+ ["19200", "19200"],
+ ["28800", "28800"],
+ ["31250", "31250"],
+ ["38400", "38400"],
+ ["57600", "57600"],
+ ["115200", "115200"],
+ ],
+ spi: [["SPI", "SPI"]],
+ spiPins: {
+ SPI: [
+ ["MOSI", "19"],
+ ["MISO", "21"],
+ ["SCK", "20"],
+ ],
+ },
+ spiClockDivide: [
+ ["2 (8MHz)", "SPI_CLOCK_DIV2"],
+ ["4 (4MHz)", "SPI_CLOCK_DIV4"],
+ ["8 (2MHz)", "SPI_CLOCK_DIV8"],
+ ["16 (1MHz)", "SPI_CLOCK_DIV16"],
+ ["32 (500KHz)", "SPI_CLOCK_DIV32"],
+ ["64 (250KHz)", "SPI_CLOCK_DIV64"],
+ ["128 (125KHz)", "SPI_CLOCK_DIV128"],
+ ],
+ i2c: [["I2C", "Wire"]],
+ i2cPins: {
+ Wire: [
+ ["SDA", "17"],
+ ["SCL", "16"],
+ ],
+ },
+ i2cSpeed: [
+ ["100kHz", "100000L"],
+ ["400kHz", "400000L"],
+ ],
+ builtinLed: [
+ ["BUILTIN_1", "7"],
+ ["BUILTIN_2", "8"],
+ ],
+ interrupt: [
+ ["interrupt1", "1"],
+ ["interrupt2", "2"],
+ ],
+ analogPins: [
+ ["A1", "A1"],
+ ["A2", "A2"],
+ ],
+ serial_baud_rate: 9600,
+ parseKey: "_*_",
+};
+
+var board = sensebox_mcu
+
+export const setBoard = (selectedBoard) => {
+ if (selectedBoard === "mini"){
+ board = sensebox_mini
+ }
+ else {
+ board = sensebox_mcu
+ }
+}
+
+
+export const selectedBoard = () => {
+ return board;
};
diff --git a/src/components/Blockly/msg/de/ui.js b/src/components/Blockly/msg/de/ui.js
index f73db9f..e11f9f9 100644
--- a/src/components/Blockly/msg/de/ui.js
+++ b/src/components/Blockly/msg/de/ui.js
@@ -142,6 +142,7 @@ export const UI = {
button_cancel: "Abbrechen",
button_close: "Schließen",
+ button_save: "Speichern",
button_accept: "Bestätigen",
button_compile: "Kompilieren",
button_create_variableCreate: "Erstelle Variable",
@@ -182,7 +183,8 @@ export const UI = {
settings_sounds: "Töne",
settings_sounds_text:
"Aktiviere oder Deaktiviere Töne beim hinzufügen und löschen von Blöcken. Standardmäßig deaktiviert",
-
+ settings_board: "Board",
+ settings_board_text: "Wähle dein verwendetes Board aus",
/**
* 404
*/
@@ -229,6 +231,12 @@ export const UI = {
builder_requirements_head: "Voraussetzungen",
builder_requirements_order:
"Beachte, dass die Reihenfolge des Anhakens maßgebend ist.",
+ builder_difficulty: "Schwierigkeitsgrad",
+ builder_public_head: "Tutorial veröffentlichen",
+ builder_public_label: "Tutorial für alle Nutzer:innen veröffentlichen",
+ builder_review_head: "Tutorial veröffentlichen",
+ builder_review_text:
+ "Du kannst dein Tutorial direkt über den Link mit anderen Personen teilen. Wenn du dein Tutorial für alle Nutzer:innen in der Überischt veröffenltichen wollen kannst du es hier aktivieren. Ein Administrator wird dein Tutorial ansehen und anschließend freischalten.",
/**
* Login
@@ -242,7 +250,7 @@ export const UI = {
/**
* Navbar
*/
-
+ navbar_blockly: "Blockly",
navbar_tutorials: "Tutorials",
navbar_tutorialbuilder: "Tutorial erstellen",
navbar_gallery: "Galerie",
@@ -283,4 +291,25 @@ export const UI = {
drawer_ideerror_head: "Hoppla da ist was schief gegangen.",
drawer_ideerror_text:
"Beim kompilieren ist ein Fehler aufgetreten, überprüfe deine Blöcke.",
+
+ /**
+ * Code Editor
+ * */
+ codeeditor_libraries_head: "Installierte Arduino Libraries",
+ codeeditor_libraries_text:
+ "Für die Dokumentation sehen Sie sich die installierten Bibliotheken und deren Beispiele an",
+ codeeditor_save_code: "Code herunterladen",
+ codeeditor_open_code: "Code öffnen",
+ codeeditor_reset_code: "Code zurücksetzen",
+ codeeditor_blockly_code: "Lade Blockly Code",
+ codeeditor_compile_progress:
+ "Dein Code wird nun kompiliert und anschließend auf deinen Computer heruntergeladen",
+
+ /**
+ * Device Selction
+ * */
+ deviceselection_head: "Welches Board benutzt du?",
+ deviceselection_keep_selection: "Speichere meine Auswahl fürs nächste Mal (Du kannst das Board später in den Einstellungen wechseln)",
+ deviceselection_footnote: "Hier kommst du zur alten Blockly Version für den ",
+ deviceselection_footnote_02: "oder die"
};
diff --git a/src/components/Blockly/msg/en/ui.js b/src/components/Blockly/msg/en/ui.js
index 530fd90..6a1e685 100644
--- a/src/components/Blockly/msg/en/ui.js
+++ b/src/components/Blockly/msg/en/ui.js
@@ -177,6 +177,8 @@ export const UI = {
settings_sounds: "Sound",
settings_sounds_text:
"Enable or disable sounds when adding and deleting blocks. Disabled by default",
+ settings_board: "Board",
+ settings_board_text: "Choose your board",
/**
* 404
@@ -223,6 +225,12 @@ export const UI = {
builder_requirements_head: "Requirements.",
builder_requirements_order:
"Note that the order of ticking is authoritative.",
+ builder_difficulty: "Difficulty level",
+ builder_public_head: "Publish tutorial",
+ builder_public_label: "Publish tutorial for all users",
+ builder_review_head: "Publish tutorial",
+ builder_review_text:
+ "You can share your tutorial with other people directly from the link. If you want to publish your tutorial for all users in the overview you can activate it here. An administrator will view your tutorial and then activate it.",
/**
* Login
@@ -238,7 +246,7 @@ export const UI = {
/**
* Navbar
*/
-
+ navbar_blockly: "Blockly",
navbar_tutorials: "Tutorials",
navbar_tutorialbuilder: "Create tutorial",
navbar_gallery: "Gallery",
@@ -278,4 +286,27 @@ export const UI = {
*/
drawer_ideerror_head: "Oops something went wrong",
drawer_ideerror_text: "An error occurred while compiling, check your blocks",
+
+ /**
+ * Code Editor
+ * */
+ codeeditor_libraries_head: "Installed Arduino Libraries",
+ codeeditor_libraries_text:
+ "For documentation, view the installed libraries and their examples",
+ codeeditor_save_code: "Download code",
+ codeeditor_open_code: "Open code",
+ codeeditor_reset_code: "Reset code",
+ codeeditor_blockly_code: "Load blockly code",
+ codeeditor_compile_progress:
+ "Your code will now be compiled and then downloaded to your computer",
+
+ /**
+ * Device Selction
+ * */
+ deviceselection_head: "Which board are you using?",
+ deviceselection_keep_selection: "Save my choice (You can change the board later in the settings)",
+ deviceselection_footnote: "Here you can access the old blockly Version for the",
+ deviceselection_footnote_02: "or the",
+
+
};
diff --git a/src/components/Blockly/toolbox/Toolbox.js b/src/components/Blockly/toolbox/Toolbox.js
index 00cc54d..7fa9f5a 100644
--- a/src/components/Blockly/toolbox/Toolbox.js
+++ b/src/components/Blockly/toolbox/Toolbox.js
@@ -21,11 +21,8 @@ class Toolbox extends React.Component {
[`${Blockly.Msg.variable_LONG}`, "long"],
[`${Blockly.Msg.variable_DECIMAL}`, "float"],
[`${Blockly.Msg.variables_TEXT}`, "String"],
- [`${Blockly.Msg.variables_ARRAY}`, "Array"],
[`${Blockly.Msg.variables_CHARACTER}`, "char"],
[`${Blockly.Msg.variables_BOOLEAN}`, "boolean"],
- [`${Blockly.Msg.variables_NULL}`, "void"],
- [`${Blockly.Msg.variables_UNDEF}`, "undefined"],
]
);
typedVarModal.init();
diff --git a/src/components/CodeEditor/CodeEditor.js b/src/components/CodeEditor/CodeEditor.js
new file mode 100644
index 0000000..08e40f5
--- /dev/null
+++ b/src/components/CodeEditor/CodeEditor.js
@@ -0,0 +1,279 @@
+import React from "react";
+import { useState, useRef } from "react";
+import { default as MonacoEditor } from "@monaco-editor/react";
+import { withRouter } from "react-router-dom";
+import { Button, Grid } from "@material-ui/core";
+import Blockly from "blockly/core";
+import Divider from "@material-ui/core/Divider";
+import { saveAs } from "file-saver";
+import Drawer from "@material-ui/core/Drawer";
+import Sidebar from "./Sidebar";
+import Dialog from "../Dialog";
+import SaveIcon from "./SaveIcon";
+import store from "../../store";
+
+const CodeEditor = (props) => {
+ //const [filehandle, setFileHandle] = useState();
+ const [fileContent, setFileContent] = useState("");
+ const [progress, setProgress] = useState(false);
+ // const [id, setId] = useState("");
+ const [open, setOpen] = useState(false);
+ const [error, setError] = useState("");
+ const editorRef = useRef(null);
+ const [autoSave, setAutoSave] = useState(false);
+ const [time, setTime] = useState(null);
+ const [value, setValue] = useState("");
+ const [resetDialog, setResetDialog] = useState(false);
+
+ const compile = () => {
+ setProgress(true);
+ const data = {
+ board: process.env.REACT_APP_BOARD,
+ sketch: editorRef.current.getValue(),
+ };
+ fetch(`${process.env.REACT_APP_COMPILER_URL}/compile`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(data),
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ if (data.code === "Internal Server Error") {
+ setProgress(false);
+ setOpen(true);
+ setError(data.message);
+ }
+ setProgress(false);
+ const result = data.data.id;
+ //setId(result);
+ const filename = "sketch";
+ window.open(
+ `${process.env.REACT_APP_COMPILER_URL}/download?id=${result}&board=${process.env.REACT_APP_BOARD}&filename=${filename}`,
+ "_self"
+ );
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+ };
+
+ const saveIno = () => {
+ var filename = "sketch";
+ var code = editorRef.current.getValue();
+
+ filename = `${filename}.ino`;
+ var blob = new Blob([code], { type: "text/plain;charset=utf-8" });
+ saveAs(blob, filename);
+ };
+
+ const openIno = async () => {
+ const [myFileHandle] = await window.showOpenFilePicker();
+ //setFileHandle(myFileHandle);
+
+ const file = await myFileHandle.getFile();
+ const contents = await file.text();
+ setFileContent(contents);
+ };
+
+ const toggleDrawer = (anchor, open) => (event) => {
+ if (
+ event.type === "keydown" &&
+ (event.key === "Tab" || event.key === "Shift")
+ ) {
+ return;
+ }
+
+ setOpen(false);
+ };
+
+ const resetCode = () => {
+ const resetCode = `
+#include //needs to be always included
+
+void setup () {
+
+}
+
+void loop() {
+
+}`;
+
+ editorRef.current.setValue(resetCode);
+ };
+
+ const resetTimeout = (id, newID) => {
+ clearTimeout(id);
+ return newID;
+ };
+
+ const editValue = (value) => {
+ setTime(resetTimeout(time, setTimeout(saveValue, 400)));
+ setValue(value);
+ };
+
+ const saveValue = () => {
+ localStorage.setItem("ArduinoCode", value);
+ setAutoSave(true);
+ setTimeout(() => setAutoSave(false), 1000);
+ };
+
+ const getBlocklyCode = () => {
+ var code = store.getState().workspace.code.arduino;
+ editorRef.current.setValue(code);
+ };
+
+ return (
+
+
+
+
+ {Blockly.Msg.drawer_ideerror_head}
+
+
+ {Blockly.Msg.drawer_ideerror_text}
+
+
+
+ {" "}
+ {`${error}`}{" "}
+
+
+
+
+
Code Editor
+
+
+
+ {
+ editValue(value);
+ }}
+ defaultLanguage="cpp"
+ defaultValue={
+ localStorage.getItem("ArduinoCode")
+ ? localStorage.getItem("ArduinoCode")
+ : `
+#include //needs to be always included
+
+void setup () {
+
+}
+
+void loop() {
+
+}`
+ }
+ value={fileContent}
+ onMount={(editor, monaco) => {
+ editorRef.current = editor;
+ }}
+ />
+
+
+
+
+
+
+
+
+ {" "}
+
+
+
+
+ );
+};
+
+export default withRouter(CodeEditor);
diff --git a/src/components/CodeEditor/Compile.js b/src/components/CodeEditor/Compile.js
new file mode 100644
index 0000000..26a9d4b
--- /dev/null
+++ b/src/components/CodeEditor/Compile.js
@@ -0,0 +1,331 @@
+import React, { Component } from "react";
+import PropTypes from "prop-types";
+import { connect } from "react-redux";
+import { workspaceName } from "../../actions/workspaceActions";
+
+import { detectWhitespacesAndReturnReadableResult } from "../../helpers/whitespace";
+
+import { withStyles } from "@material-ui/core/styles";
+import Button from "@material-ui/core/Button";
+import Backdrop from "@material-ui/core/Backdrop";
+import CircularProgress from "@material-ui/core/CircularProgress";
+import IconButton from "@material-ui/core/IconButton";
+import Tooltip from "@material-ui/core/Tooltip";
+import Divider from "@material-ui/core/Divider";
+import { faClipboardCheck } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import * as Blockly from "blockly/core";
+import Copy from "../copy.svg";
+
+import MuiDrawer from "@material-ui/core/Drawer";
+import Dialog from "../Dialog";
+
+const styles = (theme) => ({
+ backdrop: {
+ zIndex: theme.zIndex.drawer + 1,
+ color: "#fff",
+ },
+ iconButton: {
+ backgroundColor: theme.palette.button.compile,
+ color: theme.palette.primary.contrastText,
+ width: "40px",
+ height: "40px",
+ "&:hover": {
+ backgroundColor: theme.palette.button.compile,
+ color: theme.palette.primary.contrastText,
+ },
+ },
+ button: {
+ backgroundColor: theme.palette.button.compile,
+ color: theme.palette.primary.contrastText,
+ "&:hover": {
+ backgroundColor: theme.palette.button.compile,
+ color: theme.palette.primary.contrastText,
+ },
+ },
+});
+
+const Drawer = withStyles((theme) => ({
+ paperAnchorBottom: {
+ backgroundColor: "black",
+ height: "20vH",
+ },
+}))(MuiDrawer);
+
+class Compile extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ progress: false,
+ open: false,
+ file: false,
+ title: "",
+ content: "",
+ name: props.name,
+ error: "",
+ appLink: "",
+ appDialog: false,
+ };
+ }
+
+ componentDidMount() {}
+
+ componentDidUpdate(props) {
+ if (props.name !== this.props.name) {
+ this.setState({ name: this.props.name });
+ }
+ }
+
+ compile = () => {
+ this.setState({ progress: true });
+ const data = {
+ board: process.env.REACT_APP_BOARD,
+ sketch: this.props.arduino,
+ };
+ fetch(`${process.env.REACT_APP_COMPILER_URL}/compile`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(data),
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ if (data.code === "Internal Server Error") {
+ this.setState({
+ progress: false,
+ file: false,
+ open: true,
+ title: Blockly.Msg.compiledialog_headline,
+ content: Blockly.Msg.compiledialog_text,
+ error: data.message,
+ });
+ }
+ this.setState({ id: data.data.id }, () => {
+ this.createFileName();
+ });
+ })
+ .catch((err) => {
+ console.log(err);
+ //this.setState({ progress: false, file: false, open: true, title: Blockly.Msg.compiledialog_headline, content: Blockly.Msg.compiledialog_text });
+ });
+ };
+
+ download = () => {
+ const id = this.state.id;
+ const filename = detectWhitespacesAndReturnReadableResult(this.state.name);
+ this.toggleDialog();
+ this.props.workspaceName(this.state.name);
+ window.open(
+ `${process.env.REACT_APP_COMPILER_URL}/download?id=${id}&board=${process.env.REACT_APP_BOARD}&filename=${filename}`,
+ "_self"
+ );
+ this.setState({ progress: false });
+ };
+
+ toggleDialog = () => {
+ this.setState({ open: !this.state, progress: false, appDialog: false });
+ };
+
+ createFileName = () => {
+ if (this.props.platform === true) {
+ const filename = detectWhitespacesAndReturnReadableResult(
+ this.state.name
+ );
+ this.setState({
+ link: `blocklyconnect-app://sketch/${filename}/${this.state.id}`,
+ });
+ this.setState({ appDialog: true });
+ } else {
+ if (this.state.name) {
+ this.download();
+ } else {
+ this.setState({
+ file: true,
+ open: true,
+ title: "Projekt kompilieren",
+ content:
+ "Bitte gib einen Namen für die Bennenung des zu kompilierenden Programms ein und bestätige diesen mit einem Klick auf 'Eingabe'.",
+ });
+ }
+ }
+
+ // if (this.state.name) {
+ // this.download();
+ // } else {
+ // this.setState({
+ // file: true,
+ // open: true,
+ // title: "Projekt kompilieren",
+ // content:
+ // "Bitte gib einen Namen für die Bennenung des zu kompilierenden Programms ein und bestätige diesen mit einem Klick auf 'Eingabe'.",
+ // });
+ // }
+ };
+
+ setFileName = (e) => {
+ this.setState({ name: e.target.value });
+ };
+
+ toggleDrawer = (anchor, open) => (event) => {
+ if (
+ event.type === "keydown" &&
+ (event.key === "Tab" || event.key === "Shift")
+ ) {
+ return;
+ }
+
+ this.setState({ open: false });
+ };
+
+ render() {
+ return (
+
+ {this.props.iconButton ? (
+
+ this.compile()}
+ >
+
+
+
+ ) : (
+
+ )}
+
+ {this.props.platform === false ? (
+
+
+

+
{Blockly.Msg.compile_overlay_head}
+
{Blockly.Msg.compile_overlay_text}
+
+ {Blockly.Msg.compile_overlay_help}
+
+ FAQ
+
+
+
+
+
+ ) : (
+
+
+ {/*

*/}
+
Dein Code wird kompiliert!
+
übertrage ihn anschließend mithlfe der senseBoxConnect-App
+
+ {Blockly.Msg.compile_overlay_help}
+
+ FAQ
+
+
+
+
+
+ )}
+
+
+ {Blockly.Msg.drawer_ideerror_head}
+
+
+ {Blockly.Msg.drawer_ideerror_text}
+
+
+
+ {" "}
+ {`${this.state.error}`}{" "}
+
+
+
+
+ );
+ }
+}
+
+Compile.propTypes = {
+ arduino: PropTypes.string.isRequired,
+ name: PropTypes.string,
+ workspaceName: PropTypes.func.isRequired,
+ platform: PropTypes.bool.isRequired,
+};
+
+const mapStateToProps = (state) => ({
+ arduino: state.workspace.code.arduino,
+ name: state.workspace.name,
+ platform: state.general.platform,
+});
+
+export default connect(mapStateToProps, { workspaceName })(
+ withStyles(styles, { withTheme: true })(Compile)
+);
diff --git a/src/components/CodeEditor/SaveIcon.js b/src/components/CodeEditor/SaveIcon.js
new file mode 100644
index 0000000..c776485
--- /dev/null
+++ b/src/components/CodeEditor/SaveIcon.js
@@ -0,0 +1,40 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faCircleNotch, faSave } from "@fortawesome/free-solid-svg-icons";
+import Tooltip from "@material-ui/core/Tooltip";
+import React from "react";
+
+const SaveIcon = ({ loading }) => (
+
+
+ {loading && (
+
+ )}
+
+
+
+);
+
+export default SaveIcon;
diff --git a/src/components/CodeEditor/SerialMonitor.js b/src/components/CodeEditor/SerialMonitor.js
new file mode 100644
index 0000000..0636301
--- /dev/null
+++ b/src/components/CodeEditor/SerialMonitor.js
@@ -0,0 +1,91 @@
+import { useState } from "react";
+import Button from "@material-ui/core/Button";
+
+const SerialMonitor = () => {
+ const [serialPortContent, setSerialPortContent] = useState([]);
+
+ const [checked, setChecked] = useState(false);
+ const handleClick = () => setChecked(!checked);
+
+ const connectPort = async () => {
+ try {
+ const port = await navigator.serial.requestPort();
+
+ await port.open({ baudRate: 9600 });
+
+ while (port.readable) {
+ const reader = port.readable.getReader();
+
+ try {
+ while (true) {
+ const { value, done } = await reader.read();
+ if (done) {
+ // Allow the serial port to be closed later.
+ reader.releaseLock();
+ break;
+ }
+ if (value) {
+ // byte array to string: https://stackoverflow.com/a/37542820
+ const text = String.fromCharCode.apply(null, value);
+ setSerialPortContent((prevContent) => [
+ ...prevContent,
+ [new Date(), text],
+ ]);
+ }
+ }
+ } catch (error) {
+ setSerialPortContent((prevContent) => [
+ ...prevContent,
+ [new Date(), error],
+ ]);
+ }
+ }
+ } catch (error) {
+ setSerialPortContent((prevContent) => [
+ ...prevContent,
+ [new Date(), error],
+ ]);
+ }
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+ {serialPortContent.map((log) => {
+ return (
+
+ {checked && (
+ {log[0].toISOString()}
+ )}
+
+ {log[1]}
+
+ );
+ })}
+
+ >
+ );
+};
+
+export default SerialMonitor;
diff --git a/src/components/CodeEditor/Sidebar.js b/src/components/CodeEditor/Sidebar.js
new file mode 100644
index 0000000..5e2b976
--- /dev/null
+++ b/src/components/CodeEditor/Sidebar.js
@@ -0,0 +1,140 @@
+import React from "react";
+import Blockly from "blockly";
+import { useSelector } from "react-redux";
+import Accordion from "@material-ui/core/Accordion";
+import AccordionSummary from "@material-ui/core/AccordionSummary";
+import AccordionDetails from "@material-ui/core/AccordionDetails";
+import Typography from "@material-ui/core/Typography";
+import { LibraryVersions } from "../../data/versions.js";
+import { useMonaco } from "@monaco-editor/react";
+import { Button } from "@material-ui/core";
+import SerialMonitor from "./SerialMonitor.js";
+import axios from "axios";
+
+const Sidebar = () => {
+ //const [examples, setExamples] = React.useState([]);
+ const user = useSelector((state) => state.auth.user);
+ // useEffect(() => {
+ // axios
+ // .get("https://coelho.opensensemap.org/items/blocklysamples")
+ // .then((res) => {
+ // setExamples(res.data.data);
+ // });
+ // }, []);
+ const monaco = useMonaco();
+ const loadCode = (code) => {
+ monaco.editor.getModels()[0].setValue(code);
+ };
+
+ const getOsemScript = (id) => {
+ axios
+ .get(`https://api.opensensemap.org/boxes/${id}/script/`)
+ .then((res) => {
+ loadCode(res.data);
+ });
+ };
+
+ return (
+
+ {"serial" in navigator ? (
+
+
+
+
+
+
+
+
+ ) : null}
+ {/*
+
+
+
+ {examples.map((object, i) => {
+ return (
+
+ );
+ })}
+
+
+ */}
+ {user ? (
+
+
+
+
+ {user.boxes.map((box, i) => {
+ return (
+
+ );
+ })}
+
+
+
+ ) : null}
+
+
+
+
+ {Blockly.Msg.codeeditor_libraries_text}
+ {LibraryVersions().map((object, i) => {
+ return (
+
+
+ {object.library} {object.version}
+
+
+ );
+ })}
+
+
+
+
+ );
+};
+
+export default Sidebar;
diff --git a/src/components/CodeViewer.js b/src/components/CodeViewer.js
index 856272c..e7506ac 100644
--- a/src/components/CodeViewer.js
+++ b/src/components/CodeViewer.js
@@ -1,30 +1,25 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { connect } from 'react-redux';
-
-import Prism from "prismjs";
-import "prismjs/themes/prism.css";
-import "prismjs/plugins/line-numbers/prism-line-numbers";
-import "prismjs/plugins/line-numbers/prism-line-numbers.css";
-
-import withWidth from '@material-ui/core/withWidth';
-import { withStyles } from '@material-ui/core/styles';
-import MuiAccordion from '@material-ui/core/Accordion';
-import MuiAccordionSummary from '@material-ui/core/AccordionSummary';
-import MuiAccordionDetails from '@material-ui/core/AccordionDetails';
-import { Card } from '@material-ui/core';
-import * as Blockly from 'blockly'
+import React, { Component } from "react";
+import PropTypes from "prop-types";
+import { connect } from "react-redux";
+import withWidth from "@material-ui/core/withWidth";
+import { withStyles } from "@material-ui/core/styles";
+import MuiAccordion from "@material-ui/core/Accordion";
+import MuiAccordionSummary from "@material-ui/core/AccordionSummary";
+import MuiAccordionDetails from "@material-ui/core/AccordionDetails";
+import { Card } from "@material-ui/core";
+import * as Blockly from "blockly";
+import { default as MonacoEditor } from "@monaco-editor/react";
const Accordion = withStyles((theme) => ({
root: {
border: `1px solid ${theme.palette.secondary.main}`,
- boxShadow: 'none',
- '&:before': {
- display: 'none',
+ boxShadow: "none",
+ "&:before": {
+ display: "none",
},
- '&$expanded': {
- margin: 'auto',
+ "&$expanded": {
+ margin: "auto",
},
},
expanded: {},
@@ -34,15 +29,15 @@ const AccordionSummary = withStyles((theme) => ({
root: {
backgroundColor: theme.palette.secondary.main,
borderBottom: `1px solid white`,
- marginBottom: '-1px',
- minHeight: '50px',
- '&$expanded': {
- minHeight: '50px',
+ marginBottom: "-1px",
+ minHeight: "50px",
+ "&$expanded": {
+ minHeight: "50px",
},
},
content: {
- '&$expanded': {
- margin: '12px 0',
+ "&$expanded": {
+ margin: "12px 0",
},
},
expanded: {},
@@ -54,40 +49,60 @@ const AccordionDetails = withStyles((theme) => ({
},
}))(MuiAccordionDetails);
-
class CodeViewer extends Component {
-
constructor(props) {
super(props);
this.state = {
+ code: this.props.arduino,
+ changed: false,
expanded: true,
- componentHeight: null
+ componentHeight: null,
};
this.myDiv = React.createRef();
}
componentDidMount() {
- Prism.highlightAll();
- this.setState({ componentHeight: this.myDiv.current.offsetHeight + 'px' });
+ this.setState({ componentHeight: this.myDiv.current.offsetHeight + "px" });
}
- componentDidUpdate(props, state) {
- if (this.myDiv.current && this.myDiv.current.offsetHeight + 'px' !== this.state.componentHeight) {
- this.setState({ componentHeight: this.myDiv.current.offsetHeight + 'px' });
+ componentDidUpdate(prevProps, prevState) {
+ // if (this.props.arduino !== prevProps.arduino) {
+ // this.setState({ changed: true });
+
+ // console.log(`code changed: ${this.state.changed}`);
+ // if (this.state.changed && prevState.code !== this.props.arduino) {
+ // this.setState({ code: this.props.arduino });
+ // this.setState({ changed: false });
+ // }
+
+ // if (this.state.code !== prevState.code && this.state.changed) {
+ // this.setState({ changed: false });
+ // }
+
+ // if (this.props.arduino !== this.state.code) {
+ // this.setState({ changed: true });
+ // //this.setState({ code: this.props.arduino });
+ // }
+
+ if (
+ this.myDiv.current &&
+ this.myDiv.current.offsetHeight + "px" !== this.state.componentHeight
+ ) {
+ this.setState({
+ componentHeight: this.myDiv.current.offsetHeight + "px",
+ });
}
- Prism.highlightAll();
}
onChange = () => {
this.setState({ expanded: !this.state.expanded });
-
- }
+ };
render() {
- var curlyBrackets = '{ }';
- var unequal = '<>';
+ var curlyBrackets = "{ }";
+ var unequal = "<>";
return (
-
+
- {curlyBrackets}
- {Blockly.Msg.codeviewer_arduino}
+
+ {curlyBrackets}
+
+
+ {Blockly.Msg.codeviewer_arduino}
+
-
-
-
- {this.props.arduino}
-
-
+
+
- {unequal}
- {Blockly.Msg.codeviewer_xml}
+
+ {unequal}
+
+
+ {Blockly.Msg.codeviewer_xml}
+
-
-
-
- {`${this.props.xml}`}
-
-
+
+
);
- };
+ }
}
CodeViewer.propTypes = {
arduino: PropTypes.string.isRequired,
xml: PropTypes.string.isRequired,
- tooltip: PropTypes.string.isRequired
+ tooltip: PropTypes.string.isRequired,
};
-const mapStateToProps = state => ({
+const mapStateToProps = (state) => ({
arduino: state.workspace.code.arduino,
xml: state.workspace.code.xml,
- tooltip: state.workspace.code.tooltip
+ tooltip: state.workspace.code.tooltip,
});
export default connect(mapStateToProps, null)(withWidth()(CodeViewer));
diff --git a/src/components/Content.js b/src/components/Content.js
index f4b7bef..a0a8d7a 100644
--- a/src/components/Content.js
+++ b/src/components/Content.js
@@ -10,6 +10,7 @@ import Navbar from './Navbar';
import Footer from './Footer';
import Routes from './Route/Routes';
import Cookies from './Cookies';
+import { setBoard } from './Blockly/helpers/board';
class Content extends Component {
@@ -19,6 +20,7 @@ class Content extends Component {
} else if (this.props.language === 'en_US') {
Blockly.setLocale(En);
}
+ setBoard(this.props.board)
}
componentDidUpdate(props) {
@@ -29,6 +31,7 @@ class Content extends Component {
Blockly.setLocale(En);
}
}
+ setBoard(this.props.board)
}
render() {
@@ -48,7 +51,8 @@ Content.propTypes = {
};
const mapStateToProps = state => ({
- language: state.general.language
+ language: state.general.language,
+ board: state.board.board
});
export default connect(mapStateToProps, null)(Content);
diff --git a/src/components/Cookies.js b/src/components/Cookies.js
index b943f5a..87a25c8 100644
--- a/src/components/Cookies.js
+++ b/src/components/Cookies.js
@@ -9,7 +9,7 @@ class Cookies extends Component {
return (
({
+ link: {
+ color: theme.palette.primary.main,
+ textDecoration: "none",
+ "&:hover": {
+ color: theme.palette.primary.main,
+ textDecoration: `underline`,
+ },
+ },
+ label: {
+ fontSize: "0.9rem",
+ color: "grey",
+ },
+});
+
+class DeviceSeclection extends Component {
+ constructor(props) {
+ var previousPageWasAnotherDomain = props.pageVisits === 0;
+ var userWantToKeepBoard = window.localStorage.getItem("board")
+ ? true
+ : false;
+ super(props);
+ this.state = {
+ open: userWantToKeepBoard
+ ? !userWantToKeepBoard
+ : previousPageWasAnotherDomain,
+ selectedBoard : "",
+ saveSettings: false,
+
+ };
+ }
+
+ toggleDialog = () => {
+ this.setState({ open: !this.state });
+ if(this.state.saveSettings){
+ window.localStorage.setItem("board", this.state.selectedBoard)
+ }
+ this.props.setBoard(this.state.selectedBoard)
+
+ };
+
+ onChange = (e) => {
+ if (e.target.checked) {
+ this.setState({ saveSettings: true});
+ } else {
+ this.setState({ saveSettings: false});
+ }
+ };
+
+ onclick = (e, value) => {
+ console.log(e, value)
+ this.setState({selectedBoard: value})
+ };
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+DeviceSeclection.propTypes = {
+ pageVisits: PropTypes.number.isRequired,
+};
+
+const mapStateToProps = (state) => ({
+ pageVisits: state.general.pageVisits,
+ selectedBoard: state.board.board
+});
+
+export default connect(
+ mapStateToProps,
+ {setBoard}
+)(withStyles(styles, { withTheme: true })(DeviceSeclection));
diff --git a/src/components/Dialog.js b/src/components/Dialog.js
index 0dc7180..41d8aae 100644
--- a/src/components/Dialog.js
+++ b/src/components/Dialog.js
@@ -24,7 +24,7 @@ class Dialog extends Component {
{this.props.actions ? this.props.actions :
-