Compare commits

..
Author SHA1 Message Date
Bjoern Luig 0388e5c627 change order of phenomenons 2023-06-09 11:03:28 +02:00
Bjoern Luig 9c33bc4c0d small bugfix to use multiple sensors 2022-11-16 14:18:27 +01:00
Bjoern 32372bc222 finished english language and testing of compiling 2022-10-28 01:37:16 +02:00
Bjoern Luig dbc7fdc7ab added rainsensor 2022-08-28 16:25:52 +02:00
134 changed files with 46140 additions and 5854 deletions
-2
View File
@@ -2,7 +2,5 @@ REACT_APP_COMPILER_URL=https://compiler.sensebox.de
REACT_APP_BOARD=sensebox-mcu REACT_APP_BOARD=sensebox-mcu
REACT_APP_BLOCKLY_API=https://api.blockly.sensebox.de REACT_APP_BLOCKLY_API=https://api.blockly.sensebox.de
GENERATE_SOURCEMAP=false
# in days # in days
REACT_APP_SHARE_LINK_EXPIRES=30 REACT_APP_SHARE_LINK_EXPIRES=30
+1 -1
View File
@@ -22,4 +22,4 @@ npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
package-lock.json package-lock.json
package-lock.json
+44160
View File
File diff suppressed because it is too large Load Diff
+38 -49
View File
@@ -1,63 +1,45 @@
{ {
"name": "blockly-react", "name": "blockly-react",
"version": "1.0.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@blockly/block-plus-minus": "^3.0.5", "@blockly/block-plus-minus": "^2.0.10",
"@blockly/field-grid-dropdown": "^1.0.31", "@blockly/field-grid-dropdown": "^1.0.25",
"@blockly/field-slider": "^3.0.5", "@blockly/field-slider": "^2.1.1",
"@blockly/plugin-scroll-options": "^2.0.5", "@blockly/plugin-scroll-options": "^1.0.2",
"@blockly/plugin-typed-variable-modal": "^4.0.5", "@blockly/plugin-typed-variable-modal": "^3.1.26",
"@blockly/workspace-backpack": "^2.0.12", "@blockly/zoom-to-fit": "^2.0.7",
"@blockly/zoom-to-fit": "^2.0.14", "@fortawesome/fontawesome-svg-core": "^1.2.30",
"@emotion/react": "^11.10.5", "@fortawesome/free-solid-svg-icons": "^5.14.0",
"@emotion/styled": "^11.10.5", "@fortawesome/react-fontawesome": "^0.1.11",
"@fortawesome/fontawesome-svg-core": "^1.2.36", "@material-ui/core": "^4.11.0",
"@fortawesome/free-solid-svg-icons": "^5.15.4", "@sentry/react": "^6.0.0",
"@fortawesome/react-fontawesome": "^0.1.19", "@sentry/tracing": "^6.0.0",
"@monaco-editor/react": "^4.3.1",
"@mui/lab": "^5.0.0-alpha.110",
"@mui/material": "^5.10.16",
"@mui/styles": "^5.10.16",
"@testing-library/jest-dom": "^5.16.1", "@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2", "@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^7.2.1", "@testing-library/user-event": "^7.2.1",
"axios": "^0.22.0", "axios": "^0.22.0",
"blockly": "^8.0.3", "blockly": "^6.20210701.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.2",
"markdown-it": "^12.3.2",
"mnemonic-id": "^3.2.7", "mnemonic-id": "^3.2.7",
"moment": "^2.29.4", "moment": "^2.28.0",
"prismjs": "^1.27.0", "prismjs": "^1.27.0",
"qrcode.react": "^3.1.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-cookie-consent": "^7.2.1", "react-cookie-consent": "^7.0.0",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-markdown": "^8.0.0", "react-markdown": "^5.0.2",
"react-markdown-editor-lite": "^1.3.3",
"react-mde": "^11.5.0", "react-mde": "^11.5.0",
"react-rating-stars-component": "^2.2.0", "react-redux": "^7.2.4",
"react-redux": "^7.2.9", "react-router-dom": "^5.2.0",
"react-router-dom": "^5.3.3", "react-scripts": "^4.0.3",
"react-scripts": "^5.0.1", "reactour": "^1.18.0",
"react-share": "^4.4.0", "redux": "^4.0.5",
"react-spinners": "^0.13.3", "redux-thunk": "^2.3.0",
"reactour": "^1.18.7",
"redux": "^4.2.0",
"redux-thunk": "^2.4.1",
"rehype-raw": "^6.1.1",
"remark-gemoji": "^7.0.1",
"remark-gfm": "^3.0.1",
"styled-components": "^4.4.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": { "scripts": {
"start": "node_modules/react-scripts/bin/react-scripts.js start", "start": "react-scripts start",
"dev": "set \"REACT_APP_BLOCKLY_API=http://localhost:8080\" && npm start", "dev": "set \"REACT_APP_BLOCKLY_API=http://localhost:8080\" && npm start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
@@ -66,9 +48,16 @@
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"
}, },
"browserslist": [ "browserslist": {
">0.2%", "production": [
"not dead", ">0.2%",
"not op_mini all" "not dead",
] "not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 KiB

+27 -50
View File
@@ -1,74 +1,51 @@
.wrapper { .wrapper {
min-height: calc( min-height: calc(100vh - 60px); /* will cover the 100% of viewport - height of footer (padding-bottom) */
100vh - 60px
); /* will cover the 100% of viewport - height of footer (padding-bottom) */
overflow: hidden; overflow: hidden;
display: block; display: block;
position: relative; position: relative;
padding-bottom: 60px; /* height of your footer + 30px*/ padding-bottom: 60px; /* height of your footer + 30px*/
} }
.tutorial img {
.tutorial img{
display: flex; display: flex;
align-items: center; align-items: center;
max-height: 40vh; max-height: 40vH;
max-width: 100%; max-width: 100%;
margin: auto; margin: auto;
} }
.news img { .news img{
display: flex; display: flex;
align-items: center; align-items: center;
max-height: 40vh; max-height: 40vH;
max-width: 100%; max-width: 100%;
margin: auto; margin: auto;
} }
.tutorial blockquote { .tutorial blockquote{
background: #f9f9f9; background: #f9f9f9;
border-left: 10px solid#4EAF47; border-left: 10px solid#4EAF47;
margin: 1.5em 10px; margin: 1.5em 10px;
padding: 0.5em 10px; padding: 0.5em 10px;
quotes: "\201C""\201D""\2018""\2019"; quotes: "\201C""\201D""\2018""\2019";
} }
blockquote:before { blockquote:before {
color: #4eaf47; color:#4EAF47;
content: open-quote; content: open-quote;
font-size: 4em; font-size: 4em;
line-height: 0.1em; line-height: 0.1em;
margin-right: 0.25em; margin-right: 0.25em;
vertical-align: -0.4em; vertical-align: -0.4em;
} }
blockquote p { blockquote p {
display: inline; display: inline;
} }
.tutorial table,
th,
td {
border: 1px solid #ddd;
}
.tutorial th {
padding-top: 12px;
padding-bottom: 12px;
text-align: left;
border-color: #4eaf47;
color: white;
}
.overlay { .overlay {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
} }
:root {
--url: url('./data/mcu_opacity.png');
}
.blocklySvg {
background-image: var(--url);
background-position: center;
background-repeat: no-repeat;
}
+23 -24
View File
@@ -1,34 +1,35 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { Router } from "react-router-dom"; import { BrowserRouter as Router } from 'react-router-dom';
import { createBrowserHistory } from "history"; import { createBrowserHistory } from "history";
import { Provider } from "react-redux"; import { Provider } from 'react-redux';
import store from "./store"; import store from './store';
import { loadUser } from "./actions/authActions"; import { loadUser } from './actions/authActions';
import "./App.css"; import './App.css';
import { ThemeProvider, StyledEngineProvider, createTheme } from "@mui/material/styles"; import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import Content from "./components/Content"; import Content from './components/Content';
const theme = createTheme({ const theme = createMuiTheme({
palette: { palette: {
primary: { primary: {
main: "#4EAF47", main: '#4EAF47',
contrastText: "#ffffff", contrastText: '#ffffff'
}, },
secondary: { secondary: {
main: "#DDDDDD", main: '#DDDDDD'
}, },
button: { button: {
compile: "#e27136", compile: '#e27136'
}, }
}, }
}); });
class App extends Component { class App extends Component {
componentDidMount() { componentDidMount() {
store.dispatch(loadUser()); store.dispatch(loadUser());
} }
@@ -36,15 +37,13 @@ class App extends Component {
render() { render() {
const customHistory = createBrowserHistory(); const customHistory = createBrowserHistory();
return ( return (
<StyledEngineProvider injectFirst> <ThemeProvider theme={theme}>
<ThemeProvider theme={theme}> <Provider store={store}>
<Provider store={store}> <Router history={customHistory}>
<Router history={customHistory}> <Content />
<Content /> </Router>
</Router> </Provider>
</Provider> </ThemeProvider>
</ThemeProvider>
</StyledEngineProvider>
); );
} }
} }
-150
View File
@@ -1,150 +0,0 @@
import { GET_ARDUINO_EXAMPLES } from './types';
import axios from 'axios';
import { returnErrors, returnSuccess } from './messageActions';
export const getArduinoExamples = () => (dispatch) => {
const config = {
success: res => {
var examples = res.data.arduinoExamples;
console.log(examples);
dispatch({
type: GET_ARDUINO_EXAMPLES,
payload: examples
});
dispatch(returnSuccess(res.data.message, res.status));
},
error: err => {
if (err.response) {
dispatch(returnErrors(err.response.data.message, err.response.status, 'GET_PROJECTS_FAIL'));
}
}
};
axios.get(`${process.env.REACT_APP_BLOCKLY_API}/arduino`, config)
.then(res => {
res.config.success(res);
})
.catch(err => {
err.config.error(err);
});
};
// export const updateProject = (type, id) => (dispatch, getState) => {
// var workspace = getState().workspace;
// var body = {
// xml: workspace.code.xml,
// title: workspace.name,
// board: getState().board.board,
// };
// var project = getState().project;
// if (type === 'gallery') {
// body.description = project.description;
// }
// const config = {
// success: res => {
// var project = res.data[type];
// var projects = getState().project.projects;
// var index = projects.findIndex(res => res._id === project._id);
// projects[index] = project;
// dispatch({
// type: GET_PROJECTS,
// payload: projects
// });
// if (type === 'project') {
// dispatch(returnSuccess(res.data.message, res.status, 'PROJECT_UPDATE_SUCCESS'));
// } else {
// dispatch(returnSuccess(res.data.message, res.status, 'GALLERY_UPDATE_SUCCESS'));
// }
// },
// error: err => {
// if (err.response) {
// if (type === 'project') {
// dispatch(returnErrors(err.response.data.message, err.response.status, 'PROJECT_UPDATE_FAIL'));
// } else {
// dispatch(returnErrors(err.response.data.message, err.response.status, 'GALLERY_UPDATE_FAIL'));
// }
// }
// }
// };
// axios.put(`${process.env.REACT_APP_BLOCKLY_API}/${type}/${id}`, body, config)
// .then(res => {
// res.config.success(res);
// })
// .catch(err => {
// err.config.error(err);
// });
// };
// export const deleteProject = (type, id) => (dispatch, getState) => {
// const config = {
// success: res => {
// var projects = getState().project.projects;
// var index = projects.findIndex(res => res._id === id);
// projects.splice(index, 1)
// dispatch({
// type: GET_PROJECTS,
// payload: projects
// });
// if (type === 'project') {
// dispatch(returnSuccess(res.data.message, res.status, 'PROJECT_DELETE_SUCCESS'));
// } else {
// dispatch(returnSuccess(res.data.message, res.status, 'GALLERY_DELETE_SUCCESS'));
// }
// },
// error: err => {
// dispatch(returnErrors(err.response.data.message, err.response.status, 'PROJECT_DELETE_FAIL'));
// }
// };
// axios.delete(`${process.env.REACT_APP_BLOCKLY_API}/${type}/${id}`, config)
// .then(res => {
// res.config.success(res);
// })
// .catch(err => {
// if (err.response && err.response.status !== 401) {
// err.config.error(err);
// }
// });
// };
// export const shareProject = (title, type, id) => (dispatch, getState) => {
// var body = {
// title: title,
// board: getState().board.board
// };
// if (type === 'project') {
// body.projectId = id;
// } else {
// body.xml = getState().workspace.code.xml;
// }
// axios.post(`${process.env.REACT_APP_BLOCKLY_API}/share`, body)
// .then(res => {
// var shareContent = res.data.content;
// if (body.projectId) {
// var projects = getState().project.projects;
// var index = projects.findIndex(res => res._id === id);
// projects[index].shared = shareContent.expiresAt;
// dispatch({
// type: GET_PROJECTS,
// payload: projects
// });
// }
// dispatch(returnSuccess(res.data.message, shareContent._id, 'SHARE_SUCCESS'));
// })
// .catch(err => {
// if (err.response) {
// dispatch(returnErrors(err.response.data.message, err.response.status, 'SHARE_FAIL'));
// }
// });
// };
// export const resetProject = () => (dispatch) => {
// dispatch({
// type: GET_PROJECTS,
// payload: []
// });
// dispatch(setType(''));
// dispatch(setDescription(''));
// };
+59 -46
View File
@@ -63,56 +63,63 @@ export const loadUser = () => (dispatch) => {
}); });
}; };
var logoutTimerId;
const timeToLogout = 14.9 * 60 * 1000; // nearly 15 minutes corresponding to the API
// Login user // Login user
export const login = export const login =
({ email, password }) => ({ email, password }) =>
(dispatch) => { (dispatch) => {
dispatch({ dispatch({
type: USER_LOADING, type: USER_LOADING,
}); });
// Headers // Headers
const config = { const config = {
headers: { headers: {
"Content-Type": "application/json", "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,
});
});
}; };
// 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,
});
});
};
// Logout User // Logout User
export const logout = () => (dispatch) => { export const logout = () => (dispatch) => {
@@ -137,6 +144,7 @@ export const logout = () => (dispatch) => {
} }
dispatch(setLanguage(locale)); dispatch(setLanguage(locale));
dispatch(returnSuccess(res.data.message, res.status, "LOGOUT_SUCCESS")); dispatch(returnSuccess(res.data.message, res.status, "LOGOUT_SUCCESS"));
clearTimeout(logoutTimerId);
}, },
error: (err) => { error: (err) => {
dispatch( dispatch(
@@ -157,6 +165,7 @@ export const logout = () => (dispatch) => {
type: GET_STATUS, type: GET_STATUS,
payload: status, payload: status,
}); });
clearTimeout(logoutTimerId);
}, },
}; };
axios axios
@@ -213,6 +222,10 @@ export const authInterceptor = () => (dispatch, getState) => {
}) })
.then((res) => { .then((res) => {
if (res.status === 200) { if (res.status === 200) {
clearTimeout(logoutTimerId);
const logoutTimer = () =>
setTimeout(() => dispatch(logout()), timeToLogout);
logoutTimerId = logoutTimer();
dispatch({ dispatch({
type: REFRESH_TOKEN_SUCCESS, type: REFRESH_TOKEN_SUCCESS,
payload: res.data, payload: res.data,
-15
View File
@@ -1,15 +0,0 @@
import {
BOARD,
} from "./types";
import mini_opacity from "../data/mini_opacity.png"
import mcu_opacity from "../data/mcu_opacity.png"
export const setBoard = (board) => (dispatch) => {
window.sessionStorage.setItem("board", board);
const root = document.querySelector(':root');
root.style.setProperty('--url', `url(${board === "mcu" ? mcu_opacity : mini_opacity})`);
dispatch({
type: BOARD,
payload: board,
});
};
+2 -4
View File
@@ -90,8 +90,7 @@ export const updateProject = (type, id) => (dispatch, getState) => {
var workspace = getState().workspace; var workspace = getState().workspace;
var body = { var body = {
xml: workspace.code.xml, xml: workspace.code.xml,
title: workspace.name, title: workspace.name
board: getState().board.board,
}; };
var project = getState().project; var project = getState().project;
if (type === 'gallery') { if (type === 'gallery') {
@@ -166,8 +165,7 @@ export const deleteProject = (type, id) => (dispatch, getState) => {
export const shareProject = (title, type, id) => (dispatch, getState) => { export const shareProject = (title, type, id) => (dispatch, getState) => {
var body = { var body = {
title: title, title: title
board: getState().board.board
}; };
if (type === 'project') { if (type === 'project') {
body.projectId = id; body.projectId = id;
-18
View File
@@ -1,18 +0,0 @@
import axios from 'axios'
const fetchSensorWikiSuccess = sensors => ({
type: 'FETCH_SENSORWIKI_SUCCESS',
payload: { sensors }
})
export const fetchSensors = () => {
return async dispatch => {
try {
let sensors = await axios.get('https://api.sensors.wiki/sensors/all')
dispatch(fetchSensorWikiSuccess(sensors.data))
}
catch(e){
console.log(e)
}
}
}
-71
View File
@@ -84,77 +84,6 @@ 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) => { export const updateStatus = (status) => (dispatch, getState) => {
if (getState().auth.isAuthenticated) { if (getState().auth.isAuthenticated) {
// update user account in database - sync with redux store // update user account in database - sync with redux store
-28
View File
@@ -4,9 +4,6 @@ import {
BUILDER_CHANGE, BUILDER_CHANGE,
BUILDER_ERROR, BUILDER_ERROR,
BUILDER_TITLE, BUILDER_TITLE,
BUILDER_PUBLIC,
BUILDER_DIFFICULTY,
BUILDER_REVIEW,
BUILDER_ID, BUILDER_ID,
BUILDER_ADD_STEP, BUILDER_ADD_STEP,
BUILDER_DELETE_STEP, BUILDER_DELETE_STEP,
@@ -38,30 +35,6 @@ export const tutorialTitle = (title) => (dispatch) => {
dispatch(changeTutorialBuilder()); 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) => { export const tutorialSteps = (steps) => (dispatch) => {
dispatch({ dispatch({
type: BUILDER_ADD_STEP, type: BUILDER_ADD_STEP,
@@ -347,7 +320,6 @@ export const readJSON = (json) => (dispatch, getState) => {
return object; return object;
}); });
dispatch(tutorialTitle(json.title)); dispatch(tutorialTitle(json.title));
dispatch(tutorialDifficulty(json.difficulty));
dispatch(tutorialSteps(steps)); dispatch(tutorialSteps(steps));
dispatch(setSubmitError()); dispatch(setSubmitError());
dispatch(progress(false)); dispatch(progress(false));
-9
View File
@@ -21,7 +21,6 @@ export const NAME = "NAME";
export const TUTORIAL_PROGRESS = "TUTORIAL_PROGRESS"; export const TUTORIAL_PROGRESS = "TUTORIAL_PROGRESS";
export const GET_TUTORIAL = "GET_TUTORIAL"; export const GET_TUTORIAL = "GET_TUTORIAL";
export const GET_TUTORIALS = "GET_TUTORIALS"; export const GET_TUTORIALS = "GET_TUTORIALS";
export const GET_USERTUTORIALS = "GET_USERTUTORIALS";
export const GET_STATUS = "GET_STATUS"; export const GET_STATUS = "GET_STATUS";
export const TUTORIAL_SUCCESS = "TUTORIAL_SUCCESS"; export const TUTORIAL_SUCCESS = "TUTORIAL_SUCCESS";
export const TUTORIAL_ERROR = "TUTORIAL_ERROR"; export const TUTORIAL_ERROR = "TUTORIAL_ERROR";
@@ -33,9 +32,6 @@ export const JSON_STRING = "JSON_STRING";
export const BUILDER_CHANGE = "BUILDER_CHANGE"; export const BUILDER_CHANGE = "BUILDER_CHANGE";
export const BUILDER_TITLE = "BUILDER_TITLE"; 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_ID = "BUILDER_ID";
export const BUILDER_ADD_STEP = "BUILDER_ADD_STEP"; export const BUILDER_ADD_STEP = "BUILDER_ADD_STEP";
export const BUILDER_DELETE_STEP = "BUILDER_DELETE_STEP"; export const BUILDER_DELETE_STEP = "BUILDER_DELETE_STEP";
@@ -63,8 +59,3 @@ export const GET_PROJECT = "GET_PROJECT";
export const GET_PROJECTS = "GET_PROJECTS"; export const GET_PROJECTS = "GET_PROJECTS";
export const PROJECT_TYPE = "PROJECT_TYPE"; export const PROJECT_TYPE = "PROJECT_TYPE";
export const PROJECT_DESCRIPTION = "PROJECT_DESCRIPTION"; export const PROJECT_DESCRIPTION = "PROJECT_DESCRIPTION";
export const GET_ARDUINO_EXAMPLES = "GET_ARDUINO_EXAMPLES";
//board
export const BOARD = "BOARD";
-6
View File
@@ -20,15 +20,9 @@ export const onChangeCode = () => (dispatch, getState) => {
if (selectedBlock !== null) { if (selectedBlock !== null) {
code.helpurl = selectedBlock.helpUrl code.helpurl = selectedBlock.helpUrl
code.tooltip = selectedBlock.tooltip code.tooltip = selectedBlock.tooltip
if (selectedBlock.data) {
code.data = selectedBlock.data
} else {
code.data = null
}
} else if (selectedBlock === null) { } else if (selectedBlock === null) {
code.tooltip = Blockly.Msg.tooltip_hint code.tooltip = Blockly.Msg.tooltip_hint
code.helpurl = '' code.helpurl = ''
code.data = null
} }
+18 -13
View File
@@ -1,29 +1,34 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import withStyles from '@mui/styles/withStyles'; import { withStyles } from '@material-ui/core/styles';
import { alpha } from "@mui/material/styles"; import { fade } from '@material-ui/core/styles/colorManipulator';
import Typography from "@mui/material/Typography"; import Typography from '@material-ui/core/Typography';
const styles = (theme) => ({ const styles = (theme) => ({
alert: { alert: {
marginBottom: "20px", marginBottom: '20px',
border: `1px solid ${theme.palette.primary.main}`, border: `1px solid ${theme.palette.primary.main}`,
padding: "10px 20px", padding: '10px 20px',
borderRadius: "4px", borderRadius: '4px',
background: alpha(theme.palette.primary.main, 0.3), background: fade(theme.palette.primary.main, 0.3),
color: "rgb(70,70,70)", color: 'rgb(70,70,70)'
}, }
}); });
export class Alert extends Component { export class Alert extends Component {
render() {
return ( render(){
return(
<div className={this.props.classes.alert}> <div className={this.props.classes.alert}>
<Typography>{this.props.children}</Typography> <Typography>
{this.props.children}
</Typography>
</div> </div>
); );
} }
} }
export default withStyles(styles, { withTheme: true })(Alert); export default withStyles(styles, { withTheme: true })(Alert);
+1 -1
View File
@@ -27,7 +27,7 @@ import Blockly from "blockly/core";
import "blockly/blocks"; import "blockly/blocks";
import Toolbox from "./toolbox/Toolbox"; import Toolbox from "./toolbox/Toolbox";
import { Card } from "@mui/material"; import { Card } from "@material-ui/core";
import { import {
ScrollOptions, ScrollOptions,
ScrollBlockDragger, ScrollBlockDragger,
+3 -17
View File
@@ -12,7 +12,6 @@ import "./generator/index";
import { ZoomToFitControl } from "@blockly/zoom-to-fit"; import { ZoomToFitControl } from "@blockly/zoom-to-fit";
import { initialXml } from "./initialXml.js"; import { initialXml } from "./initialXml.js";
import { getMaxInstances } from "./helpers/maxInstances"; import { getMaxInstances } from "./helpers/maxInstances";
import { Backpack } from "@blockly/workspace-backpack";
class BlocklyWindow extends Component { class BlocklyWindow extends Component {
constructor(props) { constructor(props) {
@@ -36,23 +35,12 @@ class BlocklyWindow extends Component {
Blockly.svgResize(workspace); Blockly.svgResize(workspace);
const zoomToFit = new ZoomToFitControl(workspace); const zoomToFit = new ZoomToFitControl(workspace);
zoomToFit.init(); zoomToFit.init();
// Initialize plugin.
const backpack = new Backpack(workspace);
backpack.init();
} }
componentDidUpdate(props) { componentDidUpdate(props) {
const workspace = Blockly.getMainWorkspace(); 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_);
}
var xml = this.props.initialXml;
// if svg is true, then the update process is done in the BlocklySvg component // if svg is true, then the update process is done in the BlocklySvg component
if (props.initialXml !== xml && !this.props.svg) { if (props.initialXml !== xml && !this.props.svg) {
// guarantees that the current xml-code (this.props.initialXml) is rendered // guarantees that the current xml-code (this.props.initialXml) is rendered
@@ -63,7 +51,7 @@ class BlocklyWindow extends Component {
if (props.language !== this.props.language) { if (props.language !== this.props.language) {
// change language // change language
if (!xml) xml = initialXml; if (!xml) xml = initialXml;
xmlDom = Blockly.Xml.textToDom(xml); var xmlDom = Blockly.Xml.textToDom(xml);
Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom, workspace); Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom, workspace);
// var toolbox = workspace.getToolbox(); // var toolbox = workspace.getToolbox();
// workspace.updateToolbox(toolbox.toolboxDef_); // workspace.updateToolbox(toolbox.toolboxDef_);
@@ -136,16 +124,14 @@ BlocklyWindow.propTypes = {
onChangeWorkspace: PropTypes.func.isRequired, onChangeWorkspace: PropTypes.func.isRequired,
clearStats: PropTypes.func.isRequired, clearStats: PropTypes.func.isRequired,
renderer: PropTypes.string.isRequired, renderer: PropTypes.string.isRequired,
sounds: PropTypes.bool.isRequired, sounds: PropTypes.string.isRequired,
language: PropTypes.string.isRequired, language: PropTypes.string.isRequired,
selectedBoard: PropTypes.string.isRequired,
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
renderer: state.general.renderer, renderer: state.general.renderer,
sounds: state.general.sounds, sounds: state.general.sounds,
language: state.general.language, language: state.general.language,
selectedBoard: state.board.board,
}); });
export default connect(mapStateToProps, { onChangeWorkspace, clearStats })( export default connect(mapStateToProps, { onChangeWorkspace, clearStats })(
-1
View File
@@ -6,7 +6,6 @@ import "./sensebox-telegram";
import "./sensebox-osem"; import "./sensebox-osem";
import "./sensebox-web"; import "./sensebox-web";
import "./sensebox-display"; import "./sensebox-display";
import "./sensebox-motors";
import "./sensebox-lora"; import "./sensebox-lora";
import "./sensebox-led"; import "./sensebox-led";
import "./sensebox-rtc"; import "./sensebox-rtc";
+10 -2
View File
@@ -45,11 +45,15 @@ Blockly.Blocks['sensebox_rgb_led'] = {
Blockly.Blocks['sensebox_ws2818_led_init'] = { Blockly.Blocks['sensebox_ws2818_led_init'] = {
init: function () { 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.setColour(getColour().sensebox);
this.appendDummyInput() this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_ws2818_rgb_led_init) .appendField(Blockly.Msg.senseBox_ws2818_rgb_led_init)
.appendField("Port:") .appendField("Port:")
.appendField(new Blockly.FieldDropdown(selectedBoard().digitalPinsRGB), "Port") .appendField(new Blockly.FieldDropdown(dropdownOptions), "Port")
this.appendValueInput("BRIGHTNESS", "brightness") this.appendValueInput("BRIGHTNESS", "brightness")
.appendField((Blockly.Msg.senseBox_ws2818_rgb_led_brightness)); .appendField((Blockly.Msg.senseBox_ws2818_rgb_led_brightness));
this.appendValueInput("NUMBER", "number") this.appendValueInput("NUMBER", "number")
@@ -62,11 +66,15 @@ Blockly.Blocks['sensebox_ws2818_led_init'] = {
Blockly.Blocks['sensebox_ws2818_led'] = { Blockly.Blocks['sensebox_ws2818_led'] = {
init: function () { 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.setColour(getColour().sensebox);
this.appendDummyInput() this.appendDummyInput()
.appendField(Blockly.Msg.senseBox_ws2818_rgb_led) .appendField(Blockly.Msg.senseBox_ws2818_rgb_led)
.appendField("Port:") .appendField("Port:")
.appendField(new Blockly.FieldDropdown(selectedBoard().digitalPinsRGB), "Port") .appendField(new Blockly.FieldDropdown(dropdownOptions), "Port")
this.appendValueInput("POSITION", "position") this.appendValueInput("POSITION", "position")
.appendField((Blockly.Msg.senseBox_ws2818_rgb_led_position)); .appendField((Blockly.Msg.senseBox_ws2818_rgb_led_position));
this.appendValueInput("COLOR", 'Number') this.appendValueInput("COLOR", 'Number')
@@ -1,144 +0,0 @@
import * as Blockly from "blockly/core";
import { getColour } from "../helpers/colour";
import { selectedBoard } from "../helpers/board";
import { FieldSlider } from "@blockly/field-slider";
/**
* Servo Motor
*
*/
Blockly.Blocks["sensebox_motors_beginServoMotor"] = {
init: function () {
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_motors_beginServoMotor);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_motors_beginServoMotor_pin)
.appendField(new Blockly.FieldDropdown(selectedBoard().digitalPins), "pin")
.setAlign(Blockly.ALIGN_RIGHT);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(getColour().motors);
this.setTooltip(Blockly.Msg.sensebox_motors_beginServoMotor_tooltip);
this.setHelpUrl(Blockly.Msg.sensebox_motors_beginServoMotor_helpurl);
},
};
Blockly.Blocks["sensebox_motors_moveServoMotor"] = {
init: function () {
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_motors_moveServoMotor);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_motors_moveServoMotor_pin)
.appendField(new Blockly.FieldDropdown(selectedBoard().digitalPins), "pin")
.setAlign(Blockly.ALIGN_RIGHT);
this.appendValueInput("degrees", "Number")
.appendField(Blockly.Msg.sensebox_motors_moveServoMotor_degrees)
.setAlign(Blockly.ALIGN_RIGHT);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(getColour().motors);
this.setTooltip(Blockly.Msg.sensebox_motors_moveServoMotor_tooltip);
this.setHelpUrl(Blockly.Msg.sensebox_motors_moveServoMotor_helpurl);
},
};
/**
* I2C Motor Board
*
*/
Blockly.Blocks["sensebox_motors_I2CMotorBoard_begin"] = {
init: function () {
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_motors_I2CMotorBoard_begin);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(getColour().motors);
this.setTooltip(Blockly.Msg.sensebox_motors_I2CMotorBoard_begin_tooltip);
this.setHelpUrl(Blockly.Msg.sensebox_motors_I2CMotorBoard_begin_helpurl);
},
};
Blockly.Blocks["sensebox_motors_I2CMotorBoard_moveDCMotor"] = {
init: function () {
var dropdownOptions = [[Blockly.Msg.sensebox_motors_I2CMotorBoard_moveDCMotor_left, '1'],
[Blockly.Msg.sensebox_motors_I2CMotorBoard_moveDCMotor_right, '2']];
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_motors_I2CMotorBoard_moveDCMotor)
.appendField(new Blockly.FieldDropdown(dropdownOptions), "motor")
.appendField(Blockly.Msg.sensebox_motors_I2CMotorBoard_moveDCMotor_motor);
this.appendValueInput("speed", "Number")
.appendField(Blockly.Msg.sensebox_motors_I2CMotorBoard_moveDCMotor_speed)
.setAlign(Blockly.ALIGN_RIGHT);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(getColour().motors);
this.setTooltip(Blockly.Msg.sensebox_motors_I2CMotorBoard_moveDCMotor_tooltip);
this.setHelpUrl(Blockly.Msg.sensebox_motors_I2CMotorBoard_moveDCMotor_helpurl);
},
};
Blockly.Blocks["sensebox_motors_I2CMotorBoard_stopDCMotor"] = {
init: function () {
var dropdownOptions = [[Blockly.Msg.sensebox_motors_I2CMotorBoard_stopDCMotor_left, '1'],
[Blockly.Msg.sensebox_motors_I2CMotorBoard_stopDCMotor_right, '2']];
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_motors_I2CMotorBoard_stopDCMotor)
.appendField(new Blockly.FieldDropdown(dropdownOptions), "motor")
.appendField(Blockly.Msg.sensebox_motors_I2CMotorBoard_stopDCMotor_motor)
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(getColour().motors);
this.setTooltip(Blockly.Msg.sensebox_motors_I2CMotorBoard_stopDCMotor_tooltip);
this.setHelpUrl(Blockly.Msg.sensebox_motors_I2CMotorBoard_stopDCMotor_helpurl);
},
};
/**
* Stepper Motor
*
*/
Blockly.Blocks["sensebox_motors_beginStepperMotor"] = {
init: function () {
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_motors_beginStepperMotor);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_motors_beginStepperMotor_pins);
this.appendDummyInput()
.setAlign(Blockly.ALIGN_RIGHT)
.appendField(new Blockly.FieldDropdown(selectedBoard().digitalPins), "in1")
.appendField(new Blockly.FieldDropdown(selectedBoard().digitalPins), "in2")
.appendField(new Blockly.FieldDropdown(selectedBoard().digitalPins), "in3")
.appendField(new Blockly.FieldDropdown(selectedBoard().digitalPins), "in4");
this.setFieldValue("1", "in1");
this.setFieldValue("2", "in2");
this.setFieldValue("3", "in3");
this.setFieldValue("4", "in4");
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_motors_beginStepperMotor_rpm)
.appendField(new FieldSlider(3, 1, 3), "rpm");
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(getColour().motors);
this.setTooltip(Blockly.Msg.sensebox_motors_beginStepperMotor_tooltip);
this.setHelpUrl(Blockly.Msg.sensebox_motors_beginStepperMotor_helpurl);
},
};
Blockly.Blocks["sensebox_motors_moveStepperMotor"] = {
init: function () {
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_motors_moveStepperMotor);
this.appendValueInput("steps", "Number")
.setAlign(Blockly.ALIGN_RIGHT)
.appendField(Blockly.Msg.sensebox_motors_moveStepperMotor_step);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(getColour().motors);
this.setTooltip(Blockly.Msg.sensebox_motors_moveStepperMotor_tooltip);
this.setHelpUrl(Blockly.Msg.sensebox_motors_moveStepperMotor_helpurl);
},
};
@@ -26,7 +26,6 @@ Blockly.Blocks["sensebox_sensor_temp_hum"] = {
this.setColour(getColour().sensebox); this.setColour(getColour().sensebox);
this.setTooltip(Blockly.Msg.senseBox_temp_hum_tooltip); this.setTooltip(Blockly.Msg.senseBox_temp_hum_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_temp_hum_helpurl); this.setHelpUrl(Blockly.Msg.senseBox_temp_hum_helpurl);
this.data = {name: "hdc1080", connection: "I2C"};
}, },
}; };
@@ -52,7 +51,6 @@ Blockly.Blocks["sensebox_sensor_uv_light"] = {
this.setColour(getColour().sensebox); this.setColour(getColour().sensebox);
this.setTooltip(Blockly.Msg.senseBox_uv_light_tooltip); this.setTooltip(Blockly.Msg.senseBox_uv_light_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_uv_light_helpurl); this.setHelpUrl(Blockly.Msg.senseBox_uv_light_helpurl);
this.data = {name: "veml6070"};
}, },
}; };
@@ -93,7 +91,6 @@ Blockly.Blocks["sensebox_sensor_bmx055_accelerometer"] = {
this.setColour(getColour().sensebox); this.setColour(getColour().sensebox);
this.setTooltip(Blockly.Msg.senseBox_bmx055_accelerometer_tooltip); this.setTooltip(Blockly.Msg.senseBox_bmx055_accelerometer_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_bmx055_helpurl); this.setHelpUrl(Blockly.Msg.senseBox_bmx055_helpurl);
this.data = {name: "bmx055"};
}, },
}; };
@@ -117,15 +114,16 @@ Blockly.Blocks["sensebox_sensor_sds011"] = {
) )
.appendField(Blockly.Msg.senseBox_sds011_dimension) .appendField(Blockly.Msg.senseBox_sds011_dimension)
.appendField( .appendField(
new Blockly.FieldDropdown( new Blockly.FieldDropdown([
selectedBoard().serial), [Blockly.Msg.senseBox_sds011_serial1, "Serial1"],
[Blockly.Msg.senseBox_sds011_serial2, "Serial2"],
]),
"SERIAL" "SERIAL"
); );
this.setOutput(true, Types.DECIMAL.typeName); this.setOutput(true, Types.DECIMAL.typeName);
this.setColour(getColour().sensebox); this.setColour(getColour().sensebox);
this.setTooltip(Blockly.Msg.senseBox_sds011_tooltip); this.setTooltip(Blockly.Msg.senseBox_sds011_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_sds011_helpurl); this.setHelpUrl(Blockly.Msg.senseBox_sds011_helpurl);
this.data = {name: "sds011"};
}, },
}; };
@@ -159,7 +157,6 @@ Blockly.Blocks["sensebox_sensor_pressure"] = {
this.setOutput(true, Types.DECIMAL.typeName); this.setOutput(true, Types.DECIMAL.typeName);
this.setTooltip(Blockly.Msg.senseBox_pressure_tooltip); this.setTooltip(Blockly.Msg.senseBox_pressure_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_pressure_helpurl); this.setHelpUrl(Blockly.Msg.senseBox_pressure_helpurl);
this.data = {name: "bmp280"};
this.getField("NAME").setValidator( this.getField("NAME").setValidator(
function (val) { function (val) {
this.updateShape_(val === "Altitude"); this.updateShape_(val === "Altitude");
@@ -206,7 +203,6 @@ Blockly.Blocks["sensebox_sensor_bme680_bsec"] = {
this.setColour(getColour().sensebox); this.setColour(getColour().sensebox);
this.setTooltip(Blockly.Msg.senseBox_bme_tooltip); this.setTooltip(Blockly.Msg.senseBox_bme_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_bme680_helpurl); this.setHelpUrl(Blockly.Msg.senseBox_bme680_helpurl);
this.data = {name: "bme680"};
}, },
}; };
@@ -251,7 +247,6 @@ Blockly.Blocks["sensebox_sensor_ultrasonic_ranger"] = {
this.setOutput(true, Types.NUMBER.typeName); this.setOutput(true, Types.NUMBER.typeName);
this.setTooltip(Blockly.Msg.senseBox_ultrasonic_tooltip); this.setTooltip(Blockly.Msg.senseBox_ultrasonic_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_ultrasonic_helpurl); this.setHelpUrl(Blockly.Msg.senseBox_ultrasonic_helpurl);
this.data = {name: "hc-sr04"};
}, },
/** /**
* Parse XML to restore the number of pins available. * Parse XML to restore the number of pins available.
@@ -389,7 +384,6 @@ Blockly.Blocks["sensebox_scd30"] = {
this.setColour(getColour().sensebox); this.setColour(getColour().sensebox);
this.setTooltip(Blockly.Msg.senseBox_scd_tooltip); this.setTooltip(Blockly.Msg.senseBox_scd_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_scd_helpurl); this.setHelpUrl(Blockly.Msg.senseBox_scd_helpurl);
this.data = {name: "scd30"};
}, },
onchange: function (e) { onchange: function (e) {
var dropdown = this.getFieldValue("dropdown"); var dropdown = this.getFieldValue("dropdown");
@@ -456,7 +450,6 @@ Blockly.Blocks["sensebox_sensor_truebner_smt50"] = {
this.setOutput(true, Types.NUMBER.typeName); this.setOutput(true, Types.NUMBER.typeName);
this.setTooltip(Blockly.Msg.senseBox_smt50_tooltip); this.setTooltip(Blockly.Msg.senseBox_smt50_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_smt50_helpurl); this.setHelpUrl(Blockly.Msg.senseBox_smt50_helpurl);
this.data = {name: "smt50"};
}, },
}; };
@@ -479,7 +472,6 @@ Blockly.Blocks["sensebox_sensor_watertemperature"] = {
.appendField(new Blockly.FieldDropdown(dropdownOptions), "Port"); .appendField(new Blockly.FieldDropdown(dropdownOptions), "Port");
this.setOutput(true, Types.NUMBER.typeName); this.setOutput(true, Types.NUMBER.typeName);
this.setTooltip(Blockly.Msg.senseBox_watertemperature_tip); this.setTooltip(Blockly.Msg.senseBox_watertemperature_tip);
this.data = {name: "ds18b20"};
}, },
}; };
@@ -522,6 +514,40 @@ Blockly.Blocks["sensebox_soundsensor_dfrobot"] = {
}, },
}; };
/**
* rainsensor hydreon rg-15
*/
Blockly.Blocks["sensebox_rainsensor_hydreon_rg15"] = {
init: function () {
var dropdownOptionsPorts = [
[Blockly.Msg.sensebox_rainsensor_hydreon_rg15_serial1, "Serial1"],
[Blockly.Msg.sensebox_rainsensor_hydreon_rg15_serial2, "Serial2"],
];
var dropdownOptionsValues = [
[Blockly.Msg.sensebox_rainsensor_hydreon_rg15_acc, "getAccumulation"],
[Blockly.Msg.sensebox_rainsensor_hydreon_rg15_totalAcc, "getTotalAccumulation"],
[Blockly.Msg.sensebox_rainsensor_hydreon_rg15_eventAcc, "getEventAccumulation"],
[Blockly.Msg.sensebox_rainsensor_hydreon_rg15_rainInt, "getRainfallIntensity"],
];
this.setColour(getColour().sensebox);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_rainsensor_hydreon_rg15);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_rainsensor_hydreon_rg15_port)
.appendField(new Blockly.FieldDropdown(dropdownOptionsPorts), "SERIAL");
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_rainsensor_hydreon_rg15_value)
.appendField(new Blockly.FieldDropdown(dropdownOptionsValues), "VALUE");
this.setOutput(true, Types.DECIMAL.typeName);
this.setTooltip(Blockly.Msg.sensebox_rainsensor_hydreon_rg15_tooltip);
this.setHelpUrl(Blockly.Msg.sensebox_rainsensor_hydreon_rg15_helpurl);
},
};
/** /**
* Infineon DPS310 Pressure Sensor * Infineon DPS310 Pressure Sensor
* *
@@ -552,7 +578,6 @@ Blockly.Blocks["sensebox_sensor_dps310"] = {
this.setOutput(true, Types.DECIMAL.typeName); this.setOutput(true, Types.DECIMAL.typeName);
this.setTooltip(Blockly.Msg.senseBox_sensor_dps310_tooltip); this.setTooltip(Blockly.Msg.senseBox_sensor_dps310_tooltip);
this.setHelpUrl(Blockly.Msg.senseBox_sensor_dps310_helpurl); this.setHelpUrl(Blockly.Msg.senseBox_sensor_dps310_helpurl);
this.data = {name: "dps310"};
this.getField("NAME").setValidator( this.getField("NAME").setValidator(
function (val) { function (val) {
this.updateShape_(val === "Altitude"); this.updateShape_(val === "Altitude");
+3 -3
View File
@@ -9,10 +9,10 @@ Blockly.Blocks["variables_set_dynamic"] = {
this.setPreviousStatement(true, null); this.setPreviousStatement(true, null);
this.setNextStatement(true, null); this.setNextStatement(true, null);
this.appendValueInput("VALUE") this.appendValueInput("VALUE")
.appendField(Blockly.Msg.variables_set, Blockly.Msg.variables_set) .appendField("set", "set")
// .appendField("", "type") .appendField("", "type")
.appendField(new Blockly.FieldVariable("VAR"), "VAR") .appendField(new Blockly.FieldVariable("VAR"), "VAR")
.appendField(Blockly.Msg.variables_to); .appendField("to");
}, },
onchange: function (e) { onchange: function (e) {
let variableID = this.getFieldValue("VAR"); let variableID = this.getFieldValue("VAR");
@@ -148,7 +148,53 @@ Blockly["Arduino"].init = function (workspace) {
// Blockly.Names.DEVELOPER_VARIABLE_TYPE)); // 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;
}; };
/** /**
@@ -5,7 +5,6 @@ import "./sensebox-telegram";
import "./sensebox-osem"; import "./sensebox-osem";
import "./sensebox-web"; import "./sensebox-web";
import "./sensebox-display"; import "./sensebox-display";
import "./sensebox-motors";
import "./sensebox-lora"; import "./sensebox-lora";
import "./sensebox-led"; import "./sensebox-led";
import "./sensebox"; import "./sensebox";
@@ -101,6 +101,7 @@ Blockly.Arduino.sensebox_phyphox_graph = function () {
Blockly.Arduino.sensebox_phyphox_experiment_send = function () { Blockly.Arduino.sensebox_phyphox_experiment_send = function () {
var branch = Blockly.Arduino.statementToCode(this, "sendValues"); var branch = Blockly.Arduino.statementToCode(this, "sendValues");
var blocks = this.getDescendants(); var blocks = this.getDescendants();
console.log(blocks);
var count = 0; var count = 0;
if (blocks !== undefined) { if (blocks !== undefined) {
for (var i = 0; i < blocks.length; i++) { for (var i = 0; i < blocks.length; i++) {
@@ -114,6 +115,7 @@ Blockly.Arduino.sensebox_phyphox_experiment_send = function () {
var string = ""; var string = "";
for (var j = 1; j <= count; j++) { for (var j = 1; j <= count; j++) {
console.log("append");
if (string === "") { if (string === "") {
string += `channel${j}`; string += `channel${j}`;
} else if (string !== "") { } else if (string !== "") {
@@ -1,77 +0,0 @@
import * as Blockly from "blockly/core";
/**
* Servo Motor
*
*/
Blockly.Arduino.sensebox_motors_beginServoMotor = function () {
var pin = this.getFieldValue("pin");
Blockly.Arduino.libraries_["library_senseBoxIO"] = "#include <senseBoxIO.h>";
Blockly.Arduino.libraries_["include_servo_motor"] = "#include <Servo.h>";
Blockly.Arduino.definitions_[`define_servo_motor_${pin}`] = `Servo servo_motor_${pin}; // servo Motor`;
Blockly.Arduino.setupCode_[`setup_servo_motor_${pin}`] = `servo_motor_${pin}.attach(${pin}); // attach servo motor to pin ${pin}`;
var code = "";
return code;
};
Blockly.Arduino.sensebox_motors_moveServoMotor = function () {
var pin = this.getFieldValue("pin");
var degrees = Blockly.Arduino.valueToCode(this, 'degrees', Blockly.Arduino.ORDER_ATOMIC) || "90";
var code = `servo_motor_${pin}.write(${degrees}); // move servo motor to ${degrees} degrees\n`;
return code;
};
/**
* I2C Motor Board
*
*/
Blockly.Arduino.sensebox_motors_I2CMotorBoard_begin = function () {
Blockly.Arduino.libraries_["library_senseBoxIO"] = "#include <senseBoxIO.h>";
Blockly.Arduino.libraries_["include_i2c_motor_board"] = "#include <Grove_I2C_Motor_Driver.h>";
Blockly.Arduino.definitions_["define_i2c_motor_board"] = `
#define I2C_MOTOR_BOARD_ADDRESS 0x0f // default I2C address of I2C Motor Board`;
Blockly.Arduino.setupCode_["setup_i2c_motor_board"] = `
Motor.begin(I2C_MOTOR_BOARD_ADDRESS); // Initialize I2C Motor Board`;
var code = "";
return code;
};
Blockly.Arduino.sensebox_motors_I2CMotorBoard_moveDCMotor = function () {
var motor = this.getFieldValue("motor");
var speed = Blockly.Arduino.valueToCode(this, 'speed', Blockly.Arduino.ORDER_ATOMIC) || "50";
var code = `Motor.speed(MOTOR${motor}, ${speed}); // set speed of motor\n`;
return code;
};
Blockly.Arduino.sensebox_motors_I2CMotorBoard_stopDCMotor = function () {
var motor = this.getFieldValue("motor");
var code = `Motor.stop(MOTOR${motor}); // stop motor\n`;
return code;
};
/**
* Stepper Motor
*/
Blockly.Arduino.sensebox_motors_beginStepperMotor = function () {
var in1 = this.getFieldValue("in1");
var in2 = this.getFieldValue("in2");
var in3 = this.getFieldValue("in3");
var in4 = this.getFieldValue("in4");
var rpm = this.getFieldValue("rpm");
Blockly.Arduino.libraries_["library_senseBoxIO"] = "#include <senseBoxIO.h>";
Blockly.Arduino.libraries_["include_stepper_motor"] = "#include <Stepper.h>";
Blockly.Arduino.definitions_["define_stepper_motor"] = `
Stepper stepper_motor(2048, ${in1}, ${in2}, ${in3}, ${in4}); // stepper Motor with 2048 steps per rotation`;
Blockly.Arduino.setupCode_["setup_stepper_motor"] =
`stepper_motor.setSpeed(${rpm}); // speed in rotations per minute`;
var code = "";
return code;
};
Blockly.Arduino.sensebox_motors_moveStepperMotor = function () {
var steps = Blockly.Arduino.valueToCode(this, 'steps', Blockly.Arduino.ORDER_ATOMIC) || '2048';
var code = `stepper_motor.step(${steps}); // 2048 steps correspond to one rotation\n`;
return code;
};
@@ -203,5 +203,6 @@ Blockly.Arduino.sensebox_sd_save_for_osem = function (block) {
Blockly.Arduino.definitions_["SENSOR_ID" + id + ""] = Blockly.Arduino.definitions_["SENSOR_ID" + id + ""] =
"const char SENSOR_ID" + id + '[] PROGMEM = "' + sensor_id + '";'; "const char SENSOR_ID" + id + '[] PROGMEM = "' + sensor_id + '";';
code += "addMeasurement(SENSOR_ID" + id + "," + sensor_value + ");\n"; code += "addMeasurement(SENSOR_ID" + id + "," + sensor_value + ");\n";
console.log(code);
return code; return code;
}; };
@@ -654,6 +654,23 @@ float getSoundValue(){
return [code, Blockly.Arduino.ORDER_ATOMIC]; return [code, Blockly.Arduino.ORDER_ATOMIC];
}; };
/**
* rainsensor hydreon rg-15
*/
Blockly.Arduino.sensebox_rainsensor_hydreon_rg15 = function () {
var port = this.getFieldValue("SERIAL");
var value = this.getFieldValue("VALUE");
Blockly.Arduino.libraries_["library_senseBoxIO"] = "#include <senseBoxIO.h>";
Blockly.Arduino.libraries_["library_rainsensor_rg15"] = "#include <hydreon.h>";
Blockly.Arduino.definitions_["def_rainsensor_rg15_" + port] = "HYDREON rainsensor_" + port + "(" + port + ");";
Blockly.Arduino.setupCode_["setup_rainsensor_rg15_" + port] = "rainsensor_" + port + ".begin();";
Blockly.Arduino.loopCodeOnce_["loop_rainsensor_rg15_" + port] = "rainsensor_" + port + ".readAllData();"
var code = "rainsensor_" + port + "." + value + "()";
return [code, Blockly.Arduino.ORDER_ATOMIC];
};
/** /**
* Infineon DPS310 Pressure Sensor * Infineon DPS310 Pressure Sensor
* *
@@ -1,12 +1,4 @@
import Blockly from "blockly"; 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*/ /* Wifi connection and openSenseMap Blocks*/
Blockly.Arduino.sensebox_wifi = function (block) { Blockly.Arduino.sensebox_wifi = function (block) {
@@ -118,5 +110,3 @@ Blockly.Arduino.sensebox_ethernetIp = function () {
var code = "Ethernet.localIP()"; var code = "Ethernet.localIP()";
return [code, Blockly.Arduino.ORDER_ATOMIC]; return [code, Blockly.Arduino.ORDER_ATOMIC];
}; };
+34 -31
View File
@@ -2,17 +2,10 @@ import Blockly from "blockly";
const setVariableFunction = function (defaultValue) { const setVariableFunction = function (defaultValue) {
return function (block) { return function (block) {
var id = block.getFieldValue("VAR"); const variableName = Blockly["Arduino"].nameDB_.getName(
block.getFieldValue("VAR"),
const variableName = Blockly.Variables.getVariable( Blockly.Variables.NAME_TYPE
Blockly.getMainWorkspace(), );
id
).name;
// const variableName = Blockly["Arduino"].nameDB_.getName(
// id,
// Blockly.Variables.NAME_TYPE
// );
const variableValue = Blockly["Arduino"].valueToCode( const variableValue = Blockly["Arduino"].valueToCode(
block, block,
"VALUE", "VALUE",
@@ -24,32 +17,42 @@ const setVariableFunction = function (defaultValue) {
.getAllVariables(); .getAllVariables();
const myVar = allVars.filter((v) => v.name === variableName)[0]; const myVar = allVars.filter((v) => v.name === variableName)[0];
var code = ""; var code = "";
if (myVar !== undefined) {
Blockly.Arduino.variables_[variableName + myVar.type] = switch (myVar.type) {
myVar.type + " " + myVar.name + ";\n"; default:
code = variableName + " = " + (variableValue || defaultValue) + ";\n"; 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;
} }
return code; return code;
}; };
}; };
const getVariableFunction = function (block) { const getVariableFunction = function (block) {
var id = block.getFieldValue("VAR"); const variableName = Blockly["Arduino"].nameDB_.getName(
block.getFieldValue("VAR"),
const variableName = Blockly.Variables.getVariable( Blockly.Variables.NAME_TYPE
Blockly.getMainWorkspace(), );
id var code = variableName;
).name;
const allVars = Blockly.getMainWorkspace()
.getVariableMap()
.getAllVariables();
const myVar = allVars.filter((v) => v.name === variableName)[0];
// const variableName = Blockly["Arduino"].nameDB_.getName(
// block.getFieldValue("VAR"),
// Blockly.Variables.NAME_TYPE
// );
var code = myVar.name;
return [code, Blockly["Arduino"].ORDER_ATOMIC]; return [code, Blockly["Arduino"].ORDER_ATOMIC];
}; };
+1 -124
View File
@@ -23,11 +23,6 @@ const sensebox_mcu = {
["C5", "5"], ["C5", "5"],
["C6", "6"], ["C6", "6"],
], ],
digitalPinsRGB: [
["A", "1"],
["B", "3"],
["C", "5"],
],
digitalPinsButton: [ digitalPinsButton: [
["on Board", "0"], ["on Board", "0"],
["A1", "1"], ["A1", "1"],
@@ -131,124 +126,6 @@ const sensebox_mcu = {
parseKey: "_*_", parseKey: "_*_",
}; };
//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 = () => { export const selectedBoard = () => {
return board; return sensebox_mcu;
}; };
-1
View File
@@ -14,7 +14,6 @@ const colours = {
serial: 230, serial: 230,
webserver: 40, webserver: 40,
phyphox: 25, phyphox: 25,
motors: 190,
}; };
export const getColour = () => { export const getColour = () => {
-2
View File
@@ -7,7 +7,6 @@ import { LOOPS } from "./de/loops";
import { MATH } from "./de/math"; import { MATH } from "./de/math";
import { MQTT } from "./de/mqtt"; import { MQTT } from "./de/mqtt";
import { DISPLAY } from "./de/sensebox-display"; import { DISPLAY } from "./de/sensebox-display";
import { MOTORS } from "./de/sensebox-motors";
import { LED } from "./de/sensebox-led"; import { LED } from "./de/sensebox-led";
import { LORA } from "./de/sensebox-lora"; import { LORA } from "./de/sensebox-lora";
import { OSEM } from "./de/sensebox-osem"; import { OSEM } from "./de/sensebox-osem";
@@ -35,7 +34,6 @@ export const De = {
...MATH, ...MATH,
...MQTT, ...MQTT,
...DISPLAY, ...DISPLAY,
...MOTORS,
...LED, ...LED,
...LORA, ...LORA,
...OSEM, ...OSEM,
@@ -1,48 +0,0 @@
export const MOTORS = {
/**
* Motors
*/
sensebox_motors_beginServoMotor: "Initialisiere Servomotor",
sensebox_motors_beginServoMotor_pin: "Pin:",
sensebox_motors_beginServoMotor_tooltip: "Verbinde einen digitalen Pin mit dem orangen Kabel des Servomotrors. Verbinde zusätzlich GND mit GND (schwarzes und braunes Kabel) und 5V mit 5V (rotes Kabel).",
sensebox_motors_beginServoMotor_helpurl: "https://de.wikipedia.org/wiki/Servomotor",
sensebox_motors_moveServoMotor: "Bewege Servomotor",
sensebox_motors_moveServoMotor_pin: "Pin:",
sensebox_motors_moveServoMotor_degrees: "Winkel in Grad:",
sensebox_motors_moveServoMotor_tooltip: "Der Servomotor kann zwischen 0 und 180 Grad bewegt werden.",
sensebox_motors_moveServoMotor_helpurl: "https://de.wikipedia.org/wiki/Servomotor",
sensebox_motors_I2CMotorBoard_begin: "Initialize I2C Motor Board",
sensebox_motors_I2CMotorBoard_begin_tooltip: "Connect the I2C Moztor Board with the two DC Motors to one of the five I2C-Ports. Use this Block inside the setup()-function.",
sensebox_motors_I2CMotorBoard_begin_helpurl: "TODO",
sensebox_motors_I2CMotorBoard_moveDCMotor: "Move",
sensebox_motors_I2CMotorBoard_moveDCMotor_left: "left",
sensebox_motors_I2CMotorBoard_moveDCMotor_right: "right",
sensebox_motors_I2CMotorBoard_moveDCMotor_motor: "DC Motor at I2C Motor Board",
sensebox_motors_I2CMotorBoard_moveDCMotor_speed: "Speed:",
sensebox_motors_I2CMotorBoard_moveDCMotor_tooltip: "You can set the Speed between -100 and 100.",
sensebox_motors_I2CMotorBoard_moveDCMotor_helpurl: "TODO",
sensebox_motors_I2CMotorBoard_stopDCMotor: "Stop ",
sensebox_motors_I2CMotorBoard_stopDCMotor_left: "left",
sensebox_motors_I2CMotorBoard_stopDCMotor_right: "right",
sensebox_motors_I2CMotorBoard_stopDCMotor_motor: "DC Motor at I2C Motor Board",
sensebox_motors_I2CMotorBoard_stopDCMotor_tooltip: "TODO",
sensebox_motors_I2CMotorBoard_stopDCMotor_helpurl: "TODO",
sensebox_motors_beginStepperMotor: "Initialize Stepper Motor (28BYJ-48)",
sensebox_motors_beginStepperMotor_rpm: "Rotations per Minute:",
sensebox_motors_beginStepperMotor_pins: "Input Pins (IN1-IN4):",
sensebox_motors_beginStepperMotor_tooltip: "Connect four of the the digital pins in the right order to the inputs IN1-IN4 on the stepper motor board. Connect a GND pin (black wire) to the minus pin and a 5V pin (red wire) to the plus pin on the board. Also connect the stepper motor to this Board. Use this Block inside the setup()-function.",
sensebox_motors_beginStepperMotor_helpurl: "TODO",
sensebox_motors_moveStepperMotor: "Move Stepper Motor (28BYJ-48)",
sensebox_motors_moveStepperMotor_step: "Steps:",
sensebox_motors_moveStepperMotor_tooltip: "Move stepper motor. 2048 steps equal a full rotation.",
sensebox_motors_moveStepperMotor_helpurl: "TODO",
};
@@ -132,10 +132,25 @@ Wenn die maximale Distanz überschritten wird, wird ein Wert von **O** ausgegebe
"Schließe den Sensor an einen der 3 **digital/analog Ports** an. Der Sensor gibt dir den Messwert in dB mit einer Nachkommastelle", "Schließe den Sensor an einen der 3 **digital/analog Ports** an. Der Sensor gibt dir den Messwert in dB mit einer Nachkommastelle",
senseBox_soundsensor_dfrobot_helpurl: senseBox_soundsensor_dfrobot_helpurl:
"https://docs.sensebox.de/hardware/sensoren-lautstaerke/", "https://docs.sensebox.de/hardware/sensoren-lautstaerke/",
/*
* BME680
*/
/*
* rainsensor hydreon rg-15
*/
sensebox_rainsensor_hydreon_rg15: "Regensensor (RG-15)",
sensebox_rainsensor_hydreon_rg15_port: "Serieller Port:",
sensebox_rainsensor_hydreon_rg15_serial1: "Serial1",
sensebox_rainsensor_hydreon_rg15_serial2: "Serial2",
sensebox_rainsensor_hydreon_rg15_value: "Messwert:",
sensebox_rainsensor_hydreon_rg15_totalAcc: "gesamter Niederschlag in mm",
sensebox_rainsensor_hydreon_rg15_rainInt: "Niederschlagsintensität in mm/h",
sensebox_rainsensor_hydreon_rg15_eventAcc: "Niederschlag pro Event in mm",
sensebox_rainsensor_hydreon_rg15_acc: "Niederschlag seit letzter Messung in mm",
sensebox_rainsensor_hydreon_rg15_tooltip: "Regensensor (RG-15) an einen der Seriellen Ports anschließen.",
sensebox_rainsensor_hydreon_rg15_helpurl: "https://rainsensors.com/products/rg-15/",
/*
* BME680
*/
senseBox_bme680: "Umweltsensor (BME680)", senseBox_bme680: "Umweltsensor (BME680)",
senseBox_bme_iaq: "Innenraumluftqualität (IAQ)", senseBox_bme_iaq: "Innenraumluftqualität (IAQ)",
senseBox_bme_iaq_accuracy: "Kalibrierungswert", senseBox_bme_iaq_accuracy: "Kalibrierungswert",
+3 -46
View File
@@ -12,19 +12,16 @@ export const UI = {
toolbox_variables: "Variablen", toolbox_variables: "Variablen",
toolbox_serial: "Seriell", toolbox_serial: "Seriell",
toolbox_advanced: "Erweitert", toolbox_advanced: "Erweitert",
toolbox_motors: "Motoren",
variable_NUMBER: "Zahl (int)", variable_NUMBER: "Zahl (int)",
variable_SHORT_NUMBER: "char", variable_SHORT_NUMBER: "char",
variable_LONG: "große Zahl (long)", variable_LONG: "große Zahl (long)",
variable_DECIMAL: "Kommazahl (float)", variable_DECIMAL: "Kommazahl (float)",
variables_TEXT: "Text (string)", variables_TEXT: "Text (string)",
variables_ARRAY: "Array (array)", variables_ARRAY: "Array (array)",
variables_CHARACTER: "Buchstabe (char)", variables_CHARACTER: "char (char)",
variables_BOOLEAN: "Boolean (boolean)", variables_BOOLEAN: "Boolean (boolean)",
variables_NULL: "void (void)", variables_NULL: "void (void)",
variables_UNDEF: "undefined", variables_UNDEF: "undefined",
variables_set: "Schreibe",
variables_to: "",
/** /**
* Tooltips * Tooltips
@@ -144,7 +141,6 @@ export const UI = {
button_cancel: "Abbrechen", button_cancel: "Abbrechen",
button_close: "Schließen", button_close: "Schließen",
button_save: "Speichern",
button_accept: "Bestätigen", button_accept: "Bestätigen",
button_compile: "Kompilieren", button_compile: "Kompilieren",
button_create_variableCreate: "Erstelle Variable", button_create_variableCreate: "Erstelle Variable",
@@ -152,7 +148,6 @@ export const UI = {
button_next: "nächster Schritt", button_next: "nächster Schritt",
button_tutorial_overview: "Tutorial Übersicht", button_tutorial_overview: "Tutorial Übersicht",
button_login: "Anmelden", button_login: "Anmelden",
button_createVariable: "Typisierte Variable erstellen",
/** /**
* *
@@ -186,8 +181,7 @@ export const UI = {
settings_sounds: "Töne", settings_sounds: "Töne",
settings_sounds_text: settings_sounds_text:
"Aktiviere oder Deaktiviere Töne beim hinzufügen und löschen von Blöcken. Standardmäßig deaktiviert", "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 * 404
*/ */
@@ -234,12 +228,6 @@ export const UI = {
builder_requirements_head: "Voraussetzungen", builder_requirements_head: "Voraussetzungen",
builder_requirements_order: builder_requirements_order:
"Beachte, dass die Reihenfolge des Anhakens maßgebend ist.", "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 * Login
@@ -253,7 +241,7 @@ export const UI = {
/** /**
* Navbar * Navbar
*/ */
navbar_blockly: "Blockly",
navbar_tutorials: "Tutorials", navbar_tutorials: "Tutorials",
navbar_tutorialbuilder: "Tutorial erstellen", navbar_tutorialbuilder: "Tutorial erstellen",
navbar_gallery: "Galerie", navbar_gallery: "Galerie",
@@ -294,35 +282,4 @@ export const UI = {
drawer_ideerror_head: "Hoppla da ist was schief gegangen.", drawer_ideerror_head: "Hoppla da ist was schief gegangen.",
drawer_ideerror_text: drawer_ideerror_text:
"Beim kompilieren ist ein Fehler aufgetreten, überprüfe deine Blöcke.", "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",
/**
* Sensor Markdown Information
*/
sensorinfo_info: "Informationen zum Sensor",
sensorinfo_description: "Beschreibung",
sensorinfo_measurable_phenos: "Messbare Phänomene",
sensorinfo_manufacturer: "Hersteller",
sensorinfo_lifetime: "Lebensdauer",
sensorinfo_explanation: "Diese Informationen wurden aus [sensors.wiki](https://sensors.wiki) entnommen. Für weitere Informationen besuchen Sie den Abschnitt über diesen Sensor "
}; };
-2
View File
@@ -8,7 +8,6 @@ import { MATH } from "./en/math";
import { MQTT } from "./en/mqtt"; import { MQTT } from "./en/mqtt";
import { SENSEBOX } from "./en/sensebox"; import { SENSEBOX } from "./en/sensebox";
import { DISPLAY } from "./en/sensebox-display"; import { DISPLAY } from "./en/sensebox-display";
import { MOTORS } from "./en/sensebox-motors";
import { LED } from "./en/sensebox-led"; import { LED } from "./en/sensebox-led";
import { LORA } from "./en/sensebox-lora"; import { LORA } from "./en/sensebox-lora";
import { OSEM } from "./en/sensebox-osem"; import { OSEM } from "./en/sensebox-osem";
@@ -35,7 +34,6 @@ export const En = {
...MATH, ...MATH,
...MQTT, ...MQTT,
...DISPLAY, ...DISPLAY,
...MOTORS,
...LED, ...LED,
...LORA, ...LORA,
...OSEM, ...OSEM,
@@ -1,48 +0,0 @@
export const MOTORS = {
/**
* Motors
*/
sensebox_motors_beginServoMotor: "Initialize Servo Motor",
sensebox_motors_beginServoMotor_pin: "Pin:",
sensebox_motors_beginServoMotor_tooltip: "Connect one of the the digital pins to the orange wire of the Servo Motor. Also Connect GND to GND (black and brown wire) and 5V to 5V (red wire).",
sensebox_motors_beginServoMotor_helpurl: "https://en.wikipedia.org/wiki/Servomotor",
sensebox_motors_moveServoMotor: "Move Servo Motor",
sensebox_motors_moveServoMotor_pin: "Pin:",
sensebox_motors_moveServoMotor_degrees: "Degrees:",
sensebox_motors_moveServoMotor_tooltip: "The Servo Motor can be moved to a spezific angle between 0 and 180 degrees.",
sensebox_motors_moveServoMotor_helpurl: "https://en.wikipedia.org/wiki/Servomotor",
sensebox_motors_I2CMotorBoard_begin: "Initialize I2C Motor Board",
sensebox_motors_I2CMotorBoard_begin_tooltip: "Connect the I2C Moztor Board with the two DC Motors to one of the five I2C-Ports. Use this Block inside the setup()-function.",
sensebox_motors_I2CMotorBoard_begin_helpurl: "TODO",
sensebox_motors_I2CMotorBoard_moveDCMotor: "Move",
sensebox_motors_I2CMotorBoard_moveDCMotor_left: "left",
sensebox_motors_I2CMotorBoard_moveDCMotor_right: "right",
sensebox_motors_I2CMotorBoard_moveDCMotor_motor: "DC Motor at I2C Motor Board",
sensebox_motors_I2CMotorBoard_moveDCMotor_speed: "Speed:",
sensebox_motors_I2CMotorBoard_moveDCMotor_tooltip: "You can set the Speed between -100 and 100.",
sensebox_motors_I2CMotorBoard_moveDCMotor_helpurl: "TODO",
sensebox_motors_I2CMotorBoard_stopDCMotor: "Stop ",
sensebox_motors_I2CMotorBoard_stopDCMotor_left: "left",
sensebox_motors_I2CMotorBoard_stopDCMotor_right: "right",
sensebox_motors_I2CMotorBoard_stopDCMotor_motor: "DC Motor at I2C Motor Board",
sensebox_motors_I2CMotorBoard_stopDCMotor_tooltip: "TODO",
sensebox_motors_I2CMotorBoard_stopDCMotor_helpurl: "TODO",
sensebox_motors_beginStepperMotor: "Initialize Stepper Motor (28BYJ-48)",
sensebox_motors_beginStepperMotor_rpm: "Rotations per Minute:",
sensebox_motors_beginStepperMotor_pins: "Input Pins (IN1-IN4):",
sensebox_motors_beginStepperMotor_tooltip: "Connect four of the the digital pins in the right order to the inputs IN1-IN4 on the stepper motor board. Connect a GND pin (black wire) to the minus pin and a 5V pin (red wire) to the plus pin on the board. Also connect the stepper motor to this Board. Use this Block inside the setup()-function.",
sensebox_motors_beginStepperMotor_helpurl: "TODO",
sensebox_motors_moveStepperMotor: "Move Stepper Motor (28BYJ-48)",
sensebox_motors_moveStepperMotor_step: "Steps:",
sensebox_motors_moveStepperMotor_tooltip: "Move stepper motor. 2048 steps equal a full rotation.",
sensebox_motors_moveStepperMotor_helpurl: "TODO",
};
@@ -134,6 +134,21 @@ If the max distance is reached the a value of **O** will be returned`,
senseBox_soundsensor_dfrobot_helpurl: senseBox_soundsensor_dfrobot_helpurl:
"https://en.docs.sensebox.de/hardware/sensoren-lautstaerke/", "https://en.docs.sensebox.de/hardware/sensoren-lautstaerke/",
/*
* rainsensor hydreon rg-15
*/
sensebox_rainsensor_hydreon_rg15: "Rainsensor (RG-15)",
sensebox_rainsensor_hydreon_rg15_port: "Serial Port:",
sensebox_rainsensor_hydreon_rg15_serial1: "Serial1",
sensebox_rainsensor_hydreon_rg15_serial2: "Serial2",
sensebox_rainsensor_hydreon_rg15_value: "value:",
sensebox_rainsensor_hydreon_rg15_totalAcc: "total Accumulation in mm",
sensebox_rainsensor_hydreon_rg15_rainInt: "Rainfall Intensity in mm/h",
sensebox_rainsensor_hydreon_rg15_eventAcc: "Accumulation per Event in mm",
sensebox_rainsensor_hydreon_rg15_acc: "Accumulation since last loop in mm",
sensebox_rainsensor_hydreon_rg15_tooltip: "Connect Rainsensor (RG-15) to one of the Serial ports.",
sensebox_rainsensor_hydreon_rg15_helpurl: "https://rainsensors.com/products/rg-15/",
/* /*
* BME680 * BME680
*/ */
+3 -46
View File
@@ -12,19 +12,16 @@ export const UI = {
toolbox_variables: "Variables", toolbox_variables: "Variables",
toolbox_serial: "Serial", toolbox_serial: "Serial",
toolbox_advanced: "Erweitert", toolbox_advanced: "Erweitert",
toolbox_motors: "Motors",
variable_NUMBER: "Number (int)", variable_NUMBER: "Number (int)",
variable_SHORT_NUMBER: "char", variable_SHORT_NUMBER: "char",
variable_LONG: "Big number (long)", variable_LONG: " Zahl (long)",
variable_DECIMAL: "Decimal (float)", variable_DECIMAL: "Decimal (float)",
variables_TEXT: "Text (string)", variables_TEXT: "Text (string)",
variables_ARRAY: "Array (array)", variables_ARRAY: "Array (array)",
variables_CHARACTER: "Character (char)", variables_CHARACTER: "char (char)",
variables_BOOLEAN: "Boolean (boolean)", variables_BOOLEAN: "Boolean (boolean)",
variables_NULL: "void (void)", variables_NULL: "void (void)",
variables_UNDEF: "undefined", variables_UNDEF: "undefined",
variables_set: "set",
variables_to: "to",
/** /**
* Tooltips * Tooltips
@@ -147,7 +144,6 @@ export const UI = {
button_next: "Next step", button_next: "Next step",
button_tutorial_overview: "Tutorial overview", button_tutorial_overview: "Tutorial overview",
button_login: "Login", button_login: "Login",
button_createVariable: "Create Typed Variable",
/** /**
* *
@@ -180,8 +176,6 @@ export const UI = {
settings_sounds: "Sound", settings_sounds: "Sound",
settings_sounds_text: settings_sounds_text:
"Enable or disable sounds when adding and deleting blocks. Disabled by default", "Enable or disable sounds when adding and deleting blocks. Disabled by default",
settings_board: "Board",
settings_board_text: "Choose your board",
/** /**
* 404 * 404
@@ -228,12 +222,6 @@ export const UI = {
builder_requirements_head: "Requirements.", builder_requirements_head: "Requirements.",
builder_requirements_order: builder_requirements_order:
"Note that the order of ticking is authoritative.", "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 * Login
@@ -249,7 +237,7 @@ export const UI = {
/** /**
* Navbar * Navbar
*/ */
navbar_blockly: "Blockly",
navbar_tutorials: "Tutorials", navbar_tutorials: "Tutorials",
navbar_tutorialbuilder: "Create tutorial", navbar_tutorialbuilder: "Create tutorial",
navbar_gallery: "Gallery", navbar_gallery: "Gallery",
@@ -289,35 +277,4 @@ export const UI = {
*/ */
drawer_ideerror_head: "Oops something went wrong", drawer_ideerror_head: "Oops something went wrong",
drawer_ideerror_text: "An error occurred while compiling, check your blocks", 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",
/**
* Sensor Markdown Information
* */
sensorinfo_info: "Informationen regarding the Sensor",
sensorinfo_description: "Description",
sensorinfo_measurable_phenos: "Measurable Phenomena",
sensorinfo_manufacturer: "Manufacturer",
sensorinfo_lifetime: "Lifetime",
sensorinfo_explanation: "This information was fetched from [sensors.wiki](https://sensors.wiki). For more information visit the section on this sensor ",
}; };
+7 -30
View File
@@ -16,12 +16,16 @@ class Toolbox extends React.Component {
this.props.workspace, this.props.workspace,
"callbackName", "callbackName",
[ [
[`${Blockly.Msg.variable_SHORT_NUMBER}`, "char"],
[`${Blockly.Msg.variable_NUMBER}`, "int"], [`${Blockly.Msg.variable_NUMBER}`, "int"],
[`${Blockly.Msg.variable_LONG}`, "long"], [`${Blockly.Msg.variable_LONG}`, "long"],
[`${Blockly.Msg.variable_DECIMAL}`, "float"], [`${Blockly.Msg.variable_DECIMAL}`, "float"],
[`${Blockly.Msg.variables_TEXT}`, "String"], [`${Blockly.Msg.variables_TEXT}`, "String"],
[`${Blockly.Msg.variables_ARRAY}`, "Array"],
[`${Blockly.Msg.variables_CHARACTER}`, "char"], [`${Blockly.Msg.variables_CHARACTER}`, "char"],
[`${Blockly.Msg.variables_BOOLEAN}`, "boolean"], [`${Blockly.Msg.variables_BOOLEAN}`, "boolean"],
[`${Blockly.Msg.variables_NULL}`, "void"],
[`${Blockly.Msg.variables_UNDEF}`, "undefined"],
] ]
); );
typedVarModal.init(); typedVarModal.init();
@@ -32,7 +36,7 @@ class Toolbox extends React.Component {
// Add your button and give it a callback name. // Add your button and give it a callback name.
const button = document.createElement("button"); const button = document.createElement("button");
button.setAttribute("text", Blockly.Msg.button_createVariable); button.setAttribute("text", "Create Typed Variable");
button.setAttribute("callbackKey", "callbackName"); button.setAttribute("callbackKey", "callbackName");
xmlList.push(button); xmlList.push(button);
@@ -40,7 +44,6 @@ class Toolbox extends React.Component {
// This gets all the variables that the user creates and adds them to the // This gets all the variables that the user creates and adds them to the
// flyout. // flyout.
const blockList = Blockly.VariablesDynamic.flyoutCategoryBlocks(workspace); const blockList = Blockly.VariablesDynamic.flyoutCategoryBlocks(workspace);
console.log(blockList);
xmlList = xmlList.concat(blockList); xmlList = xmlList.concat(blockList);
return xmlList; return xmlList;
} }
@@ -73,6 +76,7 @@ class Toolbox extends React.Component {
<Block type="sensebox_sensor_watertemperature" /> <Block type="sensebox_sensor_watertemperature" />
{/* <Block type="sensebox_windspeed" /> */} {/* <Block type="sensebox_windspeed" /> */}
<Block type="sensebox_soundsensor_dfrobot" /> <Block type="sensebox_soundsensor_dfrobot" />
<Block type="sensebox_rainsensor_hydreon_rg15" />
<Block type="sensebox_multiplexer_init"> <Block type="sensebox_multiplexer_init">
<Value name="nrChannels"> <Value name="nrChannels">
<Block type="math_number"> <Block type="math_number">
@@ -295,7 +299,6 @@ class Toolbox extends React.Component {
</Value> </Value>
</Block> </Block>
</Category> </Category>
{/* <Category name="Telegram" colour={getColour().sensebox}> {/* <Category name="Telegram" colour={getColour().sensebox}>
<Block type="sensebox_telegram" /> <Block type="sensebox_telegram" />
<Block type="sensebox_telegram_do" /> <Block type="sensebox_telegram_do" />
@@ -611,6 +614,7 @@ class Toolbox extends React.Component {
colour={getColour().procedures} colour={getColour().procedures}
custom="PROCEDURE" custom="PROCEDURE"
></Category> ></Category>
<sep></sep>
<Category name={Blockly.Msg.toolbox_advanced} colour={getColour().io}> <Category name={Blockly.Msg.toolbox_advanced} colour={getColour().io}>
<Category <Category
name={Blockly.Msg.toolbox_serial} name={Blockly.Msg.toolbox_serial}
@@ -642,33 +646,6 @@ class Toolbox extends React.Component {
</Value> </Value>
</Block> </Block>
</Category> </Category>
<Category name={Blockly.Msg.toolbox_motors} colour={getColour().motors}>
<Block type="sensebox_motors_beginServoMotor" />
<Block type="sensebox_motors_moveServoMotor">
<Value name="degrees">
<Block type="math_number">
<Field name="NUM">90</Field>
</Block>
</Value>
</Block>
{/* <Block type="sensebox_motors_I2CMotorBoard_begin" />
<Block type="sensebox_motors_I2CMotorBoard_moveDCMotor">
<Value name="speed">
<Block type="math_number">
<Field name="NUM">100</Field>
</Block>
</Value>
</Block>
<Block type="sensebox_motors_I2CMotorBoard_stopDCMotor" />
<Block type="sensebox_motors_beginStepperMotor" />
<Block type="sensebox_motors_moveStepperMotor">
<Value name="steps">
<Block type="math_number">
<Field name="NUM">2048</Field>
</Block>
</Value>
</Block> */}
</Category>
<Category name="Watchdog" colour={getColour().io}> <Category name="Watchdog" colour={getColour().io}>
<Block type="watchdog_enable"></Block> <Block type="watchdog_enable"></Block>
<Block type="watchdog_reset"></Block> <Block type="watchdog_reset"></Block>
+4 -4
View File
@@ -3,9 +3,9 @@ import React, { Component } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import clsx from 'clsx'; import clsx from 'clsx';
import withStyles from '@mui/styles/withStyles'; import { withStyles } from '@material-ui/core/styles';
import MaterialUIBreadcrumbs from '@mui/material/Breadcrumbs'; import MaterialUIBreadcrumbs from '@material-ui/core/Breadcrumbs';
import Typography from '@mui/material/Typography'; import Typography from '@material-ui/core/Typography';
import { faHome } from "@fortawesome/free-solid-svg-icons"; import { faHome } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -30,7 +30,7 @@ class Breadcrumbs extends Component {
this.props.content && this.props.content.length > 0 ? this.props.content && this.props.content.length > 0 ?
<MaterialUIBreadcrumbs separator="›" style={{marginBottom: '20px'}}> <MaterialUIBreadcrumbs separator="›" style={{marginBottom: '20px'}}>
<Link to={'/'} style={{textDecoration: 'none'}}> <Link to={'/'} style={{textDecoration: 'none'}}>
<FontAwesomeIcon className={clsx(this.props.classes.home, this.props.classes.hover)} icon={faHome} size="xs"/> <FontAwesomeIcon className={clsx(this.props.classes.home, this.props.classes.hover)} icon={faHome} size="xs"/>
</Link> </Link>
{this.props.content.splice(0, this.props.content.length-1).map((content, i) => ( {this.props.content.splice(0, this.props.content.length-1).map((content, i) => (
<Link to={content.link} style={{textDecoration: 'none'}} key={i}> <Link to={content.link} style={{textDecoration: 'none'}} key={i}>
-281
View File
@@ -1,281 +0,0 @@
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 "@mui/material";
import Blockly from "blockly/core";
import Divider from "@mui/material/Divider";
import { saveAs } from "file-saver";
import Drawer from "@mui/material/Drawer";
import Sidebar from "./Sidebar";
import Dialog from "../Dialog";
import SaveIcon from "./SaveIcon";
import store from "../../store";
import DeviceSelection from "../DeviceSelection";
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 <senseBoxIO.h> //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 (
<div>
<Grid container spacing={2}>
<Drawer
anchor={"bottom"}
open={open}
onClose={toggleDrawer("bottom", false)}
>
<h2
style={{
color: "#4EAF47",
paddingLeft: "1rem",
paddingRight: "1rem",
}}
>
{Blockly.Msg.drawer_ideerror_head}
</h2>
<p
style={{
color: "#4EAF47",
paddingLeft: "1rem",
paddingRight: "1rem",
}}
>
{Blockly.Msg.drawer_ideerror_text}
</p>
<Divider style={{ backgroundColor: "white" }} />
<p
style={{
backgroundColor: "black",
color: "#E47128",
padding: "1rem",
}}
>
{" "}
{`${error}`}{" "}
</p>
</Drawer>
<Grid item lg={8} md={8}>
<div style={{ display: "flex", alignItems: "center" }}>
<h1>Code Editor</h1>
<SaveIcon loading={autoSave} />
</div>
<MonacoEditor
height="80vh"
onChange={(value) => {
editValue(value);
}}
defaultLanguage="cpp"
defaultValue={
localStorage.getItem("ArduinoCode")
? localStorage.getItem("ArduinoCode")
: `#include <senseBoxIO.h> //needs to be always included
void setup () {
}
void loop() {
}`
}
value={fileContent}
onMount={(editor, monaco) => {
editorRef.current = editor;
saveValue();
}}
/>
</Grid>
<DeviceSelection />
<Grid item lg={4} md={4}>
<Button
style={{ padding: "1rem", margin: "1rem" }}
variant="contained"
color="primary"
onClick={() => compile()}
>
Kompilieren
</Button>
<Button
style={{ padding: "1rem", margin: "1rem" }}
variant="contained"
color="primary"
onClick={() => saveIno()}
>
{Blockly.Msg.codeeditor_save_code}
</Button>
<Button
style={{ padding: "1rem", margin: "1rem" }}
variant="contained"
color="primary"
onClick={() => openIno()}
>
{Blockly.Msg.codeeditor_open_code}
</Button>
<Button
style={{ padding: "1rem", margin: "1rem" }}
variant="contained"
color="primary"
onClick={() => setResetDialog(true)}
>
{Blockly.Msg.codeeditor_reset_code}
</Button>
<Button
style={{ padding: "1rem", margin: "1rem" }}
variant="contained"
color="primary"
onClick={() => getBlocklyCode()}
>
{Blockly.Msg.codeeditor_blockly_code}
</Button>
<Sidebar />
<Dialog
style={{ zIndex: 9999999 }}
fullWidth
maxWidth={"sm"}
open={progress}
title={"Code wird kompiliert"}
content={""}
>
<div>{Blockly.Msg.codeeditor_compile_progress}</div>
</Dialog>{" "}
<Dialog
open={resetDialog}
title={Blockly.Msg.resetDialog_headline}
content={Blockly.Msg.resetDialog_text}
onClose={() => {
setResetDialog(false);
}}
onClick={() => {
setResetDialog(false);
}}
button={Blockly.Msg.button_cancel}
>
{" "}
<div style={{ marginTop: "10px" }}>
<Button
variant="contained"
color="primary"
onClick={() => {
resetCode();
setResetDialog(false);
}}
>
Zurücksetzen
</Button>
</div>
</Dialog>
</Grid>
</Grid>
</div>
);
};
export default withRouter(CodeEditor);
-333
View File
@@ -1,333 +0,0 @@
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 '@mui/styles/withStyles';
import Button from "@mui/material/Button";
import Backdrop from "@mui/material/Backdrop";
import CircularProgress from "@mui/material/CircularProgress";
import IconButton from "@mui/material/IconButton";
import Tooltip from "@mui/material/Tooltip";
import Divider from "@mui/material/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 "@mui/material/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 (
<div style={{}}>
{this.props.iconButton ? (
<Tooltip
title={Blockly.Msg.tooltip_compile_code}
arrow
style={{ marginRight: "5px" }}
>
<IconButton
className={`compileBlocks ${this.props.classes.iconButton}`}
onClick={() => this.compile()}
size="large">
<FontAwesomeIcon icon={faClipboardCheck} size="xs" />
</IconButton>
</Tooltip>
) : (
<Button
style={{ float: "right", color: "white" }}
variant="contained"
className={this.props.classes.button}
onClick={() => this.compile()}
>
<FontAwesomeIcon
icon={faClipboardCheck}
style={{ marginRight: "5px" }}
/>{" "}
Kompilieren
</Button>
)}
{this.props.platform === false ? (
<Backdrop
className={this.props.classes.backdrop}
open={this.state.progress}
>
<div className="overlay">
<img src={Copy} width="400" alt="copyimage"></img>
<h2>{Blockly.Msg.compile_overlay_head}</h2>
<p>{Blockly.Msg.compile_overlay_text}</p>
<p>
{Blockly.Msg.compile_overlay_help}
<a href="/faq" target="_blank">
FAQ
</a>
</p>
<CircularProgress color="inherit" />
</div>
</Backdrop>
) : (
<Backdrop
className={this.props.classes.backdrop}
open={this.state.progress}
>
<div className="overlay">
{/* <img src={Copy} width="400" alt="copyimage"></img> */}
<h2>Dein Code wird kompiliert!</h2>
<p>übertrage ihn anschließend mithlfe der senseBoxConnect-App</p>
<p>
{Blockly.Msg.compile_overlay_help}
<a href="/faq" target="_blank">
FAQ
</a>
</p>
<CircularProgress color="inherit" />
</div>
</Backdrop>
)}
<Drawer
anchor={"bottom"}
open={this.state.open}
onClose={this.toggleDrawer("bottom", false)}
>
<h2
style={{
color: "#4EAF47",
paddingLeft: "1rem",
paddingRight: "1rem",
}}
>
{Blockly.Msg.drawer_ideerror_head}
</h2>
<p
style={{
color: "#4EAF47",
paddingLeft: "1rem",
paddingRight: "1rem",
}}
>
{Blockly.Msg.drawer_ideerror_text}
</p>
<Divider style={{ backgroundColor: "white" }} />
<p
style={{
backgroundColor: "black",
color: "#E47128",
padding: "1rem",
}}
>
{" "}
{`${this.state.error}`}{" "}
</p>
</Drawer>
<Dialog
style={{ zIndex: 9999999 }}
fullWidth
maxWidth={"sm"}
open={this.state.appDialog}
title=""
content={""}
onClose={this.toggleDialog}
onClick={this.toggleDialog}
button={Blockly.Msg.button_close}
>
<div>
<p>Dein Code wurde erfolgreich kompiliert</p>
<a href={this.state.link}>
<Button
style={{ color: "white" }}
variant="contained"
className={this.props.classes.button}
onClick={() => this.toggleDialog()}
>
<FontAwesomeIcon
icon={faClipboardCheck}
style={{ marginRight: "5px" }}
/>{" "}
Starte Übertragung
</Button>
</a>
</div>
</Dialog>
</div>
);
}
}
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)
);
-42
View File
@@ -1,42 +0,0 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCircleNotch, faSave } from "@fortawesome/free-solid-svg-icons";
import Tooltip from "@mui/material/Tooltip";
import React from "react";
const SaveIcon = ({ loading }) => (
<Tooltip title={"Auto save enabled"} arrow placement="right">
<div
style={{
position: "relative",
width: "2rem",
height: "2rem",
margin: "1rem",
}}
>
{loading && (
<FontAwesomeIcon
style={{ position: "absolute" }}
icon={faCircleNotch}
spin={true}
size="2x"
color="grey"
/>
)}
<FontAwesomeIcon
style={{
position: "absolute",
left: "50%",
top: "50%",
transform: "translate(-50%,-50%)",
}}
icon={faSave}
color={loading ? "grey" : "green"}
size={loading ? "1x" : "lg"}
/>
</div>
</Tooltip>
);
export default SaveIcon;
@@ -1,91 +0,0 @@
import { useState } from "react";
import Button from "@mui/material/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 (
<>
<div className="flex items-center">
<Button type="button" variant="outlined" onClick={() => connectPort()}>
Connect to senseBox
</Button>
<label className="m-4 text-gray-700 text-base font-semibold px-6 py-3 rounded-lg">
Show timestamps
<input
onChange={handleClick}
checked={checked}
type="checkbox"
className="mx-4"
/>
</label>
<Button
type="button"
variant="outlined"
onClick={() => setSerialPortContent([])}
>
Clear
</Button>
</div>
<div className="font-mono">
{serialPortContent.map((log) => {
return (
<p>
{checked && (
<span className="font-medium mr-4">{log[0].toISOString()}</span>
)}
<span>{log[1]}</span>
</p>
);
})}
</div>
</>
);
};
export default SerialMonitor;
-143
View File
@@ -1,143 +0,0 @@
import React, { useEffect } from "react";
import Blockly from "blockly";
import { useSelector } from "react-redux";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import Typography from "@mui/material/Typography";
import { LibraryVersions } from "../../data/versions.js";
import { useMonaco } from "@monaco-editor/react";
import { Button } from "@mui/material";
import SerialMonitor from "./SerialMonitor.js";
import axios from "axios";
import { useDispatch } from "react-redux";
import { getArduinoExamples } from "../../actions/arduinoActions";
//import getArduinoExamples from "../../reducers/arduinoReducer"
const Sidebar = () => {
const examples = useSelector((state) => state.arduino.examples);
const user = useSelector((state) => state.auth.user);
const dispatch = useDispatch()
useEffect(() => {
dispatch(getArduinoExamples());
}, [dispatch]);
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 (
<div>
{"serial" in navigator ? (
<Accordion>
<AccordionSummary
expandIcon={""}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography>Serial Monitor</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
<SerialMonitor />
</Typography>
</AccordionDetails>
</Accordion>
) : null}
<Accordion>
<AccordionSummary
expandIcon={""}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography>Beispiele</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
{examples.map((object, i) => {
return (
<Button
style={{ padding: "1rem", margin: "1rem" }}
variant="contained"
color="primary"
key={i}
onClick={() => loadCode(object.code)}
>
{object.title}
</Button>
);
})}
</Typography>
</AccordionDetails>
</Accordion>
{user ? (
<Accordion>
<AccordionSummary
expandIcon={""}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography>Deine openSenseMap Codes</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
{user.boxes.map((box, i) => {
return (
<Button
style={{ padding: "1rem", margin: "1rem" }}
variant="contained"
color="primary"
key={i}
onClick={() => getOsemScript(box._id)}
>
{box.name}
</Button>
);
})}
</Typography>
</AccordionDetails>
</Accordion>
) : null}
<Accordion>
<AccordionSummary
expandIcon={""}
aria-controls="panel2a-content"
id="panel2a-header"
>
<Typography>{Blockly.Msg.codeeditor_libraries_head}</Typography>
</AccordionSummary>
<AccordionDetails
style={{ padding: 0, height: "60vH", backgroundColor: "white" }}
>
<Typography
style={{ overflow: "auto", width: "100%", padding: "1rem" }}
>
<p>{Blockly.Msg.codeeditor_libraries_text}</p>
{LibraryVersions().map((object, i) => {
return (
<p>
<a href={object.link} target="_blank" rel="noreferrer">
{object.library} {object.version}
</a>
</p>
);
})}
</Typography>
</AccordionDetails>
</Accordion>
</div>
);
};
export default Sidebar;
+60 -105
View File
@@ -1,27 +1,30 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import withStyles from '@mui/styles/withStyles'; import Prism from "prismjs";
import MuiAccordion from "@mui/material/Accordion"; import "prismjs/themes/prism.css";
import MuiAccordionSummary from "@mui/material/AccordionSummary"; import "prismjs/plugins/line-numbers/prism-line-numbers";
import MuiAccordionDetails from "@mui/material/AccordionDetails"; import "prismjs/plugins/line-numbers/prism-line-numbers.css";
import { Card } from "@mui/material";
import * as Blockly from "blockly"; import withWidth from '@material-ui/core/withWidth';
import { default as MonacoEditor } from "@monaco-editor/react"; 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'
// FIXME checkout https://mui.com/components/use-media-query/#migrating-from-withwidth
const withWidth = () => (WrappedComponent) => (props) => <WrappedComponent {...props} width="xs" />;
const Accordion = withStyles((theme) => ({ const Accordion = withStyles((theme) => ({
root: { root: {
border: `1px solid ${theme.palette.secondary.main}`, border: `1px solid ${theme.palette.secondary.main}`,
boxShadow: "none", boxShadow: 'none',
"&:before": { '&:before': {
display: "none", display: 'none',
}, },
"&$expanded": { '&$expanded': {
margin: "auto", margin: 'auto',
}, },
}, },
expanded: {}, expanded: {},
@@ -31,15 +34,15 @@ const AccordionSummary = withStyles((theme) => ({
root: { root: {
backgroundColor: theme.palette.secondary.main, backgroundColor: theme.palette.secondary.main,
borderBottom: `1px solid white`, borderBottom: `1px solid white`,
marginBottom: "-1px", marginBottom: '-1px',
minHeight: "50px", minHeight: '50px',
"&$expanded": { '&$expanded': {
minHeight: "50px", minHeight: '50px',
}, },
}, },
content: { content: {
"&$expanded": { '&$expanded': {
margin: "12px 0", margin: '12px 0',
}, },
}, },
expanded: {}, expanded: {},
@@ -51,60 +54,40 @@ const AccordionDetails = withStyles((theme) => ({
}, },
}))(MuiAccordionDetails); }))(MuiAccordionDetails);
class CodeViewer extends Component { class CodeViewer extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
code: this.props.arduino,
changed: false,
expanded: true, expanded: true,
componentHeight: null, componentHeight: null
}; };
this.myDiv = React.createRef(); this.myDiv = React.createRef();
} }
componentDidMount() { componentDidMount() {
this.setState({ componentHeight: this.myDiv.current.offsetHeight + "px" }); Prism.highlightAll();
this.setState({ componentHeight: this.myDiv.current.offsetHeight + 'px' });
} }
componentDidUpdate(prevProps, prevState) { componentDidUpdate(props, state) {
// if (this.props.arduino !== prevProps.arduino) { if (this.myDiv.current && this.myDiv.current.offsetHeight + 'px' !== this.state.componentHeight) {
// this.setState({ changed: true }); this.setState({ componentHeight: this.myDiv.current.offsetHeight + 'px' });
// 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 = () => { onChange = () => {
this.setState({ expanded: !this.state.expanded }); this.setState({ expanded: !this.state.expanded });
};
}
render() { render() {
var curlyBrackets = "{ }"; var curlyBrackets = '{ }';
var unequal = "<>"; var unequal = '<>';
return ( return (
<Card style={{ height: "100%", maxHeight: "60vH" }} ref={this.myDiv}> <Card style={{ height: '100%', maxHeight: '60vH' }} ref={this.myDiv}>
<Accordion <Accordion
square={true} square={true}
style={{ margin: 0 }} style={{ margin: 0 }}
@@ -112,32 +95,15 @@ class CodeViewer extends Component {
onChange={this.onChange} onChange={this.onChange}
> >
<AccordionSummary> <AccordionSummary>
<b style={{ fontSize: "20px", marginRight: "5px", width: "35px" }}> <b style={{ fontSize: '20px', marginRight: '5px', width: '35px' }}>{curlyBrackets}</b>
{curlyBrackets} <div style={{ margin: 'auto 5px 2px 0px' }}>{Blockly.Msg.codeviewer_arduino}</div>
</b>
<div style={{ margin: "auto 5px 2px 0px" }}>
{Blockly.Msg.codeviewer_arduino}
</div>
</AccordionSummary> </AccordionSummary>
<AccordionDetails <AccordionDetails style={{ padding: 0, height: `calc(${this.state.componentHeight} - 50px - 50px)`, backgroundColor: 'white' }}>
style={{ <pre className="line-numbers" style={{ paddingBottom: 0, width: '100%', overflow: 'auto', scrollbarWidth: 'thin', height: 'calc(100% - 30px)', margin: '15px 0', paddingTop: 0, whiteSpace: 'pre-wrap', backgroundColor: 'white' }}>
padding: 0, <code className="language-clike">
height: `calc(${this.state.componentHeight} - 50px - 50px)`, {this.props.arduino}
backgroundColor: "white", </code>
}} </pre>
>
<MonacoEditor
height="80vh"
defaultLanguage="cpp"
value={this.props.arduino}
// modified={this.props.arduino}
// original={this.state.code}
options={{
readOnly: true,
fontSize: "16px",
}}
/>
</AccordionDetails> </AccordionDetails>
</Accordion> </Accordion>
<Accordion <Accordion
@@ -147,43 +113,32 @@ class CodeViewer extends Component {
onChange={this.onChange} onChange={this.onChange}
> >
<AccordionSummary> <AccordionSummary>
<b style={{ fontSize: "20px", marginRight: "5px", width: "35px" }}> <b style={{ fontSize: '20px', marginRight: '5px', width: '35px' }}>{unequal}</b>
{unequal} <div style={{ margin: 'auto 5px 2px 0px' }}>{Blockly.Msg.codeviewer_xml}</div>
</b>
<div style={{ margin: "auto 5px 2px 0px" }}>
{Blockly.Msg.codeviewer_xml}
</div>
</AccordionSummary> </AccordionSummary>
<AccordionDetails <AccordionDetails style={{ padding: 0, height: `calc(${this.state.componentHeight} - 50px - 50px)`, backgroundColor: 'white' }}>
style={{ <pre className="line-numbers" style={{ paddingBottom: 0, width: '100%', overflow: 'auto', scrollbarWidth: 'thin', height: 'calc(100% - 30px)', margin: '15px 0', paddingTop: 0, whiteSpace: 'pre-wrap', backgroundColor: 'white' }}>
padding: 0, <code className="language-xml">
height: `calc(${this.state.componentHeight} - 50px - 50px)`, {`${this.props.xml}`}
backgroundColor: "white", </code>
}} </pre>
>
<MonacoEditor
height="80vh"
defaultLanguage="xml"
value={this.props.xml}
readOnly={true}
/>
</AccordionDetails> </AccordionDetails>
</Accordion> </Accordion>
</Card> </Card>
); );
} };
} }
CodeViewer.propTypes = { CodeViewer.propTypes = {
arduino: PropTypes.string.isRequired, arduino: PropTypes.string.isRequired,
xml: 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, arduino: state.workspace.code.arduino,
xml: state.workspace.code.xml, xml: state.workspace.code.xml,
tooltip: state.workspace.code.tooltip, tooltip: state.workspace.code.tooltip
}); });
export default connect(mapStateToProps, null)(withWidth()(CodeViewer)); export default connect(mapStateToProps, null)(withWidth()(CodeViewer));
+1 -12
View File
@@ -10,27 +10,18 @@ import Navbar from './Navbar';
import Footer from './Footer'; import Footer from './Footer';
import Routes from './Route/Routes'; import Routes from './Route/Routes';
import Cookies from './Cookies'; import Cookies from './Cookies';
import { setBoard } from './Blockly/helpers/board';
class Content extends Component { class Content extends Component {
componentDidMount() { componentDidMount() {
console.log(this.props.board);
setBoard(this.props.board)
if (this.props.language === 'de_DE') { if (this.props.language === 'de_DE') {
Blockly.setLocale(De); Blockly.setLocale(De);
} else if (this.props.language === 'en_US') { } else if (this.props.language === 'en_US') {
Blockly.setLocale(En); Blockly.setLocale(En);
} }
} }
componentDidUpdate(props) { componentDidUpdate(props) {
console.log(props.board);
console.log(this.props.board);
// if (props.board !== this.props.board) {
setBoard(this.props.board);
if (props.language !== this.props.language) { if (props.language !== this.props.language) {
if (this.props.language === 'de_DE') { if (this.props.language === 'de_DE') {
Blockly.setLocale(De); Blockly.setLocale(De);
@@ -38,7 +29,6 @@ class Content extends Component {
Blockly.setLocale(En); Blockly.setLocale(En);
} }
} }
} }
render() { render() {
@@ -58,8 +48,7 @@ Content.propTypes = {
}; };
const mapStateToProps = state => ({ const mapStateToProps = state => ({
language: state.general.language, language: state.general.language
board: state.board.board
}); });
export default connect(mapStateToProps, null)(Content); export default connect(mapStateToProps, null)(Content);
+1 -1
View File
@@ -9,7 +9,7 @@ class Cookies extends Component {
return ( return (
<CookieConsent <CookieConsent
location="bottom" location="bottom"
buttonText="Okay!" buttonText="Okay!!"
cookieName="cookieConsent" cookieName="cookieConsent"
style={{ background: "#2B373B" }} style={{ background: "#2B373B" }}
buttonStyle={{ background: "white", color: "#4EAF47", fontSize: "1rem" }} buttonStyle={{ background: "white", color: "#4EAF47", fontSize: "1rem" }}
-133
View File
@@ -1,133 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import Dialog from "./Dialog";
import withStyles from '@mui/styles/withStyles';
import * as Blockly from "blockly";
import { setBoard } from "../actions/boardActions";
import { IconButton, Grid, Avatar, Typography } from "@mui/material";
const styles = (theme) => ({
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) {
super(props);
this.state = {
open: props.selectedBoard ? false : true,
selectedBoard: "",
saveSettings: false,
};
}
toggleDialog = () => {
this.props.setBoard(this.state.selectedBoard)
};
onChange = (e) => {
if (e.target.checked) {
this.setState({ saveSettings: true });
} else {
this.setState({ saveSettings: false });
}
};
onclick = (e, value) => {
this.setState({ selectedBoard: value })
this.props.setBoard(value)
this.setState({ open: !this.state });
};
render() {
return (
<Dialog
style={{ zIndex: 9999999 }}
fullWidth
maxWidth={"xl"}
open={this.state.open}
title={Blockly.Msg.deviceselection_head}
content={""}
onClick={this.toggleDialog}
disabled={this.state.selectedBoard === ""}
>
<div>
<Grid container spacing={2} style={{ textAlign: "center" }}>
<Grid item xs={6}>
<IconButton onClick={(e) => this.onclick(e, "mcu")} size="large">
<Avatar
alt="Sensebox MCU"
src="/media/hardware/senseboxmcu.png"
style={{
border: this.state.selectedBoard === "mcu" ? 'medium solid DeepSkyBlue' : "0.1px solid lightgray",
width: "20vw",
height: "20vw"
}}
/>
</IconButton>
<p>senseBox MCU</p>
</Grid>
{/* <Grid item xs={4}>
<IconButton onClick={(e) => this.onclick(e, "esp")}>
<Avatar
alt="Sensebox ESP"
src="/media/hardware/senseboxmcu.png"
style={{
border: this.state.selectedBoard == "esp" ? 'medium solid DeepSkyBlue': "0.1px solid lightgray",
width:"20vw",
height: "20vw"
}}
/>
</IconButton>
<p>Sensebox ESP</p>
</Grid> */}
<Grid item xs={6}>
<IconButton onClick={(e) => this.onclick(e, "mini")} size="large">
<Avatar
alt="Sensebox Mini"
src="/media/hardware/senseboxmcumini.png"
style={{
border: this.state.selectedBoard === "mini" ? 'medium solid DeepSkyBlue' : "0.1px solid lightgray",
width: "20vw",
height: "20vw"
}}
/>
</IconButton>
<p>senseBox MCU:mini</p>
</Grid>
</Grid>
</div>
<Typography variant="body1" >
{Blockly.Msg.deviceselection_footnote} <a href="https://sensebox.github.io/blockly/">Arduino UNO</a> {Blockly.Msg.deviceselection_footnote_02} <a href="https://sensebox-blockly.netlify.app/ardublockly/?board=sensebox-mcu">senseBox MCU</a>
</Typography>
</Dialog>
);
}
}
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));
+6 -6
View File
@@ -1,10 +1,10 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import Button from '@mui/material/Button'; import Button from '@material-ui/core/Button';
import DialogTitle from '@mui/material/DialogTitle'; import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@mui/material/DialogContent'; import DialogContent from '@material-ui/core/DialogContent';
import DialogActions from '@mui/material/DialogActions'; import DialogActions from '@material-ui/core/DialogActions';
import MaterialUIDialog from '@mui/material/Dialog'; import MaterialUIDialog from '@material-ui/core/Dialog';
class Dialog extends Component { class Dialog extends Component {
@@ -24,7 +24,7 @@ class Dialog extends Component {
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
{this.props.actions ? this.props.actions : {this.props.actions ? this.props.actions :
<Button onClick={this.props.onClick} disabled={this.props.disabled} color="primary"> <Button onClick={this.props.onClick} color="primary">
{this.props.button} {this.props.button}
</Button> </Button>
} }
+165 -88
View File
@@ -1,102 +1,179 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import Breadcrumbs from "./Breadcrumbs"; import Breadcrumbs from './Breadcrumbs';
import { withRouter } from "react-router-dom"; import { withRouter } from 'react-router-dom';
import Button from "@mui/material/Button"; import Button from '@material-ui/core/Button';
import Typography from "@mui/material/Typography"; import Typography from '@material-ui/core/Typography';
import * as Blockly from "blockly"; import * as Blockly from 'blockly'
import ReactMarkdown from "react-markdown"; import ReactMarkdown from 'react-markdown';
import rehypeRaw from "rehype-raw"; import Container from '@material-ui/core/Container';
import Container from "@mui/material/Container"; import ExpansionPanel from '@material-ui/core/ExpansionPanel';
import Accordion from "@mui/material/Accordion"; import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
import AccordionSummary from "@mui/material/AccordionSummary"; import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
import AccordionDetails from "@mui/material/AccordionDetails";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChevronDown } from "@fortawesome/free-solid-svg-icons"; import { faChevronDown } from "@fortawesome/free-solid-svg-icons";
import { FaqQuestions } from "../data/faq"; import { FaqQuestions } from '../data/faq'
class Faq extends Component { class Faq extends Component {
state = {
panel: "",
expanded: false,
};
handleChange = (panel) => { state = {
this.setState({ panel: this.state.panel === panel ? "" : panel }); panel: '',
}; expanded: false
}
componentDidMount() {
// Ensure that Blockly.setLocale is adopted in the component.
// Otherwise, the text will not be displayed until the next update of the component.
window.scrollTo(0, 0); handleChange = (panel) => {
this.forceUpdate(); this.setState({ panel: this.state.panel === panel ? '' : panel });
} };
render() { componentDidMount() {
const { panel } = this.state; // Ensure that Blockly.setLocale is adopted in the component.
return ( // Otherwise, the text will not be displayed until the next update of the component.
<div>
<Breadcrumbs window.scrollTo(0, 0)
content={[{ link: this.props.location.pathname, title: "FAQ" }]} this.forceUpdate();
/> }
<Container fixed>
<div style={{ margin: "0px 24px 0px 24px" }}> render() {
<h1>FAQ</h1> const { panel } = this.state;
{FaqQuestions().map((object, i) => { return (
return ( <div>
<Accordion <Breadcrumbs content={[{ link: this.props.location.pathname, title: 'FAQ' }]} />
expanded={panel === `panel${i}`} <Container fixed>
onChange={() => this.handleChange(`panel${i}`)} <div style={{ margin: '0px 24px 0px 24px' }}>
> <h1>FAQ</h1>
<AccordionSummary {FaqQuestions().map((object, i) => {
expandIcon={<FontAwesomeIcon icon={faChevronDown} />} return (
> <ExpansionPanel expanded={panel === `panel${i}`} onChange={() => this.handleChange(`panel${i}`)}>
<Typography variant="h6">{object.question}</Typography> <ExpansionPanelSummary
</AccordionSummary> expandIcon={
<AccordionDetails> <FontAwesomeIcon icon={faChevronDown} />
<Typography> }
<ReactMarkdown >
className="news" <Typography variant="h6">{object.question}</Typography>
allowDangerousHtml="true" </ExpansionPanelSummary>
children={object.answer} <ExpansionPanelDetails>
rehypePlugins={[rehypeRaw]} <Typography>
></ReactMarkdown> <ReactMarkdown className="news" allowDangerousHtml="true" children={object.answer}>
</Typography> </ReactMarkdown>
</AccordionDetails> </Typography>
</Accordion> </ExpansionPanelDetails>
); </ExpansionPanel>
})} )
{this.props.button ? ( })}
<Button {
style={{ marginTop: "20px" }} this.props.button ?
variant="contained" <Button
color="primary" style={{ marginTop: '20px' }}
onClick={() => { variant="contained"
this.props.history.push(this.props.button.link); color="primary"
}} onClick={() => { this.props.history.push(this.props.button.link) }}
> >
{this.props.button.title} {this.props.button.title}
</Button> </Button>
) : ( :
<Button <Button
style={{ marginTop: "20px" }} style={{ marginTop: '20px' }}
variant="contained" variant="contained"
color="primary" color="primary"
onClick={() => { onClick={() => { this.props.history.push('/') }}
this.props.history.push("/"); >
}} {Blockly.Msg.button_back}
> </Button>
{Blockly.Msg.button_back} }
</Button> </div>
)} </Container>
</div> </div >
</Container> );
</div> };
);
}
} }
export default withRouter(Faq); export default withRouter(Faq);
/*
<ExpansionPanel expanded={panel === 'panel1'} onChange={() => this.handleChange('panel1')}>
<ExpansionPanelSummary
expandIcon={
<FontAwesomeIcon icon={faChevronDown} />
}
>
<Typography variant="h6">{Blockly.Msg.faq_q1_question}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Typography>
<ReactMarkdown className="news" allowDangerousHtml="true" children={Blockly.Msg.faq_q1_answer}>
</ReactMarkdown>
</Typography>
</ExpansionPanelDetails>
</ExpansionPanel>
<ExpansionPanel expanded={panel === 'panel2'} onChange={() => this.handleChange('panel2')}>
<ExpansionPanelSummary
expandIcon={
<FontAwesomeIcon icon={faChevronDown} />
}
>
<Typography>Frage 2</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Typography>
Donec placerat, lectus sed mattis semper, neque lectus feugiat lectus, varius pulvinar
diam eros in elit. Pellentesque convallis laoreet laoreet.
</Typography>
</ExpansionPanelDetails>
</ExpansionPanel>
<ExpansionPanel expanded={panel === 'panel3'} onChange={() => this.handleChange('panel3')}>
<ExpansionPanelSummary
expandIcon={
<FontAwesomeIcon icon={faChevronDown} />
}
>
<Typography>Frage 3</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Typography>
Nunc vitae orci ultricies, auctor nunc in, volutpat nisl. Integer sit amet egestas eros,
vitae egestas augue. Duis vel est augue.
</Typography>
</ExpansionPanelDetails>
</ExpansionPanel>
<ExpansionPanel expanded={panel === 'panel4'} onChange={() => this.handleChange('panel4')}>
<ExpansionPanelSummary
expandIcon={
<FontAwesomeIcon icon={faChevronDown} />
}
>
<Typography>Frage 4</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Typography>
Nunc vitae orci ultricies, auctor nunc in, volutpat nisl. Integer sit amet egestas eros,
vitae egestas augue. Duis vel est augue.
</Typography>
</ExpansionPanelDetails>
</ExpansionPanel>
*/
// {{
// this.props.button ?
// <Button
// style={{ marginTop: '20px' }}
// variant="contained"
// color="primary"
// onClick={() => { this.props.history.push(this.props.button.link) }}
// >
// {this.props.button.title}
// </Button>
// :
// <Button
// style={{ marginTop: '20px' }}
// variant="contained"
// color="primary"
// onClick={() => { this.props.history.push('/') }}
// >
// {Blockly.Msg.button_back}
// </Button>
// }}
+1 -1
View File
@@ -2,7 +2,7 @@ import React, { Component } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Typography from '@mui/material/Typography'; import Typography from '@material-ui/core/Typography';
class Footer extends Component { class Footer extends Component {
render() { render() {
+15 -30
View File
@@ -2,28 +2,27 @@ import React, { Component } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { clearStats, workspaceName } from "../actions/workspaceActions"; import { clearStats, workspaceName } from "../actions/workspaceActions";
import { setBoard } from "../actions/boardActions";
import * as Blockly from "blockly/core"; import * as Blockly from "blockly/core";
import { createNameId } from "mnemonic-id"; import { createNameId } from "mnemonic-id";
import * as BoardHelper from "./Blockly/helpers/board";
import WorkspaceStats from "./Workspace/WorkspaceStats"; import WorkspaceStats from "./Workspace/WorkspaceStats";
import WorkspaceFunc from "./Workspace/WorkspaceFunc"; import WorkspaceFunc from "./Workspace/WorkspaceFunc";
import BlocklyWindow from "./Blockly/BlocklyWindow"; import BlocklyWindow from "./Blockly/BlocklyWindow";
import CodeViewer from "./CodeViewer"; import CodeViewer from "./CodeViewer";
import TrashcanButtons from "./Workspace/TrashcanButtons"; import TrashcanButtons from "./Workspace/TrashcanButtons";
// import HintTutorialExists from "./Tutorial/HintTutorialExists"; import HintTutorialExists from "./Tutorial/HintTutorialExists";
import DeviceSelection from "./DeviceSelection";
import Grid from "@mui/material/Grid"; import Grid from "@material-ui/core/Grid";
import IconButton from "@mui/material/IconButton"; import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@material-ui/core/Tooltip";
import withStyles from '@mui/styles/withStyles'; import { withStyles } from "@material-ui/core/styles";
import { faCode } from "@fortawesome/free-solid-svg-icons"; import { faCode } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import TooltipViewer from "./TooltipViewer"; import TooltipViewer from "./TooltipViewer";
import Dialog from "./Dialog"; import Dialog from "./Dialog";
// import Autosave from "./Workspace/AutoSave";
const styles = (theme) => ({ const styles = (theme) => ({
codeOn: { codeOn: {
backgroundColor: theme.palette.primary.main, backgroundColor: theme.palette.primary.main,
@@ -55,21 +54,17 @@ class Home extends Component {
key: "", key: "",
message: "", message: "",
open: true, open: true,
initialXml: localStorage.getItem("autoSaveXML"),
}; };
} }
componentDidMount() { componentDidMount() {
console.log(this.props.platform);
if (this.props.platform === true) { if (this.props.platform === true) {
this.setState({ codeOn: false }); this.setState({ codeOn: false });
} }
this.setState({ stats: window.localStorage.getItem("stats") }); this.setState({ stats: window.localStorage.getItem("stats") });
if (!this.props.project) { if (!this.props.project) {
this.props.workspaceName(createNameId()); this.props.workspaceName(createNameId());
} else {
console.log(this.props.project);
this.props.setBoard(this.props.project.board);
BoardHelper.setBoard(this.props.project.board);
} }
if (this.props.message && this.props.message.id === "GET_SHARE_FAIL") { if (this.props.message && this.props.message.id === "GET_SHARE_FAIL") {
this.setState({ this.setState({
@@ -124,12 +119,10 @@ class Home extends Component {
<WorkspaceStats /> <WorkspaceStats />
</div> </div>
) : null} ) : null}
<div <div
className="workspaceFunc" className="workspaceFunc"
style={{ float: "right", height: "40px", marginBottom: "20px" }} style={{ float: "right", height: "40px", marginBottom: "20px" }}
> >
{/* <Autosave /> */}
<WorkspaceFunc <WorkspaceFunc
project={this.props.project} project={this.props.project}
projectType={this.props.projectType} projectType={this.props.projectType}
@@ -164,11 +157,10 @@ class Home extends Component {
zIndex: 21, zIndex: 21,
}} }}
onClick={() => this.onChange()} onClick={() => this.onChange()}
size="large"> >
<FontAwesomeIcon icon={faCode} size="xs" /> <FontAwesomeIcon icon={faCode} size="xs" />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<TrashcanButtons /> <TrashcanButtons />
<div className="blocklyWindow"> <div className="blocklyWindow">
{this.props.project ? ( {this.props.project ? (
@@ -177,10 +169,7 @@ class Home extends Component {
initialXml={this.props.project.xml} initialXml={this.props.project.xml}
/> />
) : ( ) : (
<BlocklyWindow <BlocklyWindow blocklyCSS={{ height: "80vH" }} />
blocklyCSS={{ height: "80vH" }}
initialXml={this.state.initialXml}
/>
)} )}
</div> </div>
</Grid> </Grid>
@@ -191,9 +180,7 @@ class Home extends Component {
</Grid> </Grid>
) : null} ) : null}
</Grid> </Grid>
{!this.props.board ? <DeviceSelection /> : null} <HintTutorialExists />
{/* <DeviceSelection /> */}
{/* <HintTutorialExists /> */}
{this.props.platform ? ( {this.props.platform ? (
<Dialog <Dialog
style={{ zIndex: 9999999 }} style={{ zIndex: 9999999 }}
@@ -225,21 +212,19 @@ class Home extends Component {
} }
Home.propTypes = { Home.propTypes = {
setBoard: PropTypes.func.isRequired,
clearStats: PropTypes.func.isRequired, clearStats: PropTypes.func.isRequired,
workspaceName: PropTypes.func.isRequired, workspaceName: PropTypes.func.isRequired,
message: PropTypes.object.isRequired, message: PropTypes.object.isRequired,
statistics: PropTypes.bool.isRequired, statistics: PropTypes.bool.isRequired,
platform: PropTypes.bool.isRequired, platform: PropTypes.object.isRequired,
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
message: state.message, message: state.message,
statistics: state.general.statistics, statistics: state.general.statistics,
platform: state.general.platform, platform: state.general.platform,
board: state.board.board
}); });
export default connect(mapStateToProps, { clearStats, workspaceName, setBoard })( export default connect(mapStateToProps, { clearStats, workspaceName })(
withStyles(styles, { withTheme: true })(Home) withStyles(styles, { withTheme: true })(Home)
); );
+1 -1
View File
@@ -1,6 +1,6 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { withRouter } from "react-router-dom"; import { withRouter } from "react-router-dom";
import Container from "@mui/material/Container"; import Container from "@material-ui/core/Container";
class Impressum extends Component { class Impressum extends Component {
render() { render() {
return ( return (
+4 -4
View File
@@ -5,8 +5,8 @@ import { workspaceChange } from '../actions/workspaceActions';
import * as Blockly from 'blockly/core'; import * as Blockly from 'blockly/core';
import TextField from '@mui/material/TextField'; import TextField from '@material-ui/core/TextField';
import Button from '@mui/material/Button'; import Button from '@material-ui/core/Button';
class MaxBlocks extends Component { class MaxBlocks extends Component {
@@ -28,12 +28,12 @@ class MaxBlocks extends Component {
return ( return (
<div style={{display: 'inline', marginLeft: '10px'}}> <div style={{display: 'inline', marginLeft: '10px'}}>
<TextField <TextField
variant="standard"
style={{width: '50px', marginRight: '5px'}} style={{width: '50px', marginRight: '5px'}}
name="max" name="max"
type="number" type="number"
onChange={this.onChange} onChange={this.onChange}
value={this.state.max} /> value={this.state.max}
/>
<Button style={{marginRight: '10px', color: 'white'}} variant="contained" color="primary" onClick={this.setMaxBlocks}> <Button style={{marginRight: '10px', color: 'white'}} variant="contained" color="primary" onClick={this.setMaxBlocks}>
Maximale Blöcke Maximale Blöcke
</Button> </Button>
+24 -43
View File
@@ -8,20 +8,19 @@ import senseboxLogo from "./sensebox_logo.svg";
import { withRouter } from "react-router-dom"; import { withRouter } from "react-router-dom";
import withStyles from '@mui/styles/withStyles'; import { withStyles } from "@material-ui/core/styles";
import Drawer from "@mui/material/Drawer"; import Drawer from "@material-ui/core/Drawer";
import AppBar from "@mui/material/AppBar"; import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@mui/material/Toolbar"; import Toolbar from "@material-ui/core/Toolbar";
import List from "@mui/material/List"; import List from "@material-ui/core/List";
import Typography from "@mui/material/Typography"; import Typography from "@material-ui/core/Typography";
import Divider from "@mui/material/Divider"; import Divider from "@material-ui/core/Divider";
import IconButton from "@mui/material/IconButton"; import IconButton from "@material-ui/core/IconButton";
import ListItem from "@mui/material/ListItem"; import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@mui/material/ListItemIcon"; import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@mui/material/ListItemText"; import ListItemText from "@material-ui/core/ListItemText";
import LinearProgress from "@mui/material/LinearProgress"; import LinearProgress from "@material-ui/core/LinearProgress";
import Tour from "reactour"; import Tour from "reactour";
import { Badge } from "@mui/material";
import { home, assessment } from "./Tour"; import { home, assessment } from "./Tour";
import { import {
faBars, faBars,
@@ -35,12 +34,10 @@ import {
faChalkboardTeacher, faChalkboardTeacher,
faTools, faTools,
faLightbulb, faLightbulb,
faCode,
faPuzzlePiece,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import * as Blockly from "blockly"; import * as Blockly from "blockly";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@material-ui/core/Tooltip";
const styles = (theme) => ({ const styles = (theme) => ({
drawerWidth: { drawerWidth: {
@@ -116,7 +113,7 @@ class Navbar extends Component {
onClick={this.toggleDrawer} onClick={this.toggleDrawer}
style={{ margin: "0 10px" }} style={{ margin: "0 10px" }}
className="MenuButton" className="MenuButton"
size="large"> >
<FontAwesomeIcon icon={faBars} /> <FontAwesomeIcon icon={faBars} />
</IconButton> </IconButton>
<Link to={"/"} style={{ textDecoration: "none", color: "inherit" }}> <Link to={"/"} style={{ textDecoration: "none", color: "inherit" }}>
@@ -142,7 +139,7 @@ class Navbar extends Component {
</Link> </Link>
) : null} ) : null}
{isHome ? ( {isHome ? (
<Tooltip title={'Start Tour'} arrow> <Tooltip title={Blockly.Msg.tooltip_start_tour} arrow>
<IconButton <IconButton
color="inherit" color="inherit"
className={`openTour ${this.props.classes.button}`} className={`openTour ${this.props.classes.button}`}
@@ -150,13 +147,13 @@ class Navbar extends Component {
this.openTour(); this.openTour();
}} }}
style={{ margin: "0 30px 0 auto" }} style={{ margin: "0 30px 0 auto" }}
size="large"> >
<FontAwesomeIcon icon={faQuestionCircle} /> <FontAwesomeIcon icon={faQuestionCircle} />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
) : null} ) : null}
{isAssessment ? ( {isAssessment ? (
<Tooltip title={'Start tour'} arrow> <Tooltip title={Blockly.Msg.tooltip_start_tour} arrow>
<IconButton <IconButton
color="inherit" color="inherit"
className={`openTour ${this.props.classes.button}`} className={`openTour ${this.props.classes.button}`}
@@ -164,7 +161,7 @@ class Navbar extends Component {
this.openTour(); this.openTour();
}} }}
style={{ margin: "0 30px 0 auto" }} style={{ margin: "0 30px 0 auto" }}
size="large"> >
<FontAwesomeIcon icon={faQuestionCircle} /> <FontAwesomeIcon icon={faQuestionCircle} />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
@@ -214,11 +211,6 @@ class Navbar extends Component {
</div> </div>
<List> <List>
{[ {[
{
text: Blockly.Msg.navbar_blockly,
icon: faPuzzlePiece,
link: "/",
},
{ {
text: Blockly.Msg.navbar_tutorials, text: Blockly.Msg.navbar_tutorials,
icon: faChalkboardTeacher, icon: faChalkboardTeacher,
@@ -244,11 +236,6 @@ class Navbar extends Component {
link: "/project", link: "/project",
restriction: this.props.isAuthenticated, restriction: this.props.isAuthenticated,
}, },
{
text: "Code Editor",
icon: faCode,
link: "/codeeditor",
},
].map((item, index) => { ].map((item, index) => {
if ( if (
item.restriction || item.restriction ||
@@ -266,13 +253,7 @@ class Navbar extends Component {
<ListItemIcon> <ListItemIcon>
<FontAwesomeIcon icon={item.icon} /> <FontAwesomeIcon icon={item.icon} />
</ListItemIcon> </ListItemIcon>
{item.text === "Code Editor" ? ( <ListItemText primary={item.text} />
<Badge badgeContent={"Experimental"} color="primary" overlap="rectangular">
<ListItemText primary={item.text} />
</Badge>
) : (
<ListItemText primary={item.text} />
)}
</ListItem> </ListItem>
</Link> </Link>
); );
@@ -329,9 +310,9 @@ class Navbar extends Component {
onClick={ onClick={
item.function item.function
? () => { ? () => {
item.function(); item.function();
this.toggleDrawer(); this.toggleDrawer();
} }
: this.toggleDrawer : this.toggleDrawer
} }
> >
@@ -365,9 +346,9 @@ class Navbar extends Component {
Navbar.propTypes = { Navbar.propTypes = {
tutorialIsLoading: PropTypes.bool.isRequired, tutorialIsLoading: PropTypes.bool.isRequired,
projectIsLoading: PropTypes.bool.isRequired, projectIsLoading: PropTypes.bool.isRequired,
isAuthenticated: PropTypes.bool, isAuthenticated: PropTypes.bool.isRequired,
user: PropTypes.object, user: PropTypes.object,
tutorial: PropTypes.object, tutorial: PropTypes.object.isRequired,
activeStep: PropTypes.number.isRequired, activeStep: PropTypes.number.isRequired,
}; };
+3 -3
View File
@@ -4,11 +4,11 @@ import Breadcrumbs from './Breadcrumbs';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import Button from '@mui/material/Button'; import Button from '@material-ui/core/Button';
import Typography from '@mui/material/Typography'; import Typography from '@material-ui/core/Typography';
import * as Blockly from 'blockly' import * as Blockly from 'blockly'
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import Container from '@mui/material/Container'; import Container from '@material-ui/core/Container';
const news = ` const news = `
+2 -2
View File
@@ -4,8 +4,8 @@ import Breadcrumbs from './Breadcrumbs';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import Button from '@mui/material/Button'; import Button from '@material-ui/core/Button';
import Typography from '@mui/material/Typography'; import Typography from '@material-ui/core/Typography';
import * as Blockly from 'blockly' import * as Blockly from 'blockly'
class NotFound extends Component { class NotFound extends Component {
+1 -1
View File
@@ -1,6 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import Container from '@mui/material/Container'; import Container from '@material-ui/core/Container';
class Privay extends Component { class Privay extends Component {
render() { render() {
return ( return (
+2 -2
View File
@@ -10,8 +10,8 @@ import { withRouter } from 'react-router-dom';
import Home from '../Home'; import Home from '../Home';
import Breadcrumbs from '../Breadcrumbs'; import Breadcrumbs from '../Breadcrumbs';
import Backdrop from '@mui/material/Backdrop'; import Backdrop from '@material-ui/core/Backdrop';
import CircularProgress from '@mui/material/CircularProgress'; import CircularProgress from '@material-ui/core/CircularProgress';
class Project extends Component { class Project extends Component {
+14 -14
View File
@@ -11,14 +11,13 @@ import BlocklyWindow from "../Blockly/BlocklyWindow";
import Snackbar from "../Snackbar"; import Snackbar from "../Snackbar";
import WorkspaceFunc from "../Workspace/WorkspaceFunc"; import WorkspaceFunc from "../Workspace/WorkspaceFunc";
import withStyles from '@mui/styles/withStyles'; import { withStyles } from "@material-ui/core/styles";
import Grid from "@mui/material/Grid"; import Grid from "@material-ui/core/Grid";
import Paper from "@mui/material/Paper"; import Paper from "@material-ui/core/Paper";
import Divider from "@mui/material/Divider"; import Divider from "@material-ui/core/Divider";
import Typography from "@mui/material/Typography"; import Typography from "@material-ui/core/Typography";
import Backdrop from "@mui/material/Backdrop"; import Backdrop from "@material-ui/core/Backdrop";
import CircularProgress from "@mui/material/CircularProgress"; import CircularProgress from "@material-ui/core/CircularProgress";
import DeviceSelection from "../DeviceSelection";
const styles = (theme) => ({ const styles = (theme) => ({
link: { link: {
@@ -61,8 +60,9 @@ class ProjectHome extends Component {
this.setState({ this.setState({
snackbar: true, snackbar: true,
key: Date.now(), key: Date.now(),
message: `Dein angefragtes ${type === "gallery" ? "Galerie-" : "" message: `Dein angefragtes ${
}Projekt konnte nicht gefunden werden.`, type === "gallery" ? "Galerie-" : ""
}Projekt konnte nicht gefunden werden.`,
type: "error", type: "error",
}); });
} }
@@ -108,7 +108,6 @@ class ProjectHome extends Component {
/> />
<h1>{data}</h1> <h1>{data}</h1>
<DeviceSelection />
{this.props.progress ? ( {this.props.progress ? (
<Backdrop open invisible> <Backdrop open invisible>
<CircularProgress color="primary" /> <CircularProgress color="primary" />
@@ -128,8 +127,9 @@ class ProjectHome extends Component {
}} }}
> >
<Link <Link
to={`/${data === "Projekte" ? "project" : "gallery" to={`/${
}/${project._id}`} data === "Projekte" ? "project" : "gallery"
}/${project._id}`}
style={{ textDecoration: "none", color: "inherit" }} style={{ textDecoration: "none", color: "inherit" }}
> >
<h3 style={{ marginTop: 0 }}>{project.title}</h3> <h3 style={{ marginTop: 0 }}>{project.title}</h3>
@@ -153,7 +153,7 @@ class ProjectHome extends Component {
</Typography> </Typography>
</Link> </Link>
{this.props.user && {this.props.user &&
this.props.user.email === project.creator ? ( this.props.user.email === project.creator ? (
<div> <div>
<Divider <Divider
style={{ style={{
+26 -23
View File
@@ -1,41 +1,44 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { Route, Redirect, withRouter } from 'react-router-dom';
import { Route, Redirect, withRouter } from "react-router-dom";
class PrivateRoute extends Component { class PrivateRoute extends Component {
render() { render() {
return !this.props.progress ? ( return (
!this.props.progress ?
<Route <Route
{...this.props.exact} {...this.props.exact}
render={({ location }) => render={({ location }) =>
this.props.isAuthenticated this.props.isAuthenticated ? (
? this.props.children this.props.children
: (() => { ) : (()=>{
return ( return (
<Redirect <Redirect
to={{ to={{
pathname: "/user/login", pathname: "/user/login",
state: { from: location }, state: { from: location }
}} }}
/> />
); )
})() })()
} }
/> /> : null
) : null; );
} }
} }
PrivateRoute.propTypes = { PrivateRoute.propTypes = {
isAuthenticated: PropTypes.bool, isAuthenticated: PropTypes.bool.isRequired,
progress: PropTypes.bool.isRequired, progress: PropTypes.bool.isRequired
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated, isAuthenticated: state.auth.isAuthenticated,
progress: state.auth.progress, progress: state.auth.progress
}); });
export default connect(mapStateToProps, null)(withRouter(PrivateRoute)); export default connect(mapStateToProps, null)(withRouter(PrivateRoute));
+1 -5
View File
@@ -24,7 +24,6 @@ import Login from "../User/Login";
import Account from "../User/Account"; import Account from "../User/Account";
import News from "../News"; import News from "../News";
import Faq from "../Faq"; import Faq from "../Faq";
import CodeEditor from "../CodeEditor/CodeEditor";
class Routes extends Component { class Routes extends Component {
componentDidUpdate() { componentDidUpdate() {
@@ -48,9 +47,6 @@ class Routes extends Component {
<Route path="/tutorial/:tutorialId" exact> <Route path="/tutorial/:tutorialId" exact>
<Tutorial /> <Tutorial />
</Route> </Route>
<Route path="/CodeEditor" exact>
<CodeEditor />
</Route>
{/* Sharing */} {/* Sharing */}
<PublicRoute path="/share/:shareId" exact> <PublicRoute path="/share/:shareId" exact>
<Project /> <Project />
@@ -104,7 +100,7 @@ class Routes extends Component {
} }
Home.propTypes = { Home.propTypes = {
visitPage: PropTypes.func, visitPage: PropTypes.func.isRequired,
}; };
export default connect(null, { visitPage })(withRouter(Routes)); export default connect(null, { visitPage })(withRouter(Routes));
-77
View File
@@ -1,77 +0,0 @@
import React from "react";
import Box from "@mui/material/Box";
import Tab from "@mui/material/Tab";
import TabContext from "@mui/lab/TabContext";
import TabList from "@mui/lab/TabList";
import TabPanel from "@mui/lab/TabPanel";
import store from "../store";
import ReactMarkdown from "react-markdown";
import * as Blockly from "blockly";
export default function LabTabs() {
const [value, setValue] = React.useState('1');
const handleChange = (event, newValue) => {
setValue(newValue);
};
// get the description in the current language if no lang is give return english or the first one
function filterLanguage(options) {
var lang;
if (window.localStorage.getItem("locale")) {
lang = window.localStorage.getItem("locale").split("_")[0];
}
else {
lang = "en";
}
for (var i = 0; i < options.length; i++) {
if (options[i].languageCode === lang) {
return options[i].text;
}
}
return options[0].text;
}
var currentStore = store.getState();
// ALL SENSOR DATA FROM WIKI
var sensorData = currentStore.sensorwiki;
// NAME OF SELECTED BLOCK
var sensorName = currentStore.workspace.code.data.name;
// SEARCH ALL DATA FOR SELECTED BLOCK NAME
var sensorInfo = sensorData.find(function (element) {
return element.slug === sensorName;
});
if (sensorInfo) {
sensorInfo.markdown = "Coming soon...";
// GET DESCRIPTION OF SELECTED BLOCK
sensorInfo.details = `# ${sensorName.toUpperCase()}
${Blockly.Msg.sensorinfo_explanation} [${sensorName.toUpperCase()}](https://sensors.wiki/sensor/detail/${sensorName})
## ${Blockly.Msg.sensorinfo_description}
${filterLanguage(sensorInfo.description.item)}
## ${Blockly.Msg.sensorinfo_measurable_phenos}
## ${Blockly.Msg.sensorinfo_manufacturer}
${sensorInfo.manufacturer}
## ${Blockly.Msg.sensorinfo_lifetime}
${sensorInfo.lifePeriod}`
}
return (
<Box sx={{ width: '100%', typography: 'body1' }}>
<TabContext value={value}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<TabList onChange={handleChange}>
<Tab label="Basic Information" value="1" />
<Tab label="Details" value="2" />
</TabList>
</Box>
<TabPanel value="1">
{sensorInfo ? <ReactMarkdown>{sensorInfo.markdown}</ReactMarkdown> : "No data available"}
</TabPanel>
<TabPanel value="2">
{sensorInfo ? <ReactMarkdown>{sensorInfo.details}</ReactMarkdown> : "No data available"}
</TabPanel>
</TabContext>
</Box>
);
}
-55
View File
@@ -1,55 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { setBoard } from '../../actions/boardActions';
import * as Blockly from 'blockly/core';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
import Typography from '@mui/material/Typography';
import FormHelperText from '@mui/material/FormHelperText';
class DeviceSelector extends Component {
componentDidMount(){
// Ensure that Blockly.setLocale is adopted in the component.
// Otherwise, the text will not be displayed until the next update of the component.
this.forceUpdate();
}
render(){
return (
<div>
<Typography style={{fontWeight: 'bold'}}>{Blockly.Msg.settings_board}</Typography>
<FormHelperText style={{color: 'black', lineHeight: 1.3, marginBottom: '8px'}}>{Blockly.Msg.settings_board_text}</FormHelperText>
<FormControl variant="standard">
<InputLabel id="demo-simple-select-label">{Blockly.Msg.settings_board}</InputLabel>
<Select
variant="standard"
labelId="demo-simple-select-label"
id="demo-simple-select"
value={this.props.selectedBoard}
onChange={(e) => this.props.setBoard(e.target.value)}>
<MenuItem value="mcu">senseBox MCU</MenuItem>
<MenuItem value="mini">senseBox MCU mini</MenuItem>
</Select>
</FormControl>
</div>
);
}
}
DeviceSelector.propTypes = {
setBoard: PropTypes.func.isRequired,
selectedBoard: PropTypes.string.isRequired
};
const mapStateToProps = state => ({
selectedBoard: state.board.board
});
export default connect(mapStateToProps, { setBoard })(DeviceSelector);
+10 -10
View File
@@ -5,12 +5,12 @@ import { setLanguage } from '../../actions/generalActions';
import * as Blockly from 'blockly/core'; import * as Blockly from 'blockly/core';
import InputLabel from '@mui/material/InputLabel'; import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@mui/material/MenuItem'; import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@mui/material/FormControl'; import FormControl from '@material-ui/core/FormControl';
import Select from '@mui/material/Select'; import Select from '@material-ui/core/Select';
import Typography from '@mui/material/Typography'; import Typography from '@material-ui/core/Typography';
import FormHelperText from '@mui/material/FormHelperText'; import FormHelperText from '@material-ui/core/FormHelperText';
class LanguageSelector extends Component { class LanguageSelector extends Component {
@@ -25,18 +25,18 @@ class LanguageSelector extends Component {
} }
render(){ render(){
return ( return(
<div> <div>
<Typography style={{fontWeight: 'bold'}}>{Blockly.Msg.settings_language}</Typography> <Typography style={{fontWeight: 'bold'}}>{Blockly.Msg.settings_language}</Typography>
<FormHelperText style={{color: 'black', lineHeight: 1.3, marginBottom: '8px'}}>{Blockly.Msg.settings_language_text}</FormHelperText> <FormHelperText style={{color: 'black', lineHeight: 1.3, marginBottom: '8px'}}>{Blockly.Msg.settings_language_text}</FormHelperText>
<FormControl variant="standard"> <FormControl>
<InputLabel id="demo-simple-select-label">{Blockly.Msg.settings_language}</InputLabel> <InputLabel id="demo-simple-select-label">{Blockly.Msg.settings_language}</InputLabel>
<Select <Select
variant="standard"
labelId="demo-simple-select-label" labelId="demo-simple-select-label"
id="demo-simple-select" id="demo-simple-select"
value={this.props.language} value={this.props.language}
onChange={this.handleChange}> onChange={this.handleChange}
>
<MenuItem value={'de_DE'}>{Blockly.Msg.settings_language_de}</MenuItem> <MenuItem value={'de_DE'}>{Blockly.Msg.settings_language_de}</MenuItem>
<MenuItem value={'en_US'}>{Blockly.Msg.settings_language_en}</MenuItem> <MenuItem value={'en_US'}>{Blockly.Msg.settings_language_en}</MenuItem>
</Select> </Select>
+9 -9
View File
@@ -5,12 +5,12 @@ import { setPlatform } from "../../actions/generalActions";
import * as Blockly from "blockly/core"; import * as Blockly from "blockly/core";
import InputLabel from "@mui/material/InputLabel"; import InputLabel from "@material-ui/core/InputLabel";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@mui/material/FormControl"; import FormControl from "@material-ui/core/FormControl";
import Select from "@mui/material/Select"; import Select from "@material-ui/core/Select";
import Typography from "@mui/material/Typography"; import Typography from "@material-ui/core/Typography";
import FormHelperText from "@mui/material/FormHelperText"; import FormHelperText from "@material-ui/core/FormHelperText";
class OtaSelector extends Component { class OtaSelector extends Component {
componentDidMount() { componentDidMount() {
@@ -33,16 +33,16 @@ class OtaSelector extends Component {
https://sensebox.de/app https://sensebox.de/app
</a> </a>
</FormHelperText> </FormHelperText>
<FormControl variant="standard"> <FormControl>
<InputLabel id="demo-simple-select-label"> <InputLabel id="demo-simple-select-label">
{Blockly.Msg.settings_statistics} {Blockly.Msg.settings_statistics}
</InputLabel> </InputLabel>
<Select <Select
variant="standard"
labelId="demo-simple-select-label" labelId="demo-simple-select-label"
id="demo-simple-select" id="demo-simple-select"
value={this.props.platform} value={this.props.platform}
onChange={(e) => this.props.setPlatform(e.target.value)}> onChange={(e) => this.props.setPlatform(e.target.value)}
>
<MenuItem value={true}>{Blockly.Msg.settings_ota_on}</MenuItem> <MenuItem value={true}>{Blockly.Msg.settings_ota_on}</MenuItem>
<MenuItem value={false}>{Blockly.Msg.settings_ota_off}</MenuItem> <MenuItem value={false}>{Blockly.Msg.settings_ota_off}</MenuItem>
</Select> </Select>
+9 -9
View File
@@ -5,12 +5,12 @@ import { setRenderer } from '../../actions/generalActions';
import * as Blockly from 'blockly/core' import * as Blockly from 'blockly/core'
import InputLabel from '@mui/material/InputLabel'; import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@mui/material/MenuItem'; import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@mui/material/FormControl'; import FormControl from '@material-ui/core/FormControl';
import Select from '@mui/material/Select'; import Select from '@material-ui/core/Select';
import Typography from '@mui/material/Typography'; import Typography from '@material-ui/core/Typography';
import FormHelperText from '@mui/material/FormHelperText'; import FormHelperText from '@material-ui/core/FormHelperText';
class RenderSelector extends Component { class RenderSelector extends Component {
@@ -26,14 +26,14 @@ class RenderSelector extends Component {
<div> <div>
<Typography style={{fontWeight: 'bold'}}>{Blockly.Msg.settings_renderer}</Typography> <Typography style={{fontWeight: 'bold'}}>{Blockly.Msg.settings_renderer}</Typography>
<FormHelperText style={{color: 'black', lineHeight: 1.3, marginBottom: '8px'}}>{Blockly.Msg.settings_renderer_text}</FormHelperText> <FormHelperText style={{color: 'black', lineHeight: 1.3, marginBottom: '8px'}}>{Blockly.Msg.settings_renderer_text}</FormHelperText>
<FormControl variant="standard"> <FormControl>
<InputLabel id="demo-simple-select-label">{Blockly.Msg.settings_renderer}</InputLabel> <InputLabel id="demo-simple-select-label">{Blockly.Msg.settings_renderer}</InputLabel>
<Select <Select
variant="standard"
labelId="demo-simple-select-label" labelId="demo-simple-select-label"
id="demo-simple-select" id="demo-simple-select"
value={this.props.renderer} value={this.props.renderer}
onChange={(e) => this.props.setRenderer(e.target.value)}> onChange={(e) => this.props.setRenderer(e.target.value)}
>
<MenuItem value={'geras'}>Geras</MenuItem> <MenuItem value={'geras'}>Geras</MenuItem>
<MenuItem value={'zelos'}>Zelos</MenuItem> <MenuItem value={'zelos'}>Zelos</MenuItem>
</Select> </Select>
+2 -6
View File
@@ -12,10 +12,9 @@ import RenderSelector from "./RenderSelector";
import StatsSelector from "./StatsSelector"; import StatsSelector from "./StatsSelector";
import OtaSelector from "./OtaSelector"; import OtaSelector from "./OtaSelector";
import SoundsSelector from "./SoundsSelector"; import SoundsSelector from "./SoundsSelector";
import DeviceSelector from "./DeviceSelector";
import Button from "@mui/material/Button"; import Button from "@material-ui/core/Button";
import Paper from "@mui/material/Paper"; import Paper from "@material-ui/core/Paper";
class Settings extends Component { class Settings extends Component {
componentDidMount() { componentDidMount() {
@@ -53,9 +52,6 @@ class Settings extends Component {
<Paper style={{ margin: "10px 0px", padding: "10px" }}> <Paper style={{ margin: "10px 0px", padding: "10px" }}>
<SoundsSelector /> <SoundsSelector />
</Paper> </Paper>
<Paper style={{ margin: "10px 0px", padding: "10px" }}>
<DeviceSelector />
</Paper>
<Button <Button
style={{ marginTop: "10px" }} style={{ marginTop: "10px" }}
+10 -10
View File
@@ -5,12 +5,12 @@ import { setSounds } from "../../actions/generalActions";
import * as Blockly from "blockly/core"; import * as Blockly from "blockly/core";
import InputLabel from "@mui/material/InputLabel"; import InputLabel from "@material-ui/core/InputLabel";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@mui/material/FormControl"; import FormControl from "@material-ui/core/FormControl";
import Select from "@mui/material/Select"; import Select from "@material-ui/core/Select";
import Typography from "@mui/material/Typography"; import Typography from "@material-ui/core/Typography";
import FormHelperText from "@mui/material/FormHelperText"; import FormHelperText from "@material-ui/core/FormHelperText";
class SoundsSelector extends Component { class SoundsSelector extends Component {
componentDidMount() { componentDidMount() {
@@ -30,16 +30,16 @@ class SoundsSelector extends Component {
> >
{Blockly.Msg.settings_sounds_text} {Blockly.Msg.settings_sounds_text}
</FormHelperText> </FormHelperText>
<FormControl variant="standard"> <FormControl>
<InputLabel id="demo-simple-select-label"> <InputLabel id="demo-simple-select-label">
{Blockly.Msg.settings_sounds} {Blockly.Msg.settings_sounds}
</InputLabel> </InputLabel>
<Select <Select
variant="standard"
labelId="demo-simple-select-label" labelId="demo-simple-select-label"
id="demo-simple-select" id="demo-simple-select"
value={this.props.sounds} value={this.props.sounds}
onChange={(e) => this.props.setSounds(e.target.value)}> onChange={(e) => this.props.setSounds(e.target.value)}
>
<MenuItem value={false}>{Blockly.Msg.settings_ota_off}</MenuItem> <MenuItem value={false}>{Blockly.Msg.settings_ota_off}</MenuItem>
<MenuItem value={true}>{Blockly.Msg.settings_ota_on}</MenuItem> <MenuItem value={true}>{Blockly.Msg.settings_ota_on}</MenuItem>
</Select> </Select>
@@ -52,7 +52,7 @@ class SoundsSelector extends Component {
SoundsSelector.propTypes = { SoundsSelector.propTypes = {
setSounds: PropTypes.func.isRequired, setSounds: PropTypes.func.isRequired,
language: PropTypes.string.isRequired, language: PropTypes.string.isRequired,
sounds: PropTypes.bool.isRequired, sounds: PropTypes.string.isRequired,
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
+9 -9
View File
@@ -5,12 +5,12 @@ import { setStatistics } from '../../actions/generalActions';
import * as Blockly from 'blockly/core' import * as Blockly from 'blockly/core'
import InputLabel from '@mui/material/InputLabel'; import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@mui/material/MenuItem'; import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@mui/material/FormControl'; import FormControl from '@material-ui/core/FormControl';
import Select from '@mui/material/Select'; import Select from '@material-ui/core/Select';
import Typography from '@mui/material/Typography'; import Typography from '@material-ui/core/Typography';
import FormHelperText from '@mui/material/FormHelperText'; import FormHelperText from '@material-ui/core/FormHelperText';
class StatsSelector extends Component { class StatsSelector extends Component {
@@ -25,14 +25,14 @@ class StatsSelector extends Component {
<div> <div>
<Typography style={{fontWeight: 'bold'}}>{Blockly.Msg.settings_statistics}</Typography> <Typography style={{fontWeight: 'bold'}}>{Blockly.Msg.settings_statistics}</Typography>
<FormHelperText style={{color: 'black', lineHeight: 1.3, marginBottom: '8px'}}>{Blockly.Msg.settings_statistics_text}</FormHelperText> <FormHelperText style={{color: 'black', lineHeight: 1.3, marginBottom: '8px'}}>{Blockly.Msg.settings_statistics_text}</FormHelperText>
<FormControl variant="standard"> <FormControl>
<InputLabel id="demo-simple-select-label">{Blockly.Msg.settings_statistics}</InputLabel> <InputLabel id="demo-simple-select-label">{Blockly.Msg.settings_statistics}</InputLabel>
<Select <Select
variant="standard"
labelId="demo-simple-select-label" labelId="demo-simple-select-label"
id="demo-simple-select" id="demo-simple-select"
value={this.props.statistics} value={this.props.statistics}
onChange={(e) => this.props.setStatistics(e.target.value)}> onChange={(e) => this.props.setStatistics(e.target.value)}
>
<MenuItem value={true}>{Blockly.Msg.settings_statistics_on}</MenuItem> <MenuItem value={true}>{Blockly.Msg.settings_statistics_on}</MenuItem>
<MenuItem value={false}>{Blockly.Msg.settings_statistics_off}</MenuItem> <MenuItem value={false}>{Blockly.Msg.settings_statistics_off}</MenuItem>
</Select> </Select>
+6 -6
View File
@@ -1,9 +1,9 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import withStyles from '@mui/styles/withStyles'; import { withStyles } from '@material-ui/core/styles';
import IconButton from '@mui/material/IconButton'; import IconButton from '@material-ui/core/IconButton';
import MaterialUISnackbar from '@mui/material/Snackbar'; import MaterialUISnackbar from '@material-ui/core/Snackbar';
import SnackbarContent from '@mui/material/SnackbarContent'; import SnackbarContent from '@material-ui/core/SnackbarContent';
import { faTimes } from "@fortawesome/free-solid-svg-icons"; import { faTimes } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -69,8 +69,8 @@ class Snackbar extends Component {
style={{flexWrap: 'nowrap'}} style={{flexWrap: 'nowrap'}}
className={this.props.type === 'error' ? this.props.classes.error : this.props.classes.success} className={this.props.type === 'error' ? this.props.classes.error : this.props.classes.success}
action={ action={
<IconButton onClick={this.onClose} style={{color: 'inherit'}} size="large"> <IconButton onClick={this.onClose} style={{color: 'inherit'}}>
<FontAwesomeIcon icon={faTimes} size="xs" /> <FontAwesomeIcon icon={faTimes} size="xs"/>
</IconButton> </IconButton>
} }
message={this.props.message} message={this.props.message}
+20 -86
View File
@@ -1,113 +1,47 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { Button, Card } from "@mui/material";
import * as Blockly from "blockly";
import CardContent from "@mui/material/CardContent";
import Typography from "@mui/material/Typography";
import ReactMarkdown from "react-markdown";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import Slide from "@mui/material/Slide";
import SensorInfo from "./SensorInfo";
import store from "../store";
// FIXME checkout https://mui.com/components/use-media-query/#migrating-from-withwidth import withWidth from '@material-ui/core/withWidth';
const withWidth = () => (WrappedComponent) => (props) => <WrappedComponent {...props} width="xs" />;
const Transition = React.forwardRef(function Transition(props, ref) { import { Card } from '@material-ui/core';
return <Slide direction="up" ref={ref} {...props} />; import * as Blockly from 'blockly'
}); import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';
import ReactMarkdown from 'react-markdown';
class TooltipViewer extends Component { class TooltipViewer extends Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
this.state = {
open: false,
};
}
toggleDialog = () => {
this.setState({ open: !this.state });
};
openDialog = () => {
this.setState({ open: true });
};
render() { render() {
return ( return (
<Card <Card className="tooltipViewer" style={{ height: '100%', margin: '1vH 0 0 0', maxHeight: '19vH', overflow: 'auto' }} ref={this.myDiv}>
className="tooltipViewer"
style={{
height: "100%",
margin: "1vH 0 0 0",
maxHeight: "19vH",
overflow: "auto",
}}
ref={this.myDiv}
>
<CardContent> <CardContent>
<Typography variant="h5" component="h2"> <Typography variant="h5" component="h2">
{Blockly.Msg.tooltip_viewer} {Blockly.Msg.tooltip_viewer}
</Typography> </Typography>
<Typography variant="body2" component="p">
<ReactMarkdown linkTarget="_blank">{this.props.tooltip}</ReactMarkdown>
{this.props.helpurl !== '' ? <ReactMarkdown>{`${Blockly.Msg.tooltip_moreInformation} [${Blockly.Msg.labels_here}](${this.props.helpurl})`}</ReactMarkdown> : null}
<Typography variant="body2" component="span">
<ReactMarkdown linkTarget="_blank">
{this.props.tooltip}
</ReactMarkdown>
{store.getState().workspace.code.data ? (
<Button
label="Mehr"
variant="contained"
color="primary"
onClick={() => {
this.openDialog();
}}
>
Sensor Informationen
</Button>
) : (
<ReactMarkdown>{`${Blockly.Msg.tooltip_moreInformation} [${Blockly.Msg.labels_here}](${this.props.helpurl})`}</ReactMarkdown>
)}
</Typography> </Typography>
</CardContent> </CardContent>
{store.getState().workspace.code.data ? (<Dialog
open={this.state.open}
TransitionComponent={Transition}
keepMounted
aria-describedby="alert-dialog-slide-description"
onClose={() => {
this.toggleDialog();
}}
maxWidth={"md"}
fullWidth={true}
>
<DialogContent>
<SensorInfo></SensorInfo>
</DialogContent>
<DialogActions>
<Button onClick={() => {
this.toggleDialog();
}}>Close</Button>
</DialogActions>
</Dialog>) : (null)}
</Card> </Card>
); );
} };
} }
TooltipViewer.propTypes = { TooltipViewer.propTypes = {
tooltip: PropTypes.string.isRequired, tooltip: PropTypes.string.isRequired,
helpurl: PropTypes.string.isRequired, helpurl: PropTypes.string.isRequired
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = state => ({
tooltip: state.workspace.code.tooltip, tooltip: state.workspace.code.tooltip,
helpurl: state.workspace.code.helpurl, helpurl: state.workspace.code.helpurl
}); });
export default connect(mapStateToProps, null)(withWidth()(TooltipViewer)); export default connect(mapStateToProps, null)(withWidth()(TooltipViewer));
+33 -112
View File
@@ -4,58 +4,17 @@ import { connect } from "react-redux";
import { workspaceName } from "../../actions/workspaceActions"; import { workspaceName } from "../../actions/workspaceActions";
import BlocklyWindow from "../Blockly/BlocklyWindow"; import BlocklyWindow from "../Blockly/BlocklyWindow";
import CodeViewer from "../CodeViewer";
import WorkspaceFunc from "../Workspace/WorkspaceFunc"; import WorkspaceFunc from "../Workspace/WorkspaceFunc";
import Grid from "@mui/material/Grid"; import withWidth, { isWidthDown } from "@material-ui/core/withWidth";
import Card from "@mui/material/Card"; import Grid from "@material-ui/core/Grid";
import Typography from "@mui/material/Typography"; import Card from "@material-ui/core/Card";
import Typography from "@material-ui/core/Typography";
import * as Blockly from "blockly"; import * as Blockly from "blockly";
import { initialXml } from "../Blockly/initialXml"; import { initialXml } from "../Blockly/initialXml";
import IconButton from "@mui/material/IconButton";
import CodeViewer from "../CodeViewer";
import TooltipViewer from "../TooltipViewer";
import Tooltip from "@mui/material/Tooltip";
import ReactMarkdown from "react-markdown";
import { faCode } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import withStyles from '@mui/styles/withStyles';
import remarkGfm from "remark-gfm";
import remarkGemoji from "remark-gemoji";
import { isWidthDown } from "../../helpers/handleBreakpoints";
// FIXME checkout https://mui.com/components/use-media-query/#migrating-from-withwidth
const withWidth = () => (WrappedComponent) => (props) => <WrappedComponent {...props} width="xs" />;
const styles = (theme) => ({
codeOn: {
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
"&:hover": {
backgroundColor: theme.palette.primary.contrastText,
color: theme.palette.primary.main,
border: `1px solid ${theme.palette.secondary.main}`,
},
},
codeOff: {
backgroundColor: theme.palette.primary.contrastText,
color: theme.palette.primary.main,
border: `1px solid ${theme.palette.secondary.main}`,
"&:hover": {
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
},
},
});
class Assessment extends Component { class Assessment extends Component {
constructor(props) {
super(props);
this.state = {
codeOn: false,
};
}
componentDidMount() { componentDidMount() {
this.props.workspaceName(this.props.name); this.props.workspaceName(this.props.name);
} }
@@ -66,10 +25,6 @@ class Assessment extends Component {
} }
} }
onChange = () => {
this.setState({ codeOn: !this.state.codeOn });
};
render() { render() {
var tutorialId = this.props.tutorial._id; var tutorialId = this.props.tutorial._id;
var currentTask = this.props.step; var currentTask = this.props.step;
@@ -80,97 +35,63 @@ class Assessment extends Component {
(task) => task._id === currentTask._id (task) => task._id === currentTask._id
); );
var statusTask = status.tasks[taskIndex]; var statusTask = status.tasks[taskIndex];
console.log(statusTask);
return ( return (
<div className="assessmentDiv" style={{ width: "100%" }}> <div className="assessmentDiv" style={{ width: "100%" }}>
<Typography
variant="h4"
style={{
float: "left",
marginBottom: "5px",
height: "40px",
display: "table",
}}
>
{currentTask.headline}
</Typography>
<div style={{ float: "right", height: "40px" }}> <div style={{ float: "right", height: "40px" }}>
<WorkspaceFunc assessment /> <WorkspaceFunc assessment />
</div> </div>
<Grid container spacing={2} style={{ marginBottom: "5px" }}> <Grid container spacing={2} style={{ marginBottom: "5px" }}>
<Grid <Grid item xs={12} md={6} lg={8}>
item
xs={12}
md={this.state.codeOn ? 6 : 9}
lg={this.state.codeOn ? 6 : 9}
style={{ position: "relative" }}
>
<Tooltip
title={
this.state.codeOn
? Blockly.Msg.tooltip_hide_code
: Blockly.Msg.tooltip_show_code
}
>
<IconButton
className={`showCode ${this.state.codeOn
? this.props.classes.codeOn
: this.props.classes.codeOff
}`}
style={{
width: "40px",
height: "40px",
position: "absolute",
top: 6,
right: 8,
zIndex: 21,
}}
onClick={() => this.onChange()}
size="large">
<FontAwesomeIcon icon={faCode} size="xs" />
</IconButton>
</Tooltip>
<BlocklyWindow <BlocklyWindow
initialXml={initialXml} initialXml={initialXml}
blockDisabled blockDisabled
blocklyCSS={{ height: "65vH" }} blocklyCSS={{ height: "65vH" }}
/> />
</Grid> </Grid>
{this.state.codeOn ? (
<Grid item xs={12} md={4} lg={3}>
<CodeViewer />
</Grid>
) : null}
<Grid <Grid
item item
xs={12} xs={12}
md={3} md={6}
lg={3} lg={4}
style={{ style={
position: "relative", isWidthDown("sm", this.props.width)
// isWidthDown("sm", this.props.width) ? { height: "max-content" }
// ? { height: "max-content" } : {}
// : {} }
}}
> >
<Card <Card
style={{ style={{
height: "calc(44vH - 35px)", height: "calc(50% - 30px)",
padding: "10px", padding: "10px",
marginBottom: "10px", marginBottom: "10px",
}} }}
> >
<Typography> <Typography variant="h5">
<ReactMarkdown remarkPlugins={[remarkGfm, remarkGemoji]}> {Blockly.Msg.tutorials_assessment_task}
{currentTask.text}
</ReactMarkdown>
</Typography> </Typography>
<Typography>{currentTask.text}</Typography>
</Card> </Card>
{/* <Card
style={{
height: "20vH",
padding: "10px",
marginBottom: "10px",
}}
> */}
<TooltipViewer />
{/* </Card> */}
<div <div
style={ style={
isWidthDown("sm", this.props.width) isWidthDown("sm", this.props.width)
? { height: "500px" } ? { height: "500px" }
: { height: "50%" } : { height: "50%" }
} }
></div> >
<CodeViewer />
</div>
</Grid> </Grid>
</Grid> </Grid>
</div> </div>
@@ -192,5 +113,5 @@ const mapStateToProps = (state) => ({
}); });
export default connect(mapStateToProps, { workspaceName })( export default connect(mapStateToProps, { workspaceName })(
withWidth()(withStyles(styles, { withTheme: true })(Assessment)) withWidth()(Assessment)
); );
+85 -137
View File
@@ -1,57 +1,53 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { import { changeContent, deleteProperty, setError, deleteError } from '../../../actions/tutorialBuilderActions';
changeContent,
deleteProperty,
setError,
deleteError,
} from "../../../actions/tutorialBuilderActions";
import moment from "moment"; import moment from 'moment';
import localization from "moment/locale/de"; import localization from 'moment/locale/de';
import * as Blockly from "blockly/core"; import * as Blockly from 'blockly/core';
import { initialXml } from "../../Blockly//initialXml.js"; import { initialXml } from '../../Blockly//initialXml.js';
import BlocklyWindow from "../../Blockly/BlocklyWindow"; import BlocklyWindow from '../../Blockly/BlocklyWindow';
import withStyles from '@mui/styles/withStyles'; import { withStyles } from '@material-ui/core/styles';
import Switch from "@mui/material/Switch"; import Switch from '@material-ui/core/Switch';
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormHelperText from "@mui/material/FormHelperText"; import FormHelperText from '@material-ui/core/FormHelperText';
import FormLabel from "@mui/material/FormLabel"; import FormLabel from '@material-ui/core/FormLabel';
import Button from "@mui/material/Button"; import Button from '@material-ui/core/Button';
import Grid from "@mui/material/Grid"; import Grid from '@material-ui/core/Grid';
const styles = (theme) => ({ const styles = (theme) => ({
errorColor: { errorColor: {
color: theme.palette.error.dark, color: theme.palette.error.dark
}, },
errorBorder: { errorBorder: {
border: `1px solid ${theme.palette.error.dark}`, border: `1px solid ${theme.palette.error.dark}`
}, },
errorButton: { errorButton: {
marginTop: "5px", marginTop: '5px',
height: "40px", height: '40px',
backgroundColor: theme.palette.error.dark, backgroundColor: theme.palette.error.dark,
"&:hover": { '&:hover': {
backgroundColor: theme.palette.error.dark, backgroundColor: theme.palette.error.dark
}, }
}, }
}); });
class BlocklyExample extends Component { class BlocklyExample extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
checked: props.task ? props.task : props.value ? true : false, checked: props.task ? props.task : props.value ? true : false,
input: null, input: null,
disabled: false, disabled: false
}; };
} }
componentDidMount() { componentDidMount() {
moment.updateLocale("de", localization); moment.updateLocale('de', localization);
this.isError(); this.isError();
// if(this.props.task){ // if(this.props.task){
// this.props.setError(this.props.index, 'xml'); // this.props.setError(this.props.index, 'xml');
@@ -60,14 +56,7 @@ class BlocklyExample extends Component {
componentDidUpdate(props, state) { componentDidUpdate(props, state) {
if (props.task !== this.props.task || props.value !== this.props.value) { if (props.task !== this.props.task || props.value !== this.props.value) {
this.setState( this.setState({ checked: this.props.task ? this.props.task : this.props.value ? true : false },
{
checked: this.props.task
? this.props.task
: this.props.value
? true
: false,
},
() => this.isError() () => this.isError()
); );
} }
@@ -88,11 +77,12 @@ class BlocklyExample extends Component {
// check if value is valid xml; // check if value is valid xml;
try { try {
Blockly.Xml.textToDom(xml); Blockly.Xml.textToDom(xml);
this.props.deleteError(this.props.index, "xml"); this.props.deleteError(this.props.index, 'xml');
} catch (err) { }
catch (err) {
xml = initialXml; xml = initialXml;
// not valid xml, throw error in redux store // not valid xml, throw error in redux store
this.props.setError(this.props.index, "xml"); this.props.setError(this.props.index, 'xml');
} }
if (!this.props.task) { if (!this.props.task) {
// instruction can also display only one block, which does not necessarily // instruction can also display only one block, which does not necessarily
@@ -100,38 +90,31 @@ class BlocklyExample extends Component {
xml = xml.replace('deletable="false"', 'deletable="true"'); xml = xml.replace('deletable="false"', 'deletable="true"');
} }
this.setState({ xml: xml }); this.setState({ xml: xml });
} else {
this.props.deleteError(this.props.index, "xml");
} }
}; else {
this.props.deleteError(this.props.index, 'xml');
}
}
onChange = (value) => { onChange = (value) => {
var oldValue = this.state.checked; var oldValue = this.state.checked;
this.setState({ checked: value }); this.setState({ checked: value });
if (oldValue !== value && !value) { if (oldValue !== value && !value) {
this.props.deleteError(this.props.index, "xml"); this.props.deleteError(this.props.index, 'xml');
this.props.deleteProperty(this.props.index, "xml"); this.props.deleteProperty(this.props.index, 'xml');
} }
}; }
setXml = () => { setXml = () => {
var xml = this.props.xml; var xml = this.props.xml;
this.props.changeContent(xml, this.props.index, "xml"); this.props.changeContent(xml, this.props.index, 'xml');
this.setState({ input: moment(Date.now()).format("LTS") }); this.setState({ input: moment(Date.now()).format('LTS') });
}; }
render() { render() {
return ( return (
<div <div style={{ marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)' }}>
style={{ {!this.props.task ?
marginBottom: "10px",
padding: "18.5px 14px",
borderRadius: "25px",
border: "1px solid lightgrey",
width: "calc(100% - 28px)",
}}
>
{!this.props.task ? (
<FormControlLabel <FormControlLabel
labelPlacement="end" labelPlacement="end"
label={"Blockly Beispiel"} label={"Blockly Beispiel"}
@@ -139,80 +122,49 @@ class BlocklyExample extends Component {
<Switch <Switch
checked={this.state.checked} checked={this.state.checked}
onChange={(e) => this.onChange(e.target.checked)} onChange={(e) => this.onChange(e.target.checked)}
color="primary"
/> />
} }
/> />
) : ( : <FormLabel style={{ color: 'black' }}>{Blockly.Msg.builder_solution}</FormLabel>}
<FormLabel style={{ color: "black" }}> {this.state.checked ? !this.props.value || this.props.error ?
{Blockly.Msg.builder_solution} <FormHelperText style={{ lineHeight: 'initial' }} className={this.props.classes.errorColor}>{`Reiche deine Blöcke ein, indem du auf den '${this.props.task ? Blockly.Msg.builder_solution_submit : Blockly.Msg.builder_example_submit}'-Button klickst.`}</FormHelperText>
</FormLabel> : this.state.input ? <FormHelperText style={{ lineHeight: 'initial' }}>Die letzte Einreichung erfolgte um {this.state.input} Uhr.</FormHelperText> : null
)} : null}
{this.state.checked ? ( {this.state.checked && !this.props.task ?
!this.props.value || this.props.error ? ( <FormHelperText style={{ lineHeight: 'initial' }}>{Blockly.Msg.builder_comment}</FormHelperText>
<FormHelperText : null}
style={{ lineHeight: "initial" }}
className={this.props.classes.errorColor}
>{`Reiche deine Blöcke ein, indem du auf den '${
this.props.task
? Blockly.Msg.builder_solution_submit
: Blockly.Msg.builder_example_submit
}'-Button klickst.`}</FormHelperText>
) : this.state.input ? (
<FormHelperText style={{ lineHeight: "initial" }}>
Die letzte Einreichung erfolgte um {this.state.input} Uhr.
</FormHelperText>
) : null
) : null}
{this.state.checked && !this.props.task ? (
<FormHelperText style={{ lineHeight: "initial" }}>
{Blockly.Msg.builder_comment}
</FormHelperText>
) : null}
{/* ensure that the correct xml-file is displayed in the workspace */} {/* ensure that the correct xml-file is displayed in the workspace */}
{this.state.checked && this.state.xml {this.state.checked && this.state.xml ? (() => {
? (() => { return (
return ( <div style={{ marginTop: '10px' }}>
<div style={{ marginTop: "10px" }}> <Grid container className={!this.props.value || this.props.error ? this.props.classes.errorBorder : null}>
<Grid <Grid item xs={12}>
container <BlocklyWindow
className={ blockDisabled={this.props.task}
!this.props.value || this.props.error trashcan={false}
? this.props.classes.errorBorder initialXml={this.state.xml}
: null blocklyCSS={{ height: '500px' }}
} />
> </Grid>
<Grid item xs={12}> </Grid>
<BlocklyWindow <Button
blockDisabled={this.props.task} className={!this.props.value || this.props.error ? this.props.classes.errorButton : null}
trashcan={false} style={{ marginTop: '5px', height: '40px' }}
initialXml={this.state.xml} variant='contained'
blocklyCSS={{ height: "500px" }} color='primary'
/> disabled={this.state.disabled}
</Grid> onClick={() => this.setXml()}
</Grid> >
<Button {this.props.task ? Blockly.Msg.builder_solution_submit : Blockly.Msg.builder_example_submit}
className={ </Button>
!this.props.value || this.props.error </div>
? this.props.classes.errorButton )
: null })()
}
style={{ marginTop: "5px", height: "40px" }}
variant="contained"
color="primary"
disabled={this.state.disabled}
onClick={() => this.setXml()}
>
{this.props.task
? Blockly.Msg.builder_solution_submit
: Blockly.Msg.builder_example_submit}
</Button>
</div>
);
})()
: null} : null}
</div> </div>
); );
} };
} }
BlocklyExample.propTypes = { BlocklyExample.propTypes = {
@@ -220,16 +172,12 @@ BlocklyExample.propTypes = {
deleteProperty: PropTypes.func.isRequired, deleteProperty: PropTypes.func.isRequired,
setError: PropTypes.func.isRequired, setError: PropTypes.func.isRequired,
deleteError: PropTypes.func.isRequired, deleteError: PropTypes.func.isRequired,
xml: PropTypes.string.isRequired, xml: PropTypes.string.isRequired
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = state => ({
xml: state.workspace.code.xml, xml: state.workspace.code.xml
}); });
export default connect(mapStateToProps, {
changeContent, export default connect(mapStateToProps, { changeContent, deleteProperty, setError, deleteError })(withStyles(styles, { withTheme: true })(BlocklyExample));
deleteProperty,
setError,
deleteError,
})(withStyles(styles, { withTheme: true })(BlocklyExample));
+40 -122
View File
@@ -10,8 +10,6 @@ import {
resetTutorial as resetTutorialBuilder, resetTutorial as resetTutorialBuilder,
} from "../../../actions/tutorialBuilderActions"; } from "../../../actions/tutorialBuilderActions";
import { import {
getAllTutorials,
getUserTutorials,
getTutorials, getTutorials,
resetTutorial, resetTutorial,
deleteTutorial, deleteTutorial,
@@ -21,31 +19,26 @@ import { clearMessages } from "../../../actions/messageActions";
import axios from "axios"; import axios from "axios";
import { withRouter } from "react-router-dom"; import { withRouter } from "react-router-dom";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEyeSlash, faUserCheck } from "@fortawesome/free-solid-svg-icons";
import Breadcrumbs from "../../Breadcrumbs"; import Breadcrumbs from "../../Breadcrumbs";
import Textfield from "./Textfield"; import Textfield from "./Textfield";
import Difficulty from "./Difficulty";
import Step from "./Step"; import Step from "./Step";
import Dialog from "../../Dialog"; import Dialog from "../../Dialog";
import Snackbar from "../../Snackbar"; import Snackbar from "../../Snackbar";
import Public from "./Public";
import Review from "./Review";
import withStyles from '@mui/styles/withStyles'; import { withStyles } from "@material-ui/core/styles";
import Button from "@mui/material/Button"; import Button from "@material-ui/core/Button";
import Backdrop from "@mui/material/Backdrop"; import Backdrop from "@material-ui/core/Backdrop";
import CircularProgress from "@mui/material/CircularProgress"; import CircularProgress from "@material-ui/core/CircularProgress";
import Divider from "@mui/material/Divider"; import Divider from "@material-ui/core/Divider";
import FormHelperText from "@mui/material/FormHelperText"; import FormHelperText from "@material-ui/core/FormHelperText";
import Radio from "@mui/material/Radio"; import Radio from "@material-ui/core/Radio";
import RadioGroup from "@mui/material/RadioGroup"; import RadioGroup from "@material-ui/core/RadioGroup";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@material-ui/core/FormControlLabel";
import InputLabel from "@mui/material/InputLabel"; import InputLabel from "@material-ui/core/InputLabel";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@mui/material/FormControl"; import FormControl from "@material-ui/core/FormControl";
import Select from "@mui/material/Select"; import Select from "@material-ui/core/Select";
import * as Blockly from "blockly"; import * as Blockly from "blockly";
const styles = (theme) => ({ const styles = (theme) => ({
@@ -73,8 +66,6 @@ class Builder extends Component {
tutorial: "new", tutorial: "new",
open: false, open: false,
title: "", title: "",
public: false,
difficulty: "",
content: "", content: "",
string: false, string: false,
snackbar: false, snackbar: false,
@@ -89,11 +80,7 @@ class Builder extends Component {
// retrieve tutorials only if a potential user is loaded - authentication // retrieve tutorials only if a potential user is loaded - authentication
// is finished (success or failed) // is finished (success or failed)
if (!this.props.authProgress) { if (!this.props.authProgress) {
if (this.props.user.role === "admin") { this.props.getTutorials();
this.props.getAllTutorials();
} else {
this.props.getUserTutorials();
}
} }
} }
@@ -102,12 +89,8 @@ class Builder extends Component {
props.authProgress !== this.props.authProgress && props.authProgress !== this.props.authProgress &&
!this.props.authProgress !this.props.authProgress
) { ) {
if (this.props.user.role === "admin") { // authentication is completed
// authentication is completed this.props.getTutorials();
this.props.getAllTutorials();
} else {
this.props.getUserTutorials();
}
} }
if (props.message !== this.props.message) { if (props.message !== this.props.message) {
if (this.props.message.id === "GET_TUTORIALS_FAIL") { if (this.props.message.id === "GET_TUTORIALS_FAIL") {
@@ -275,9 +258,6 @@ class Builder extends Component {
var steps = this.props.steps; var steps = this.props.steps;
var newTutorial = new FormData(); var newTutorial = new FormData();
newTutorial.append("title", this.props.title); newTutorial.append("title", this.props.title);
newTutorial.append("difficulty", this.props.difficulty);
newTutorial.append("public", this.props.public);
newTutorial.append("review", this.props.review);
steps.forEach((step, i) => { steps.forEach((step, i) => {
if (step._id) { if (step._id) {
newTutorial.append(`steps[${i}][_id]`, step._id); newTutorial.append(`steps[${i}][_id]`, step._id);
@@ -303,6 +283,21 @@ class Builder extends Component {
// optional // optional
newTutorial.append(`steps[${i}][xml]`, step.xml); newTutorial.append(`steps[${i}][xml]`, step.xml);
} }
if (step.media) {
// optional
if (step.media.youtube) {
newTutorial.append(
`steps[${i}][media][youtube]`,
step.media.youtube
);
}
if (step.media.picture) {
newTutorial.append(
`steps[${i}][media][picture]`,
step.media.picture
);
}
}
}); });
return newTutorial; return newTutorial;
} }
@@ -375,19 +370,9 @@ class Builder extends Component {
}; };
render() { render() {
if (this.props.user.role === "admin") { var filteredTutorials = this.props.tutorials.filter(
var filteredTutorials = this.props.tutorials; (tutorial) => tutorial.creator === this.props.user.email
} else { );
filteredTutorials = this.props.tutorials.filter(
(tutorial) => tutorial.creator === this.props.user.email
);
}
// } else {
// filteredTutorials = this.props.userTutorials.filter(
// (tutorial) => tutorial.creator === this.props.user.email
// );
return ( return (
<div> <div>
<Breadcrumbs <Breadcrumbs
@@ -407,7 +392,7 @@ class Builder extends Component {
<FormControlLabel <FormControlLabel
style={{ color: "black" }} style={{ color: "black" }}
value="new" value="new"
control={<Radio />} control={<Radio color="primary" />}
label={Blockly.Msg.builder_createNew} label={Blockly.Msg.builder_createNew}
labelPlacement="end" labelPlacement="end"
/> />
@@ -417,7 +402,7 @@ class Builder extends Component {
style={{ color: "black" }} style={{ color: "black" }}
disabled={this.props.index === 0} disabled={this.props.index === 0}
value="change" value="change"
control={<Radio />} control={<Radio color="primary" />}
label={Blockly.Msg.builder_changeExisting} label={Blockly.Msg.builder_changeExisting}
labelPlacement="end" labelPlacement="end"
/> />
@@ -425,7 +410,7 @@ class Builder extends Component {
style={{ color: "black" }} style={{ color: "black" }}
disabled={this.props.index === 0} disabled={this.props.index === 0}
value="delete" value="delete"
control={<Radio />} control={<Radio color="primary" />}
label={Blockly.Msg.builder_deleteExisting} label={Blockly.Msg.builder_deleteExisting}
labelPlacement="end" labelPlacement="end"
/> />
@@ -470,31 +455,14 @@ class Builder extends Component {
<FormControl variant="outlined" style={{ width: "100%" }}> <FormControl variant="outlined" style={{ width: "100%" }}>
<InputLabel id="select-outlined-label">Tutorial</InputLabel> <InputLabel id="select-outlined-label">Tutorial</InputLabel>
<Select <Select
variant="standard"
color="primary" color="primary"
labelId="select-outlined-label" labelId="select-outlined-label"
value={this.props.id} value={this.props.id}
onChange={(e) => this.onChangeId(e.target.value)} onChange={(e) => this.onChangeId(e.target.value)}
label="Tutorial"> label="Tutorial"
>
{filteredTutorials.map((tutorial) => ( {filteredTutorials.map((tutorial) => (
<MenuItem value={tutorial._id}> <MenuItem value={tutorial._id}>{tutorial.title}</MenuItem>
{tutorial.title}{" "}
{tutorial.review && tutorial.public === false ? (
<div>
<FontAwesomeIcon icon={faUserCheck} />
<FontAwesomeIcon icon={faEyeSlash} />
</div>
) : tutorial.public === false ? (
<FontAwesomeIcon icon={faEyeSlash} />
) : null}
</MenuItem>
/* ) : tutorial.public === false ? (
<MenuItem value={tutorial._id}>
{tutorial.title} <FontAwesomeIcon icon={faEyeSlash} />
</MenuItem>
) : (
<MenuItem value={tutorial._id}>{tutorial.title}</MenuItem>
)} */
))} ))}
</Select> </Select>
</FormControl> </FormControl>
@@ -519,45 +487,6 @@ class Builder extends Component {
label={"Titel"} label={"Titel"}
error={this.props.error.title} error={this.props.error.title}
/> />
<div
style={{
borderRadius: "25px",
border: "1px solid lightgrey",
padding: "10px 14px 10px 10px",
marginBottom: "20px",
}}
>
<Difficulty
value={this.props.difficulty}
property={"difficulty"}
label={"difficulty"}
error={this.props.error.difficulty}
/>
<Divider
variant="fullWidth"
style={{ margin: "30px 0 10px 0" }}
/>
<Review
value={this.props.review}
property={"review"}
label={"review"}
error={this.props.error.review}
/>
<Divider
variant="fullWidth"
style={{ margin: "30px 0 10px 0" }}
/>
{this.props.user.blocklyRole === "admin" ? (
<Public
value={this.props.public}
property={"public"}
label={"public"}
error={this.props.error.public}
/>
) : null}
</div>
{this.props.steps.map((step, i) => ( {this.props.steps.map((step, i) => (
<Step step={step} index={i} key={i} /> <Step step={step} index={i} key={i} />
@@ -690,8 +619,6 @@ class Builder extends Component {
} }
Builder.propTypes = { Builder.propTypes = {
getAllTutorials: PropTypes.func.isRequired,
getUserTutorials: PropTypes.func.isRequired,
getTutorials: PropTypes.func.isRequired, getTutorials: PropTypes.func.isRequired,
resetTutorial: PropTypes.func.isRequired, resetTutorial: PropTypes.func.isRequired,
clearMessages: PropTypes.func.isRequired, clearMessages: PropTypes.func.isRequired,
@@ -704,9 +631,6 @@ Builder.propTypes = {
resetTutorialBuilder: PropTypes.func.isRequired, resetTutorialBuilder: PropTypes.func.isRequired,
tutorialProgress: PropTypes.func.isRequired, tutorialProgress: PropTypes.func.isRequired,
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
difficulty: PropTypes.number.isRequired,
public: PropTypes.bool.isRequired,
review: PropTypes.bool.isRequired,
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
steps: PropTypes.array.isRequired, steps: PropTypes.array.isRequired,
change: PropTypes.number.isRequired, change: PropTypes.number.isRequired,
@@ -721,16 +645,12 @@ Builder.propTypes = {
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
title: state.builder.title, title: state.builder.title,
difficulty: state.builder.difficulty,
review: state.builder.review,
public: state.builder.public,
id: state.builder.id, id: state.builder.id,
steps: state.builder.steps, steps: state.builder.steps,
change: state.builder.change, change: state.builder.change,
error: state.builder.error, error: state.builder.error,
json: state.builder.json, json: state.builder.json,
isProgress: state.builder.progress, isProgress: state.builder.progress,
userTutorials: state.tutorial.userTutorials,
tutorials: state.tutorial.tutorials, tutorials: state.tutorial.tutorials,
message: state.message, message: state.message,
user: state.auth.user, user: state.auth.user,
@@ -745,8 +665,6 @@ export default connect(mapStateToProps, {
tutorialId, tutorialId,
resetTutorialBuilder, resetTutorialBuilder,
getTutorials, getTutorials,
getUserTutorials,
getAllTutorials,
resetTutorial, resetTutorial,
tutorialProgress, tutorialProgress,
clearMessages, clearMessages,
@@ -1,99 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import {
tutorialDifficulty,
jsonString,
changeContent,
setError,
deleteError,
} from "../../../actions/tutorialBuilderActions";
import withStyles from '@mui/styles/withStyles';
import ReactStars from "react-rating-stars-component";
import * as Blockly from "blockly";
import FormGroup from "@mui/material/FormGroup";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl";
import FormLabel from "@mui/material/FormLabel";
const styles = (theme) => ({
multiline: {
padding: "18.5px 14px 18.5px 24px",
},
errorColor: {
color: `${theme.palette.error.dark} !important`,
},
errorColorShrink: {
color: `rgba(0, 0, 0, 0.54) !important`,
},
errorBorder: {
borderColor: `${theme.palette.error.dark} !important`,
},
});
class Difficulty extends Component {
ratingChanged = (newRating) => {
this.handleChange(newRating);
};
handleChange = (e) => {
var value = e;
if (this.props.property === "difficulty") {
this.props.tutorialDifficulty(value);
} else if (this.props.property === "json") {
this.props.jsonString(value);
} else {
this.props.changeContent(
value,
this.props.index,
this.props.property,
this.props.property2
);
}
};
render() {
return (
<FormControl variant="standard" component="fieldset">
<FormLabel component="legend">
{Blockly.Msg.builder_difficulty}
</FormLabel>
<FormGroup aria-label="position" row>
<FormControlLabel
value="Tutorial veröffentlichen"
control={
<ReactStars
count={5}
onChange={this.handleChange}
value={this.props.value}
size={30}
isHalf={true}
emptyIcon={<i className="fa fa-star"></i>}
halfIcon={<i className="fa fa-star-half-alt"></i>}
fullIcon={<i className="fa fa-star"></i>}
activeColor="#ffd700"
/>
}
label="Schwierigkeitsgrad"
labelPlacement="start"
/>
</FormGroup>
</FormControl>
);
}
}
Difficulty.propTypes = {
tutorialDifficulty: PropTypes.func.isRequired,
jsonString: PropTypes.func.isRequired,
changeContent: PropTypes.func.isRequired,
};
export default connect(null, {
tutorialDifficulty,
jsonString,
changeContent,
setError,
deleteError,
})(withStyles(styles, { withTheme: true })(Difficulty));
+59 -114
View File
@@ -1,162 +1,107 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { import { changeContent, setError, deleteError } from '../../../actions/tutorialBuilderActions';
changeContent,
setError,
deleteError,
} from "../../../actions/tutorialBuilderActions";
import hardware from "../../../data/hardware.json"; import hardware from '../../../data/hardware.json';
import withStyles from '@mui/styles/withStyles'; import { withStyles } from '@material-ui/core/styles';
import ImageList from "@mui/material/ImageList"; import withWidth, { isWidthDown } from '@material-ui/core/withWidth';
import ImageListItem from "@mui/material/ImageListItem"; import GridList from '@material-ui/core/GridList';
import ImageListItemBar from "@mui/material/ImageListItemBar"; import GridListTile from '@material-ui/core/GridListTile';
import FormHelperText from "@mui/material/FormHelperText"; import GridListTileBar from '@material-ui/core/GridListTileBar';
import FormLabel from "@mui/material/FormLabel"; import FormHelperText from '@material-ui/core/FormHelperText';
import * as Blockly from "blockly"; import FormLabel from '@material-ui/core/FormLabel';
import { isWidthDown } from "../../../helpers/handleBreakpoints"; import * as Blockly from 'blockly'
// FIXME checkout https://mui.com/components/use-media-query/#migrating-from-withwidth
const withWidth = () => (WrappedComponent) => (props) => <WrappedComponent {...props} width="xs" />;
const styles = (theme) => ({ const styles = theme => ({
multiImageListItem: { multiGridListTile: {
background: theme.palette.primary.main, background: theme.palette.primary.main,
opacity: 0.9, opacity: 0.9,
height: "30px", height: '30px'
}, },
multiImageListItemTitle: { multiGridListTileTitle: {
color: theme.palette.text.primary, color: theme.palette.text.primary
}, },
border: { border: {
cursor: "pointer", cursor: 'pointer',
"&:hover": { '&:hover': {
width: "calc(100% - 4px)", width: 'calc(100% - 4px)',
height: "calc(100% - 4px)", height: 'calc(100% - 4px)',
border: `2px solid ${theme.palette.primary.main}`, border: `2px solid ${theme.palette.primary.main}`
}, }
}, },
active: { active: {
cursor: "pointer", cursor: 'pointer',
width: "calc(100% - 4px)", width: 'calc(100% - 4px)',
height: "calc(100% - 4px)", height: 'calc(100% - 4px)',
border: `2px solid ${theme.palette.primary.main}`, border: `2px solid ${theme.palette.primary.main}`
}, },
errorColor: { errorColor: {
color: theme.palette.error.dark, color: theme.palette.error.dark,
lineHeight: "initial", lineHeight: 'initial',
marginBottom: "10px", marginBottom: '10px'
}, }
}); });
class Requirements extends Component { class Requirements extends Component {
onChange = (hardware) => { onChange = (hardware) => {
var hardwareArray = this.props.value; var hardwareArray = this.props.value;
if (hardwareArray.filter((value) => value === hardware).length > 0) { if (hardwareArray.filter(value => value === hardware).length > 0) {
hardwareArray = hardwareArray.filter((value) => value !== hardware); hardwareArray = hardwareArray.filter(value => value !== hardware);
} else { }
else {
hardwareArray.push(hardware); hardwareArray.push(hardware);
if (this.props.error) { if (this.props.error) {
this.props.deleteError(this.props.index, "hardware"); this.props.deleteError(this.props.index, 'hardware');
} }
} }
this.props.changeContent(hardwareArray, this.props.index, "hardware"); this.props.changeContent(hardwareArray, this.props.index, 'hardware');
if (hardwareArray.length === 0) { if (hardwareArray.length === 0) {
this.props.setError(this.props.index, "hardware"); this.props.setError(this.props.index, 'hardware');
} }
}; }
render() { render() {
var cols = isWidthDown("md", this.props.width) var cols = isWidthDown('md', this.props.width) ? isWidthDown('sm', this.props.width) ? isWidthDown('xs', this.props.width) ? 2 : 3 : 4 : 6;
? isWidthDown("sm", this.props.width)
? isWidthDown("xs", this.props.width)
? 2
: 3
: 4
: 6;
return ( return (
<div <div style={{ marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)' }}>
style={{ <FormLabel style={{ color: 'black' }}>Hardware</FormLabel>
marginBottom: "10px", <FormHelperText style={this.props.error ? { lineHeight: 'initial', marginTop: '5px' } : { marginTop: '5px', lineHeight: 'initial', marginBottom: '10px' }}>{Blockly.Msg.builder_hardware_order}</FormHelperText>
padding: "18.5px 14px", {this.props.error ? <FormHelperText className={this.props.classes.errorColor}>{Blockly.Msg.builder_hardware_helper}</FormHelperText> : null}
borderRadius: "25px", <GridList cellHeight={100} cols={cols} spacing={10}>
border: "1px solid lightgrey",
width: "calc(100% - 28px)",
}}
>
<FormLabel style={{ color: "black" }}>Hardware</FormLabel>
<FormHelperText
style={
this.props.error
? { lineHeight: "initial", marginTop: "5px" }
: {
marginTop: "5px",
lineHeight: "initial",
marginBottom: "10px",
}
}
>
{Blockly.Msg.builder_hardware_order}
</FormHelperText>
{this.props.error ? (
<FormHelperText className={this.props.classes.errorColor}>
{Blockly.Msg.builder_hardware_helper}
</FormHelperText>
) : null}
<ImageList rowHeight={100} cols={cols} gap={10}>
{hardware.map((picture, i) => ( {hardware.map((picture, i) => (
<ImageListItem <GridListTile key={i} onClick={() => this.onChange(picture.id)} classes={{ tile: this.props.value.filter(value => value === picture.id).length > 0 ? this.props.classes.active : this.props.classes.border }}>
key={i} <div style={{ margin: 'auto', width: 'max-content' }}>
onClick={() => this.onChange(picture.id)} <img src={`/media/hardware/${picture.src}`} alt={picture.name} height={100} />
classes={{
item:
this.props.value.filter((value) => value === picture.id)
.length > 0
? this.props.classes.active
: this.props.classes.border,
}}
>
<div style={{ margin: "auto", width: "max-content" }}>
<img
src={`/media/hardware/${picture.src}`}
alt={picture.name}
height={100}
/>
</div> </div>
<ImageListItemBar <GridListTileBar
classes={{ root: this.props.classes.multiImageListItem }} classes={{ root: this.props.classes.multiGridListTile }}
title={ title={
<div <div style={{ overflow: 'hidden', textOverflow: 'ellipsis' }} className={this.props.classes.multiGridListTileTitle}>
style={{ overflow: "hidden", textOverflow: "ellipsis" }}
className={this.props.classes.multiImageListItemTitle}
>
{picture.name} {picture.name}
</div> </div>
} }
/> />
</ImageListItem> </GridListTile>
))} ))}
</ImageList> </GridList>
</div> </div>
); );
} };
} }
Requirements.propTypes = { Requirements.propTypes = {
changeContent: PropTypes.func.isRequired, changeContent: PropTypes.func.isRequired,
setError: PropTypes.func.isRequired, setError: PropTypes.func.isRequired,
deleteError: PropTypes.func.isRequired, deleteError: PropTypes.func.isRequired,
change: PropTypes.number.isRequired, change: PropTypes.number.isRequired
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = state => ({
change: state.builder.change, change: state.builder.change
}); });
export default connect(mapStateToProps, { export default connect(mapStateToProps, { changeContent, setError, deleteError })(withStyles(styles, { withTheme: true })(withWidth()(Requirements)));
changeContent,
setError,
deleteError,
})(withStyles(styles, { withTheme: true })(withWidth()(Requirements)));
+6 -6
View File
@@ -3,12 +3,12 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { tutorialId, setError, deleteError } from '../../../actions/tutorialBuilderActions'; import { tutorialId, setError, deleteError } from '../../../actions/tutorialBuilderActions';
import withStyles from '@mui/styles/withStyles'; import { withStyles } from '@material-ui/core/styles';
import Button from '@mui/material/Button'; import Button from '@material-ui/core/Button';
import OutlinedInput from '@mui/material/OutlinedInput'; import OutlinedInput from '@material-ui/core/OutlinedInput';
import InputLabel from '@mui/material/InputLabel'; import InputLabel from '@material-ui/core/InputLabel';
import FormControl from '@mui/material/FormControl'; import FormControl from '@material-ui/core/FormControl';
import FormHelperText from '@mui/material/FormHelperText'; import FormHelperText from '@material-ui/core/FormHelperText';
import { faPlus, faMinus } from "@fortawesome/free-solid-svg-icons"; import { faPlus, faMinus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -1,80 +0,0 @@
import React from "react";
import { connect } from "react-redux";
import {
tutorialTitle,
jsonString,
changeContent,
setError,
deleteError,
} from "../../../actions/tutorialBuilderActions";
import FormControl from "@mui/material/FormControl";
import MarkdownIt from "markdown-it";
import Editor from "react-markdown-editor-lite";
import "react-markdown-editor-lite/lib/index.css";
import axios from "axios";
const mdParser = new MarkdownIt(/* Markdown-it options */);
const MarkdownEditor = (props) => {
const [value, setValue] = React.useState(props.value);
const mdEditor = React.useRef(null);
function handleChange({ html, text }) {
setValue(text);
var value = text;
props.changeContent(value, props.index, props.property, props.property2);
if (value.replace(/\s/g, "") === "") {
props.setError(props.index, props.property);
} else {
props.deleteError(props.index, props.property);
}
}
async function uploadImage(files) {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append("files", files);
axios({
method: "post",
url: `${process.env.REACT_APP_BLOCKLY_API}/upload/uploadImage`,
data: formData,
headers: { "Content-Type": "multipart/form-data" },
})
.then((res) => {
resolve(
`${process.env.REACT_APP_BLOCKLY_API}/upload/` + res.data.filename
);
})
.catch((err) => {
reject(new Error("error"));
});
});
}
return (
<FormControl variant="outlined" fullWidth style={{ marginBottom: "10px" }}>
<Editor
ref={mdEditor}
style={{ height: "500px" }}
renderHTML={(text) => mdParser.render(text)}
onChange={handleChange}
value={value}
id={props.property}
label={props.label}
property={props.property}
onImageUpload={uploadImage}
/>
</FormControl>
);
};
export default connect(null, {
tutorialTitle,
jsonString,
changeContent,
setError,
deleteError,
})(MarkdownEditor);
+10 -9
View File
@@ -5,13 +5,13 @@ import { changeContent, deleteProperty, setError, deleteError } from '../../../a
import Textfield from './Textfield'; import Textfield from './Textfield';
import withStyles from '@mui/styles/withStyles'; import { withStyles } from '@material-ui/core/styles';
import Switch from '@mui/material/Switch'; import Switch from '@material-ui/core/Switch';
import FormControlLabel from '@mui/material/FormControlLabel'; import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormHelperText from '@mui/material/FormHelperText'; import FormHelperText from '@material-ui/core/FormHelperText';
import Radio from '@mui/material/Radio'; import Radio from '@material-ui/core/Radio';
import RadioGroup from '@mui/material/RadioGroup'; import RadioGroup from '@material-ui/core/RadioGroup';
import Button from '@mui/material/Button'; import Button from '@material-ui/core/Button';
const styles = (theme) => ({ const styles = (theme) => ({
errorColor: { errorColor: {
@@ -97,6 +97,7 @@ class Media extends Component {
<Switch <Switch
checked={this.state.checked} checked={this.state.checked}
onChange={(e) => this.onChangeSwitch(e.target.checked)} onChange={(e) => this.onChangeSwitch(e.target.checked)}
color="primary"
/> />
} }
/> />
@@ -105,13 +106,13 @@ class Media extends Component {
<RadioGroup row value={this.state.radioValue} onChange={(e) => {this.onChangeRadio(e.target.value);}}> <RadioGroup row value={this.state.radioValue} onChange={(e) => {this.onChangeRadio(e.target.value);}}>
<FormControlLabel style={{color: 'black'}} <FormControlLabel style={{color: 'black'}}
value="picture" value="picture"
control={<Radio />} control={<Radio color="primary" />}
label="Bild" label="Bild"
labelPlacement="end" labelPlacement="end"
/> />
<FormControlLabel style={{color: 'black'}} <FormControlLabel style={{color: 'black'}}
value="youtube" value="youtube"
control={<Radio />} control={<Radio color="primary" />}
label="Youtube-Video" label="Youtube-Video"
labelPlacement="end" labelPlacement="end"
/> />
-91
View File
@@ -1,91 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import {
tutorialPublic,
jsonString,
changeContent,
setError,
deleteError,
} from "../../../actions/tutorialBuilderActions";
import withStyles from '@mui/styles/withStyles';
import * as Blockly from "blockly";
import Checkbox from "@mui/material/Checkbox";
import FormGroup from "@mui/material/FormGroup";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl";
import FormLabel from "@mui/material/FormLabel";
const styles = (theme) => ({
multiline: {
padding: "18.5px 14px 18.5px 24px",
},
errorColor: {
color: `${theme.palette.error.dark} !important`,
},
errorColorShrink: {
color: `rgba(0, 0, 0, 0.54) !important`,
},
errorBorder: {
borderColor: `${theme.palette.error.dark} !important`,
},
});
class Public extends Component {
handleChange = (e) => {
var value = e.target.checked;
if (this.props.property === "public") {
this.props.tutorialPublic(value);
} else if (this.props.property === "json") {
this.props.jsonString(value);
} else {
this.props.changeContent(
value,
this.props.index,
this.props.property,
this.props.property2
);
}
};
render() {
return (
<FormControl variant="standard" component="fieldset">
<FormLabel component="legend">
{Blockly.Msg.builder_public_head}
</FormLabel>
<FormGroup aria-label="position" row>
<FormControlLabel
value="Tutorial veröffentlichen"
control={
<Checkbox
checked={this.props.value}
onChange={this.handleChange}
name="checkedA"
inputProps={{ "aria-label": "secondary checkbox" }}
/>
}
label={Blockly.Msg.builder_public_label}
labelPlacement="start"
/>
</FormGroup>
</FormControl>
);
}
}
Public.propTypes = {
tutorialPublic: PropTypes.func.isRequired,
jsonString: PropTypes.func.isRequired,
changeContent: PropTypes.func.isRequired,
};
export default connect(null, {
tutorialPublic,
jsonString,
changeContent,
setError,
deleteError,
})(withStyles(styles, { withTheme: true })(Public));
@@ -3,12 +3,12 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { changeContent } from '../../../actions/tutorialBuilderActions'; import { changeContent } from '../../../actions/tutorialBuilderActions';
import FormGroup from '@mui/material/FormGroup'; import FormGroup from '@material-ui/core/FormGroup';
import Checkbox from '@mui/material/Checkbox'; import Checkbox from '@material-ui/core/Checkbox';
import FormControlLabel from '@mui/material/FormControlLabel'; import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormLabel from '@mui/material/FormLabel'; import FormLabel from '@material-ui/core/FormLabel';
import FormHelperText from '@mui/material/FormHelperText'; import FormHelperText from '@material-ui/core/FormHelperText';
import FormControl from '@mui/material/FormControl'; import FormControl from '@material-ui/core/FormControl';
import * as Blockly from 'blockly' import * as Blockly from 'blockly'
class Requirements extends Component { class Requirements extends Component {
@@ -26,13 +26,11 @@ class Requirements extends Component {
render() { render() {
return ( return (
<FormControl <FormControl style={{ marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)' }}>
variant="standard"
style={{ marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)' }}>
<FormLabel style={{ color: 'black' }}>{Blockly.Msg.builder_requirements_head}</FormLabel> <FormLabel style={{ color: 'black' }}>{Blockly.Msg.builder_requirements_head}</FormLabel>
<FormHelperText style={{ marginTop: '5px' }}>{Blockly.Msg.builder_requirements_order}</FormHelperText> <FormHelperText style={{ marginTop: '5px' }}>{Blockly.Msg.builder_requirements_order}</FormHelperText>
<FormGroup> <FormGroup>
{this.props.tutorials.filter(tutorial => tutorial._id !== this.props.id && tutorial.public).map((tutorial, i) => {this.props.tutorials.filter(tutorial => tutorial._id !== this.props.id).map((tutorial, i) =>
<FormControlLabel <FormControlLabel
key={i} key={i}
control={ control={
@@ -41,6 +39,7 @@ class Requirements extends Component {
checked={this.props.value.filter(id => id === tutorial._id).length > 0} checked={this.props.value.filter(id => id === tutorial._id).length > 0}
onChange={(e) => this.onChange(e)} onChange={(e) => this.onChange(e)}
name="requirements" name="requirements"
color="primary"
/> />
} }
label={tutorial.title} label={tutorial.title}
-92
View File
@@ -1,92 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import {
tutorialReview,
jsonString,
changeContent,
setError,
deleteError,
} from "../../../actions/tutorialBuilderActions";
import withStyles from '@mui/styles/withStyles';
import * as Blockly from "blockly";
import Checkbox from "@mui/material/Checkbox";
import FormGroup from "@mui/material/FormGroup";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl";
import FormLabel from "@mui/material/FormLabel";
const styles = (theme) => ({
multiline: {
padding: "18.5px 14px 18.5px 24px",
},
errorColor: {
color: `${theme.palette.error.dark} !important`,
},
errorColorShrink: {
color: `rgba(0, 0, 0, 0.54) !important`,
},
errorBorder: {
borderColor: `${theme.palette.error.dark} !important`,
},
});
class Review extends Component {
handleChange = (e) => {
var value = e.target.checked;
if (this.props.property === "review") {
this.props.tutorialReview(value);
} else if (this.props.property === "json") {
this.props.jsonString(value);
} else {
this.props.changeContent(
value,
this.props.index,
this.props.property,
this.props.property2
);
}
};
render() {
return (
<FormControl variant="standard" component="fieldset">
<FormLabel component="legend">
{Blockly.Msg.builder_review_head}
</FormLabel>
{Blockly.Msg.builder_review_text}
<FormGroup aria-label="position" row>
<FormControlLabel
value="Tutorial veröffentlichen"
control={
<Checkbox
checked={this.props.value}
onChange={this.handleChange}
name="checkedA"
inputProps={{ "aria-label": "secondary checkbox" }}
/>
}
label="Ich möchte mein Tutorial öffentlich machen"
labelPlacement="start"
/>
</FormGroup>
</FormControl>
);
}
}
Review.propTypes = {
tutorialReview: PropTypes.func.isRequired,
jsonString: PropTypes.func.isRequired,
changeContent: PropTypes.func.isRequired,
};
export default connect(null, {
tutorialReview,
jsonString,
changeContent,
setError,
deleteError,
})(withStyles(styles, { withTheme: true })(Review));
+65 -145
View File
@@ -1,194 +1,118 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { import { addStep, removeStep, changeStepIndex } from '../../../actions/tutorialBuilderActions';
addStep,
removeStep,
changeStepIndex,
} from "../../../actions/tutorialBuilderActions";
import clsx from "clsx"; import clsx from 'clsx';
import Textfield from "./Textfield"; import Textfield from './Textfield';
import StepType from "./StepType"; 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 Media from './Media';
import withStyles from '@mui/styles/withStyles'; import { withStyles } from '@material-ui/core/styles';
import Typography from "@mui/material/Typography"; import Typography from '@material-ui/core/Typography';
import IconButton from "@mui/material/IconButton"; import IconButton from '@material-ui/core/IconButton';
import Tooltip from "@mui/material/Tooltip"; import Tooltip from '@material-ui/core/Tooltip';
import { import { faPlus, faAngleDoubleUp, faAngleDoubleDown, faTrash } from "@fortawesome/free-solid-svg-icons";
faPlus,
faAngleDoubleUp,
faAngleDoubleDown,
faTrash,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import MarkdownEditor from "./MarkdownEditor";
const styles = (theme) => ({ const styles = (theme) => ({
button: { button: {
backgroundColor: theme.palette.primary.main, backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText, color: theme.palette.primary.contrastText,
width: "40px", width: '40px',
height: "40px", height: '40px',
"&:hover": { '&:hover': {
backgroundColor: theme.palette.primary.main, backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText, color: theme.palette.primary.contrastText,
}, }
}, },
delete: { delete: {
backgroundColor: theme.palette.error.dark, backgroundColor: theme.palette.error.dark,
color: theme.palette.error.contrastText, color: theme.palette.error.contrastText,
"&:hover": { '&:hover': {
backgroundColor: theme.palette.error.dark, backgroundColor: theme.palette.error.dark,
color: theme.palette.error.contrastText, color: theme.palette.error.contrastText,
}, }
}, }
}); });
class Step extends Component { class Step extends Component {
render() { render() {
var index = this.props.index; var index = this.props.index;
var steps = this.props.steps; var steps = this.props.steps;
return ( return (
<div <div style={{borderRadius: '25px', border: '1px solid lightgrey', padding: '10px 14px 10px 10px', marginBottom: '20px'}}>
style={{ <Typography variant='h6' style={{marginBottom: '10px', marginLeft: '4px'}}>Schritt {index+1}</Typography>
borderRadius: "25px", <div style={{display: 'flex', position: 'relative'}}>
border: "1px solid lightgrey", <div style={{width: '40px', marginRight: '10px', position: 'absolute', left: '4px', bottom: '10px'}}>
padding: "10px 14px 10px 10px", <Tooltip title='Schritt hinzufügen' arrow>
marginBottom: "20px",
}}
>
<Typography
variant="h6"
style={{ marginBottom: "10px", marginLeft: "4px" }}
>
Schritt {index + 1}
</Typography>
<div style={{ display: "flex", position: "relative" }}>
<div
style={{
width: "40px",
marginRight: "10px",
position: "absolute",
left: "4px",
bottom: "10px",
}}
>
<Tooltip title="Schritt hinzufügen" arrow>
<IconButton <IconButton
className={this.props.classes.button} className={this.props.classes.button}
style={index === 0 ? {} : { marginBottom: "5px" }} style={index === 0 ? {} : {marginBottom: '5px'}}
onClick={() => this.props.addStep(index + 1)} onClick={() => this.props.addStep(index+1)}
size="large"> >
<FontAwesomeIcon icon={faPlus} size="xs" /> <FontAwesomeIcon icon={faPlus} size="xs"/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{index !== 0 ? ( {index !== 0 ?
<div> <div>
<Tooltip <Tooltip title={`Schritt ${index+1} nach oben schieben`} arrow>
title={`Schritt ${index + 1} nach oben schieben`}
arrow
>
<IconButton <IconButton
disabled={index < 2} disabled={index < 2}
className={this.props.classes.button} className={this.props.classes.button}
style={{ marginBottom: "5px" }} style={{marginBottom: '5px'}}
onClick={() => this.props.changeStepIndex(index, index - 1)} onClick={() => this.props.changeStepIndex(index, index-1)}
size="large"> >
<FontAwesomeIcon icon={faAngleDoubleUp} size="xs" /> <FontAwesomeIcon icon={faAngleDoubleUp} size="xs"/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Tooltip <Tooltip title={`Schritt ${index+1} nach unten schieben`} arrow>
title={`Schritt ${index + 1} nach unten schieben`}
arrow
>
<IconButton <IconButton
disabled={index === steps.length - 1} disabled={index === steps.length-1}
className={this.props.classes.button} className={this.props.classes.button}
style={{ marginBottom: "5px" }} style={{marginBottom: '5px'}}
onClick={() => this.props.changeStepIndex(index, index + 1)} onClick={() => this.props.changeStepIndex(index, index+1)}
size="large"> >
<FontAwesomeIcon icon={faAngleDoubleDown} size="xs" /> <FontAwesomeIcon icon={faAngleDoubleDown} size="xs"/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Tooltip title={`Schritt ${index + 1} löschen`} arrow> <Tooltip title={`Schritt ${index+1} löschen`} arrow>
<IconButton <IconButton
disabled={index === 0} disabled={index === 0}
className={clsx( className={clsx(this.props.classes.button, this.props.classes.delete)}
this.props.classes.button,
this.props.classes.delete
)}
onClick={() => this.props.removeStep(index)} onClick={() => this.props.removeStep(index)}
size="large"> >
<FontAwesomeIcon icon={faTrash} size="xs" /> <FontAwesomeIcon icon={faTrash} size="xs"/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
</div> </div>
) : null} : null}
</div> </div>
<div style={{ width: "100%", marginLeft: "54px" }}> <div style={{width: '100%', marginLeft: '54px'}}>
<StepType value={this.props.step.type} index={index} /> <StepType value={this.props.step.type} index={index} />
<Textfield <Textfield value={this.props.step.headline} property={'headline'} label={'Überschrift'} index={index} error={this.props.error.steps[index].headline} errorText={`Gib eine Überschrift für die ${this.props.step.type === 'task' ? 'Aufgabe' : 'Anleitung'} ein.`} />
value={this.props.step.headline} <Textfield value={this.props.step.text} property={'text'} label={this.props.step.type === 'task' ? 'Aufgabenstellung' : 'Instruktionen'} index={index} multiline error={this.props.error.steps[index].text} errorText={`Gib Instruktionen für die ${this.props.step.type === 'task' ? 'Aufgabe' : 'Anleitung'} ein.`}/>
property={"headline"} {index === 0 ?
label={"Überschrift"}
index={index}
error={this.props.error.steps[index].headline}
errorText={`Gib eine Überschrift für die ${
this.props.step.type === "task" ? "Aufgabe" : "Anleitung"
} ein.`}
/>
<MarkdownEditor
value={this.props.step.text}
property={"text"}
label={
this.props.step.type === "task"
? "Aufgabenstellung"
: "Instruktionen"
}
index={index}
multiline
error={this.props.error.steps[index].text}
errorText={`Gib Instruktionen für die ${
this.props.step.type === "task" ? "Aufgabe" : "Anleitung"
} ein.`}
/>
{index === 0 ? (
<div> <div>
<Requirements <Requirements value={this.props.step.requirements ? this.props.step.requirements : []} index={index}/>
value={ <Hardware value={this.props.step.hardware ? this.props.step.hardware : []} index={index} error={this.props.error.steps[index].hardware}/>
this.props.step.requirements
? this.props.step.requirements
: []
}
index={index}
/>
<Hardware
value={
this.props.step.hardware ? this.props.step.hardware : []
}
index={index}
error={this.props.error.steps[index].hardware}
/>
</div> </div>
) : null} : null}
<BlocklyExample {this.props.step.type === 'instruction' ?
value={this.props.step.xml} <Media value={this.props.step.media} picture={this.props.step.media && this.props.step.media.picture} youtube={this.props.step.media && this.props.step.media.youtube} url={this.props.step.url} index={index} error={this.props.error.steps[index].media} />
index={index} : null}
task={this.props.step.type === "task"} <BlocklyExample value={this.props.step.xml} index={index} task={this.props.step.type === 'task'} error={this.props.error.steps[index].xml ? true : false}/>
error={this.props.error.steps[index].xml ? true : false}
/>
</div> </div>
</div> </div>
</div> </div>
); );
} };
} }
Step.propTypes = { Step.propTypes = {
@@ -200,14 +124,10 @@ Step.propTypes = {
error: PropTypes.object.isRequired, error: PropTypes.object.isRequired,
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = state => ({
steps: state.builder.steps, steps: state.builder.steps,
change: state.builder.change, change: state.builder.change,
error: state.builder.error, error: state.builder.error
}); });
export default connect(mapStateToProps, { export default connect(mapStateToProps, { addStep, removeStep, changeStepIndex })(withStyles(styles, {withTheme: true})(Step));
addStep,
removeStep,
changeStepIndex,
})(withStyles(styles, { withTheme: true })(Step));
+5 -5
View File
@@ -3,9 +3,9 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { changeContent, deleteProperty, deleteError } from '../../../actions/tutorialBuilderActions'; import { changeContent, deleteProperty, deleteError } from '../../../actions/tutorialBuilderActions';
import Radio from '@mui/material/Radio'; import Radio from '@material-ui/core/Radio';
import RadioGroup from '@mui/material/RadioGroup'; import RadioGroup from '@material-ui/core/RadioGroup';
import FormControlLabel from '@mui/material/FormControlLabel'; import FormControlLabel from '@material-ui/core/FormControlLabel';
class StepType extends Component { class StepType extends Component {
@@ -23,14 +23,14 @@ class StepType extends Component {
<RadioGroup row value={this.props.value === 'task' ? 'task' : 'instruction'} onChange={(e) => this.onChange(e.target.value)}> <RadioGroup row value={this.props.value === 'task' ? 'task' : 'instruction'} onChange={(e) => this.onChange(e.target.value)}>
<FormControlLabel style={{color: 'black'}} <FormControlLabel style={{color: 'black'}}
value="instruction" value="instruction"
control={<Radio />} control={<Radio color="primary" />}
label="Anleitung" label="Anleitung"
labelPlacement="end" labelPlacement="end"
/> />
<FormControlLabel style={{color: 'black'}} <FormControlLabel style={{color: 'black'}}
disabled={this.props.index === 0} disabled={this.props.index === 0}
value="task" value="task"
control={<Radio />} control={<Radio color="primary" />}
label="Aufgabe" label="Aufgabe"
labelPlacement="end" labelPlacement="end"
/> />
+13 -5
View File
@@ -9,11 +9,11 @@ import {
deleteError, deleteError,
} from "../../../actions/tutorialBuilderActions"; } from "../../../actions/tutorialBuilderActions";
import withStyles from '@mui/styles/withStyles'; import { withStyles } from "@material-ui/core/styles";
import OutlinedInput from "@mui/material/OutlinedInput"; import OutlinedInput from "@material-ui/core/OutlinedInput";
import InputLabel from "@mui/material/InputLabel"; import InputLabel from "@material-ui/core/InputLabel";
import FormControl from "@mui/material/FormControl"; import FormControl from "@material-ui/core/FormControl";
import FormHelperText from "@mui/material/FormHelperText"; import FormHelperText from "@material-ui/core/FormHelperText";
const styles = (theme) => ({ const styles = (theme) => ({
multiline: { multiline: {
@@ -31,6 +31,14 @@ const styles = (theme) => ({
}); });
class Textfield extends Component { class Textfield extends Component {
componentDidMount() {
if (this.props.error) {
if (this.props.property !== "media") {
this.props.deleteError(this.props.index, this.props.property);
}
}
}
handleChange = (e) => { handleChange = (e) => {
var value = e.target.value; var value = e.target.value;
if (this.props.property === "title") { if (this.props.property === "title") {
+43 -78
View File
@@ -1,49 +1,47 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import Dialog from "../Dialog"; import Dialog from '../Dialog';
import hardware from "../../data/hardware.json"; import hardware from '../../data/hardware.json';
import withStyles from '@mui/styles/withStyles'; import { withStyles } from '@material-ui/core/styles';
import Link from "@mui/material/Link"; import withWidth, { isWidthDown } from '@material-ui/core/withWidth';
import Typography from "@mui/material/Typography"; import Link from '@material-ui/core/Link';
import IconButton from "@mui/material/IconButton"; import Typography from '@material-ui/core/Typography';
import ImageList from "@mui/material/ImageList"; import IconButton from '@material-ui/core/IconButton';
import ImageListItem from "@mui/material/ImageListItem"; import GridList from '@material-ui/core/GridList';
import ImageListItemBar from "@mui/material/ImageListItemBar"; import GridListTile from '@material-ui/core/GridListTile';
import GridListTileBar from '@material-ui/core/GridListTileBar';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExpandAlt } from "@fortawesome/free-solid-svg-icons"; import { faExpandAlt } from "@fortawesome/free-solid-svg-icons";
import * as Blockly from "blockly"; import * as Blockly from 'blockly'
import { isWidthDown } from "../../helpers/handleBreakpoints"; const styles = theme => ({
// FIXME checkout https://mui.com/components/use-media-query/#migrating-from-withwidth
const withWidth = () => (WrappedComponent) => (props) => <WrappedComponent {...props} width="xs" />;
const styles = (theme) => ({
expand: { expand: {
"&:hover": { '&:hover': {
color: theme.palette.primary.main, color: theme.palette.primary.main,
}, },
"&:active": { '&:active': {
color: theme.palette.primary.main, color: theme.palette.primary.main,
}, },
color: theme.palette.text.primary, color: theme.palette.text.primary
}, },
multiImageListItem: { multiGridListTile: {
background: theme.palette.primary.main, background: theme.palette.primary.main,
opacity: 0.9, opacity: 0.9,
height: "30px", height: '30px'
},
multiImageListItemTitle: {
color: theme.palette.text.primary,
}, },
multiGridListTileTitle: {
color: theme.palette.text.primary
}
}); });
class Hardware extends Component { class Hardware extends Component {
state = { state = {
open: false, open: false,
hardwareInfo: {}, hardwareInfo: {}
}; };
handleClickOpen = (hardwareInfo) => { handleClickOpen = (hardwareInfo) => {
@@ -55,57 +53,36 @@ class Hardware extends Component {
}; };
render() { render() {
var cols = isWidthDown("md", window.innerWidth) var cols = isWidthDown('md', this.props.width) ? isWidthDown('sm', this.props.width) ? isWidthDown('xs', this.props.width) ? 2 : 3 : 4 : 6;
? isWidthDown("sm", window.innerWidth)
? isWidthDown("xs", window.innerWidth)
? 2
: 3
: 4
: 6;
return ( return (
<div style={{ marginTop: "10px", marginBottom: "5px" }}> <div style={{ marginTop: '10px', marginBottom: '5px' }}>
<Typography>{Blockly.Msg.tutorials_hardware_head}</Typography> <Typography>{Blockly.Msg.tutorials_hardware_head}</Typography>
<ImageList rowHeight={100} cols={cols} gap={10}> <GridList cellHeight={100} cols={cols} spacing={10}>
{this.props.picture.map((picture, i) => { {this.props.picture.map((picture, i) => {
var hardwareInfo = hardware.filter( var hardwareInfo = hardware.filter(hardware => hardware.id === picture)[0];
(hardware) => hardware.id === picture
)[0];
return ( return (
<ImageListItem key={i}> <GridListTile key={i}>
<div style={{ margin: "auto", width: "max-content" }}> <div style={{ margin: 'auto', width: 'max-content' }}>
<img <img src={`/media/hardware/${hardwareInfo.src}`} alt={hardwareInfo.name} height={100} style={{ cursor: 'pointer' }} onClick={() => this.handleClickOpen(hardwareInfo)} />
src={`/media/hardware/${hardwareInfo.src}`}
alt={hardwareInfo.name}
height={100}
style={{ cursor: "pointer" }}
onClick={() => this.handleClickOpen(hardwareInfo)}
/>
</div> </div>
<ImageListItemBar <GridListTileBar
classes={{ root: this.props.classes.multiImageListItem }} classes={{ root: this.props.classes.multiGridListTile }}
title={ title={
<div <div style={{ overflow: 'hidden', textOverflow: 'ellipsis' }} className={this.props.classes.multiGridListTileTitle}>
style={{ overflow: "hidden", textOverflow: "ellipsis" }}
className={this.props.classes.multiImageListItemTitle}
>
{hardwareInfo.name} {hardwareInfo.name}
</div> </div>
} }
actionIcon={ actionIcon={
<IconButton <IconButton className={this.props.classes.expand} aria-label='Vollbild' onClick={() => this.handleClickOpen(hardwareInfo)}>
className={this.props.classes.expand} <FontAwesomeIcon icon={faExpandAlt} size="xs" />
aria-label="Vollbild"
onClick={() => this.handleClickOpen(hardwareInfo)}
size="large">
<FontAwesomeIcon icon={faExpandAlt} size="xs" />
</IconButton> </IconButton>
} }
/> />
</ImageListItem> </GridListTile>
); )
})} })}
</ImageList> </GridList>
<Dialog <Dialog
style={{ zIndex: 1500 }} style={{ zIndex: 1500 }}
@@ -117,26 +94,14 @@ class Hardware extends Component {
button={Blockly.Msg.button_close} button={Blockly.Msg.button_close}
> >
<div> <div>
<img <img src={`/media/hardware/${this.state.hardwareInfo.src}`} width="100%" alt={this.state.hardwareInfo.name} />
src={`/media/hardware/${this.state.hardwareInfo.src}`} {Blockly.Msg.tutorials_hardware_moreInformation} <Link rel="noreferrer" target="_blank" href={this.state.hardwareInfo.url} color="primary">{Blockly.Msg.tutorials_hardware_here}</Link>.
width="100%"
alt={this.state.hardwareInfo.name}
/>
{Blockly.Msg.tutorials_hardware_moreInformation}{" "}
<Link
rel="noreferrer"
target="_blank"
href={this.state.hardwareInfo.url}
color="primary"
underline="hover">
{Blockly.Msg.tutorials_hardware_here}
</Link>
.
</div> </div>
</Dialog> </Dialog>
</div> </div>
); );
} };
} }
export default withWidth()(withStyles(styles, { withTheme: true })(Hardware)); export default withWidth()(withStyles(styles, { withTheme: true })(Hardware));
+36 -42
View File
@@ -1,101 +1,95 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import Dialog from "../Dialog"; import Dialog from '../Dialog';
import withStyles from '@mui/styles/withStyles'; import { withStyles } from '@material-ui/core/styles';
import Checkbox from "@mui/material/Checkbox"; import Checkbox from '@material-ui/core/Checkbox';
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from '@material-ui/core/FormControlLabel';
import * as Blockly from "blockly"; import * as Blockly from 'blockly'
import ReactMarkdown from "react-markdown"; import ReactMarkdown from 'react-markdown';
const styles = (theme) => ({ const styles = (theme) => ({
link: { link: {
color: theme.palette.primary.main, color: theme.palette.primary.main,
textDecoration: "none", textDecoration: 'none',
"&:hover": { '&:hover': {
color: theme.palette.primary.main, color: theme.palette.primary.main,
textDecoration: `underline`, textDecoration: `underline`
}, }
}, },
label: { label: {
fontSize: "0.9rem", fontSize: '0.9rem',
color: "grey", color: 'grey'
}, }
}); });
class HintTutorialExists extends Component { class HintTutorialExists extends Component {
constructor(props) { constructor(props) {
var previousPageWasAnotherDomain = props.pageVisits === 0; var previousPageWasAnotherDomain = props.pageVisits === 0;
var userDoNotWantToSeeNews = window.localStorage.getItem("news") var userDoNotWantToSeeNews = window.localStorage.getItem('news') ? true : false;
? true
: false;
super(props); super(props);
this.state = { this.state = {
open: userDoNotWantToSeeNews open: userDoNotWantToSeeNews ? !userDoNotWantToSeeNews : previousPageWasAnotherDomain
? !userDoNotWantToSeeNews
: previousPageWasAnotherDomain,
}; };
} }
toggleDialog = () => { toggleDialog = () => {
this.setState({ open: !this.state }); this.setState({ open: !this.state });
}; }
onChange = (e) => { onChange = (e) => {
if (e.target.checked) { if (e.target.checked) {
window.localStorage.setItem("news", e.target.checked); window.localStorage.setItem('news', e.target.checked);
} else {
window.localStorage.removeItem("news");
} }
}; else {
window.localStorage.removeItem('news');
}
}
render() { render() {
return ( return (
<Dialog <Dialog
style={{ zIndex: 9999999 }} style={{ zIndex: 9999999 }}
fullWidth fullWidth
maxWidth={"sm"} maxWidth={'sm'}
open={this.state.open} open={this.state.open}
title={Blockly.Msg.messages_newblockly_head} title={Blockly.Msg.messages_newblockly_head}
content={""} content={''}
onClose={this.toggleDialog} onClose={this.toggleDialog}
onClick={this.toggleDialog} onClick={this.toggleDialog}
button={Blockly.Msg.button_close} button={Blockly.Msg.button_close}
> >
<div> <div>
<ReactMarkdown linkTarget="_blank"> <ReactMarkdown linkTarget="_blank">{Blockly.Msg.messages_newblockly_text}</ReactMarkdown>
{Blockly.Msg.messages_newblockly_text}
</ReactMarkdown>
</div> </div>
<FormControlLabel <FormControlLabel
style={{ marginTop: "20px" }} style={{ marginTop: '20px' }}
classes={{ label: this.props.classes.label }} classes={{ label: this.props.classes.label }}
control={ control={
<Checkbox <Checkbox
size={"small"} size={'small'}
value={true} value={true}
checked={this.state.checked} checked={this.state.checked}
onChange={(e) => this.onChange(e)} onChange={(e) => this.onChange(e)}
name="dialog" name="dialog"
color="primary"
/> />
} }
label={Blockly.Msg.labels_donotshowagain} label={Blockly.Msg.labels_donotshowagain}
/> />
</Dialog> </Dialog>
); );
} };
} }
HintTutorialExists.propTypes = { HintTutorialExists.propTypes = {
pageVisits: PropTypes.number.isRequired, pageVisits: PropTypes.number.isRequired
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = state => ({
pageVisits: state.general.pageVisits, pageVisits: state.general.pageVisits
}); });
export default connect( export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(HintTutorialExists));
mapStateToProps,
null
)(withStyles(styles, { withTheme: true })(HintTutorialExists));
+5 -6
View File
@@ -4,11 +4,9 @@ import Hardware from "./Hardware";
import Requirement from "./Requirement"; import Requirement from "./Requirement";
import BlocklyWindow from "../Blockly/BlocklyWindow"; import BlocklyWindow from "../Blockly/BlocklyWindow";
import Grid from "@mui/material/Grid"; import Grid from "@material-ui/core/Grid";
import Typography from "@mui/material/Typography"; import Typography from "@material-ui/core/Typography";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import remarkGemoji from "remark-gemoji";
class Instruction extends Component { class Instruction extends Component {
render() { render() {
@@ -17,13 +15,14 @@ class Instruction extends Component {
var areRequirements = step.requirements && step.requirements.length > 0; var areRequirements = step.requirements && step.requirements.length > 0;
return ( return (
<div> <div>
<Typography variant="h4" style={{ marginBottom: "5px" }}>
{step.headline}
</Typography>
<Typography style={isHardware ? {} : { marginBottom: "5px" }}> <Typography style={isHardware ? {} : { marginBottom: "5px" }}>
<ReactMarkdown <ReactMarkdown
className={"tutorial"} className={"tutorial"}
linkTarget={"_blank"} linkTarget={"_blank"}
skipHtml={false} skipHtml={false}
allowDangerousHtml={true}
remarkPlugins={[remarkGfm, remarkGemoji]}
> >
{step.text} {step.text}
</ReactMarkdown> </ReactMarkdown>
+71 -181
View File
@@ -1,234 +1,124 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import clsx from "clsx"; import clsx from 'clsx';
import { withRouter, Link } from "react-router-dom"; import { withRouter, Link } from 'react-router-dom';
import { alpha } from "@mui/material/styles";
import withStyles from '@mui/styles/withStyles'; import { fade } from '@material-ui/core/styles/colorManipulator';
import Typography from "@mui/material/Typography"; import { withStyles } from '@material-ui/core/styles';
import List from "@mui/material/List"; import Typography from '@material-ui/core/Typography';
import Tooltip from "@mui/material/Tooltip"; import List from '@material-ui/core/List';
import Tooltip from '@material-ui/core/Tooltip';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons";
import * as Blockly from "blockly"; import * as Blockly from 'blockly'
const styles = (theme) => ({ const styles = theme => ({
outerDiv: { outerDiv: {
width: "50px", width: '50px',
height: "50px", height: '50px',
position: "absolute", position: 'absolute',
color: alpha(theme.palette.secondary.main, 0.6), color: fade(theme.palette.secondary.main, 0.6)
}, },
outerDivError: { outerDivError: {
stroke: alpha(theme.palette.error.dark, 0.6), stroke: fade(theme.palette.error.dark, 0.6),
color: alpha(theme.palette.error.dark, 0.6), color: fade(theme.palette.error.dark, 0.6)
}, },
outerDivSuccess: { outerDivSuccess: {
stroke: alpha(theme.palette.primary.main, 0.6), stroke: fade(theme.palette.primary.main, 0.6),
color: alpha(theme.palette.primary.main, 0.6), color: fade(theme.palette.primary.main, 0.6)
}, },
outerDivOther: { outerDivOther: {
stroke: alpha(theme.palette.secondary.main, 0.6), stroke: fade(theme.palette.secondary.main, 0.6)
}, },
innerDiv: { innerDiv: {
width: "inherit", width: 'inherit',
height: "inherit", height: 'inherit',
display: "table-cell", display: 'table-cell',
verticalAlign: "middle", verticalAlign: 'middle',
textAlign: "center", textAlign: 'center'
}, },
link: { link: {
color: theme.palette.text.primary, color: theme.palette.text.primary,
position: "relative", position: 'relative',
height: "50px", height: '50px',
display: "flex", display: 'flex',
margin: "5px 0 5px 10px", margin: '5px 0 5px 10px',
textDecoration: "none", textDecoration: 'none'
}, },
hoverLink: { hoverLink: {
"&:hover": { '&:hover': {
background: alpha(theme.palette.secondary.main, 0.5), background: fade(theme.palette.secondary.main, 0.5),
borderRadius: "0 25px 25px 0 ", borderRadius: '0 25px 25px 0 '
}, }
}, }
}); });
class Requirement extends Component { class Requirement extends Component {
render() { render() {
var requirements = this.props.requirements; var requirements = this.props.requirements;
var tutorialIds = requirements.map((requirement) => requirement._id); var tutorialIds = requirements.map(requirement => requirement._id);
return ( return (
<div style={{ marginTop: "20px", marginBottom: "5px" }}> <div style={{ marginTop: '20px', marginBottom: '5px' }}>
<Typography>{Blockly.Msg.tutorials_requirements}</Typography> <Typography>{Blockly.Msg.tutorials_requirements}</Typography>
<List component="div"> <List component="div">
{tutorialIds.map((tutorialId, i) => { {tutorialIds.map((tutorialId, i) => {
var title = requirements[i].title; var title = requirements[i].title
var status = this.props.status.filter( var status = this.props.status.filter(status => status._id === tutorialId)[0];
(status) => status._id === tutorialId
)[0];
var tasks = status.tasks; var tasks = status.tasks;
var error = var error = status.tasks.filter(task => task.type === 'error').length > 0;
status.tasks.filter((task) => task.type === "error").length > 0; var success = status.tasks.filter(task => task.type === 'success').length / tasks.length
var success = var tutorialStatus = success === 1 ? 'Success' : error ? 'Error' : 'Other';
status.tasks.filter((task) => task.type === "success").length /
tasks.length;
var tutorialStatus =
success === 1 ? "Success" : error ? "Error" : "Other";
return ( return (
<Link <Link to={`/tutorial/${tutorialId}`} className={this.props.classes.link} key={i}>
target={"_blank"} <Tooltip style={{ height: '50px', width: '50px', position: 'absolute', background: 'white', zIndex: 1, borderRadius: '25px' }} title={error ? `Mind. eine Aufgabe im Tutorial wurde nicht richtig gelöst.` : success === 1 ? `Das Tutorial wurde bereits erfolgreich abgeschlossen.` : `Das Tutorial ist zu ${success * 100}% abgeschlossen.`} arrow>
to={`/tutorial/${tutorialId}`}
className={this.props.classes.link}
key={i}
>
<Tooltip
style={{
height: "50px",
width: "50px",
position: "absolute",
background: "white",
zIndex: 1,
borderRadius: "25px",
}}
title={
error
? `Mind. eine Aufgabe im Tutorial wurde nicht richtig gelöst.`
: success === 1
? `Das Tutorial wurde bereits erfolgreich abgeschlossen.`
: `Das Tutorial ist zu ${success * 100}% abgeschlossen.`
}
arrow
>
<div> <div>
<div <div className={clsx(this.props.classes.outerDiv)} style={{ width: '50px', height: '50px', border: 0 }}>
className={clsx(this.props.classes.outerDiv)} <svg style={{ width: '100%', height: '100%' }}>
style={{ width: "50px", height: "50px", border: 0 }} {error || success === 1 ?
> <circle className={error ? this.props.classes.outerDivError : this.props.classes.outerDivSuccess} r="22.5" cx="50%" cy="50%" fill="none" strokeWidth="5"></circle>
<svg style={{ width: "100%", height: "100%" }}> : <circle className={this.props.classes.outerDivOther} r="22.5" cx="50%" cy="50%" fill="none" strokeWidth="5"></circle>}
{error || success === 1 ? ( {success < 1 && !error ?
<circle <circle className={this.props.classes.outerDivSuccess} style={{ transform: 'rotate(-90deg)', transformOrigin: "50% 50%" }} r="22.5" cx="50%" cy="50%" fill="none" strokeWidth="5" strokeDashoffset={`${(22.5 * 2 * Math.PI) * (1 - success)}`} strokeDasharray={`${(22.5 * 2 * Math.PI)}`}>
className={ </circle>
error : null}
? this.props.classes.outerDivError
: this.props.classes.outerDivSuccess
}
r="22.5"
cx="50%"
cy="50%"
fill="none"
strokeWidth="5"
></circle>
) : (
<circle
className={this.props.classes.outerDivOther}
r="22.5"
cx="50%"
cy="50%"
fill="none"
strokeWidth="5"
></circle>
)}
{success < 1 && !error ? (
<circle
className={this.props.classes.outerDivSuccess}
style={{
transform: "rotate(-90deg)",
transformOrigin: "50% 50%",
}}
r="22.5"
cx="50%"
cy="50%"
fill="none"
strokeWidth="5"
strokeDashoffset={`${
22.5 * 2 * Math.PI * (1 - success)
}`}
strokeDasharray={`${22.5 * 2 * Math.PI}`}
></circle>
) : null}
</svg> </svg>
</div> </div>
<div <div className={clsx(this.props.classes.outerDiv, tutorialStatus === 'Error' ? this.props.classes.outerDivError : tutorialStatus === 'Success' ? this.props.classes.outerDivSuccess : null)}>
className={clsx(
this.props.classes.outerDiv,
tutorialStatus === "Error"
? this.props.classes.outerDivError
: tutorialStatus === "Success"
? this.props.classes.outerDivSuccess
: null
)}
>
<div className={this.props.classes.innerDiv}> <div className={this.props.classes.innerDiv}>
{error || success === 1 ? ( {error || success === 1 ?
<FontAwesomeIcon <FontAwesomeIcon icon={tutorialStatus === 'Success' ? faCheck : faTimes} />
icon={ : <Typography variant='h7' className={success > 0 ? this.props.classes.outerDivSuccess : {}}>{Math.round(success * 100)}%</Typography>
tutorialStatus === "Success" ? faCheck : faTimes }
}
/>
) : (
<Typography
variant="h7"
className={
success > 0
? this.props.classes.outerDivSuccess
: {}
}
>
{Math.round(success * 100)}%
</Typography>
)}
</div> </div>
</div> </div>
</div> </div>
</Tooltip> </Tooltip>
<div <div style={{ height: '50px', width: 'calc(100% - 25px)', transform: 'translate(25px)' }} className={this.props.classes.hoverLink}>
style={{ <Typography style={{ margin: 0, position: 'absolute', top: '50%', transform: 'translate(45px, -50%)', maxHeight: '50px', overflow: 'hidden', maxWidth: 'calc(100% - 45px)', textOverflow: 'ellipsis', whiteSpace: 'pre-line', overflowWrap: 'anywhere' }}>{title}</Typography>
height: "50px",
width: "calc(100% - 25px)",
transform: "translate(25px)",
}}
className={this.props.classes.hoverLink}
>
<Typography
style={{
margin: 0,
position: "absolute",
top: "50%",
transform: "translate(45px, -50%)",
maxHeight: "50px",
overflow: "hidden",
maxWidth: "calc(100% - 45px)",
textOverflow: "ellipsis",
whiteSpace: "pre-line",
overflowWrap: "anywhere",
}}
>
{title}
</Typography>
</div> </div>
</Link> </Link>
); )
})} }
)}
</List> </List>
</div> </div>
); );
} };
} }
Requirement.propTypes = { Requirement.propTypes = {
status: PropTypes.array.isRequired, status: PropTypes.array.isRequired,
change: PropTypes.number.isRequired, change: PropTypes.number.isRequired
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = state => ({
change: state.tutorial.change, change: state.tutorial.change,
status: state.tutorial.status, status: state.tutorial.status,
}); });
export default connect( export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(withRouter(Requirement)));
mapStateToProps,
null
)(withStyles(styles, { withTheme: true })(withRouter(Requirement)));
+46 -50
View File
@@ -1,49 +1,51 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { tutorialCheck, tutorialStep } from "../../actions/tutorialActions"; import { tutorialCheck, tutorialStep } from '../../actions/tutorialActions';
import { withRouter } from "react-router-dom"; import { withRouter } from 'react-router-dom';
import Compile from "../Workspace/Compile"; import Compile from '../Workspace/Compile';
import Dialog from "../Dialog"; import Dialog from '../Dialog';
import { checkXml } from "../../helpers/compareXml"; import { checkXml } from '../../helpers/compareXml';
import withStyles from '@mui/styles/withStyles'; import { withStyles } from '@material-ui/core/styles';
import IconButton from "@mui/material/IconButton"; import IconButton from '@material-ui/core/IconButton';
import Tooltip from "@mui/material/Tooltip"; import Tooltip from '@material-ui/core/Tooltip';
import Button from "@mui/material/Button"; import Button from '@material-ui/core/Button';
import { faClipboardCheck } from "@fortawesome/free-solid-svg-icons"; import { faClipboardCheck } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import * as Blockly from "blockly"; import * as Blockly from 'blockly'
const styles = (theme) => ({ const styles = (theme) => ({
compile: { compile: {
backgroundColor: theme.palette.button.compile, backgroundColor: theme.palette.button.compile,
color: theme.palette.primary.contrastText, color: theme.palette.primary.contrastText,
"&:hover": { '&:hover': {
backgroundColor: theme.palette.button.compile, backgroundColor: theme.palette.button.compile,
color: theme.palette.primary.contrastText, color: theme.palette.primary.contrastText,
}, }
}, }
}); });
class SolutionCheck extends Component { class SolutionCheck extends Component {
state = { state = {
open: false, open: false,
msg: "", msg: ''
}; }
toggleDialog = () => { toggleDialog = () => {
if (this.state.open) { if (this.state.open) {
this.setState({ open: false, msg: "" }); this.setState({ open: false, msg: '' });
} else { }
else {
this.setState({ open: !this.state }); this.setState({ open: !this.state });
} }
}; }
check = () => { check = () => {
const tutorial = this.props.tutorial; const tutorial = this.props.tutorial;
@@ -51,7 +53,7 @@ class SolutionCheck extends Component {
var msg = checkXml(step.xml, this.props.xml); var msg = checkXml(step.xml, this.props.xml);
this.props.tutorialCheck(msg.type, step); this.props.tutorialCheck(msg.type, step);
this.setState({ msg, open: true }); this.setState({ msg, open: true });
}; }
render() { render() {
const steps = this.props.tutorial.steps; const steps = this.props.tutorial.steps;
@@ -60,74 +62,68 @@ class SolutionCheck extends Component {
<Tooltip title={Blockly.Msg.tooltip_check_solution} arrow> <Tooltip title={Blockly.Msg.tooltip_check_solution} arrow>
<IconButton <IconButton
className={`solutionCheck ${this.props.classes.compile}`} className={`solutionCheck ${this.props.classes.compile}`}
style={{ width: "40px", height: "40px", marginRight: "5px" }} style={{ width: '40px', height: '40px', marginRight: '5px' }}
onClick={() => this.check()} onClick={() => this.check()}
size="large"> >
<FontAwesomeIcon icon={faClipboardCheck} size="xs" /> <FontAwesomeIcon icon={faClipboardCheck} size="l" />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Dialog <Dialog
style={{ zIndex: 9999999 }} style={{ zIndex: 9999999 }}
fullWidth fullWidth
maxWidth={"sm"} maxWidth={'sm'}
open={this.state.open} open={this.state.open}
title={this.state.msg.type === "error" ? "Fehler" : "Erfolg"} title={this.state.msg.type === 'error' ? 'Fehler' : 'Erfolg'}
content={this.state.msg.text} content={this.state.msg.text}
onClose={this.toggleDialog} onClose={this.toggleDialog}
onClick={this.toggleDialog} onClick={this.toggleDialog}
button={Blockly.Msg.button_close} button={Blockly.Msg.button_close}
> >
{this.state.msg.type === "success" ? ( {this.state.msg.type === 'success' ?
<div style={{ marginTop: "20px", display: "flex" }}> <div style={{ marginTop: '20px', display: 'flex' }}>
<Compile /> <Compile />
{this.props.activeStep === steps.length - 1 ? ( {this.props.activeStep === steps.length - 1 ?
<Button <Button
style={{ marginLeft: "10px" }} style={{ marginLeft: '10px' }}
variant="contained" variant="contained"
color="primary" color="primary"
onClick={() => { onClick={() => { this.toggleDialog(); this.props.history.push(`/tutorial/`) }}
this.toggleDialog();
this.props.history.push(`/tutorial/`);
}}
> >
{Blockly.Msg.button_tutorial_overview} {Blockly.Msg.button_tutorial_overview}
</Button> </Button>
) : ( :
<Button <Button
style={{ marginLeft: "10px" }} style={{ marginLeft: '10px' }}
variant="contained" variant="contained"
color="primary" color="primary"
onClick={() => { onClick={() => { this.toggleDialog(); this.props.tutorialStep(this.props.activeStep + 1) }}
this.toggleDialog();
this.props.tutorialStep(this.props.activeStep + 1);
}}
> >
{Blockly.Msg.button_next} {Blockly.Msg.button_next}
</Button> </Button>
)} }
</div> </div>
) : null} : null}
</Dialog> </Dialog>
</div> </div>
); );
} };
} }
SolutionCheck.propTypes = { SolutionCheck.propTypes = {
tutorialCheck: PropTypes.func.isRequired, tutorialCheck: PropTypes.func.isRequired,
tutorialStep: PropTypes.func.isRequired, tutorialStep: PropTypes.func.isRequired,
activeStep: PropTypes.number.isRequired, activeStep: PropTypes.number.isRequired,
xml: PropTypes.string.isRequired, xml: PropTypes.string.isRequired,
tutorial: PropTypes.object.isRequired, tutorial: PropTypes.object.isRequired
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = state => ({
activeStep: state.tutorial.activeStep, activeStep: state.tutorial.activeStep,
xml: state.workspace.code.xml, xml: state.workspace.code.xml,
tutorial: state.tutorial.tutorials[0], tutorial: state.tutorial.tutorials[0]
}); });
export default connect(mapStateToProps, { tutorialCheck, tutorialStep })( export default connect(mapStateToProps, { tutorialCheck, tutorialStep })(withStyles(styles, { withTheme: true })(withRouter(SolutionCheck)));
withStyles(styles, { withTheme: true })(withRouter(SolutionCheck))
);
+52 -127
View File
@@ -1,184 +1,109 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { withRouter } from "react-router-dom"; import { withRouter } from 'react-router-dom';
import clsx from "clsx"; import clsx from 'clsx';
// import tutorials from '../../data/tutorials'; // import tutorials from '../../data/tutorials';
import { alpha } from "@mui/material/styles";
import withStyles from '@mui/styles/withStyles'; import { fade } from '@material-ui/core/styles/colorManipulator';
import Typography from "@mui/material/Typography"; import { withStyles } from '@material-ui/core/styles';
import Tooltip from "@mui/material/Tooltip"; import Typography from '@material-ui/core/Typography';
import Button from "@mui/material/Button"; import Tooltip from '@material-ui/core/Tooltip';
import Button from '@material-ui/core/Button';
import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const styles = (theme) => ({ const styles = (theme) => ({
stepper: { stepper: {
width: "calc(100% - 40px)", width: 'calc(100% - 40px)',
height: "40px", height: '40px',
borderRadius: "25px", borderRadius: '25px',
padding: "0 20px", padding: '0 20px',
margin: "20px 0", margin: '20px 0',
display: "flex", display: 'flex',
justifyContent: "space-between", justifyContent: 'space-between'
}, },
stepperSuccess: { stepperSuccess: {
backgroundColor: alpha(theme.palette.primary.main, 0.6), backgroundColor: fade(theme.palette.primary.main, 0.6),
}, },
stepperError: { stepperError: {
backgroundColor: alpha(theme.palette.error.dark, 0.6), backgroundColor: fade(theme.palette.error.dark, 0.6),
}, },
stepperOther: { stepperOther: {
backgroundColor: alpha(theme.palette.secondary.main, 0.6), backgroundColor: fade(theme.palette.secondary.main, 0.6),
}, },
color: { color: {
backgroundColor: "transparent ", backgroundColor: 'transparent '
}, },
iconDivSuccess: { iconDivSuccess: {
color: theme.palette.primary.main, color: theme.palette.primary.main
}, },
iconDivError: { iconDivError: {
color: theme.palette.error.dark, color: theme.palette.error.dark
}, }
}); });
class StepperHorizontal extends Component { class StepperHorizontal extends Component {
render() { render() {
var tutorialId = this.props.tutorial._id; var tutorialId = this.props.tutorial._id;
var status = this.props.status.filter( var status = this.props.status.filter(status => status._id === tutorialId)[0];
(status) => status._id === tutorialId
)[0];
var tasks = status.tasks; var tasks = status.tasks;
var error = tasks.filter((task) => task.type === "error").length > 0; var error = tasks.filter(task => task.type === 'error').length > 0;
var success = var success = tasks.filter(task => task.type === 'success').length / tasks.length;
tasks.filter((task) => task.type === "success").length / tasks.length; var tutorialStatus = success === 1 ? 'Success' : error ? 'Error' : 'Other';
var tutorialStatus = success === 1 ? "Success" : error ? "Error" : "Other";
var title = this.props.tutorial.title; var title = this.props.tutorial.title;
var activeStep = this.props.activeStep;
return ( return (
<div style={{ position: "relative" }}> <div style={{ position: 'relative' }}>
{error || success > 0 ? ( {error || success > 0 ?
<div <div style={{ zIndex: -1, width: error ? 'calc(100% - 40px)' : `calc(${success * 100}% - 40px)`, borderRadius: success === 1 || error ? '25px' : '25px 0 0 25px', position: 'absolute', margin: 0, left: 0 }} className={clsx(this.props.classes.stepper, error ? this.props.classes.stepperError : this.props.classes.stepperSuccess)}>
style={{ </div>
zIndex: -1, : null}
width: error {success < 1 && !error ?
? "calc(100% - 40px)" <div style={{ zIndex: -2, width: `calc(${(1 - success) * 100}% - 40px)`, borderRadius: success === 0 ? '25px' : '0px 25px 25px 0', position: 'absolute', margin: 0, right: 0 }} className={clsx(this.props.classes.stepper, this.props.classes.stepperOther)}>
: `calc(${success * 100}% - 40px)`, </div>
borderRadius: success === 1 || error ? "25px" : "25px 0 0 25px", : null}
position: "absolute",
margin: 0,
left: 0,
}}
className={clsx(
this.props.classes.stepper,
error
? this.props.classes.stepperError
: this.props.classes.stepperSuccess
)}
></div>
) : null}
{success < 1 && !error ? (
<div
style={{
zIndex: -2,
width: `calc(${(1 - success) * 100}% - 40px)`,
borderRadius: success === 0 ? "25px" : "0px 25px 25px 0",
position: "absolute",
margin: 0,
right: 0,
}}
className={clsx(
this.props.classes.stepper,
this.props.classes.stepperOther
)}
></div>
) : null}
<div className={this.props.classes.stepper}> <div className={this.props.classes.stepper}>
<Button <Button
disabled //={tutorialIndex === 0} disabled//={tutorialIndex === 0}
//onClick={() => { this.props.history.push(`/tutorial/${tutorials[tutorialIndex - 1].id}`) }} //onClick={() => { this.props.history.push(`/tutorial/${tutorials[tutorialIndex - 1].id}`) }}
> >
{"<"} {'<'}
</Button> </Button>
<Tooltip <Tooltip style={{ display: 'flex', width: 'calc(100% - 64px - 64px)', justifyContent: 'center' }} title={title} arrow>
style={{
display: "flex",
width: "calc(100% - 64px - 64px)",
justifyContent: "center",
}}
title={title}
arrow
>
<div> <div>
{tutorialStatus !== "Other" ? ( {tutorialStatus !== 'Other' ? <div className={tutorialStatus === 'Success' && success === 1 ? this.props.classes.iconDivSuccess : this.props.classes.iconDivError} style={{ margin: 'auto 10px auto 0' }}><FontAwesomeIcon className={this.props.classes.icon} icon={tutorialStatus === 'Success' ? faCheck : faTimes} /></div> : null}
<div <Typography variant='body2' style={{ fontWeight: 'bold', fontSize: '1.75em', margin: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'rgba(0, 0, 0, 0.54)' }}>{title}</Typography>
className={
tutorialStatus === "Success" && success === 1
? this.props.classes.iconDivSuccess
: this.props.classes.iconDivError
}
style={{ margin: "auto 10px auto 0" }}
>
<FontAwesomeIcon
className={this.props.classes.icon}
icon={tutorialStatus === "Success" ? faCheck : faTimes}
/>
</div>
) : null}
<Typography
variant="body2"
style={{
fontWeight: "bold",
fontSize: "1.75em",
margin: 0,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
color: "rgba(0, 0, 0, 0.54)",
}}
>
{title}
{title !== this.props.tutorial.steps[activeStep].headline
? ` - ${this.props.tutorial.steps[activeStep].headline}`
: null}
</Typography>
</div> </div>
</Tooltip> </Tooltip>
<Button <Button
disabled //={tutorialIndex + 1 === tutorials.length} disabled//={tutorialIndex + 1 === tutorials.length}
//onClick={() => { this.props.history.push(`/tutorial/${tutorials[tutorialIndex + 1].id}`) }} //onClick={() => { this.props.history.push(`/tutorial/${tutorials[tutorialIndex + 1].id}`) }}
> >
{">"} {'>'}
</Button> </Button>
</div> </div>
</div> </div>
); );
} };
} }
StepperHorizontal.propTypes = { StepperHorizontal.propTypes = {
status: PropTypes.array.isRequired, status: PropTypes.array.isRequired,
change: PropTypes.number.isRequired, change: PropTypes.number.isRequired,
currentTutorialIndex: PropTypes.number.isRequired, currentTutorialIndex: PropTypes.number.isRequired,
tutorial: PropTypes.object.isRequired, tutorial: PropTypes.object.isRequired
activeStep: PropTypes.number.isRequired,
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = state => ({
change: state.tutorial.change, change: state.tutorial.change,
status: state.tutorial.status, status: state.tutorial.status,
currentTutorialIndex: state.tutorial.currentIndex, currentTutorialIndex: state.tutorial.currentIndex,
activeStep: state.tutorial.activeStep, tutorial: state.tutorial.tutorials[0]
tutorial: state.tutorial.tutorials[0],
}); });
export default connect( export default connect(mapStateToProps, null)(withRouter(withStyles(styles, { withTheme: true })(StepperHorizontal)));
mapStateToProps,
null
)(withRouter(withStyles(styles, { withTheme: true })(StepperHorizontal)));
+54 -94
View File
@@ -1,34 +1,36 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { tutorialStep } from "../../actions/tutorialActions"; import { tutorialStep } from '../../actions/tutorialActions';
import { withRouter } from "react-router-dom"; import { withRouter } from 'react-router-dom';
import clsx from "clsx"; import clsx from 'clsx';
import { alpha } from "@mui/material/styles";
import withStyles from '@mui/styles/withStyles'; import { fade } from '@material-ui/core/styles/colorManipulator';
import Stepper from "@mui/material/Stepper"; import { withStyles } from '@material-ui/core/styles';
import Step from "@mui/material/Step"; import Stepper from '@material-ui/core/Stepper';
import StepLabel from "@mui/material/StepLabel"; import Step from '@material-ui/core/Step';
import Tooltip from "@mui/material/Tooltip"; import StepLabel from '@material-ui/core/StepLabel';
import Tooltip from '@material-ui/core/Tooltip';
const styles = (theme) => ({ const styles = (theme) => ({
verticalStepper: { verticalStepper: {
padding: 0, padding: 0,
width: "30px", width: '30px',
}, },
stepIcon: { stepIcon: {
borderStyle: `solid`, borderStyle: `solid`,
// borderWidth: '2px', // borderWidth: '2px',
borderRadius: "50%", borderRadius: '50%',
borderColor: theme.palette.secondary.main, borderColor: theme.palette.secondary.main,
width: "1rem", width: '12px',
margin: "0 auto", height: '12px',
margin: '0 auto',
}, },
stepIconLarge: { stepIconLarge: {
width: "2rem", width: '24px',
height: "1rem", height: '24px'
}, },
stepIconLargeSuccess: { stepIconLargeSuccess: {
borderColor: theme.palette.primary.main, borderColor: theme.palette.primary.main,
@@ -37,27 +39,28 @@ const styles = (theme) => ({
borderColor: theme.palette.error.dark, borderColor: theme.palette.error.dark,
}, },
stepIconActiveOther: { stepIconActiveOther: {
backgroundColor: theme.palette.secondary.main, backgroundColor: theme.palette.secondary.main
}, },
stepIconActiveSuccess: { stepIconActiveSuccess: {
backgroundColor: alpha(theme.palette.primary.main, 0.6), backgroundColor: fade(theme.palette.primary.main, 0.6)
}, },
stepIconActiveError: { stepIconActiveError: {
backgroundColor: alpha(theme.palette.error.dark, 0.6), backgroundColor: fade(theme.palette.error.dark, 0.6)
}, },
connector: { connector: {
height: "10px", height: '10px',
borderLeft: `2px solid black`, borderLeft: `2px solid black`,
margin: "auto", margin: 'auto'
}, }
}); });
class StepperVertical extends Component { class StepperVertical extends Component {
componentDidMount() {
componentDidMount(){
this.props.tutorialStep(0); this.props.tutorialStep(0);
} }
componentDidUpdate(props) { componentDidUpdate(props){
if (props.tutorial._id !== this.props.match.params.tutorialId) { if (props.tutorial._id !== this.props.match.params.tutorialId) {
this.props.tutorialStep(0); this.props.tutorialStep(0);
} }
@@ -66,103 +69,60 @@ class StepperVertical extends Component {
render() { render() {
var steps = this.props.steps; var steps = this.props.steps;
var activeStep = this.props.activeStep; var activeStep = this.props.activeStep;
var tutorialStatus = this.props.status.filter( var tutorialStatus = this.props.status.filter(status => status._id === this.props.tutorial._id)[0];
(status) => status._id === this.props.tutorial._id
)[0];
return ( return (
<div style={{ marginRight: "10px" }}> <div style={{marginRight: '10px'}}>
<Stepper <Stepper
activeStep={activeStep} activeStep={activeStep}
orientation="vertical" orientation="vertical"
connector={<div className={this.props.classes.connector}></div>} connector={<div className={this.props.classes.connector}></div>}
classes={{ root: this.props.classes.verticalStepper }} classes={{root: this.props.classes.verticalStepper}}
> >
{steps.map((step, i) => { {steps.map((step, i) => {
var tasksIndex = tutorialStatus.tasks.findIndex( var tasksIndex = tutorialStatus.tasks.findIndex(task => task._id === step._id);
(task) => task._id === step._id var taskType = tasksIndex > -1 ? tutorialStatus.tasks[tasksIndex].type : null;
); var taskStatus = taskType === 'success' ? 'Success' : taskType === 'error' ? 'Error' : 'Other';
var taskType =
tasksIndex > -1 ? tutorialStatus.tasks[tasksIndex].type : null;
var taskStatus =
taskType === "success"
? "Success"
: taskType === "error"
? "Error"
: "Other";
return ( return (
<Step key={i}> <Step key={i}>
<Tooltip title={step.headline} placement="right" arrow> <Tooltip title={step.headline} placement='right' arrow >
<div <div style={i === activeStep ? {padding: '5px 0'} : {padding: '5px 0', cursor: 'pointer'}} onClick={i === activeStep ? null : () => { this.props.tutorialStep(i)}}>
style={
i === activeStep
? { padding: "5px 0" }
: { padding: "5px 0", cursor: "pointer" }
}
onClick={
i === activeStep
? null
: () => {
this.props.tutorialStep(i);
}
}
>
<StepLabel <StepLabel
StepIconComponent={"div"} StepIconComponent={'div'}
classes={{ classes={{
root: root: step.type === 'task' ?
step.type === "task" i === activeStep ?
? i === activeStep clsx(this.props.classes.stepIcon, this.props.classes.stepIconLarge, this.props.classes['stepIconLarge'+taskStatus], this.props.classes['stepIconActive'+taskStatus])
? clsx( : clsx(this.props.classes.stepIcon, this.props.classes.stepIconLarge, this.props.classes['stepIconLarge'+taskStatus])
this.props.classes.stepIcon, : i === activeStep ?
this.props.classes.stepIconLarge, clsx(this.props.classes.stepIcon, this.props.classes.stepIconActiveOther)
this.props.classes[ : clsx(this.props.classes.stepIcon)
"stepIconLarge" + taskStatus
],
this.props.classes[
"stepIconActive" + taskStatus
]
)
: clsx(
this.props.classes.stepIcon,
this.props.classes.stepIconLarge,
this.props.classes[
"stepIconLarge" + taskStatus
]
)
: i === activeStep
? clsx(
this.props.classes.stepIcon,
this.props.classes.stepIconActiveOther
)
: clsx(this.props.classes.stepIcon),
}} }}
></StepLabel> >
</StepLabel>
</div> </div>
</Tooltip> </Tooltip>
</Step> </Step>
); )})}
})}
</Stepper> </Stepper>
</div> </div>
); );
} };
} }
StepperVertical.propTypes = { StepperVertical.propTypes = {
status: PropTypes.array.isRequired, status: PropTypes.array.isRequired,
change: PropTypes.number.isRequired, change: PropTypes.number.isRequired,
activeStep: PropTypes.number.isRequired, activeStep: PropTypes.number.isRequired,
tutorialStep: PropTypes.func.isRequired, tutorialStep: PropTypes.func.isRequired,
tutorial: PropTypes.object.isRequired, tutorial: PropTypes.object.isRequired
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = state => ({
change: state.tutorial.change, change: state.tutorial.change,
status: state.tutorial.status, status: state.tutorial.status,
activeStep: state.tutorial.activeStep, activeStep: state.tutorial.activeStep,
tutorial: state.tutorial.tutorials[0], tutorial: state.tutorial.tutorials[0]
}); });
export default connect(mapStateToProps, { tutorialStep })( export default connect(mapStateToProps, { tutorialStep })(withRouter(withStyles(styles, {withTheme: true})(StepperVertical)));
withRouter(withStyles(styles, { withTheme: true })(StepperVertical))
);
+2 -2
View File
@@ -21,8 +21,8 @@ import NotFound from "../NotFound";
import * as Blockly from "blockly"; import * as Blockly from "blockly";
import { detectWhitespacesAndReturnReadableResult } from "../../helpers/whitespace"; import { detectWhitespacesAndReturnReadableResult } from "../../helpers/whitespace";
import Card from "@mui/material/Card"; import Card from "@material-ui/core/Card";
import Button from "@mui/material/Button"; import Button from "@material-ui/core/Button";
class Tutorial extends Component { class Tutorial extends Component {
componentDidMount() { componentDidMount() {
+34 -251
View File
@@ -3,13 +3,9 @@ import PropTypes from "prop-types";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { import {
getTutorials, getTutorials,
getAllTutorials,
getUserTutorials,
resetTutorial, resetTutorial,
tutorialProgress, tutorialProgress,
} from "../../actions/tutorialActions"; } from "../../actions/tutorialActions";
import { progress } from "../../actions/tutorialBuilderActions";
import { clearMessages } from "../../actions/messageActions"; import { clearMessages } from "../../actions/messageActions";
import clsx from "clsx"; import clsx from "clsx";
@@ -17,27 +13,16 @@ import clsx from "clsx";
import Breadcrumbs from "../Breadcrumbs"; import Breadcrumbs from "../Breadcrumbs";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { alpha } from "@mui/material/styles";
import withStyles from '@mui/styles/withStyles';
import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import { import { fade } from "@material-ui/core/styles/colorManipulator";
faCheck, import { withStyles } from "@material-ui/core/styles";
faTimes, import Grid from "@material-ui/core/Grid";
faShareAlt, import Paper from "@material-ui/core/Paper";
faEye, import Typography from "@material-ui/core/Typography";
faUserCheck,
} from "@fortawesome/free-solid-svg-icons"; import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import * as Blockly from "blockly"; import * as Blockly from "blockly";
import ReactStars from "react-rating-stars-component";
import Tooltip from "@mui/material/Tooltip";
import IconButton from "@mui/material/IconButton";
import Snackbar from "../Snackbar";
import Divider from "@mui/material/Divider";
import DeviceSelection from "../DeviceSelection";
const styles = (theme) => ({ const styles = (theme) => ({
outerDiv: { outerDiv: {
@@ -46,18 +31,18 @@ const styles = (theme) => ({
bottom: "-30px", bottom: "-30px",
width: "160px", width: "160px",
height: "160px", height: "160px",
color: alpha(theme.palette.secondary.main, 0.6), color: fade(theme.palette.secondary.main, 0.6),
}, },
outerDivError: { outerDivError: {
stroke: alpha(theme.palette.error.dark, 0.6), stroke: fade(theme.palette.error.dark, 0.6),
color: alpha(theme.palette.error.dark, 0.6), color: fade(theme.palette.error.dark, 0.6),
}, },
outerDivSuccess: { outerDivSuccess: {
stroke: alpha(theme.palette.primary.main, 0.6), stroke: fade(theme.palette.primary.main, 0.6),
color: alpha(theme.palette.primary.main, 0.6), color: fade(theme.palette.primary.main, 0.6),
}, },
outerDivOther: { outerDivOther: {
stroke: alpha(theme.palette.secondary.main, 0.6), stroke: fade(theme.palette.secondary.main, 0.6),
}, },
innerDiv: { innerDiv: {
width: "inherit", width: "inherit",
@@ -66,85 +51,23 @@ const styles = (theme) => ({
verticalAlign: "middle", verticalAlign: "middle",
textAlign: "center", textAlign: "center",
}, },
button: {
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
width: "40px",
height: "40px",
"&:hover": {
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
},
},
link: {
color: theme.palette.primary.main,
textDecoration: "none",
"&:hover": {
color: theme.palette.primary.main,
textDecoration: "underline",
},
},
}); });
class TutorialHome extends Component { class TutorialHome extends Component {
constructor(props) {
super(props);
this.state = {
userTutorials: [],
tutorials: [],
snackbar: false,
};
}
componentDidMount() { componentDidMount() {
this.props.tutorialProgress(); this.props.tutorialProgress();
// retrieve tutorials only if a potential user is loaded - authentication // retrieve tutorials only if a potential user is loaded - authentication
// is finished (success or failed) // is finished (success or failed)
// if (!this.props.progress) { if (!this.props.progress) {
// if (this.props.user) { this.props.getTutorials();
// if (this.props.user.blocklyRole === "admin") {
// this.props.getAllTutorials();
// }
// if (this.props.user.blocklyRole === "creator") {
// this.props.getUserTutorials();
// this.props.getTutorials();
// console.log("get user tutorials");
// console.log(this.props.userTutorials);
// }
// } else {
// this.props.getTutorials();
// }
// }
if (!this.props.authProgress) {
if (this.props.user) {
if (this.props.user.role === "admin") {
this.props.getAllTutorials();
} else {
this.props.getUserTutorials();
//this.props.getTutorials();
}
} else {
this.props.getTutorials();
}
} }
} }
componentDidUpdate(props, state) { componentDidUpdate(props, state) {
if ( if (props.progress !== this.props.progress && !this.props.progress) {
props.authProgress !== this.props.authProgress && // authentication is completed
!this.props.authProgress this.props.getTutorials();
) }
if (this.props.user) {
if (this.props.user.role === "admin") {
// authentication is completed
this.props.getAllTutorials();
} else {
this.props.getUserTutorials();
}
} else {
this.props.getTutorials();
}
if (this.props.message.id === "GET_TUTORIALS_FAIL") { if (this.props.message.id === "GET_TUTORIALS_FAIL") {
alert(this.props.message.msg); alert(this.props.message.msg);
} }
@@ -158,25 +81,13 @@ class TutorialHome extends Component {
} }
render() { render() {
var userTutorials = [];
const publicTutorials = this.props.tutorials.filter(
(tutorial) => tutorial.public === true
);
if (this.props.user && this.props.user.blocklyRole === "admin") {
userTutorials = this.props.tutorials;
}
if (this.props.user && this.props.user.blocklyRole === "creator") {
userTutorials = this.props.tutorials.filter(
(tutorial) => tutorial.creator === this.props.user.email
);
}
return this.props.isLoading ? null : ( return this.props.isLoading ? null : (
<div> <div>
<Breadcrumbs content={[{ link: "/tutorial", title: "Tutorial" }]} /> <Breadcrumbs content={[{ link: "/tutorial", title: "Tutorial" }]} />
<h1>{Blockly.Msg.tutorials_home_head}</h1> <h1>{Blockly.Msg.tutorials_home_head}</h1>
<h2>Alle Tutorials</h2>
<Grid container spacing={2}> <Grid container spacing={2}>
{publicTutorials.map((tutorial, i) => { {this.props.tutorials.map((tutorial, i) => {
var status = this.props.status.filter( var status = this.props.status.filter(
(status) => status._id === tutorial._id (status) => status._id === tutorial._id
)[0]; )[0];
@@ -188,12 +99,6 @@ class TutorialHome extends Component {
tasks.length; tasks.length;
var tutorialStatus = var tutorialStatus =
success === 1 ? "Success" : error ? "Error" : "Other"; success === 1 ? "Success" : error ? "Error" : "Other";
const firstExample = {
size: 30,
value: tutorial.difficulty,
edit: false,
isHalf: true,
};
return ( return (
<Grid item xs={12} sm={6} md={4} xl={3} key={i} style={{}}> <Grid item xs={12} sm={6} md={4} xl={3} key={i} style={{}}>
<Link <Link
@@ -209,7 +114,6 @@ class TutorialHome extends Component {
}} }}
> >
{tutorial.title} {tutorial.title}
<ReactStars {...firstExample} />
<div <div
className={clsx(this.props.classes.outerDiv)} className={clsx(this.props.classes.outerDiv)}
style={{ width: "160px", height: "160px", border: 0 }} style={{ width: "160px", height: "160px", border: 0 }}
@@ -244,11 +148,14 @@ class TutorialHome extends Component {
cy="50%" cy="50%"
fill="none" fill="none"
stroke-width="10" stroke-width="10"
stroke-dashoffset={`${75 * 2 * Math.PI * (1 - (50 / 100 + success / 2)) stroke-dashoffset={`${
}`} 75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
stroke-dasharray={`${75 * 2 * Math.PI * (1 - (50 / 100 - success / 2)) }`}
} ${75 * 2 * Math.PI * (1 - (50 / 100 + success / 2)) stroke-dasharray={`${
}`} 75 * 2 * Math.PI * (1 - (50 / 100 - success / 2))
} ${
75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
}`}
></circle> ></circle>
)} )}
{success < 1 && !error ? ( {success < 1 && !error ? (
@@ -263,8 +170,9 @@ class TutorialHome extends Component {
cy="50%" cy="50%"
fill="none" fill="none"
stroke-width="10" stroke-width="10"
stroke-dashoffset={`${75 * 2 * Math.PI * (1 - (50 / 100 + success / 2)) stroke-dashoffset={`${
}`} 75 * 2 * Math.PI * (1 - (50 / 100 + success / 2))
}`}
stroke-dasharray={`${75 * 2 * Math.PI}`} stroke-dasharray={`${75 * 2 * Math.PI}`}
></circle> ></circle>
) : null} ) : null}
@@ -276,8 +184,8 @@ class TutorialHome extends Component {
tutorialStatus === "Error" tutorialStatus === "Error"
? this.props.classes.outerDivError ? this.props.classes.outerDivError
: tutorialStatus === "Success" : tutorialStatus === "Success"
? this.props.classes.outerDivSuccess ? this.props.classes.outerDivSuccess
: null : null
)} )}
> >
<div className={this.props.classes.innerDiv}> <div className={this.props.classes.innerDiv}>
@@ -287,7 +195,6 @@ class TutorialHome extends Component {
icon={ icon={
tutorialStatus === "Success" ? faCheck : faTimes tutorialStatus === "Success" ? faCheck : faTimes
} }
/> />
) : ( ) : (
<Typography <Typography
@@ -309,120 +216,6 @@ class TutorialHome extends Component {
); );
})} })}
</Grid> </Grid>
<DeviceSelection />
{this.props.user ? (
<div>
<h2>User Tutorials</h2>
<Grid container spacing={2}>
{userTutorials.map((tutorial, i) => {
var status = this.props.status.filter(
(status) => status._id === tutorial._id
)[0];
var tasks = status.tasks;
var error =
status.tasks.filter((task) => task.type === "error").length >
0;
var success =
status.tasks.filter((task) => task.type === "success")
.length / tasks.length;
var tutorialStatus =
success === 1 ? "Success" : error ? "Error" : "Other";
const firstExample = {
size: 30,
value: tutorial.difficulty,
edit: false,
isHalf: true,
};
return (
<Grid item xs={12} sm={6} md={4} xl={3} key={i} style={{}}>
<Paper
style={{
height: "150",
padding: "10px",
position: "relative",
overflow: "hidden",
backgroundColor: tutorial.review
? "lightyellow"
: "white",
}}
>
<Link
to={`/tutorial/${tutorial._id}`}
style={{ textDecoration: "none", color: "inherit" }}
>
{tutorial.title}
<ReactStars {...firstExample} />
</Link>
<Divider
style={{
marginTop: "10px",
marginBottom: "10px",
}}
/>
<p>
Creator:{tutorial.creator} <br />
<div style={this.props.style}>
<Tooltip
title={Blockly.Msg.tooltip_share_tutorial}
arrow
>
<IconButton
className={`shareTutorial ${this.props.classes.button}`}
onClick={() => {
navigator.clipboard.writeText(
`${window.location.origin}/tutorial/${tutorial._id}`
);
this.setState({
snackbar: true,
key: Date.now(),
message:
Blockly.Msg.messages_copylink_success,
type: "success",
});
}}
size="large">
<FontAwesomeIcon icon={faShareAlt} size="xs" />
</IconButton>
</Tooltip>
<Tooltip
title={Blockly.Msg.tooltip_share_tutorial}
arrow
>
<IconButton
className={`publicTutorial ${this.props.classes.button}`}
disabled={!tutorial.public}
size="large">
<FontAwesomeIcon icon={faEye} size="xs" />
</IconButton>
</Tooltip>
{tutorial.review ? (
<Tooltip
title={Blockly.Msg.tooltip_share_tutorial}
arrow
>
<IconButton
className={`publicTutorial ${this.props.classes.button}`}
disabled={!tutorial.review}
size="large">
<FontAwesomeIcon icon={faUserCheck} size="xs" />
</IconButton>
</Tooltip>
) : null}
<Snackbar
open={this.state.snackbar}
message={Blockly.Msg.messages_copylink_success}
type={this.state.type}
key={this.state.key}
/>
</div>
</p>
</Paper>
</Grid>
);
})}
</Grid>
</div>
) : null}
</div> </div>
); );
} }
@@ -430,8 +223,6 @@ class TutorialHome extends Component {
TutorialHome.propTypes = { TutorialHome.propTypes = {
getTutorials: PropTypes.func.isRequired, getTutorials: PropTypes.func.isRequired,
getAllTutorials: PropTypes.func.isRequired,
getUserTutorials: PropTypes.func.isRequired,
resetTutorial: PropTypes.func.isRequired, resetTutorial: PropTypes.func.isRequired,
tutorialProgress: PropTypes.func.isRequired, tutorialProgress: PropTypes.func.isRequired,
clearMessages: PropTypes.func.isRequired, clearMessages: PropTypes.func.isRequired,
@@ -441,27 +232,19 @@ TutorialHome.propTypes = {
isLoading: PropTypes.bool.isRequired, isLoading: PropTypes.bool.isRequired,
message: PropTypes.object.isRequired, message: PropTypes.object.isRequired,
progress: PropTypes.bool.isRequired, progress: PropTypes.bool.isRequired,
user: PropTypes.object.isRequired,
authProgress: PropTypes.bool.isRequired,
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
change: state.tutorial.change, change: state.tutorial.change,
status: state.tutorial.status, status: state.tutorial.status,
tutorials: state.tutorial.tutorials, tutorials: state.tutorial.tutorials,
userTutorials: state.tutorial.userTutorials,
isLoading: state.tutorial.progress, isLoading: state.tutorial.progress,
message: state.message, message: state.message,
progress: state.auth.progress, progress: state.auth.progress,
user: state.auth.user,
authProgress: state.auth.progress,
}); });
export default connect(mapStateToProps, { export default connect(mapStateToProps, {
getTutorials, getTutorials,
progress,
getUserTutorials,
getAllTutorials,
resetTutorial, resetTutorial,
clearMessages, clearMessages,
tutorialProgress, tutorialProgress,

Some files were not shown because too many files have changed in this diff Show More