From 335a3a92a4382974ac72629020d4f2376bf5dfea Mon Sep 17 00:00:00 2001 From: pyphilia Date: Thu, 30 Apr 2020 14:13:48 +0200 Subject: [PATCH] feat: add selection when exporting a space --- public/app/listeners/exportSpace.js | 50 ++-- src/App.js | 7 + src/actions/space.js | 3 +- .../__snapshots__/MediaCard.test.js.snap | 4 +- src/components/space/ExportButton.js | 20 +- .../space/export/ExportSelectionScreen.js | 214 ++++++++++++++++++ src/config/paths.js | 4 + 7 files changed, 280 insertions(+), 22 deletions(-) create mode 100644 src/components/space/export/ExportSelectionScreen.js diff --git a/public/app/listeners/exportSpace.js b/public/app/listeners/exportSpace.js index a7e934ae..2caf943a 100644 --- a/public/app/listeners/exportSpace.js +++ b/public/app/listeners/exportSpace.js @@ -20,25 +20,49 @@ const fsPromises = fs.promises; // In the future we can add options so that the behaviour can be slightly modified const exportSpace = (mainWindow, db) => async ( event, - { archivePath, id, userId: user } + { + archivePath, + id, + userId, + selection: { + space: isSpaceSelected, + actions: isActionsSelected, + resources: isResourcesSelected, + }, + } ) => { try { // get space from local database - const space = db - .get(SPACES_COLLECTION) - .find({ id }) - .value(); + const space = isSpaceSelected + ? db + .get(SPACES_COLLECTION) + .find({ id }) + .value() + : {}; + + // build conditions when fetching resources and actions + // teachers can fetch every user's data + // students can only fetch their own data + const isStudent = db.get('user.settings.studentMode').value(); + const conditions = { spaceId: id }; + if (isStudent) { + conditions.user = userId; + } // export the user's resources, private and public - const resources = db - .get(APP_INSTANCE_RESOURCES_COLLECTION) - .filter({ user, spaceId: id }) - .value(); + const resources = isResourcesSelected + ? db + .get(APP_INSTANCE_RESOURCES_COLLECTION) + .filter(conditions) + .value() + : []; - const actions = db - .get(ACTIONS_COLLECTION) - .filter({ user, spaceId: id }) - .value(); + const actions = isActionsSelected + ? db + .get(ACTIONS_COLLECTION) + .filter(conditions) + .value() + : []; // abort if space does not exist if (!space) { diff --git a/src/App.js b/src/App.js index 461f97ff..3532799e 100644 --- a/src/App.js +++ b/src/App.js @@ -16,6 +16,7 @@ import Settings from './components/Settings'; import LoadSpace from './components/LoadSpace'; import SpaceScreen from './components/space/SpaceScreen'; import SyncScreen from './components/space/SyncScreen'; +import ExportSelectionScreen from './components/space/export/ExportSelectionScreen'; import DeveloperScreen from './components/developer/DeveloperScreen'; import { OnlineTheme, OfflineTheme } from './themes'; import Dashboard from './components/dashboard/Dashboard'; @@ -33,6 +34,7 @@ import { DASHBOARD_PATH, SIGN_IN_PATH, SAVED_SPACES_PATH, + buildExportSelectionPathForSpaceId, } from './config/paths'; import { getGeolocation, @@ -193,6 +195,11 @@ export class App extends Component { path={SYNC_SPACE_PATH} component={Authorization()(SyncScreen)} /> + dispatch => { }); }; -const exportSpace = (id, spaceName, userId) => dispatch => { +const exportSpace = (id, spaceName, userId, selection) => dispatch => { window.ipcRenderer.send(SHOW_EXPORT_SPACE_PROMPT_CHANNEL, spaceName); window.ipcRenderer.once( RESPOND_EXPORT_SPACE_PROMPT_CHANNEL, @@ -266,6 +266,7 @@ const exportSpace = (id, spaceName, userId) => dispatch => { archivePath, id, userId, + selection, }); } else { dispatch(flagExportingSpace(false)); diff --git a/src/components/common/__snapshots__/MediaCard.test.js.snap b/src/components/common/__snapshots__/MediaCard.test.js.snap index ac77900f..ead0a803 100644 --- a/src/components/common/__snapshots__/MediaCard.test.js.snap +++ b/src/components/common/__snapshots__/MediaCard.test.js.snap @@ -89,7 +89,7 @@ exports[` with showActions = true with text defined r - with showActions = true with text undefined - { - const { space, dispatchExportSpace, userId } = this.props; - const { id, name } = space; - dispatchExportSpace(id, name, userId); + const { + history: { push }, + space, + } = this.props; + push({ + pathname: buildExportSelectionPathForSpaceId(space.id), + state: { space }, + }); }; render() { @@ -67,4 +75,4 @@ const StyledComponent = withStyles(Styles, { withTheme: true })( const TranslatedComponent = withTranslation()(StyledComponent); -export default TranslatedComponent; +export default withRouter(TranslatedComponent); diff --git a/src/components/space/export/ExportSelectionScreen.js b/src/components/space/export/ExportSelectionScreen.js new file mode 100644 index 00000000..b4dfa41f --- /dev/null +++ b/src/components/space/export/ExportSelectionScreen.js @@ -0,0 +1,214 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import AppBar from '@material-ui/core/AppBar/AppBar'; +import Toolbar from '@material-ui/core/Toolbar/Toolbar'; +import { withRouter } from 'react-router'; +import clsx from 'clsx'; +import { withTranslation } from 'react-i18next'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Checkbox from '@material-ui/core/Checkbox'; +import Button from '@material-ui/core//Button'; +import { withStyles } from '@material-ui/core/styles'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { Typography } from '@material-ui/core'; +import { connect } from 'react-redux'; +import { exportSpace } from '../../../actions'; +import Styles from '../../../Styles'; +import Loader from '../../common/Loader'; +import Main from '../../common/Main'; +import SpaceNotFound from '../SpaceNotFound'; + +const styles = theme => ({ + ...Styles(theme), + buttonGroup: { + textAlign: 'center', + }, +}); + +class ExportSelectionScreen extends Component { + static propTypes = { + classes: PropTypes.shape({ + root: PropTypes.string.isRequired, + appBar: PropTypes.string.isRequired, + appBarShift: PropTypes.string.isRequired, + menuButton: PropTypes.string.isRequired, + hide: PropTypes.string.isRequired, + drawer: PropTypes.string.isRequired, + drawerPaper: PropTypes.string.isRequired, + drawerHeader: PropTypes.string.isRequired, + content: PropTypes.string.isRequired, + contentShift: PropTypes.string.isRequired, + buttonGroup: PropTypes.string.isRequired, + submitButton: PropTypes.string.isRequired, + button: PropTypes.string.isRequired, + }).isRequired, + theme: PropTypes.shape({ direction: PropTypes.string }).isRequired, + dispatchExportSpace: PropTypes.func.isRequired, + activity: PropTypes.bool.isRequired, + history: PropTypes.shape({ + goBack: PropTypes.func.isRequired, + length: PropTypes.number.isRequired, + }).isRequired, + userId: PropTypes.string.isRequired, + location: PropTypes.shape({ + search: PropTypes.string.isRequired, + state: PropTypes.shape({ + space: PropTypes.shape({ + id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + }), + }), + }).isRequired, + t: PropTypes.func.isRequired, + }; + + state = { + space: true, + actions: true, + resources: true, + }; + + handleChange = event => { + this.setState({ [event.target.name]: event.target.checked }); + }; + + handleBack = () => { + const { + history: { goBack }, + } = this.props; + goBack(); + }; + + handleSubmit = () => { + const { + userId, + location: { + state: { + space: { id, name }, + }, + }, + dispatchExportSpace, + } = this.props; + const { space, actions, resources } = this.state; + const selection = { space, actions, resources }; + dispatchExportSpace(id, name, userId, selection); + }; + + render() { + const { + classes, + t, + location: { state }, + activity, + } = this.props; + const { + space: isSpaceChecked, + resources: isResourcesChecked, + actions: isActionsChecked, + } = this.state; + + if (!state || !state.space) { + return ; + } + + if (activity) { + return ( +
+ + + + +
+ +
+
+ ); + } + + const spaceCheckbox = ( + + ); + + const resourcesCheckbox = ( + + ); + const actionsCheckbox = ( + + ); + + return ( +
+
+ + {t('What do you want to export?')} + + +
+ + + + + +
+
+ + +
+
+
+ ); + } +} + +const mapStateToProps = ({ authentication, Space }) => ({ + userId: authentication.getIn(['user', 'id']), + activity: Boolean(Space.getIn(['current', 'activity']).size), +}); + +const mapDispatchToProps = { + dispatchExportSpace: exportSpace, +}; + +const TranslatedComponent = withTranslation()(ExportSelectionScreen); + +export default withRouter( + withStyles(styles, { withTheme: true })( + connect(mapStateToProps, mapDispatchToProps)(TranslatedComponent) + ) +); diff --git a/src/config/paths.js b/src/config/paths.js index 685077cb..ae174833 100644 --- a/src/config/paths.js +++ b/src/config/paths.js @@ -9,3 +9,7 @@ export const DASHBOARD_PATH = '/dashboard'; export const SIGN_IN_PATH = '/signin'; export const SYNC_SPACE_PATH = '/space/sync/:id'; export const SAVED_SPACES_PATH = '/saved-spaces'; + +export const buildExportSelectionPathForSpaceId = (id = ':id') => { + return `/space/export/${id}/selection`; +};