diff --git a/src/actions/generalActions.js b/src/actions/generalActions.js index 754f438..f4dd22f 100644 --- a/src/actions/generalActions.js +++ b/src/actions/generalActions.js @@ -1,32 +1,38 @@ -import { VISIT, LANGUAGE, RENDERER, STATISTICS } from './types'; - +import { VISIT, LANGUAGE, RENDERER, STATISTICS, PLATFORM } from "./types"; export const visitPage = () => (dispatch) => { dispatch({ - type: VISIT + type: VISIT, + }); +}; + +export const setPlatform = (platform) => (dispatch) => { + dispatch({ + type: PLATFORM, + payload: platform, }); }; export const setLanguage = (language) => (dispatch, getState) => { - if(!getState().auth.progress && !getState().auth.isAuthenticated){ - window.localStorage.setItem('locale', language); + if (!getState().auth.progress && !getState().auth.isAuthenticated) { + window.localStorage.setItem("locale", language); } dispatch({ type: LANGUAGE, - payload: language + payload: language, }); }; export const setRenderer = (renderer) => (dispatch) => { dispatch({ type: RENDERER, - payload: renderer + payload: renderer, }); }; export const setStatistics = (showStatistics) => (dispatch) => { dispatch({ type: STATISTICS, - payload: showStatistics + payload: showStatistics, }); }; diff --git a/src/actions/types.js b/src/actions/types.js index 7b474d6..223154d 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -43,6 +43,7 @@ export const PROGRESS = "PROGRESS"; export const VISIT = "VISIT"; export const LANGUAGE = "LANGUAGE"; +export const PLATFORM = "PLATFORM"; export const RENDERER = "RENDERER"; export const STATISTICS = "STATISTICS"; diff --git a/src/components/Blockly/generator/generator.js b/src/components/Blockly/generator/generator.js index e1073c9..29b77a1 100644 --- a/src/components/Blockly/generator/generator.js +++ b/src/components/Blockly/generator/generator.js @@ -26,6 +26,17 @@ import * as Blockly from "blockly/core"; +import store from "../../../store"; + +var ota = store.getState().general.platform + ? store.getState().general.platform + : null; +store.subscribe(() => { + ota = store.getState().general.platform + ? store.getState().general.platform + : null; +}); + /** * Arduino code generator. * @type !Blockly.Generator @@ -254,26 +265,49 @@ Blockly["Arduino"].finish = function (code) { "\n}\n"; let loopCode = "\nvoid loop() { \n" + loopCodeOnce + code + "\n}\n"; - - // Convert the definitions dictionary into a list. - code = - devVariables + - "\n" + - libraryCode + - "\n" + - variablesCode + - "\n" + - definitionsCode + - "\n" + - codeFunctions + - "\n" + - Blockly["Arduino"].variablesInitCode_ + - "\n" + - functionsCode + - "\n" + - setupCode + - "\n" + - loopCode; + // only add OTA code if tablet mode is enabled + if (ota === true) { + code = + devVariables + + "\n" + + "#include " + + "\n" + + libraryCode + + "\n" + + variablesCode + + "\n" + + definitionsCode + + "\n" + + codeFunctions + + "\n" + + Blockly["Arduino"].variablesInitCode_ + + "\n" + + functionsCode + + "\n" + + setupCode + + "\n" + + loopCode; + } else { + // Convert the definitions dictionary into a list. + code = + devVariables + + "\n" + + libraryCode + + "\n" + + variablesCode + + "\n" + + definitionsCode + + "\n" + + codeFunctions + + "\n" + + Blockly["Arduino"].variablesInitCode_ + + "\n" + + functionsCode + + "\n" + + setupCode + + "\n" + + loopCode; + } // Clean up temporary data. delete Blockly["Arduino"].definitions_; diff --git a/src/components/Blockly/msg/de/ui.js b/src/components/Blockly/msg/de/ui.js index 88dad89..47feefb 100644 --- a/src/components/Blockly/msg/de/ui.js +++ b/src/components/Blockly/msg/de/ui.js @@ -90,6 +90,16 @@ export const UI = { messages_LOGIN_FAIL: "Der Benutzername oder das Passwort ist nicht korrekt.", messages_copy_code: "Code wurde in die Zwischenablage kopiert", + /** + * Tablet Dialog + */ + + tabletDialog_headline: "Tablet Modus ist aktiviert!", + tabletDialog_text: + "Der Tablet Modus wurde aktiviert. Du kannst nun Programmcodes über die senseBox Connect App auf deine senseBox kopieren. Der Tablet Modus kann in den Einstellungen deaktiviert werden", + tabletDialog_more: + "Weitere Informationen und den Link zum Download der App findest du unter: ", + /** * Reset Dialog */ @@ -161,6 +171,11 @@ export const UI = { "Die Anzeige von Statistiken zur Nutzung der Blöcke oberhalb der Arbeitsfläche kann ein- oder ausgeblendet werden.", settings_statistics_on: "An", settings_statistics_off: "Aus", + settings_ota_head: "Tablet Modus", + settings_ota_text: + "Der Tablet Modus deaktiviert die Code anzeige und aktiviert die Möglichkeit den Programmcode über die senseBox Connect App zu übertragen. Weitere Informationen dazu findest du unter: ", + settings_ota_on: "Aktiviert", + settings_ota_off: "Deaktiviert", /** * 404 diff --git a/src/components/Blockly/msg/en/ui.js b/src/components/Blockly/msg/en/ui.js index 2f05953..e074276 100644 --- a/src/components/Blockly/msg/en/ui.js +++ b/src/components/Blockly/msg/en/ui.js @@ -88,6 +88,15 @@ export const UI = { messages_login_error: "Enter both a username and a password.", messages_copy_code: "Copy code to clipboard succesfull", + /** + * Tablet Dialog + */ + tabletDialog_headline: "Tablet mode is enabled!", + tabletDialog_text: + "Tablet mode has been activated. You can now copy program codes to your senseBox via the senseBox Connect app. Tablet mode can be deactivated in the settings", + tabletDialog_more: + "For more information and the link to download the app, please visit: ", + /** * Reset Dialog */ @@ -157,6 +166,11 @@ export const UI = { "The display of statistics on the usage of the blocks above the workspace can be shown or hidden.", settings_statistics_on: "On", settings_statistics_off: "Off", + settings_ota_head: "tablet mode", + settings_ota_text: + "Tablet mode disables the code display and enables the possibility to transfer the program code via the senseBox Connect app. You can find more information on: ", + settings_ota_on: "Activated", + settings_ota_off: "Deactivated", /** * 404 diff --git a/src/components/Home.js b/src/components/Home.js index 60cb09f..32000b0 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -14,6 +14,7 @@ import TrashcanButtons from "./Workspace/TrashcanButtons"; import HintTutorialExists from "./Tutorial/HintTutorialExists"; import Snackbar from "./Snackbar"; + import Grid from "@material-ui/core/Grid"; import IconButton from "@material-ui/core/IconButton"; import Tooltip from "@material-ui/core/Tooltip"; @@ -22,6 +23,7 @@ import { withStyles } from "@material-ui/core/styles"; import { faCode } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import TooltipViewer from "./TooltipViewer"; +import Dialog from "./Dialog"; const styles = (theme) => ({ codeOn: { @@ -45,15 +47,23 @@ const styles = (theme) => ({ }); class Home extends Component { - state = { - codeOn: true, - snackbar: false, - type: "", - key: "", - message: "", - }; + constructor(props) { + super(props); + this.state = { + codeOn: true, + snackbar: false, + type: "", + key: "", + message: "", + open: true, + }; + } componentDidMount() { + console.log(this.props.platform); + if (this.props.platform === true) { + this.setState({ codeOn: false }); + } this.setState({ stats: window.localStorage.getItem("stats") }); if (!this.props.project) { this.props.workspaceName(createNameId()); @@ -82,6 +92,18 @@ class Home extends Component { this.props.workspaceName(null); } + toggleDialog = () => { + this.setState({ open: !this.state }); + }; + + onChangeCheckbox = (e) => { + if (e.target.checked) { + window.localStorage.setItem("ota", e.target.checked); + } else { + window.localStorage.removeItem("ota"); + } + }; + onChange = () => { this.setState({ codeOn: !this.state.codeOn }); const workspace = Blockly.getMainWorkspace(); @@ -127,7 +149,7 @@ class Home extends Component { this.state.codeOn ? this.props.classes.codeOn : this.props.classes.codeOff - }`} + }} style={{ width: "40px", height: "40px", @@ -161,12 +183,31 @@ class Home extends Component { ) : null} - + {this.props.platform ? ( + +
{Blockly.Msg.tabletDialog_text}
+
+ {Blockly.Msg.tabletDialog_more}{" "} + + https://sensebox.de/app + +
+
+ ) : null} ); } @@ -177,11 +218,14 @@ Home.propTypes = { workspaceName: PropTypes.func.isRequired, message: PropTypes.object.isRequired, statistics: PropTypes.bool.isRequired, + platform: PropTypes.object.isRequired, + }; const mapStateToProps = (state) => ({ message: state.message, statistics: state.general.statistics, + platform: state.general.platform, }); export default connect(mapStateToProps, { clearStats, workspaceName })( diff --git a/src/components/Settings/OtaSelector.js b/src/components/Settings/OtaSelector.js new file mode 100644 index 0000000..8a55b82 --- /dev/null +++ b/src/components/Settings/OtaSelector.js @@ -0,0 +1,66 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { setPlatform } from "../../actions/generalActions"; + +import * as Blockly from "blockly/core"; + +import InputLabel from "@material-ui/core/InputLabel"; +import MenuItem from "@material-ui/core/MenuItem"; +import FormControl from "@material-ui/core/FormControl"; +import Select from "@material-ui/core/Select"; +import Typography from "@material-ui/core/Typography"; +import FormHelperText from "@material-ui/core/FormHelperText"; + +class OtaSelector 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 ( +
+ + {Blockly.Msg.settings_ota_head} + + + {Blockly.Msg.settings_ota_text} + + https://sensebox.de/app + + + + + {Blockly.Msg.settings_statistics} + + + +
+ ); + } +} + +OtaSelector.propTypes = { + setPlatform: PropTypes.func.isRequired, + language: PropTypes.string.isRequired, + platform: PropTypes.bool.isRequired, +}; + +const mapStateToProps = (state) => ({ + language: state.general.language, + platform: state.general.platform, +}); + +export default connect(mapStateToProps, { setPlatform })(OtaSelector); diff --git a/src/components/Settings/Settings.js b/src/components/Settings/Settings.js index 24b3bfa..ac1a538 100644 --- a/src/components/Settings/Settings.js +++ b/src/components/Settings/Settings.js @@ -1,22 +1,22 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; -import { withRouter } from 'react-router-dom'; +import { withRouter } from "react-router-dom"; -import * as Blockly from 'blockly/core'; +import * as Blockly from "blockly/core"; -import Breadcrumbs from '../Breadcrumbs'; -import LanguageSelector from './LanguageSelector'; -import RenderSelector from './RenderSelector'; -import StatsSelector from './StatsSelector'; +import Breadcrumbs from "../Breadcrumbs"; +import LanguageSelector from "./LanguageSelector"; +import RenderSelector from "./RenderSelector"; +import StatsSelector from "./StatsSelector"; +import OtaSelector from "./OtaSelector"; -import Button from '@material-ui/core/Button'; -import Paper from '@material-ui/core/Paper'; +import Button from "@material-ui/core/Button"; +import Paper from "@material-ui/core/Paper"; class Settings extends Component { - - componentDidMount(){ + 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(); @@ -25,41 +25,55 @@ class Settings extends Component { render() { return (
- +

{Blockly.Msg.settings_head}

- + - + - + + + +
); - }; + } } Settings.propTypes = { language: PropTypes.string.isRequired, - pageVisits: PropTypes.number.isRequired + pageVisits: PropTypes.number.isRequired, }; -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ language: state.general.language, - pageVisits: state.general.pageVisits + pageVisits: state.general.pageVisits, }); export default connect(mapStateToProps, null)(withRouter(Settings)); diff --git a/src/components/Workspace/Compile.js b/src/components/Workspace/Compile.js index cf8014b..1bea26c 100644 --- a/src/components/Workspace/Compile.js +++ b/src/components/Workspace/Compile.js @@ -21,6 +21,7 @@ import "prismjs/themes/prism.css"; import "prismjs/plugins/line-numbers/prism-line-numbers"; import "prismjs/plugins/line-numbers/prism-line-numbers.css"; import MuiDrawer from "@material-ui/core/Drawer"; +import Dialog from "../Dialog"; const styles = (theme) => ({ backdrop: { @@ -65,6 +66,8 @@ class Compile extends Component { content: "", name: props.name, error: "", + appLink: "", + appDialog: false, }; } @@ -126,21 +129,43 @@ class Compile extends Component { }; toggleDialog = () => { - this.setState({ open: !this.state, progress: false }); + this.setState({ open: !this.state, progress: false, appDialog: false }); }; createFileName = () => { - if (this.state.name) { - this.download(); - } else { + if (this.props.platform === true) { + const filename = detectWhitespacesAndReturnReadableResult( + this.state.name + ); 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'.", + 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) => { @@ -188,23 +213,44 @@ class Compile extends Component { Kompilieren )} - -
- copyimage -

{Blockly.Msg.compile_overlay_head}

-

{Blockly.Msg.compile_overlay_text}

-

- {Blockly.Msg.compile_overlay_help} - - FAQ - -

- -
-
+ + {this.props.platform === false ? ( + +
+ copyimage +

{Blockly.Msg.compile_overlay_head}

+

{Blockly.Msg.compile_overlay_text}

+

+ {Blockly.Msg.compile_overlay_help} + + FAQ + +

+ +
+
+ ) : ( + +
+ {/* copyimage */} +

Dein Code wird kompiliert!

+

übertrage ihn anschließend mithlfe der senseBoxConnect-App

+

+ {Blockly.Msg.compile_overlay_help} + + FAQ + +

+ +
+
+ )} - {/* { this.toggleDialog(); this.setState({ name: this.props.name }) } : this.toggleDialog} - button={this.state.file ? Blockly.Msg.button_cancel : Blockly.Msg.button_close} + onClick={this.toggleDialog} + button={Blockly.Msg.button_close} > - {this.state.file ? -
- - -
- : - -

-                {`${this.state.error}`}
-              
- - - - } -
*/} +
+

Dein Code wurde erfolgreich kompiliert

+ + + +
+ ); } @@ -272,11 +324,13 @@ 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 })( diff --git a/src/reducers/generalReducer.js b/src/reducers/generalReducer.js index 1571d63..555d093 100644 --- a/src/reducers/generalReducer.js +++ b/src/reducers/generalReducer.js @@ -1,60 +1,100 @@ -import { VISIT, LANGUAGE, RENDERER, STATISTICS } from '../actions/types'; +import { + VISIT, + LANGUAGE, + RENDERER, + STATISTICS, + PLATFORM, +} from "../actions/types"; const initialLanguage = () => { - if (window.localStorage.getItem('locale')) { - return window.localStorage.getItem('locale'); + if (window.localStorage.getItem("locale")) { + return window.localStorage.getItem("locale"); } - if (navigator.language === 'de-DE'){ - return 'de_DE'; + if (navigator.language === "de-DE") { + return "de_DE"; } - return 'en_US'; + return "en_US"; +}; + +const initialPlatform = () => { + return getPlatform(); +}; + +const getPlatform = () => { + if (window.localStorage.getItem("platform")) { + return JSON.parse(window.localStorage.getItem("platform")); + } + var userAgent = window.navigator.userAgent, + platform = window.navigator.platform, + macosPlatforms = ["Macintosh", "MacIntel", "MacPPC", "Mac68K"], + windowsPlatforms = ["Win32", "Win64", "Windows", "WinCE"], + iosPlatforms = ["iPhone", "iPad", "iPod"], + os = null; + if (macosPlatforms.indexOf(platform) !== -1) { + os = false; + } else if (iosPlatforms.indexOf(platform) !== -1) { + os = true; + } else if (windowsPlatforms.indexOf(userAgent) !== -1) { + os = false; + } else if (/Android/.test(userAgent)) { + os = true; + } else if (!os && /Linux/.test(platform)) { + os = false; + } + return os; }; const initialRenderer = () => { - if (window.localStorage.getItem('renderer')) { - return window.localStorage.getItem('renderer'); + if (window.localStorage.getItem("renderer")) { + return window.localStorage.getItem("renderer"); } - return 'geras'; + return "geras"; }; const initialStatistics = () => { - if (window.localStorage.getItem('statistics')) { - return JSON.parse(window.localStorage.getItem('statistics')); + if (window.localStorage.getItem("statistics")) { + return JSON.parse(window.localStorage.getItem("statistics")); } return false; }; - const initialState = { pageVisits: 0, // detect if previous URL was language: initialLanguage(), renderer: initialRenderer(), - statistics: initialStatistics() + statistics: initialStatistics(), + platform: initialPlatform(), }; -export default function foo(state = initialState, action){ - switch(action.type){ +export default function foo(state = initialState, action) { + switch (action.type) { case VISIT: return { ...state, - pageVisits: state.pageVisits += 1 + pageVisits: (state.pageVisits += 1), }; case LANGUAGE: return { ...state, - language: action.payload + language: action.payload, + }; + case PLATFORM: + window.localStorage.setItem("platform", action.payload); + return { + ...state, + platform: action.payload, }; case RENDERER: - window.localStorage.setItem('renderer', action.payload); + window.localStorage.setItem("renderer", action.payload); return { ...state, - renderer: action.payload + renderer: action.payload, }; case STATISTICS: - window.localStorage.setItem('statistics', action.payload); + window.localStorage.setItem("statistics", action.payload); return { ...state, - statistics: action.payload + statistics: action.payload, }; default: return state;