Skip to content

Commit 80e066a

Browse files
authored
Merge pull request #1707 from processing/chore/final-form
Migrate from redux-form to react-final-form
2 parents 7ec42a6 + 10be38d commit 80e066a

24 files changed

+1411
-1275
lines changed

client/modules/IDE/actions/files.js

Lines changed: 101 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import objectID from 'bson-objectid';
22
import blobUtil from 'blob-util';
3-
import { reset } from 'redux-form';
43
import apiClient from '../../../utils/apiClient';
54
import * as ActionTypes from '../../../constants';
65
import { setUnsavedChanges, closeNewFolderModal, closeNewFileModal } from './ide';
76
import { setProjectSavedTime } from './project';
7+
import { createError } from './ide';
88

99

1010
function appendToFilename(filename, string) {
@@ -36,106 +36,117 @@ export function updateFileContent(id, content) {
3636
};
3737
}
3838

39-
export function createFile(formProps) {
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: []
70+
};
71+
return Promise.resolve({
72+
file
73+
});
74+
}
75+
76+
export function handleCreateFile(formProps) {
4077
return (dispatch, getState) => {
4178
const state = getState();
79+
const { files } = state;
4280
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: []
81+
const projectId = state.project.id;
82+
return new Promise((resolve) => {
83+
submitFile(formProps, files, parentId, projectId).then((response) => {
84+
const { file, updatedAt } = response;
85+
dispatch(createFile(file, parentId));
86+
if (updatedAt) dispatch(setProjectSavedTime(updatedAt));
87+
dispatch(closeNewFileModal());
88+
dispatch(setUnsavedChanges(true));
89+
resolve();
90+
}).catch((error) => {
91+
const { response } = error;
92+
dispatch(createError(response.data));
93+
resolve({ error });
8494
});
85-
dispatch(reset('new-file'));
86-
// dispatch({
87-
// type: ActionTypes.HIDE_MODAL
88-
// });
89-
dispatch(setUnsavedChanges(true));
90-
dispatch(closeNewFileModal());
91-
}
95+
});
96+
};
97+
}
98+
99+
export function submitFolder(formProps, files, parentId, projectId) {
100+
if (projectId) {
101+
const postParams = {
102+
name: createUniqueName(formProps.name, parentId, files),
103+
content: '',
104+
children: [],
105+
parentId,
106+
fileType: 'folder'
107+
};
108+
return apiClient.post(`/projects/${projectId}/files`, postParams)
109+
.then(response => ({
110+
file: response.data.updatedFile,
111+
updatedAt: response.data.project.updatedAt
112+
}));
113+
}
114+
const id = objectID().toHexString();
115+
const file = {
116+
type: ActionTypes.CREATE_FILE,
117+
name: createUniqueName(formProps.name, parentId, files),
118+
id,
119+
_id: id,
120+
content: '',
121+
// TODO pass parent id from File Tree
122+
fileType: 'folder',
123+
children: []
92124
};
125+
return Promise.resolve({
126+
file
127+
});
93128
}
94129

95-
export function createFolder(formProps) {
130+
export function handleCreateFolder(formProps) {
96131
return (dispatch, getState) => {
97132
const state = getState();
133+
const { files } = state;
98134
const { parentId } = state.ide;
99-
if (state.project.id) {
100-
const postParams = {
101-
name: createUniqueName(formProps.name, parentId, state.files),
102-
content: '',
103-
children: [],
104-
parentId,
105-
fileType: 'folder'
106-
};
107-
apiClient.post(`/projects/${state.project.id}/files`, postParams)
108-
.then((response) => {
109-
dispatch({
110-
type: ActionTypes.CREATE_FILE,
111-
...response.data.updatedFile,
112-
parentId
113-
});
114-
dispatch(setProjectSavedTime(response.data.project.updatedAt));
115-
dispatch(closeNewFolderModal());
116-
})
117-
.catch((error) => {
118-
const { response } = error;
119-
dispatch({
120-
type: ActionTypes.ERROR,
121-
error: response.data
122-
});
123-
});
124-
} else {
125-
const id = objectID().toHexString();
126-
dispatch({
127-
type: ActionTypes.CREATE_FILE,
128-
name: createUniqueName(formProps.name, parentId, state.files),
129-
id,
130-
_id: id,
131-
content: '',
132-
// TODO pass parent id from File Tree
133-
parentId,
134-
fileType: 'folder',
135-
children: []
135+
const projectId = state.project.id;
136+
return new Promise((resolve) => {
137+
submitFolder(formProps, files, parentId, projectId).then((response) => {
138+
const { file, updatedAt } = response;
139+
dispatch(createFile(file, parentId));
140+
if (updatedAt) dispatch(setProjectSavedTime(updatedAt));
141+
dispatch(closeNewFolderModal());
142+
dispatch(setUnsavedChanges(true));
143+
resolve();
144+
}).catch((error) => {
145+
const { response } = error;
146+
dispatch(createError(response.data));
147+
resolve({ error });
136148
});
137-
dispatch(closeNewFolderModal());
138-
}
149+
});
139150
};
140151
}
141152

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: 73 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,82 @@
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 } from 'react-redux';
5+
import { handleCreateFile } from '../actions/files';
6+
import { CREATE_FILE_REGEX } from '../../../../server/utils/fileUtils';
57

68
import Button from '../../../common/Button';
79

8-
class NewFileForm extends React.Component {
9-
constructor(props) {
10-
super(props);
11-
this.createFile = this.props.createFile.bind(this);
12-
}
10+
function NewFileForm() {
11+
const fileNameInput = useRef(null);
12+
const { t } = useTranslation();
13+
const dispatch = useDispatch();
1314

14-
componentDidMount() {
15-
this.fileName.focus();
15+
function onSubmit(formProps) {
16+
return dispatch(handleCreateFile(formProps));
1617
}
1718

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-
);
19+
function validate(formProps) {
20+
const errors = {};
21+
22+
if (!formProps.name) {
23+
errors.name = t('NewFileModal.EnterName');
24+
} else if (!formProps.name.match(CREATE_FILE_REGEX)) {
25+
errors.name = t('NewFileModal.InvalidType');
26+
}
27+
28+
return errors;
5629
}
57-
}
5830

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-
};
31+
useEffect(() => {
32+
fileNameInput.current.focus();
33+
});
34+
35+
return (
36+
<Form
37+
fields={['name']}
38+
validate={validate}
39+
onSubmit={onSubmit}
40+
>
41+
{({
42+
handleSubmit, errors, touched, invalid, submitting
43+
}) => (
44+
<form
45+
className="new-file-form"
46+
onSubmit={handleSubmit}
47+
>
48+
<div className="new-file-form__input-wrapper">
49+
<Field name="name">
50+
{field => (
51+
<React.Fragment>
52+
<label className="new-file-form__name-label" htmlFor="name">
53+
Name:
54+
</label>
55+
<input
56+
className="new-file-form__name-input"
57+
id="name"
58+
type="text"
59+
placeholder={t('NewFileForm.Placeholder')}
60+
maxLength="128"
61+
{...field.input}
62+
ref={fileNameInput}
63+
/>
64+
</React.Fragment>
65+
)}
66+
</Field>
67+
<Button
68+
type="submit"
69+
disabled={invalid || submitting}
70+
>{t('NewFileForm.AddFileSubmit')}
71+
</Button>
72+
</div>
73+
{touched.name && errors.name && (
74+
<span className="form-error">{errors.name}</span>
75+
)}
76+
</form>
77+
)}
78+
</Form>
79+
);
80+
}
6881

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

0 commit comments

Comments
 (0)