Skip to content

Commit a684f40

Browse files
committed
Move NewFileForm to final-form
1 parent d5ac7fd commit a684f40

File tree

5 files changed

+133
-143
lines changed

5 files changed

+133
-143
lines changed

client/modules/IDE/actions/files.js

Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -36,60 +36,41 @@ export function updateFileContent(id, content) {
3636
};
3737
}
3838

39-
export function createFile(formProps) {
40-
return (dispatch, getState) => {
41-
const state = getState();
42-
const { parentId } = state.ide;
43-
if (state.project.id) {
44-
const postParams = {
45-
name: createUniqueName(formProps.name, parentId, state.files),
46-
url: formProps.url,
47-
content: formProps.content || '',
48-
parentId,
49-
children: []
50-
};
51-
apiClient.post(`/projects/${state.project.id}/files`, postParams)
52-
.then((response) => {
53-
dispatch({
54-
type: ActionTypes.CREATE_FILE,
55-
...response.data.updatedFile,
56-
parentId
57-
});
58-
dispatch(setProjectSavedTime(response.data.project.updatedAt));
59-
dispatch(closeNewFileModal());
60-
dispatch(reset('new-file'));
61-
// dispatch({
62-
// type: ActionTypes.HIDE_MODAL
63-
// });
64-
dispatch(setUnsavedChanges(true));
65-
})
66-
.catch((error) => {
67-
const { response } = error;
68-
dispatch({
69-
type: ActionTypes.ERROR,
70-
error: response.data
71-
});
72-
});
73-
} else {
74-
const id = objectID().toHexString();
75-
dispatch({
76-
type: ActionTypes.CREATE_FILE,
77-
name: createUniqueName(formProps.name, parentId, state.files),
78-
id,
79-
_id: id,
80-
url: formProps.url,
81-
content: formProps.content || '',
82-
parentId,
83-
children: []
84-
});
85-
dispatch(reset('new-file'));
86-
// dispatch({
87-
// type: ActionTypes.HIDE_MODAL
88-
// });
89-
dispatch(setUnsavedChanges(true));
90-
dispatch(closeNewFileModal());
91-
}
39+
export function createFile(file, parentId) {
40+
return {
41+
type: ActionTypes.CREATE_FILE,
42+
...file,
43+
parentId
44+
};
45+
}
46+
47+
export function submitFile(formProps, files, parentId, projectId) {
48+
if (projectId) {
49+
const postParams = {
50+
name: createUniqueName(formProps.name, parentId, files),
51+
url: formProps.url,
52+
content: formProps.content || '',
53+
parentId,
54+
children: []
55+
};
56+
return apiClient.post(`/projects/${projectId}/files`, postParams)
57+
.then(response => ({
58+
file: response.data.updatedFile,
59+
updatedAt: response.data.project.updatedAt
60+
}));
61+
}
62+
const id = objectID().toHexString();
63+
const file = {
64+
name: createUniqueName(formProps.name, parentId, files),
65+
id,
66+
_id: id,
67+
url: formProps.url,
68+
content: formProps.content || '',
69+
children: []
9270
};
71+
return Promise.resolve({
72+
file
73+
});
9374
}
9475

9576
export function createFolder(formProps) {

client/modules/IDE/actions/ide.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,3 +278,10 @@ export function stopSketch() {
278278
dispatch(stopVisualSketch());
279279
};
280280
}
281+
282+
export function createError(error) {
283+
return {
284+
type: ActionTypes.ERROR,
285+
error
286+
};
287+
}
Lines changed: 88 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,97 @@
1-
import PropTypes from 'prop-types';
2-
import React from 'react';
3-
import { withTranslation } from 'react-i18next';
4-
import { domOnlyProps } from '../../../utils/reduxFormUtils';
1+
import React, { useEffect, useRef } from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
import { Form, Field } from 'react-final-form';
4+
import { useDispatch, useSelector } from 'react-redux';
5+
import { submitFile, createFile } from '../actions/files';
6+
import { setProjectSavedTime } from '../actions/project';
7+
import { closeNewFileModal, setUnsavedChanges, createError } from '../actions/ide';
8+
import { CREATE_FILE_REGEX } from '../../../../server/utils/fileUtils';
59

610
import Button from '../../../common/Button';
711

8-
class NewFileForm extends React.Component {
9-
constructor(props) {
10-
super(props);
11-
this.createFile = this.props.createFile.bind(this);
12-
}
12+
function NewFileForm() {
13+
const fileNameInput = useRef(null);
14+
const { t } = useTranslation();
15+
const files = useSelector(state => state.files);
16+
const parentId = useSelector(state => state.ide.parentId);
17+
const projectId = useSelector(state => state.project.id);
18+
const dispatch = useDispatch();
1319

14-
componentDidMount() {
15-
this.fileName.focus();
20+
function handleCreateFile(formProps) {
21+
submitFile(formProps, files, parentId, projectId).then((response) => {
22+
const { file, updatedAt } = response;
23+
dispatch(createFile(file, parentId));
24+
if (updatedAt) dispatch(setProjectSavedTime(updatedAt));
25+
dispatch(closeNewFileModal());
26+
dispatch(setUnsavedChanges(true));
27+
// reset form
28+
}).catch((error) => {
29+
const { response } = error;
30+
dispatch(createError(response.data));
31+
});
1632
}
1733

18-
render() {
19-
const {
20-
fields: { name },
21-
handleSubmit,
22-
} = this.props;
23-
return (
24-
<form
25-
className="new-file-form"
26-
onSubmit={(data) => {
27-
this.props.focusOnModal();
28-
handleSubmit(this.createFile)(data);
29-
}}
30-
>
31-
<div className="new-file-form__input-wrapper">
32-
<label className="new-file-form__name-label" htmlFor="name">
33-
Name:
34-
</label>
35-
<input
36-
className="new-file-form__name-input"
37-
id="name"
38-
type="text"
39-
placeholder={this.props.t('NewFileForm.Placeholder')}
40-
maxLength="128"
41-
{...domOnlyProps(name)}
42-
ref={(element) => {
43-
this.fileName = element;
44-
}}
45-
/>
46-
<Button
47-
type="submit"
48-
>{this.props.t('NewFileForm.AddFileSubmit')}
49-
</Button>
50-
</div>
51-
{name.touched && name.error && (
52-
<span className="form-error">{name.error}</span>
53-
)}
54-
</form>
55-
);
34+
function validate(formProps) {
35+
const errors = {};
36+
37+
if (!formProps.name) {
38+
errors.name = t('NewFileModal.EnterName');
39+
} else if (!formProps.name.match(CREATE_FILE_REGEX)) {
40+
errors.name = t('NewFileModal.InvalidType');
41+
}
42+
43+
return errors;
5644
}
57-
}
5845

59-
NewFileForm.propTypes = {
60-
fields: PropTypes.shape({
61-
name: PropTypes.objectOf(PropTypes.shape()).isRequired
62-
}).isRequired,
63-
handleSubmit: PropTypes.func.isRequired,
64-
createFile: PropTypes.func.isRequired,
65-
focusOnModal: PropTypes.func.isRequired,
66-
t: PropTypes.func.isRequired
67-
};
46+
useEffect(() => {
47+
fileNameInput.current.focus();
48+
});
49+
50+
return (
51+
<Form
52+
fields={['name']}
53+
validate={validate}
54+
onSubmit={handleCreateFile}
55+
>
56+
{({
57+
handleSubmit, errors, touched, invalid, submitting
58+
}) => (
59+
<form
60+
className="new-file-form"
61+
onSubmit={handleSubmit}
62+
>
63+
<div className="new-file-form__input-wrapper">
64+
<Field name="name">
65+
{field => (
66+
<React.Fragment>
67+
<label className="new-file-form__name-label" htmlFor="name">
68+
Name:
69+
</label>
70+
<input
71+
className="new-file-form__name-input"
72+
id="name"
73+
type="text"
74+
placeholder={t('NewFileForm.Placeholder')}
75+
maxLength="128"
76+
{...field.input}
77+
ref={fileNameInput}
78+
/>
79+
</React.Fragment>
80+
)}
81+
</Field>
82+
<Button
83+
type="submit"
84+
disabled={invalid || submitting}
85+
>{t('NewFileForm.AddFileSubmit')}
86+
</Button>
87+
</div>
88+
{touched.name && errors.name && (
89+
<span className="form-error">{errors.name}</span>
90+
)}
91+
</form>
92+
)}
93+
</Form>
94+
);
95+
}
6896

69-
export default withTranslation()(NewFileForm);
97+
export default NewFileForm;

client/modules/IDE/components/NewFileModal.jsx

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
import PropTypes from 'prop-types';
22
import React from 'react';
33
import { connect } from 'react-redux';
4-
import { bindActionCreators, compose } from 'redux';
5-
import { reduxForm } from 'redux-form';
4+
import { bindActionCreators } from 'redux';
65
import { withTranslation } from 'react-i18next';
7-
import i18n from 'i18next';
86
import NewFileForm from './NewFileForm';
97
import { closeNewFileModal } from '../actions/ide';
10-
import { createFile } from '../actions/files';
11-
import { CREATE_FILE_REGEX } from '../../../../server/utils/fileUtils';
12-
138
import ExitIcon from '../../../images/exit.svg';
149

1510

@@ -59,7 +54,6 @@ class NewFileModal extends React.Component {
5954
</div>
6055
<NewFileForm
6156
focusOnModal={this.focusOnModal}
62-
{...this.props}
6357
/>
6458
</div>
6559
</section>
@@ -68,36 +62,16 @@ class NewFileModal extends React.Component {
6862
}
6963

7064
NewFileModal.propTypes = {
71-
createFile: PropTypes.func.isRequired,
7265
closeNewFileModal: PropTypes.func.isRequired,
7366
t: PropTypes.func.isRequired
7467
};
7568

76-
function validate(formProps) {
77-
const errors = {};
78-
79-
if (!formProps.name) {
80-
errors.name = i18n.t('NewFileModal.EnterName');
81-
} else if (!formProps.name.match(CREATE_FILE_REGEX)) {
82-
errors.name = i18n.t('NewFileModal.InvalidType');
83-
}
84-
85-
return errors;
86-
}
87-
8869
function mapStateToProps() {
8970
return {};
9071
}
9172

9273
function mapDispatchToProps(dispatch) {
93-
return bindActionCreators({ createFile, closeNewFileModal }, dispatch);
74+
return bindActionCreators({ closeNewFileModal }, dispatch);
9475
}
9576

96-
export default withTranslation()(compose(
97-
connect(mapStateToProps, mapDispatchToProps),
98-
reduxForm({
99-
form: 'new-file',
100-
fields: ['name'],
101-
validate
102-
})
103-
)(NewFileModal));
77+
export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(NewFileModal));

client/modules/User/pages/AccountView.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function SocialLoginPanel(props) {
2020
const { user } = props;
2121
return (
2222
<React.Fragment>
23-
<AccountForm {...props} />
23+
<AccountForm />
2424
{/* eslint-disable-next-line react/prop-types */}
2525
<h2 className="form-container__divider">{props.t('AccountView.SocialLogin')}</h2>
2626
<p className="account__social-text">

0 commit comments

Comments
 (0)