From f8c65bfecce9e537863b6e55aee874eb35aa3db5 Mon Sep 17 00:00:00 2001 From: Juan Carlos Farah Date: Tue, 11 Jun 2019 19:35:15 +0200 Subject: [PATCH] feat: allow user to set app to developer mode closes #88 --- public/app/config/channels.js | 2 + public/app/config/config.js | 2 + public/app/config/messages.js | 8 +++ public/electron.js | 28 +++++++- src/actions/user.js | 67 +++++++++++++++++- src/components/Settings.js | 8 ++- src/components/common/DeveloperSwitch.js | 86 ++++++++++++++++++++++++ src/components/common/LanguageSelect.js | 12 +++- src/components/common/Loader.js | 2 +- src/config/channels.js | 2 + src/config/constants.js | 1 + src/config/messages.js | 6 ++ src/reducers/UserReducer.js | 25 +++++-- src/reducers/common.js | 11 +++ src/types/user.js | 4 ++ 15 files changed, 250 insertions(+), 14 deletions(-) create mode 100644 src/components/common/DeveloperSwitch.js create mode 100644 src/reducers/common.js diff --git a/public/app/config/channels.js b/public/app/config/channels.js index 7d3e1ccb..cf1e0c66 100644 --- a/public/app/config/channels.js +++ b/public/app/config/channels.js @@ -20,6 +20,8 @@ module.exports = { GET_USER_FOLDER_CHANNEL: 'user:folder:get', GET_LANGUAGE_CHANNEL: 'user:lang:get', SET_LANGUAGE_CHANNEL: 'user:lang:set', + SET_DEVELOPER_MODE_CHANNEL: 'user:developer-mode:set', + GET_DEVELOPER_MODE_CHANNEL: 'user:developer-mode:get', GET_APP_INSTANCE_RESOURCES_CHANNEL: 'app-instance-resources:get', POST_APP_INSTANCE_RESOURCE_CHANNEL: 'app-instance-resource:post', PATCH_APP_INSTANCE_RESOURCE_CHANNEL: 'app-instance-resource:patch', diff --git a/public/app/config/config.js b/public/app/config/config.js index 757c26d3..4f31ac96 100644 --- a/public/app/config/config.js +++ b/public/app/config/config.js @@ -34,8 +34,10 @@ const VAR_FOLDER = `${app.getPath('userData')}/var`; const DATABASE_PATH = `${VAR_FOLDER}/db.json`; const TEMPORARY_EXTRACT_FOLDER = 'tmp'; const DEFAULT_LANG = 'en'; +const DEFAULT_DEVELOPER_MODE = false; module.exports = { + DEFAULT_DEVELOPER_MODE, DOWNLOADABLE_MIME_TYPES, TEMPORARY_EXTRACT_FOLDER, RESOURCE, diff --git a/public/app/config/messages.js b/public/app/config/messages.js index 2f82f63e..24621cdd 100644 --- a/public/app/config/messages.js +++ b/public/app/config/messages.js @@ -28,8 +28,15 @@ const ERROR_GETTING_USER_FOLDER = 'There was an error getting your user folder.'; const ERROR_GETTING_LANGUAGE = 'There was an error getting the language.'; const ERROR_SETTING_LANGUAGE = 'There was an error setting the language.'; +const INVALID_SPACE_ID = 'Invalid space ID.'; +const ERROR_GETTING_DEVELOPER_MODE = + 'There was an error getting the developer mode'; +const ERROR_SETTING_DEVELOPER_MODE = + 'There was an error setting the developer mode'; module.exports = { + ERROR_GETTING_DEVELOPER_MODE, + ERROR_SETTING_DEVELOPER_MODE, ERROR_GETTING_LANGUAGE, ERROR_SETTING_LANGUAGE, ERROR_DOWNLOADING_MESSAGE, @@ -52,4 +59,5 @@ module.exports = { ERROR_GETTING_GEOLOCATION, ERROR_GETTING_SPACES_NEARBY, ERROR_GETTING_USER_FOLDER, + INVALID_SPACE_ID, }; diff --git a/public/electron.js b/public/electron.js index 4609e06e..fb84dd74 100644 --- a/public/electron.js +++ b/public/electron.js @@ -38,6 +38,7 @@ const { DATABASE_PATH, TEMPORARY_EXTRACT_FOLDER, DEFAULT_LANG, + DEFAULT_DEVELOPER_MODE, } = require('./app/config/config'); const { LOAD_SPACE_CHANNEL, @@ -62,6 +63,8 @@ const { POST_APP_INSTANCE_RESOURCE_CHANNEL, PATCH_APP_INSTANCE_RESOURCE_CHANNEL, GET_APP_INSTANCE_CHANNEL, + GET_DEVELOPER_MODE_CHANNEL, + SET_DEVELOPER_MODE_CHANNEL, } = require('./app/config/channels'); const { ERROR_SPACE_ALREADY_AVAILABLE, @@ -601,7 +604,7 @@ app.on('ready', async () => { } }); - // called when getting language + // called when setting language ipcMain.on(SET_LANGUAGE_CHANNEL, (event, lang) => { try { db.set('user.lang', lang).write(); @@ -612,6 +615,29 @@ app.on('ready', async () => { } }); + // called when getting developer mode + ipcMain.on(GET_DEVELOPER_MODE_CHANNEL, () => { + try { + const developerMode = + db.get('user.developerMode').value() || DEFAULT_DEVELOPER_MODE; + mainWindow.webContents.send(GET_DEVELOPER_MODE_CHANNEL, developerMode); + } catch (e) { + logger.error(e); + mainWindow.webContents.send(GET_DEVELOPER_MODE_CHANNEL, ERROR_GENERAL); + } + }); + + // called when setting developer mode + ipcMain.on(SET_DEVELOPER_MODE_CHANNEL, (event, developerMode) => { + try { + db.set('user.developerMode', developerMode).write(); + mainWindow.webContents.send(SET_DEVELOPER_MODE_CHANNEL, developerMode); + } catch (e) { + logger.error(e); + mainWindow.webContents.send(SET_DEVELOPER_MODE_CHANNEL, ERROR_GENERAL); + } + }); + // called when getting AppInstanceResources ipcMain.on(GET_APP_INSTANCE_RESOURCES_CHANNEL, (event, data = {}) => { const defaultResponse = []; diff --git a/src/actions/user.js b/src/actions/user.js index b8bf2e04..6673d52a 100644 --- a/src/actions/user.js +++ b/src/actions/user.js @@ -7,6 +7,11 @@ import { FLAG_GETTING_LANGUAGE, FLAG_SETTING_LANGUAGE, GET_LANGUAGE_SUCCEEDED, + SET_LANGUAGE_SUCCEEDED, + FLAG_GETTING_DEVELOPER_MODE, + FLAG_SETTING_DEVELOPER_MODE, + GET_DEVELOPER_MODE_SUCCEEDED, + SET_DEVELOPER_MODE_SUCCEEDED, } from '../types'; import { ERROR_GETTING_GEOLOCATION, @@ -14,11 +19,15 @@ import { ERROR_GETTING_USER_FOLDER, ERROR_MESSAGE_HEADER, ERROR_SETTING_LANGUAGE, + ERROR_SETTING_DEVELOPER_MODE, + ERROR_GETTING_DEVELOPER_MODE, } from '../config/messages'; import { GET_USER_FOLDER_CHANNEL, GET_LANGUAGE_CHANNEL, SET_LANGUAGE_CHANNEL, + GET_DEVELOPER_MODE_CHANNEL, + SET_DEVELOPER_MODE_CHANNEL, } from '../config/channels'; import { createFlag } from './common'; import { ERROR_GENERAL } from '../config/errors'; @@ -26,6 +35,8 @@ import { ERROR_GENERAL } from '../config/errors'; const flagGettingUserFolder = createFlag(FLAG_GETTING_USER_FOLDER); const flagGettingLanguage = createFlag(FLAG_GETTING_LANGUAGE); const flagSettingLanguage = createFlag(FLAG_SETTING_LANGUAGE); +const flagGettingDeveloperMode = createFlag(FLAG_GETTING_DEVELOPER_MODE); +const flagSettingDeveloperMode = createFlag(FLAG_SETTING_DEVELOPER_MODE); const getGeolocation = async () => async dispatch => { // only fetch location if online @@ -107,7 +118,7 @@ const setLanguage = async ({ lang }) => dispatch => { toastr.error(ERROR_MESSAGE_HEADER, ERROR_SETTING_LANGUAGE); } else { dispatch({ - type: GET_LANGUAGE_SUCCEEDED, + type: SET_LANGUAGE_SUCCEEDED, payload: language, }); } @@ -119,4 +130,56 @@ const setLanguage = async ({ lang }) => dispatch => { } }; -export { getUserFolder, getGeolocation, getLanguage, setLanguage }; +const getDeveloperMode = async () => dispatch => { + try { + dispatch(flagGettingDeveloperMode(true)); + window.ipcRenderer.send(GET_DEVELOPER_MODE_CHANNEL); + window.ipcRenderer.once( + GET_DEVELOPER_MODE_CHANNEL, + (event, developerMode) => { + if (developerMode === ERROR_GENERAL) { + toastr.error(ERROR_MESSAGE_HEADER, ERROR_GETTING_DEVELOPER_MODE); + } else { + dispatch({ + type: GET_DEVELOPER_MODE_SUCCEEDED, + payload: developerMode, + }); + } + dispatch(flagGettingDeveloperMode(false)); + } + ); + } catch (e) { + console.error(e); + toastr.error(ERROR_MESSAGE_HEADER, ERROR_GETTING_DEVELOPER_MODE); + } +}; + +const setDeveloperMode = async developerMode => dispatch => { + try { + dispatch(flagSettingDeveloperMode(true)); + window.ipcRenderer.send(SET_DEVELOPER_MODE_CHANNEL, developerMode); + window.ipcRenderer.once(SET_DEVELOPER_MODE_CHANNEL, (event, mode) => { + if (mode === ERROR_GENERAL) { + toastr.error(ERROR_MESSAGE_HEADER, ERROR_SETTING_DEVELOPER_MODE); + } else { + dispatch({ + type: SET_DEVELOPER_MODE_SUCCEEDED, + payload: mode, + }); + } + dispatch(flagSettingDeveloperMode(false)); + }); + } catch (e) { + console.error(e); + toastr.error(ERROR_MESSAGE_HEADER, ERROR_SETTING_DEVELOPER_MODE); + } +}; + +export { + getUserFolder, + getGeolocation, + getLanguage, + setLanguage, + getDeveloperMode, + setDeveloperMode, +}; diff --git a/src/components/Settings.js b/src/components/Settings.js index 59e438d3..40dc5635 100644 --- a/src/components/Settings.js +++ b/src/components/Settings.js @@ -13,10 +13,12 @@ import IconButton from '@material-ui/core/IconButton'; import MenuIcon from '@material-ui/icons/Menu'; import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import { FormGroup } from '@material-ui/core'; import { withTranslation } from 'react-i18next'; import Styles from '../Styles'; import MainMenu from './common/MainMenu'; import LanguageSelect from './common/LanguageSelect'; +import DeveloperSwitch from './common/DeveloperSwitch'; class Settings extends Component { state = { @@ -46,6 +48,7 @@ class Settings extends Component { render() { const { classes, theme, t } = this.props; const { open } = this.state; + return (
@@ -96,7 +99,10 @@ class Settings extends Component { {t('Settings')} - + + + +
); diff --git a/src/components/common/DeveloperSwitch.js b/src/components/common/DeveloperSwitch.js new file mode 100644 index 00000000..d7f4e679 --- /dev/null +++ b/src/components/common/DeveloperSwitch.js @@ -0,0 +1,86 @@ +import React, { Component } from 'react'; +import FormControl from '@material-ui/core/FormControl'; +import { withStyles } from '@material-ui/core/styles'; +import { withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import Switch from '@material-ui/core/Switch'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import { getDeveloperMode, setDeveloperMode } from '../../actions'; +import Loader from './Loader'; + +const styles = theme => ({ + formControl: { + margin: theme.spacing.unit, + minWidth: 120, + }, +}); + +class DeveloperSwitch extends Component { + static propTypes = { + developerMode: PropTypes.bool.isRequired, + activity: PropTypes.bool.isRequired, + t: PropTypes.func.isRequired, + dispatchGetDeveloperMode: PropTypes.func.isRequired, + dispatchSetDeveloperMode: PropTypes.func.isRequired, + classes: PropTypes.shape({ + formControl: PropTypes.string.isRequired, + }).isRequired, + }; + + constructor(props) { + super(props); + const { dispatchGetDeveloperMode } = this.props; + dispatchGetDeveloperMode(); + } + + handleChange = async ({ target }) => { + const { dispatchSetDeveloperMode } = this.props; + const { checked } = target; + dispatchSetDeveloperMode(checked); + }; + + render() { + const { classes, t, developerMode, activity } = this.props; + + if (activity) { + return ; + } + + const control = ( + + ); + + return ( + + + + ); + } +} + +const mapStateToProps = ({ User }) => ({ + developerMode: User.getIn(['current', 'developerMode']), + activity: User.getIn(['current', 'activity']).size, +}); + +const mapDispatchToProps = { + dispatchGetDeveloperMode: getDeveloperMode, + dispatchSetDeveloperMode: setDeveloperMode, +}; + +const ConnectedComponent = connect( + mapStateToProps, + mapDispatchToProps +)(DeveloperSwitch); + +const StyledComponent = withStyles(styles)(ConnectedComponent); + +const TranslatedComponent = withTranslation()(StyledComponent); + +export default TranslatedComponent; diff --git a/src/components/common/LanguageSelect.js b/src/components/common/LanguageSelect.js index 8366647b..83b1be9e 100644 --- a/src/components/common/LanguageSelect.js +++ b/src/components/common/LanguageSelect.js @@ -9,6 +9,7 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { langs } from '../../config/i18n'; import { getLanguage, setLanguage } from '../../actions'; +import Loader from './Loader'; const styles = theme => ({ formControl: { @@ -19,6 +20,7 @@ const styles = theme => ({ class LanguageSelect extends Component { static propTypes = { + activity: PropTypes.bool.isRequired, i18n: PropTypes.shape({ changeLanguage: PropTypes.func.isRequired, }).isRequired, @@ -27,7 +29,7 @@ class LanguageSelect extends Component { dispatchSetLanguage: PropTypes.func.isRequired, dispatchGetLanguage: PropTypes.func.isRequired, classes: PropTypes.shape({ - formControl: PropTypes.object.isRequired, + formControl: PropTypes.string.isRequired, }).isRequired, }; @@ -52,7 +54,11 @@ class LanguageSelect extends Component { )); render() { - const { classes, t, lang } = this.props; + const { classes, t, lang, activity } = this.props; + + if (activity) { + return ; + } return ( @@ -74,7 +80,7 @@ class LanguageSelect extends Component { const mapStateToProps = ({ User }) => ({ lang: User.getIn(['current', 'lang']), - activity: User.getIn(['current', 'activity']), + activity: User.getIn(['current', 'activity']).size, }); const mapDispatchToProps = { diff --git a/src/components/common/Loader.js b/src/components/common/Loader.js index e3053b5e..5292e359 100644 --- a/src/components/common/Loader.js +++ b/src/components/common/Loader.js @@ -4,7 +4,7 @@ import ReactLoading from 'react-loading'; const Loader = ({ type }) => (
- +
); diff --git a/src/config/channels.js b/src/config/channels.js index 7d3e1ccb..cf1e0c66 100644 --- a/src/config/channels.js +++ b/src/config/channels.js @@ -20,6 +20,8 @@ module.exports = { GET_USER_FOLDER_CHANNEL: 'user:folder:get', GET_LANGUAGE_CHANNEL: 'user:lang:get', SET_LANGUAGE_CHANNEL: 'user:lang:set', + SET_DEVELOPER_MODE_CHANNEL: 'user:developer-mode:set', + GET_DEVELOPER_MODE_CHANNEL: 'user:developer-mode:get', GET_APP_INSTANCE_RESOURCES_CHANNEL: 'app-instance-resources:get', POST_APP_INSTANCE_RESOURCE_CHANNEL: 'app-instance-resource:post', PATCH_APP_INSTANCE_RESOURCE_CHANNEL: 'app-instance-resource:patch', diff --git a/src/config/constants.js b/src/config/constants.js index abb84f06..23ab2110 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -7,6 +7,7 @@ export const APPLICATION = 'Application'; export const IFRAME = 'application/octet-stream'; export const DEFAULT_RADIUS = 50; export const DEFAULT_LANGUAGE = 'en'; +export const DEFAULT_DEVELOPER_MODE = false; export const SHORT_ID_LENGTH = 6; export const SMART_GATEWAY_HOST = 'gateway.golabz.eu'; export const SMART_GATEWAY_QUERY_STRING_DIVIDER = '#'; diff --git a/src/config/messages.js b/src/config/messages.js index 78cb00ee..24621cdd 100644 --- a/src/config/messages.js +++ b/src/config/messages.js @@ -29,8 +29,14 @@ const ERROR_GETTING_USER_FOLDER = const ERROR_GETTING_LANGUAGE = 'There was an error getting the language.'; const ERROR_SETTING_LANGUAGE = 'There was an error setting the language.'; const INVALID_SPACE_ID = 'Invalid space ID.'; +const ERROR_GETTING_DEVELOPER_MODE = + 'There was an error getting the developer mode'; +const ERROR_SETTING_DEVELOPER_MODE = + 'There was an error setting the developer mode'; module.exports = { + ERROR_GETTING_DEVELOPER_MODE, + ERROR_SETTING_DEVELOPER_MODE, ERROR_GETTING_LANGUAGE, ERROR_SETTING_LANGUAGE, ERROR_DOWNLOADING_MESSAGE, diff --git a/src/reducers/UserReducer.js b/src/reducers/UserReducer.js index 051dfec7..4fef1acd 100644 --- a/src/reducers/UserReducer.js +++ b/src/reducers/UserReducer.js @@ -1,4 +1,4 @@ -import { Map } from 'immutable'; +import { Map, List } from 'immutable'; import { FLAG_GETTING_USER_FOLDER, FLAG_SETTING_LANGUAGE, @@ -7,15 +7,21 @@ import { GET_USER_FOLDER_SUCCEEDED, SET_LANGUAGE_SUCCEEDED, GET_LANGUAGE_SUCCEEDED, + GET_DEVELOPER_MODE_SUCCEEDED, + SET_DEVELOPER_MODE_SUCCEEDED, + FLAG_SETTING_DEVELOPER_MODE, + FLAG_GETTING_DEVELOPER_MODE, } from '../types'; -import { DEFAULT_LANGUAGE } from '../config/constants'; +import { DEFAULT_DEVELOPER_MODE, DEFAULT_LANGUAGE } from '../config/constants'; +import { updateActivityList } from './common'; const INITIAL_STATE = Map({ current: Map({ geolocation: Map(), folder: null, - activity: false, + activity: List(), lang: DEFAULT_LANGUAGE, + developerMode: DEFAULT_DEVELOPER_MODE, }), }); @@ -24,15 +30,22 @@ export default (state = INITIAL_STATE, { type, payload }) => { case FLAG_SETTING_LANGUAGE: case FLAG_GETTING_LANGUAGE: case FLAG_GETTING_USER_FOLDER: - return state.setIn(['current', 'activity'], payload); + case FLAG_SETTING_DEVELOPER_MODE: + case FLAG_GETTING_DEVELOPER_MODE: + return state.updateIn( + ['current', 'activity'], + updateActivityList(payload) + ); case GET_USER_FOLDER_SUCCEEDED: return state.setIn(['current', 'folder'], payload); case GET_GEOLOCATION_SUCCEEDED: return state.setIn(['current', 'geolocation'], Map(payload)); - case SET_LANGUAGE_SUCCEEDED: - return state.setIn(['current', 'lang'], payload); case GET_LANGUAGE_SUCCEEDED: + case SET_LANGUAGE_SUCCEEDED: return state.setIn(['current', 'lang'], payload); + case GET_DEVELOPER_MODE_SUCCEEDED: + case SET_DEVELOPER_MODE_SUCCEEDED: + return state.setIn(['current', 'developerMode'], payload); default: return state; } diff --git a/src/reducers/common.js b/src/reducers/common.js new file mode 100644 index 00000000..8bdc43f0 --- /dev/null +++ b/src/reducers/common.js @@ -0,0 +1,11 @@ +const updateActivityList = flag => { + if (flag) { + return list => list.push(flag); + } + return list => list.pop(); +}; + +export { + // eslint-disable-next-line import/prefer-default-export + updateActivityList, +}; diff --git a/src/types/user.js b/src/types/user.js index c3edb349..fc8bb0a8 100644 --- a/src/types/user.js +++ b/src/types/user.js @@ -5,3 +5,7 @@ export const GET_LANGUAGE_SUCCEEDED = 'GET_LANGUAGE_SUCCEEDED'; export const SET_LANGUAGE_SUCCEEDED = 'SET_LANGUAGE_SUCCEEDED'; export const FLAG_GETTING_LANGUAGE = 'FLAG_GETTING_LANGUAGE'; export const FLAG_SETTING_LANGUAGE = 'FLAG_SETTING_LANGUAGE'; +export const FLAG_GETTING_DEVELOPER_MODE = 'FLAG_GETTING_DEVELOPER_MODE'; +export const FLAG_SETTING_DEVELOPER_MODE = 'FLAG_SETTING_DEVELOPER_MODE'; +export const GET_DEVELOPER_MODE_SUCCEEDED = 'GET_DEVELOPER_MODE_SUCCEEDED'; +export const SET_DEVELOPER_MODE_SUCCEEDED = 'SET_DEVELOPER_MODE_SUCCEEDED';