Skip to content

Commit 335a3a9

Browse files
committed
feat: add selection when exporting a space
1 parent c874cd3 commit 335a3a9

File tree

7 files changed

+280
-22
lines changed

7 files changed

+280
-22
lines changed

public/app/listeners/exportSpace.js

+37-13
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,49 @@ const fsPromises = fs.promises;
2020
// In the future we can add options so that the behaviour can be slightly modified
2121
const exportSpace = (mainWindow, db) => async (
2222
event,
23-
{ archivePath, id, userId: user }
23+
{
24+
archivePath,
25+
id,
26+
userId,
27+
selection: {
28+
space: isSpaceSelected,
29+
actions: isActionsSelected,
30+
resources: isResourcesSelected,
31+
},
32+
}
2433
) => {
2534
try {
2635
// get space from local database
27-
const space = db
28-
.get(SPACES_COLLECTION)
29-
.find({ id })
30-
.value();
36+
const space = isSpaceSelected
37+
? db
38+
.get(SPACES_COLLECTION)
39+
.find({ id })
40+
.value()
41+
: {};
42+
43+
// build conditions when fetching resources and actions
44+
// teachers can fetch every user's data
45+
// students can only fetch their own data
46+
const isStudent = db.get('user.settings.studentMode').value();
47+
const conditions = { spaceId: id };
48+
if (isStudent) {
49+
conditions.user = userId;
50+
}
3151

3252
// export the user's resources, private and public
33-
const resources = db
34-
.get(APP_INSTANCE_RESOURCES_COLLECTION)
35-
.filter({ user, spaceId: id })
36-
.value();
53+
const resources = isResourcesSelected
54+
? db
55+
.get(APP_INSTANCE_RESOURCES_COLLECTION)
56+
.filter(conditions)
57+
.value()
58+
: [];
3759

38-
const actions = db
39-
.get(ACTIONS_COLLECTION)
40-
.filter({ user, spaceId: id })
41-
.value();
60+
const actions = isActionsSelected
61+
? db
62+
.get(ACTIONS_COLLECTION)
63+
.filter(conditions)
64+
.value()
65+
: [];
4266

4367
// abort if space does not exist
4468
if (!space) {

src/App.js

+7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import Settings from './components/Settings';
1616
import LoadSpace from './components/LoadSpace';
1717
import SpaceScreen from './components/space/SpaceScreen';
1818
import SyncScreen from './components/space/SyncScreen';
19+
import ExportSelectionScreen from './components/space/export/ExportSelectionScreen';
1920
import DeveloperScreen from './components/developer/DeveloperScreen';
2021
import { OnlineTheme, OfflineTheme } from './themes';
2122
import Dashboard from './components/dashboard/Dashboard';
@@ -33,6 +34,7 @@ import {
3334
DASHBOARD_PATH,
3435
SIGN_IN_PATH,
3536
SAVED_SPACES_PATH,
37+
buildExportSelectionPathForSpaceId,
3638
} from './config/paths';
3739
import {
3840
getGeolocation,
@@ -193,6 +195,11 @@ export class App extends Component {
193195
path={SYNC_SPACE_PATH}
194196
component={Authorization()(SyncScreen)}
195197
/>
198+
<Route
199+
exact
200+
path={buildExportSelectionPathForSpaceId()}
201+
component={Authorization()(ExportSelectionScreen)}
202+
/>
196203
<Route
197204
exact
198205
path={SPACE_PATH}

src/actions/space.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ const clearSpace = () => dispatch => {
255255
});
256256
};
257257

258-
const exportSpace = (id, spaceName, userId) => dispatch => {
258+
const exportSpace = (id, spaceName, userId, selection) => dispatch => {
259259
window.ipcRenderer.send(SHOW_EXPORT_SPACE_PROMPT_CHANNEL, spaceName);
260260
window.ipcRenderer.once(
261261
RESPOND_EXPORT_SPACE_PROMPT_CHANNEL,
@@ -266,6 +266,7 @@ const exportSpace = (id, spaceName, userId) => dispatch => {
266266
archivePath,
267267
id,
268268
userId,
269+
selection,
269270
});
270271
} else {
271272
dispatch(flagExportingSpace(false));

src/components/common/__snapshots__/MediaCard.test.js.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ exports[`<MediaCard /> <MediaCard /> with showActions = true with text defined r
8989
<Connect(withI18nextTranslation(WithStyles(ClearButton)))
9090
spaceId="id"
9191
/>
92-
<withI18nextTranslation(WithStyles(Connect(ExportButton)))
92+
<withRouter(withI18nextTranslation(WithStyles(Connect(ExportButton))))
9393
space={
9494
Object {
9595
"id": "id",
@@ -164,7 +164,7 @@ exports[`<MediaCard /> <MediaCard /> with showActions = true with text undefined
164164
<Connect(withI18nextTranslation(WithStyles(ClearButton)))
165165
spaceId="id"
166166
/>
167-
<withI18nextTranslation(WithStyles(Connect(ExportButton)))
167+
<withRouter(withI18nextTranslation(WithStyles(Connect(ExportButton))))
168168
space={
169169
Object {
170170
"id": "id",

src/components/space/ExportButton.js

+14-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
22
import PropTypes from 'prop-types';
33
import { connect } from 'react-redux';
44
import { withTranslation } from 'react-i18next';
5+
import { withRouter } from 'react-router';
56
import clsx from 'clsx';
67
import IconButton from '@material-ui/core/IconButton/IconButton';
78
import UnarchiveIcon from '@material-ui/icons/Unarchive';
@@ -10,6 +11,7 @@ import { withStyles } from '@material-ui/core';
1011
import Styles from '../../Styles';
1112
import { exportSpace } from '../../actions/space';
1213
import { SPACE_EXPORT_BUTTON_CLASS } from '../../config/selectors';
14+
import { buildExportSelectionPathForSpaceId } from '../../config/paths';
1315

1416
class ExportButton extends Component {
1517
static propTypes = {
@@ -21,15 +23,21 @@ class ExportButton extends Component {
2123
appBar: PropTypes.string.isRequired,
2224
button: PropTypes.string.isRequired,
2325
}).isRequired,
24-
dispatchExportSpace: PropTypes.func.isRequired,
2526
t: PropTypes.func.isRequired,
26-
userId: PropTypes.string.isRequired,
27+
history: PropTypes.shape({
28+
push: PropTypes.func.isRequired,
29+
}).isRequired,
2730
};
2831

2932
handleExport = () => {
30-
const { space, dispatchExportSpace, userId } = this.props;
31-
const { id, name } = space;
32-
dispatchExportSpace(id, name, userId);
33+
const {
34+
history: { push },
35+
space,
36+
} = this.props;
37+
push({
38+
pathname: buildExportSelectionPathForSpaceId(space.id),
39+
state: { space },
40+
});
3341
};
3442

3543
render() {
@@ -67,4 +75,4 @@ const StyledComponent = withStyles(Styles, { withTheme: true })(
6775

6876
const TranslatedComponent = withTranslation()(StyledComponent);
6977

70-
export default TranslatedComponent;
78+
export default withRouter(TranslatedComponent);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import React, { Component } from 'react';
2+
import PropTypes from 'prop-types';
3+
import AppBar from '@material-ui/core/AppBar/AppBar';
4+
import Toolbar from '@material-ui/core/Toolbar/Toolbar';
5+
import { withRouter } from 'react-router';
6+
import clsx from 'clsx';
7+
import { withTranslation } from 'react-i18next';
8+
import FormGroup from '@material-ui/core/FormGroup';
9+
import FormControlLabel from '@material-ui/core/FormControlLabel';
10+
import Checkbox from '@material-ui/core/Checkbox';
11+
import Button from '@material-ui/core//Button';
12+
import { withStyles } from '@material-ui/core/styles';
13+
import CssBaseline from '@material-ui/core/CssBaseline';
14+
import { Typography } from '@material-ui/core';
15+
import { connect } from 'react-redux';
16+
import { exportSpace } from '../../../actions';
17+
import Styles from '../../../Styles';
18+
import Loader from '../../common/Loader';
19+
import Main from '../../common/Main';
20+
import SpaceNotFound from '../SpaceNotFound';
21+
22+
const styles = theme => ({
23+
...Styles(theme),
24+
buttonGroup: {
25+
textAlign: 'center',
26+
},
27+
});
28+
29+
class ExportSelectionScreen extends Component {
30+
static propTypes = {
31+
classes: PropTypes.shape({
32+
root: PropTypes.string.isRequired,
33+
appBar: PropTypes.string.isRequired,
34+
appBarShift: PropTypes.string.isRequired,
35+
menuButton: PropTypes.string.isRequired,
36+
hide: PropTypes.string.isRequired,
37+
drawer: PropTypes.string.isRequired,
38+
drawerPaper: PropTypes.string.isRequired,
39+
drawerHeader: PropTypes.string.isRequired,
40+
content: PropTypes.string.isRequired,
41+
contentShift: PropTypes.string.isRequired,
42+
buttonGroup: PropTypes.string.isRequired,
43+
submitButton: PropTypes.string.isRequired,
44+
button: PropTypes.string.isRequired,
45+
}).isRequired,
46+
theme: PropTypes.shape({ direction: PropTypes.string }).isRequired,
47+
dispatchExportSpace: PropTypes.func.isRequired,
48+
activity: PropTypes.bool.isRequired,
49+
history: PropTypes.shape({
50+
goBack: PropTypes.func.isRequired,
51+
length: PropTypes.number.isRequired,
52+
}).isRequired,
53+
userId: PropTypes.string.isRequired,
54+
location: PropTypes.shape({
55+
search: PropTypes.string.isRequired,
56+
state: PropTypes.shape({
57+
space: PropTypes.shape({
58+
id: PropTypes.string.isRequired,
59+
name: PropTypes.string.isRequired,
60+
}),
61+
}),
62+
}).isRequired,
63+
t: PropTypes.func.isRequired,
64+
};
65+
66+
state = {
67+
space: true,
68+
actions: true,
69+
resources: true,
70+
};
71+
72+
handleChange = event => {
73+
this.setState({ [event.target.name]: event.target.checked });
74+
};
75+
76+
handleBack = () => {
77+
const {
78+
history: { goBack },
79+
} = this.props;
80+
goBack();
81+
};
82+
83+
handleSubmit = () => {
84+
const {
85+
userId,
86+
location: {
87+
state: {
88+
space: { id, name },
89+
},
90+
},
91+
dispatchExportSpace,
92+
} = this.props;
93+
const { space, actions, resources } = this.state;
94+
const selection = { space, actions, resources };
95+
dispatchExportSpace(id, name, userId, selection);
96+
};
97+
98+
render() {
99+
const {
100+
classes,
101+
t,
102+
location: { state },
103+
activity,
104+
} = this.props;
105+
const {
106+
space: isSpaceChecked,
107+
resources: isResourcesChecked,
108+
actions: isActionsChecked,
109+
} = this.state;
110+
111+
if (!state || !state.space) {
112+
return <SpaceNotFound />;
113+
}
114+
115+
if (activity) {
116+
return (
117+
<div className={classes.root}>
118+
<CssBaseline />
119+
<AppBar position="fixed">
120+
<Toolbar />
121+
</AppBar>
122+
<main className="Main">
123+
<Loader />
124+
</main>
125+
</div>
126+
);
127+
}
128+
129+
const spaceCheckbox = (
130+
<Checkbox
131+
checked={isSpaceChecked}
132+
onChange={this.handleChange}
133+
name="space"
134+
color="primary"
135+
/>
136+
);
137+
138+
const resourcesCheckbox = (
139+
<Checkbox
140+
checked={isResourcesChecked}
141+
onChange={this.handleChange}
142+
name="resources"
143+
color="primary"
144+
/>
145+
);
146+
const actionsCheckbox = (
147+
<Checkbox
148+
checked={isActionsChecked}
149+
onChange={this.handleChange}
150+
name="actions"
151+
color="primary"
152+
/>
153+
);
154+
155+
return (
156+
<Main fullScreen>
157+
<div>
158+
<Typography align="center" variant="h4">
159+
{t('What do you want to export?')}
160+
</Typography>
161+
162+
<br />
163+
<FormGroup>
164+
<FormControlLabel control={spaceCheckbox} label={t('This Space')} />
165+
<FormControlLabel
166+
control={resourcesCheckbox}
167+
label={t("This Space's User Inputs")}
168+
/>
169+
<FormControlLabel
170+
control={actionsCheckbox}
171+
label={t("This Space's Analytics")}
172+
/>
173+
</FormGroup>
174+
<br />
175+
<div className={classes.buttonGroup}>
176+
<Button
177+
variant="contained"
178+
color="primary"
179+
className={clsx(classes.button, classes.submitButton)}
180+
onClick={this.handleBack}
181+
>
182+
{t('Back')}
183+
</Button>
184+
<Button
185+
variant="contained"
186+
color="primary"
187+
className={clsx(classes.button, classes.submitButton)}
188+
onClick={this.handleSubmit}
189+
>
190+
{t('Export')}
191+
</Button>
192+
</div>
193+
</div>
194+
</Main>
195+
);
196+
}
197+
}
198+
199+
const mapStateToProps = ({ authentication, Space }) => ({
200+
userId: authentication.getIn(['user', 'id']),
201+
activity: Boolean(Space.getIn(['current', 'activity']).size),
202+
});
203+
204+
const mapDispatchToProps = {
205+
dispatchExportSpace: exportSpace,
206+
};
207+
208+
const TranslatedComponent = withTranslation()(ExportSelectionScreen);
209+
210+
export default withRouter(
211+
withStyles(styles, { withTheme: true })(
212+
connect(mapStateToProps, mapDispatchToProps)(TranslatedComponent)
213+
)
214+
);

src/config/paths.js

+4
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ export const DASHBOARD_PATH = '/dashboard';
99
export const SIGN_IN_PATH = '/signin';
1010
export const SYNC_SPACE_PATH = '/space/sync/:id';
1111
export const SAVED_SPACES_PATH = '/saved-spaces';
12+
13+
export const buildExportSelectionPathForSpaceId = (id = ':id') => {
14+
return `/space/export/${id}/selection`;
15+
};

0 commit comments

Comments
 (0)