Skip to content

Commit 88bcca6

Browse files
committed
Moving visual parameters from user settings to user UI data. Modifying edit forms accordingly.
1 parent bca9ebd commit 88bcca6

File tree

17 files changed

+249
-155
lines changed

17 files changed

+249
-155
lines changed

src/components/Users/UserSwitching/UserSwitching.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const UserSwitching = ({ users = [], currentUser, loginAs, removeUser }) => (
2828
key={id}
2929
title={fullName}
3030
firstName={firstName}
31-
useGravatar={safeGet(activeUser, ['privateData', 'settings', 'useGravatar'], false)}
31+
useGravatar={safeGet(activeUser, ['privateData', 'uiData', 'useGravatar'], true)}
3232
onClick={() => loginAs(id)}
3333
onRemove={() => removeUser(id)}
3434
/>

src/components/forms/EditUserSettingsForm/EditUserSettingsForm.js

Lines changed: 14 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,26 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
3+
import { FormattedMessage } from 'react-intl';
44
import { reduxForm, Field } from 'redux-form';
5-
import { defaultMemoize } from 'reselect';
65

76
import Callout from '../../widgets/Callout';
87
import FormBox from '../../widgets/FormBox';
98
import { SaveIcon } from '../../icons';
109
import SubmitButton from '../SubmitButton';
11-
import { CheckboxField, LanguageSelectField, SelectField } from '../Fields';
10+
import { CheckboxField, LanguageSelectField } from '../Fields';
1211
import { isStudentRole, isSupervisorRole } from '../../helpers/usersRoles';
1312

14-
const defaultPagesCaptions = defineMessages({
15-
dashboard: {
16-
id: 'app.editUserSettings.defaultPage.dashboard',
17-
defaultMessage: 'Dashboard',
18-
},
19-
home: {
20-
id: 'app.editUserSettings.defaultPage.home',
21-
defaultMessage: 'Home page (about)',
22-
},
23-
instance: {
24-
id: 'app.editUserSettings.defaultPage.instance',
25-
defaultMessage: 'Instance overview',
26-
},
27-
});
28-
29-
const getDefaultPages = defaultMemoize(formatMessage =>
30-
Object.keys(defaultPagesCaptions).map(key => ({
31-
key,
32-
name: formatMessage(defaultPagesCaptions[key]),
33-
}))
34-
);
35-
3613
const EditUserSettingsForm = ({
3714
user,
3815
submitting,
3916
handleSubmit,
4017
submitFailed = false,
4118
submitSucceeded = false,
42-
anyTouched,
19+
dirty,
4320
invalid,
44-
intl: { formatMessage },
4521
}) => (
4622
<FormBox
47-
title={<FormattedMessage id="app.editUserSettings.title" defaultMessage="Edit settings" />}
23+
title={<FormattedMessage id="app.editUserSettings.title" defaultMessage="User Settings" />}
4824
type={submitSucceeded ? 'success' : undefined}
4925
footer={
5026
<div className="text-center">
@@ -55,7 +31,7 @@ const EditUserSettingsForm = ({
5531
hasSucceeded={submitSucceeded}
5632
hasFailed={submitFailed}
5733
invalid={invalid}
58-
dirty={anyTouched}
34+
dirty={dirty}
5935
defaultIcon={<SaveIcon gapRight />}
6036
messages={{
6137
submit: <FormattedMessage id="generic.save" defaultMessage="Save" />,
@@ -71,67 +47,17 @@ const EditUserSettingsForm = ({
7147
</Callout>
7248
)}
7349

74-
<Field
75-
name="vimMode"
76-
component={CheckboxField}
77-
onOff
78-
label={
79-
<FormattedMessage id="app.editUserSettings.vimMode" defaultMessage="Use Vim mode in source code editors." />
80-
}
81-
/>
82-
83-
<Field
84-
name="darkTheme"
85-
component={CheckboxField}
86-
onOff
87-
label={
88-
<FormattedMessage
89-
id="app.editUserSettings.darkTheme"
90-
defaultMessage="Use a dark theme for the source code viewers and editors."
91-
/>
92-
}
93-
/>
94-
95-
<Field
96-
name="openedSidebar"
97-
component={CheckboxField}
98-
onOff
99-
label={
100-
<FormattedMessage id="app.editUserSettings.openedSidebar" defaultMessage="Sidebar is unfolded by default." />
101-
}
102-
/>
103-
104-
<Field
105-
name="useGravatar"
106-
component={CheckboxField}
107-
onOff
108-
label={
109-
<FormattedMessage
110-
id="app.editUserSettings.useGravatar"
111-
defaultMessage="Use Gravatar service for fetching user avatars."
112-
/>
113-
}
114-
/>
115-
11650
<Field
11751
name="defaultLanguage"
11852
component={LanguageSelectField}
11953
label={<FormattedMessage id="app.editUserSettings.defaultLanguage" defaultMessage="Default language:" />}
12054
/>
12155

122-
<Field
123-
name="defaultPage"
124-
component={SelectField}
125-
options={getDefaultPages(formatMessage)}
126-
addEmptyOption={true}
127-
label={<FormattedMessage id="app.editUserSettings.defaultPage" defaultMessage="Default page (after login):" />}
128-
/>
129-
13056
<hr />
13157

132-
<h4>
133-
<FormattedMessage id="app.editUserSettings.emailsTitle" defaultMessage="Email Notifications" />:
134-
</h4>
58+
<h5 className="mb-3">
59+
<FormattedMessage id="app.editUserSettings.emailsTitle" defaultMessage="Send E-mail Notifications" />:
60+
</h5>
13561

13662
{!user.isVerified && (
13763
<Callout variant="warning">
@@ -249,15 +175,12 @@ EditUserSettingsForm.propTypes = {
249175
submitFailed: PropTypes.bool,
250176
submitSucceeded: PropTypes.bool,
251177
submitting: PropTypes.bool,
252-
anyTouched: PropTypes.bool,
178+
dirty: PropTypes.bool,
253179
invalid: PropTypes.bool,
254-
intl: PropTypes.object.isRequired,
255180
};
256181

257-
export default injectIntl(
258-
reduxForm({
259-
form: 'edit-user-settings',
260-
enableReinitialize: true,
261-
keepDirtyOnReinitialize: false,
262-
})(EditUserSettingsForm)
263-
);
182+
export default reduxForm({
183+
form: 'edit-user-settings',
184+
enableReinitialize: true,
185+
keepDirtyOnReinitialize: false,
186+
})(EditUserSettingsForm);
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
4+
import { reduxForm, Field } from 'redux-form';
5+
import { defaultMemoize } from 'reselect';
6+
7+
import Callout from '../../widgets/Callout';
8+
import FormBox from '../../widgets/FormBox';
9+
import { SaveIcon } from '../../icons';
10+
import SubmitButton from '../SubmitButton';
11+
import { CheckboxField, SelectField } from '../Fields';
12+
13+
const defaultPagesCaptions = defineMessages({
14+
dashboard: {
15+
id: 'app.editUserUIData.defaultPage.dashboard',
16+
defaultMessage: 'Dashboard',
17+
},
18+
home: {
19+
id: 'app.editUserUIData.defaultPage.home',
20+
defaultMessage: 'Home page (about)',
21+
},
22+
instance: {
23+
id: 'app.editUserUIData.defaultPage.instance',
24+
defaultMessage: 'Instance overview',
25+
},
26+
});
27+
28+
const getDefaultPages = defaultMemoize(formatMessage =>
29+
Object.keys(defaultPagesCaptions).map(key => ({
30+
key,
31+
name: formatMessage(defaultPagesCaptions[key]),
32+
}))
33+
);
34+
35+
const EditUserUIDataForm = ({
36+
submitting,
37+
handleSubmit,
38+
submitFailed = false,
39+
submitSucceeded = false,
40+
dirty,
41+
invalid,
42+
intl: { formatMessage },
43+
}) => (
44+
<FormBox
45+
title={<FormattedMessage id="app.editUserUIData.title" defaultMessage="Visual Settings" />}
46+
type={submitSucceeded ? 'success' : undefined}
47+
footer={
48+
<div className="text-center">
49+
<SubmitButton
50+
id="editUserUIData"
51+
handleSubmit={handleSubmit}
52+
submitting={submitting}
53+
hasSucceeded={submitSucceeded}
54+
hasFailed={submitFailed}
55+
invalid={invalid}
56+
dirty={dirty}
57+
defaultIcon={<SaveIcon gapRight />}
58+
messages={{
59+
submit: <FormattedMessage id="generic.save" defaultMessage="Save" />,
60+
submitting: <FormattedMessage id="generic.saving" defaultMessage="Saving..." />,
61+
success: <FormattedMessage id="generic.saved" defaultMessage="Saved" />,
62+
}}
63+
/>
64+
</div>
65+
}>
66+
{submitFailed && (
67+
<Callout variant="danger">
68+
<FormattedMessage id="app.editUserUIData.failed" defaultMessage="Cannot save visual settings of the user." />
69+
</Callout>
70+
)}
71+
72+
<Field
73+
name="defaultPage"
74+
component={SelectField}
75+
options={getDefaultPages(formatMessage)}
76+
addEmptyOption={true}
77+
label={<FormattedMessage id="app.editUserUIData.defaultPage" defaultMessage="Default page (after login):" />}
78+
/>
79+
80+
<Field
81+
name="openedSidebar"
82+
component={CheckboxField}
83+
onOff
84+
label={<FormattedMessage id="app.editUserUIData.openedSidebar" defaultMessage="Sidebar is unfolded by default" />}
85+
/>
86+
87+
<Field
88+
name="useGravatar"
89+
component={CheckboxField}
90+
onOff
91+
label={
92+
<FormattedMessage
93+
id="app.editUserUIData.useGravatar"
94+
defaultMessage="Use Gravatar service for fetching user avatars"
95+
/>
96+
}
97+
/>
98+
99+
<Field
100+
name="vimMode"
101+
component={CheckboxField}
102+
onOff
103+
label={<FormattedMessage id="app.editUserUIData.vimMode" defaultMessage="Use Vim mode in source code editors" />}
104+
/>
105+
106+
<Field
107+
name="darkTheme"
108+
component={CheckboxField}
109+
onOff
110+
label={
111+
<FormattedMessage
112+
id="app.editUserUIData.darkTheme"
113+
defaultMessage="Use a dark theme for the source code viewers and editors"
114+
/>
115+
}
116+
/>
117+
</FormBox>
118+
);
119+
120+
EditUserUIDataForm.propTypes = {
121+
handleSubmit: PropTypes.func.isRequired,
122+
onSubmit: PropTypes.func.isRequired,
123+
submitFailed: PropTypes.bool,
124+
submitSucceeded: PropTypes.bool,
125+
submitting: PropTypes.bool,
126+
dirty: PropTypes.bool,
127+
invalid: PropTypes.bool,
128+
intl: PropTypes.object.isRequired,
129+
};
130+
131+
export default injectIntl(
132+
reduxForm({
133+
form: 'edit-user-uiData',
134+
enableReinitialize: true,
135+
keepDirtyOnReinitialize: false,
136+
})(EditUserUIDataForm)
137+
);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import EditUserUIDataForm from './EditUserUIDataForm';
2+
export default EditUserUIDataForm;

src/components/forms/Fields/SourceCodeField.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
33
import { Form, FormGroup, FormLabel } from 'react-bootstrap';
44
import { canUseDOM } from 'exenv';
55

6-
import { UserSettingsContext } from '../../../helpers/contexts';
6+
import { UserUIDataContext } from '../../../helpers/contexts';
77

88
// load the ACE editor only when rendering in the browser
99
import { getAceModeFromExtension } from '../../helpers/ace';
@@ -26,8 +26,8 @@ const SourceCodeField = ({
2626
)}
2727
{canUseDOM && (
2828
<div className={readOnly ? 'noselection' : ''}>
29-
<UserSettingsContext.Consumer>
30-
{({ vimMode = false, darkTheme = false }) => (
29+
<UserUIDataContext.Consumer>
30+
{({ vimMode = false, darkTheme = true }) => (
3131
<AceEditor
3232
{...props}
3333
{...input}
@@ -43,15 +43,15 @@ const SourceCodeField = ({
4343
readOnly={readOnly}
4444
fontSize={16}
4545
onBlur={
46-
() => input.onBlur() // this is a hack that will ensure blur call witout distorting the contents
46+
() => input.onBlur() // this is a hack that will ensure blur call without distorting the contents
4747
}
4848
editorProps={{
4949
$blockScrolling: Infinity,
5050
$autoScrollEditorIntoView: false,
5151
}}
5252
/>
5353
)}
54-
</UserSettingsContext.Consumer>
54+
</UserUIDataContext.Consumer>
5555
</div>
5656
)}
5757
{error && <Form.Text className="text-danger"> {error} </Form.Text>}

src/components/helpers/SourceCodeViewer/SourceCodeViewer.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33
import { canUseDOM } from 'exenv';
4-
import { UserSettingsContext } from '../../../helpers/contexts';
4+
import { UserUIDataContext } from '../../../helpers/contexts';
55
import { getAceModeFromExtension } from '../../helpers/ace';
66

77
const AceEditor = canUseDOM ? require('react-ace').default : null;
88

99
const SourceCodeViewer = ({ name, content = '' }) =>
1010
canUseDOM ? (
11-
<UserSettingsContext.Consumer>
12-
{({ vimMode = false, darkTheme = false }) => (
11+
<UserUIDataContext.Consumer>
12+
{({ vimMode = false, darkTheme = true }) => (
1313
<AceEditor
1414
value={content}
1515
mode={getAceModeFromExtension(name.split('.').pop())}
@@ -22,7 +22,7 @@ const SourceCodeViewer = ({ name, content = '' }) =>
2222
editorProps={{ $blockScrolling: true, $autoScrollEditorIntoView: true }}
2323
/>
2424
)}
25-
</UserSettingsContext.Consumer>
25+
</UserUIDataContext.Consumer>
2626
) : (
2727
<></>
2828
);

src/containers/AvatarContainer/AvatarContainer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const AvatarContainer = ({ currentUser, avatarUrl, fullName, firstName, size = 4
1313
failed={<FailedAvatar size={size} />}
1414
resource={currentUser}>
1515
{currentUser =>
16-
safeGet(currentUser, ['privateData', 'settings', 'useGravatar'], false) && avatarUrl !== null ? (
16+
safeGet(currentUser, ['privateData', 'uiData', 'useGravatar'], true) && avatarUrl !== null ? (
1717
<Avatar size={size} src={avatarUrl} title={fullName} {...props} />
1818
) : (
1919
<FakeAvatar size={size} {...props}>

0 commit comments

Comments
 (0)