diff --git a/public/app/config/channels.js b/public/app/config/channels.js
index d0ca0199..ad3dbe28 100644
--- a/public/app/config/channels.js
+++ b/public/app/config/channels.js
@@ -52,6 +52,8 @@ module.exports = {
SET_USER_MODE_CHANNEL: 'user:mode:set',
SET_SPACE_AS_FAVORITE_CHANNEL: 'user:set-space-favorite:set',
SET_SPACE_AS_RECENT_CHANNEL: 'user:set-space-recent:set',
+ SET_ACTION_ACCESSIBILITY_CHANNEL: 'user:action:accessibility:set',
+ SET_ACTION_ENABLED_CHANNEL: 'user:action:enabled:set',
GET_CLASSROOMS_CHANNEL: 'classrooms:get',
ADD_CLASSROOM_CHANNEL: 'classroom:add',
DELETE_CLASSROOM_CHANNEL: 'classroom:delete',
diff --git a/public/app/config/config.js b/public/app/config/config.js
index 4330fdc8..b94be8fd 100644
--- a/public/app/config/config.js
+++ b/public/app/config/config.js
@@ -52,6 +52,8 @@ const DEFAULT_PROTOCOL = 'https';
const DEFAULT_LOGGING_LEVEL = 'info';
const AUTHENTICATED = 'authenticated';
const DEFAULT_AUTHENTICATION = false;
+const DEFAULT_ACTION_ACCESSIBILITY = false;
+const DEFAULT_ACTION_ENABLED = true;
const DEFAULT_USER = {
geolocation: null,
@@ -61,6 +63,8 @@ const DEFAULT_USER = {
geolocationEnabled: DEFAULT_GEOLOCATION_ENABLED,
syncMode: DEFAULT_SYNC_MODE,
userMode: DEFAULT_USER_MODE,
+ actionAccessibility: DEFAULT_ACTION_ACCESSIBILITY,
+ actionEnabled: DEFAULT_ACTION_ENABLED,
},
favoriteSpaces: [],
recentSpaces: [],
diff --git a/public/app/config/messages.js b/public/app/config/messages.js
index a1337f0d..7fd7d52d 100644
--- a/public/app/config/messages.js
+++ b/public/app/config/messages.js
@@ -99,6 +99,10 @@ const ERROR_INVALID_USERNAME_MESSAGE = 'This username is invalid';
const ERROR_NO_USER_TO_DELETE_MESSAGE = 'There is no user to delete';
const ERROR_GETTING_SPACE_IN_CLASSROOM_MESSAGE =
'There was an error getting the space in this classroom';
+const ERROR_SETTING_ACTION_ACCESSIBILITY =
+ 'There was an error setting the action accessibility';
+const ERROR_SETTING_ACTION_ENABLED =
+ 'There was an error setting the action enabled';
module.exports = {
ERROR_GETTING_DEVELOPER_MODE,
@@ -167,4 +171,6 @@ module.exports = {
ERROR_NO_USER_TO_DELETE_MESSAGE,
ERROR_GETTING_SPACE_IN_CLASSROOM_MESSAGE,
ERROR_INVALID_USERNAME_MESSAGE,
+ ERROR_SETTING_ACTION_ACCESSIBILITY,
+ ERROR_SETTING_ACTION_ENABLED,
};
diff --git a/public/app/listeners/index.js b/public/app/listeners/index.js
index 063ac27c..8ebacf68 100644
--- a/public/app/listeners/index.js
+++ b/public/app/listeners/index.js
@@ -47,6 +47,8 @@ const deleteUsersInClassroom = require('./deleteUsersInClassroom');
const editUserInClassroom = require('./editUserInClassroom');
const getSpaceInClassroom = require('./getSpaceInClassroom');
const loadSpaceInClassroom = require('./loadSpaceInClassroom');
+const setActionAccessibility = require('./setActionAccessibility');
+const setActionEnabled = require('./setActionEnabled');
module.exports = {
loadSpace,
@@ -97,4 +99,6 @@ module.exports = {
editUserInClassroom,
getSpaceInClassroom,
loadSpaceInClassroom,
+ setActionAccessibility,
+ setActionEnabled,
};
diff --git a/public/app/listeners/setActionAccessibility.js b/public/app/listeners/setActionAccessibility.js
new file mode 100644
index 00000000..0d022b84
--- /dev/null
+++ b/public/app/listeners/setActionAccessibility.js
@@ -0,0 +1,24 @@
+const { SET_ACTION_ACCESSIBILITY_CHANNEL } = require('../config/channels');
+const { ERROR_GENERAL } = require('../config/errors');
+const logger = require('../logger');
+
+const setActionAccessibility = (mainWindow, db) => async (
+ event,
+ actionAccessibility
+) => {
+ try {
+ db.set('user.settings.actionAccessibility', actionAccessibility).write();
+ mainWindow.webContents.send(
+ SET_ACTION_ACCESSIBILITY_CHANNEL,
+ actionAccessibility
+ );
+ } catch (e) {
+ logger.error(e);
+ mainWindow.webContents.send(
+ SET_ACTION_ACCESSIBILITY_CHANNEL,
+ ERROR_GENERAL
+ );
+ }
+};
+
+module.exports = setActionAccessibility;
diff --git a/public/app/listeners/setActionEnabled.js b/public/app/listeners/setActionEnabled.js
new file mode 100644
index 00000000..9b9fcdd2
--- /dev/null
+++ b/public/app/listeners/setActionEnabled.js
@@ -0,0 +1,19 @@
+const { SET_ACTION_ENABLED_CHANNEL } = require('../config/channels');
+const { ERROR_GENERAL } = require('../config/errors');
+const { DEFAULT_ACTION_ENABLED } = require('../config/config');
+const logger = require('../logger');
+
+const setActionEnabled = (mainWindow, db) => async (event, actionEnabled) => {
+ try {
+ db.set('user.settings.actionEnabled', actionEnabled).write();
+ mainWindow.webContents.send(
+ SET_ACTION_ENABLED_CHANNEL,
+ actionEnabled || DEFAULT_ACTION_ENABLED
+ );
+ } catch (e) {
+ logger.error(e);
+ mainWindow.webContents.send(SET_ACTION_ENABLED_CHANNEL, ERROR_GENERAL);
+ }
+};
+
+module.exports = setActionEnabled;
diff --git a/public/electron.js b/public/electron.js
index 11b4b58f..3a6377bc 100644
--- a/public/electron.js
+++ b/public/electron.js
@@ -72,6 +72,8 @@ const {
EDIT_USER_IN_CLASSROOM_CHANNEL,
GET_SPACE_IN_CLASSROOM_CHANNEL,
LOAD_SPACE_IN_CLASSROOM_CHANNEL,
+ SET_ACTION_ACCESSIBILITY_CHANNEL,
+ SET_ACTION_ENABLED_CHANNEL,
} = require('./app/config/channels');
const env = require('./env.json');
const {
@@ -122,6 +124,8 @@ const {
editUserInClassroom,
getSpaceInClassroom,
loadSpaceInClassroom,
+ setActionAccessibility,
+ setActionEnabled,
} = require('./app/listeners');
const isMac = require('./app/utils/isMac');
@@ -463,6 +467,15 @@ app.on('ready', async () => {
setGeolocationEnabled(mainWindow, db)
);
+ // called when setting action accessibility
+ ipcMain.on(
+ SET_ACTION_ACCESSIBILITY_CHANNEL,
+ setActionAccessibility(mainWindow, db)
+ );
+
+ // called when setting action enabled
+ ipcMain.on(SET_ACTION_ENABLED_CHANNEL, setActionEnabled(mainWindow, db));
+
// called when getting student mode
ipcMain.on(GET_USER_MODE_CHANNEL, getUserMode(mainWindow, db));
diff --git a/src/actions/user.js b/src/actions/user.js
index 507b05d0..62b2961f 100644
--- a/src/actions/user.js
+++ b/src/actions/user.js
@@ -28,6 +28,10 @@ import {
SET_SPACE_AS_FAVORITE_SUCCEEDED,
FLAG_SETTING_SPACE_AS_RECENT,
SET_SPACE_AS_RECENT_SPACES_SUCCEEDED,
+ FLAG_SETTING_ACTION_ACCESSIBILITY,
+ SET_ACTION_ACCESSIBILITY_SUCCEEDED,
+ FLAG_SETTING_ACTION_ENABLED,
+ SET_ACTION_ENABLED_SUCCEEDED,
} from '../types';
import {
ERROR_GETTING_GEOLOCATION,
@@ -45,6 +49,8 @@ import {
ERROR_SETTING_USER_MODE,
ERROR_SETTING_SPACE_AS_FAVORITE,
ERROR_SETTING_SPACE_AS_RECENT,
+ ERROR_SETTING_ACTION_ACCESSIBILITY,
+ ERROR_SETTING_ACTION_ENABLED,
} from '../config/messages';
import {
GET_USER_FOLDER_CHANNEL,
@@ -60,6 +66,8 @@ import {
SET_USER_MODE_CHANNEL,
SET_SPACE_AS_FAVORITE_CHANNEL,
SET_SPACE_AS_RECENT_CHANNEL,
+ SET_ACTION_ACCESSIBILITY_CHANNEL,
+ SET_ACTION_ENABLED_CHANNEL,
} from '../config/channels';
import { createFlag } from './common';
import { ERROR_GENERAL } from '../config/errors';
@@ -81,6 +89,10 @@ const flagGettingUserMode = createFlag(FLAG_GETTING_USER_MODE);
const flagSettingUserMode = createFlag(FLAG_SETTING_USER_MODE);
const flagSettingSpaceAsFavorite = createFlag(FLAG_SETTING_SPACE_AS_FAVORITE);
const flagSettingSpaceAsRecent = createFlag(FLAG_SETTING_SPACE_AS_RECENT);
+const flagSettingActionAccessibility = createFlag(
+ FLAG_SETTING_ACTION_ACCESSIBILITY
+);
+const flagSettingActionEnabled = createFlag(FLAG_SETTING_ACTION_ENABLED);
const getGeolocation = async () => async dispatch => {
// only fetch location if online
@@ -399,6 +411,54 @@ const setSpaceAsRecent = payload => dispatch => {
}
};
+const setActionAccessibility = payload => dispatch => {
+ try {
+ dispatch(flagSettingActionAccessibility(true));
+ window.ipcRenderer.send(SET_ACTION_ACCESSIBILITY_CHANNEL, payload);
+ window.ipcRenderer.once(
+ SET_ACTION_ACCESSIBILITY_CHANNEL,
+ (event, response) => {
+ if (response === ERROR_GENERAL) {
+ toastr.error(
+ ERROR_MESSAGE_HEADER,
+ ERROR_SETTING_ACTION_ACCESSIBILITY
+ );
+ } else {
+ dispatch({
+ type: SET_ACTION_ACCESSIBILITY_SUCCEEDED,
+ payload,
+ });
+ }
+ dispatch(flagSettingActionAccessibility(false));
+ }
+ );
+ } catch (e) {
+ console.error(e);
+ toastr.error(ERROR_MESSAGE_HEADER, ERROR_SETTING_ACTION_ACCESSIBILITY);
+ }
+};
+
+const setActionEnabled = payload => dispatch => {
+ try {
+ dispatch(flagSettingActionEnabled(true));
+ window.ipcRenderer.send(SET_ACTION_ENABLED_CHANNEL, payload);
+ window.ipcRenderer.once(SET_ACTION_ENABLED_CHANNEL, (event, response) => {
+ if (response === ERROR_GENERAL) {
+ toastr.error(ERROR_MESSAGE_HEADER, ERROR_SETTING_ACTION_ENABLED);
+ } else {
+ dispatch({
+ type: SET_ACTION_ENABLED_SUCCEEDED,
+ payload,
+ });
+ }
+ dispatch(flagSettingActionEnabled(false));
+ });
+ } catch (e) {
+ console.error(e);
+ toastr.error(ERROR_MESSAGE_HEADER, ERROR_SETTING_ACTION_ENABLED);
+ }
+};
+
export {
getUserFolder,
getGeolocation,
@@ -414,4 +474,6 @@ export {
setUserMode,
setSpaceAsFavorite,
setSpaceAsRecent,
+ setActionAccessibility,
+ setActionEnabled,
};
diff --git a/src/components/Settings.js b/src/components/Settings.js
index f7cf08fe..444fe6bc 100644
--- a/src/components/Settings.js
+++ b/src/components/Settings.js
@@ -6,6 +6,7 @@ import Typography from '@material-ui/core/Typography';
import { connect } from 'react-redux';
import { FormGroup } from '@material-ui/core';
import { withTranslation } from 'react-i18next';
+import Divider from '@material-ui/core/Divider';
import Styles from '../Styles';
import LanguageSelect from './common/LanguageSelect';
import DeveloperSwitch from './common/DeveloperSwitch';
@@ -14,8 +15,17 @@ import Main from './common/Main';
import { SETTINGS_MAIN_ID } from '../config/selectors';
import SyncAdvancedSwitch from './space/sync/SyncAdvancedSwitch';
import StudentModeSwitch from './common/StudentModeSwitch';
+import ActionEnabledSwitch from './common/ActionEnabledSwitch';
+import ActionAccessibilitySwitch from './common/ActionAccessibilitySwitch';
import { USER_MODES } from '../config/constants';
+const styles = theme => ({
+ ...Styles(theme),
+ divider: {
+ margin: theme.spacing(2, 0),
+ },
+});
+
// eslint-disable-next-line react/prefer-stateless-function
export class Settings extends Component {
static propTypes = {
@@ -27,6 +37,7 @@ export class Settings extends Component {
content: PropTypes.string.isRequired,
contentShift: PropTypes.string.isRequired,
settings: PropTypes.string.isRequired,
+ divider: PropTypes.string.isRequired,
}).isRequired,
i18n: PropTypes.shape({
changeLanguage: PropTypes.func.isRequired,
@@ -40,7 +51,7 @@ export class Settings extends Component {
return (
-
+
{t('Settings')}
@@ -50,6 +61,14 @@ export class Settings extends Component {
{userMode === USER_MODES.TEACHER ? : null}
+
+
+ {t('Actions')}
+
+
+
+
+
);
@@ -62,7 +81,7 @@ const mapStateToProps = ({ authentication }) => ({
const ConnectedComponent = connect(mapStateToProps, null)(Settings);
-const StyledComponent = withStyles(Styles, { withTheme: true })(
+const StyledComponent = withStyles(styles, { withTheme: true })(
ConnectedComponent
);
diff --git a/src/components/Settings.test.js b/src/components/Settings.test.js
index c280a3ee..486b18ef 100644
--- a/src/components/Settings.test.js
+++ b/src/components/Settings.test.js
@@ -50,7 +50,7 @@ describe('', () => {
});
it('renders one component with text Settings', () => {
- const typography = wrapper.find(Typography);
+ const typography = wrapper.find(Typography).find({ variant: 'h4' });
expect(typography).toHaveLength(1);
expect(typography.contains('Settings')).toBeTruthy();
});
diff --git a/src/components/__snapshots__/Settings.test.js.snap b/src/components/__snapshots__/Settings.test.js.snap
index 4ce88813..2d0bb7ca 100644
--- a/src/components/__snapshots__/Settings.test.js.snap
+++ b/src/components/__snapshots__/Settings.test.js.snap
@@ -9,7 +9,7 @@ exports[` renders correctly 1`] = `
>
Settings
@@ -20,6 +20,25 @@ exports[` renders correctly 1`] = `
+
+
+ Actions
+
+
+
+
+
`;
diff --git a/src/components/common/ActionAccessibilitySwitch.js b/src/components/common/ActionAccessibilitySwitch.js
new file mode 100644
index 00000000..1a901a6e
--- /dev/null
+++ b/src/components/common/ActionAccessibilitySwitch.js
@@ -0,0 +1,87 @@
+import React, { Component } from 'react';
+import FormControl from '@material-ui/core/FormControl';
+import FormControlLabel from '@material-ui/core/FormControlLabel';
+import { withStyles } from '@material-ui/core/styles';
+import { withTranslation } from 'react-i18next';
+import Switch from '@material-ui/core/Switch';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import { setActionAccessibility } from '../../actions';
+import Loader from './Loader';
+import {
+ DEFAULT_ACTION_ACCESSIBILITY,
+ FORM_CONTROL_MIN_WIDTH,
+} from '../../config/constants';
+
+const styles = theme => ({
+ formControl: {
+ margin: theme.spacing(),
+ minWidth: FORM_CONTROL_MIN_WIDTH,
+ },
+});
+
+export class ActionAccessibilitySwitch extends Component {
+ static propTypes = {
+ activity: PropTypes.bool.isRequired,
+ actionAccessibility: PropTypes.bool.isRequired,
+ t: PropTypes.func.isRequired,
+ dispatchSetActionAccessibility: PropTypes.func.isRequired,
+ classes: PropTypes.shape({
+ formControl: PropTypes.string.isRequired,
+ }).isRequired,
+ };
+
+ handleChange = async ({ target }) => {
+ const { dispatchSetActionAccessibility } = this.props;
+ const { checked } = target;
+ dispatchSetActionAccessibility(checked);
+ };
+
+ render() {
+ const { t, activity, actionAccessibility, classes } = this.props;
+
+ if (activity) {
+ return ;
+ }
+
+ const control = (
+
+ );
+
+ return (
+
+
+
+ );
+ }
+}
+
+const mapStateToProps = ({ authentication }) => ({
+ activity: Boolean(authentication.getIn(['current', 'activity']).size),
+ actionAccessibility:
+ authentication.getIn(['user', 'settings', 'actionAccessibility']) ||
+ DEFAULT_ACTION_ACCESSIBILITY,
+});
+
+const mapDispatchToProps = {
+ dispatchSetActionAccessibility: setActionAccessibility,
+};
+
+const ConnectedComponent = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(ActionAccessibilitySwitch);
+
+const StyledComponent = withStyles(styles)(ConnectedComponent);
+
+const TranslatedComponent = withTranslation()(StyledComponent);
+
+export default TranslatedComponent;
diff --git a/src/components/common/ActionEnabledSwitch.js b/src/components/common/ActionEnabledSwitch.js
new file mode 100644
index 00000000..7516c6f0
--- /dev/null
+++ b/src/components/common/ActionEnabledSwitch.js
@@ -0,0 +1,84 @@
+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 { setActionEnabled } from '../../actions';
+import Loader from './Loader';
+import {
+ FORM_CONTROL_MIN_WIDTH,
+ DEFAULT_ACTION_ENABLED,
+} from '../../config/constants';
+
+const styles = theme => ({
+ formControl: {
+ margin: theme.spacing(),
+ minWidth: FORM_CONTROL_MIN_WIDTH,
+ },
+});
+
+export class ActionEnabledSwitch extends Component {
+ static propTypes = {
+ actionEnabled: PropTypes.bool.isRequired,
+ activity: PropTypes.bool.isRequired,
+ t: PropTypes.func.isRequired,
+ dispatchSetActionEnabled: PropTypes.func.isRequired,
+ classes: PropTypes.shape({
+ formControl: PropTypes.string.isRequired,
+ }).isRequired,
+ };
+
+ handleChange = async ({ target }) => {
+ const { dispatchSetActionEnabled } = this.props;
+ const { checked } = target;
+ dispatchSetActionEnabled(checked);
+ };
+
+ render() {
+ const { classes, t, actionEnabled, activity } = this.props;
+
+ if (activity) {
+ return ;
+ }
+
+ const control = (
+
+ );
+
+ return (
+
+
+
+ );
+ }
+}
+
+const mapStateToProps = ({ authentication }) => ({
+ actionEnabled:
+ authentication.getIn(['user', 'settings', 'actionEnabled']) ||
+ DEFAULT_ACTION_ENABLED,
+ activity: Boolean(authentication.getIn(['current', 'activity']).size),
+});
+
+const mapDispatchToProps = {
+ dispatchSetActionEnabled: setActionEnabled,
+};
+
+const ConnectedComponent = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(ActionEnabledSwitch);
+
+const StyledComponent = withStyles(styles)(ConnectedComponent);
+
+const TranslatedComponent = withTranslation()(StyledComponent);
+
+export default TranslatedComponent;
diff --git a/src/components/dashboard/ActionEditor.js b/src/components/dashboard/ActionEditor.js
index 59c977cf..c0705696 100644
--- a/src/components/dashboard/ActionEditor.js
+++ b/src/components/dashboard/ActionEditor.js
@@ -1,65 +1,38 @@
import React, { Component } from 'react';
import _ from 'lodash';
-import { connect } from 'react-redux';
import ReactJson from 'react-json-view';
import PropTypes from 'prop-types';
import Typography from '@material-ui/core/Typography';
import { withTranslation } from 'react-i18next';
import { withStyles } from '@material-ui/core';
-import { getDatabase } from '../../actions';
import Loader from '../common/Loader';
import Styles from '../../Styles';
-import {
- SELECT_ALL_SPACES_ID,
- SELECT_ALL_USERS_ID,
-} from '../../config/constants';
+// eslint-disable-next-line react/prefer-stateless-function
export class ActionEditor extends Component {
static propTypes = {
t: PropTypes.func.isRequired,
classes: PropTypes.shape({
button: PropTypes.string.isRequired,
}).isRequired,
- dispatchGetDatabase: PropTypes.func.isRequired,
- database: PropTypes.shape({
- user: PropTypes.object,
- spaces: PropTypes.arrayOf(PropTypes.object),
- actions: PropTypes.arrayOf(PropTypes.object),
- }),
- spaceId: PropTypes.string,
- userId: PropTypes.string,
+ actions: PropTypes.shape(PropTypes.object),
};
static defaultProps = {
- database: {},
- spaceId: SELECT_ALL_SPACES_ID,
- userId: SELECT_ALL_USERS_ID,
+ actions: [],
};
- componentDidMount() {
- const { dispatchGetDatabase } = this.props;
- dispatchGetDatabase();
- }
-
render() {
- const { database, t, spaceId, userId } = this.props;
+ const { actions, t } = this.props;
- if (!database) {
+ if (!actions) {
return ;
}
- if (_.isEmpty(database)) {
+ if (_.isEmpty(actions)) {
return
{t('The database is empty.')}
;
}
- let { actions } = database;
- if (userId !== SELECT_ALL_USERS_ID) {
- actions = actions.filter(({ user }) => user === userId);
- }
- if (spaceId !== SELECT_ALL_SPACES_ID) {
- actions = actions.filter(({ spaceId: id }) => id === spaceId);
- }
-
return (
{t('Action Database')}
@@ -69,19 +42,7 @@ export class ActionEditor extends Component {
}
}
-const mapStateToProps = ({ Developer }) => ({
- database: Developer.get('database'),
-});
-
-const mapDispatchToProps = {
- dispatchGetDatabase: getDatabase,
-};
-
const StyledComponent = withStyles(Styles, { withTheme: true })(ActionEditor);
const TranslatedComponent = withTranslation()(StyledComponent);
-const ConnectedComponent = connect(
- mapStateToProps,
- mapDispatchToProps
-)(TranslatedComponent);
-export default ConnectedComponent;
+export default TranslatedComponent;
diff --git a/src/components/dashboard/Dashboard.js b/src/components/dashboard/Dashboard.js
index 21678797..1ef79509 100644
--- a/src/components/dashboard/Dashboard.js
+++ b/src/components/dashboard/Dashboard.js
@@ -157,31 +157,8 @@ export class Dashboard extends Component {
);
};
- renderActionWidgets = () => {
- const { database, t, classes, userMode, userId } = this.props;
- const { spaceId, filteredUserId } = this.state;
-
- let filteredActions = database.actions;
-
- // filter action per user if userMode is student or with filter
- if (userMode === USER_MODES.STUDENT) {
- filteredActions = filteredActions.filter(({ user }) => user === userId);
- } else if (filteredUserId !== SELECT_ALL_USERS_ID) {
- filteredActions = filteredActions.filter(
- ({ user }) => user === filteredUserId
- );
- }
-
- // filter action per space
- if (spaceId !== SELECT_ALL_SPACES_ID) {
- filteredActions = filteredActions.filter(
- ({ spaceId: id }) => id === spaceId
- );
- }
-
- if (_.isEmpty(filteredActions)) {
- return
{t('No action has been recorded.')}
;
- }
+ renderActionWidgets = filteredActions => {
+ const { database, classes } = this.props;
return (
{
+ const { t, database, userMode, userId } = this.props;
+ const { spaceId, filteredUserId } = this.state;
+
+ let filteredActions = database.actions;
+ const { users } = database;
+ const isStudent = userMode === USER_MODES.STUDENT;
+
+ filteredActions = filteredActions.filter(({ user }) => {
+ const isOwnAction = user === userId;
+ const actionUser = users.find(({ id }) => id === user);
+ const isAccessible =
+ actionUser && actionUser.settings.actionAccessibility;
+ const filteredUserSelected = filteredUserId !== SELECT_ALL_USERS_ID;
+
+ // filter action per user if userMode is student
+ return (
+ (isStudent && isOwnAction) ||
+ // filter actions per selected user if selected
+ // only teachers have access to user filter
+ // actions are displayed either if the user set action as accessible or
+ // actions are own by the current user
+ (!isStudent &&
+ (!filteredUserSelected || user === filteredUserId) &&
+ (isAccessible || isOwnAction))
+ );
+ });
+
+ // filter action per space
+ if (spaceId !== SELECT_ALL_SPACES_ID) {
+ filteredActions = filteredActions.filter(
+ ({ spaceId: id }) => id === spaceId
+ );
+ }
+
+ return filteredActions.length ? (
+ <>
+ {this.renderActionWidgets(filteredActions)}
+
+ >
+ ) : (
+ {t('No action has been recorded.')}
+ );
+ };
+
render() {
const { classes, t, database } = this.props;
- const { spaceId, filteredUserId } = this.state;
if (!database) {
return ;
@@ -242,9 +263,7 @@ export class Dashboard extends Component {
- {this.renderActionWidgets()}
-
-
+ {this.renderContent()}
);
diff --git a/src/components/phase/PhaseApp.js b/src/components/phase/PhaseApp.js
index c18e0788..d58bd56d 100644
--- a/src/components/phase/PhaseApp.js
+++ b/src/components/phase/PhaseApp.js
@@ -25,6 +25,7 @@ import {
import {
DEFAULT_LANGUAGE,
SMART_GATEWAY_QUERY_STRING_DIVIDER,
+ DEFAULT_ACTION_ENABLED,
} from '../../config/constants';
import { isSmartGatewayUrl } from '../../utils/url';
import { getHeight, setHeight } from '../../actions/layout';
@@ -60,6 +61,7 @@ class PhaseApp extends Component {
}),
geolocation: PropTypes.instanceOf(Map),
geolocationEnabled: PropTypes.bool.isRequired,
+ actionEnabled: PropTypes.bool.isRequired,
};
static defaultProps = {
@@ -117,6 +119,7 @@ class PhaseApp extends Component {
dispatchPostAction,
geolocation,
geolocationEnabled,
+ actionEnabled,
} = this.props;
// get app instance id in message
@@ -139,13 +142,16 @@ class PhaseApp extends Component {
case GET_APP_INSTANCE:
return dispatchGetAppInstance(payload, this.postMessage);
case POST_ACTION: {
- // add geolocation to action if enabled
- payload.geolocation = null;
- if (geolocationEnabled) {
- const { latitude, longitude } = geolocation.getIn(['coords']);
- payload.geolocation = { ll: [latitude, longitude] };
+ if (actionEnabled) {
+ // add geolocation to action if enabled
+ payload.geolocation = null;
+ if (geolocationEnabled) {
+ const { latitude, longitude } = geolocation.getIn(['coords']);
+ payload.geolocation = { ll: [latitude, longitude] };
+ }
+ return dispatchPostAction(payload, this.postMessage);
}
- return dispatchPostAction(payload, this.postMessage);
+ break;
}
default:
return false;
@@ -197,6 +203,7 @@ class PhaseApp extends Component {
phaseId,
appInstance,
userId,
+ actionEnabled,
} = this.props;
let uri = url;
if (asset) {
@@ -231,6 +238,7 @@ class PhaseApp extends Component {
itemId: id,
offline: true,
subSpaceId: phaseId,
+ analytics: actionEnabled,
};
const queryString = Qs.stringify(params);
@@ -308,6 +316,9 @@ const mapStateToProps = ({ authentication, Space }) => ({
'geolocationEnabled',
]),
userId: authentication.getIn(['user', 'id']),
+ actionEnabled:
+ authentication.getIn(['user', 'settings', 'actionEnabled']) ||
+ DEFAULT_ACTION_ENABLED,
});
const mapDispatchToProps = {
diff --git a/src/config/channels.js b/src/config/channels.js
index d0ca0199..ad3dbe28 100644
--- a/src/config/channels.js
+++ b/src/config/channels.js
@@ -52,6 +52,8 @@ module.exports = {
SET_USER_MODE_CHANNEL: 'user:mode:set',
SET_SPACE_AS_FAVORITE_CHANNEL: 'user:set-space-favorite:set',
SET_SPACE_AS_RECENT_CHANNEL: 'user:set-space-recent:set',
+ SET_ACTION_ACCESSIBILITY_CHANNEL: 'user:action:accessibility:set',
+ SET_ACTION_ENABLED_CHANNEL: 'user:action:enabled:set',
GET_CLASSROOMS_CHANNEL: 'classrooms:get',
ADD_CLASSROOM_CHANNEL: 'classroom:add',
DELETE_CLASSROOM_CHANNEL: 'classroom:delete',
diff --git a/src/config/constants.js b/src/config/constants.js
index 5cd3f8c2..c59daa22 100644
--- a/src/config/constants.js
+++ b/src/config/constants.js
@@ -30,6 +30,9 @@ export const CONTROL_TYPES = {
export const MIN_CARD_WIDTH = 345;
export const DEFAULT_PROTOCOL = 'https';
+export const DEFAULT_ACTION_ACCESSIBILITY = false;
+export const DEFAULT_ACTION_ENABLED = true;
+
// math
export const BLOCK_MATH_DIV = 'p';
export const INLINE_MATH_DIV = 'span';
diff --git a/src/config/messages.js b/src/config/messages.js
index 20be0ff1..34c74b9b 100644
--- a/src/config/messages.js
+++ b/src/config/messages.js
@@ -99,6 +99,10 @@ const ERROR_NO_USER_TO_DELETE_MESSAGE = 'There is no user to delete';
const ERROR_GETTING_SPACE_IN_CLASSROOM_MESSAGE =
'There was an error getting the space in this classroom';
const ERROR_INVALID_USERNAME_MESSAGE = 'This username is invalid';
+const ERROR_SETTING_ACTION_ACCESSIBILITY =
+ 'There was an error setting the action accessibility';
+const ERROR_SETTING_ACTION_ENABLED =
+ 'There was an error setting the action enabled';
module.exports = {
ERROR_GETTING_DEVELOPER_MODE,
@@ -167,4 +171,6 @@ module.exports = {
ERROR_NO_USER_TO_DELETE_MESSAGE,
ERROR_GETTING_SPACE_IN_CLASSROOM_MESSAGE,
ERROR_INVALID_USERNAME_MESSAGE,
+ ERROR_SETTING_ACTION_ACCESSIBILITY,
+ ERROR_SETTING_ACTION_ENABLED,
};
diff --git a/src/data/sample.js b/src/data/sample.js
index 5cd4427d..138c2874 100644
--- a/src/data/sample.js
+++ b/src/data/sample.js
@@ -1,4 +1,9 @@
-import { SYNC_MODES, USER_MODES } from '../config/constants';
+import {
+ SYNC_MODES,
+ USER_MODES,
+ DEFAULT_ACTION_ACCESSIBILITY,
+ DEFAULT_ACTION_ENABLED,
+} from '../config/constants';
const SampleDatabase = {
spaces: [
@@ -168,7 +173,7 @@ const SampleDatabase = {
format: 'v1',
type: 'input',
visibility: 'private',
- user: '5ce422795fe28eeca1001e0a',
+ user: '1',
id: '5ce430152f6f6672b16fca57',
verb: 'saved',
spaceId: '6ccb068bbb18b80359966631',
@@ -176,7 +181,7 @@ const SampleDatabase = {
],
users: [
{
- id: 1,
+ id: '1',
username: 'graasp',
createdAt: '2019-05-21T17:06:29.683Z',
geolocation: null,
@@ -186,6 +191,8 @@ const SampleDatabase = {
geolocationEnabled: false,
syncMode: SYNC_MODES.ADVANCED,
userMode: USER_MODES.TEACHER,
+ actionEnabled: DEFAULT_ACTION_ENABLED,
+ actionAccessibility: DEFAULT_ACTION_ACCESSIBILITY,
},
},
],
diff --git a/src/reducers/authenticationReducer.js b/src/reducers/authenticationReducer.js
index 779549a4..96c470c1 100644
--- a/src/reducers/authenticationReducer.js
+++ b/src/reducers/authenticationReducer.js
@@ -31,6 +31,10 @@ import {
FLAG_SETTING_SPACE_AS_FAVORITE,
SET_SPACE_AS_FAVORITE_SUCCEEDED,
SET_SPACE_AS_RECENT_SPACES_SUCCEEDED,
+ FLAG_SETTING_ACTION_ACCESSIBILITY,
+ SET_ACTION_ACCESSIBILITY_SUCCEEDED,
+ SET_ACTION_ENABLED_SUCCEEDED,
+ FLAG_SETTING_ACTION_ENABLED,
} from '../types';
import { updateActivityList } from './common';
import {
@@ -41,6 +45,8 @@ import {
DEFAULT_SYNC_MODE,
DEFAULT_USER_MODE,
MAX_RECENT_SPACES,
+ DEFAULT_ACTION_ACCESSIBILITY,
+ DEFAULT_ACTION_ENABLED,
} from '../config/constants';
const updateFavoriteSpaces = ({ favorite, spaceId }) => favoriteSpaces => {
@@ -85,6 +91,8 @@ export const DEFAULT_USER_SETTINGS = {
geolocationEnabled: DEFAULT_GEOLOCATION_ENABLED,
syncMode: DEFAULT_SYNC_MODE,
userMode: DEFAULT_USER_MODE,
+ actionAccessibility: DEFAULT_ACTION_ACCESSIBILITY,
+ actionEnabled: DEFAULT_ACTION_ENABLED,
};
export const DEFAULT_USER = {
@@ -120,6 +128,8 @@ export default (state = INITIAL_STATE, { type, payload }) => {
case FLAG_SETTING_SPACE_AS_FAVORITE:
case FLAG_SIGNING_IN:
case FLAG_SIGNING_OUT:
+ case FLAG_SETTING_ACTION_ACCESSIBILITY:
+ case FLAG_SETTING_ACTION_ENABLED:
return state.updateIn(
['current', 'activity'],
updateActivityList(payload)
@@ -166,6 +176,10 @@ export default (state = INITIAL_STATE, { type, payload }) => {
['user', 'recentSpaces'],
updateRecentSpaces(payload)
);
+ case SET_ACTION_ENABLED_SUCCEEDED:
+ return state.setIn(['user', 'settings', 'actionEnabled'], payload);
+ case SET_ACTION_ACCESSIBILITY_SUCCEEDED:
+ return state.setIn(['user', 'settings', 'actionAccessibility'], payload);
default:
return state;
}
diff --git a/src/types/user.js b/src/types/user.js
index c483111a..cc65b45f 100644
--- a/src/types/user.js
+++ b/src/types/user.js
@@ -34,3 +34,9 @@ export const FLAG_SETTING_SPACE_AS_RECENT = 'FLAG_SETTING_SPACE_AS_RECENT';
export const SET_SPACE_AS_RECENT = 'SET_SPACE_AS_RECENT';
export const SET_SPACE_AS_RECENT_SPACES_SUCCEEDED =
'SET_SPACE_AS_RECENT_SPACES_SUCCEEDED';
+export const FLAG_SETTING_ACTION_ACCESSIBILITY =
+ 'FLAG_SETTING_ACTION_ACCESSIBILITY';
+export const SET_ACTION_ACCESSIBILITY_SUCCEEDED =
+ 'SET_ACTION_ACCESSIBILITY_SUCCEEDED';
+export const FLAG_SETTING_ACTION_ENABLED = 'FLAG_SETTING_ACTION_ENABLED';
+export const SET_ACTION_ENABLED_SUCCEEDED = 'SET_ACTION_ENABLED_SUCCEEDED';