Skip to content

Commit e565d0e

Browse files
committed
0.0.13 - Files uploader
1 parent 8c42b3e commit e565d0e

File tree

13 files changed

+172
-72
lines changed

13 files changed

+172
-72
lines changed

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-filemanager",
3-
"version": "0.0.12",
3+
"version": "0.0.13",
44
"private": true,
55
"repository": {
66
"type": "git",

public/manifest.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,5 @@
1111
"start_url": ".",
1212
"display": "standalone",
1313
"theme_color": "#fff",
14-
"background_color": "#2196f3",
15-
"orientation": "fullscreen"
14+
"background_color": "#2196f3"
1615
}

src/Actions/Actions.js

+30-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1-
import { getFileList, createFolder, getFileBody, removeFile, moveFile, copyFile } from '../Api/ApiHandler.js';
1+
import {
2+
getFileList, createFolder, getFileBody, removeFile, moveFile, copyFile, uploadFiles as upload
3+
} from '../Api/ApiHandler.js';
24

35

46
/**
57
* Request API to get file list for the selected path then refresh UI
68
* @returns {Function}
79
*/
8-
export const uploadFiles = () => (dispatch, getState) => {
10+
export const uploadFiles = (fileList) => (dispatch, getState) => {
911
const { path } = getState();
1012
dispatch(setLoading(true));
1113
dispatch(setSelectedFiles([]));
14+
dispatch(setFileUploadProgress(50));
1215

13-
getFileList(path.join('/')).then(r => {
14-
dispatch(setLoading(false));
15-
dispatch(setFileList(r));
16+
upload(path.join('/'), fileList).then(r => {
17+
dispatch(setFileUploadProgress(100));
18+
setTimeout(f => {
19+
dispatch(resetFileUploader());
20+
}, 300);
21+
dispatch(refreshFileList());
1622
}).catch(r => {
1723
dispatch({
1824
type: 'SET_ERROR_MSG',
@@ -271,6 +277,12 @@ export const initSubList = () => (dispatch, getState) => {
271277
dispatch(refreshFileListSublist());
272278
};
273279

280+
export const resetFileUploader = () => (dispatch, getState) => {
281+
dispatch(setFileUploadProgress(0));
282+
dispatch(setVisibleModalUploadFile(false));
283+
dispatch(setFileUploadList([]));
284+
};
285+
274286
export const enterToPreviousDirectory = () => (dispatch, getState) => {
275287
const { path } = getState();
276288
dispatch(setPath(path.slice(0, -1)));
@@ -449,11 +461,23 @@ export const setVisibleModalFileEdit = (visible) => {
449461
};
450462
};
451463

452-
453464
export const setFileContent = (blob) => {
454465
return {
455466
type: 'SET_FILE_CONTENT',
456467
value: blob
457468
};
458469
};
459470

471+
export const setFileUploadProgress = (percentage) => {
472+
return {
473+
type: 'SET_FILE_UPLOAD_PROGRESS',
474+
value: percentage
475+
};
476+
};
477+
478+
export const setFileUploadList = (files) => {
479+
return {
480+
type: 'SET_FILE_UPLOAD_LIST',
481+
value: files
482+
};
483+
};

src/Api/Api.js

+22
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,25 @@ export function copy(path, destination, filenames) {
9595
})
9696
});
9797
};
98+
99+
/**
100+
* Fetch API to copy files
101+
* @param {String} path
102+
* @param {Object<FileList>} fileList
103+
* @returns {Object}
104+
*/
105+
export function upload(path, fileList, formData = new FormData()) {
106+
[...fileList].forEach(f => {
107+
formData.append('file[]', f);
108+
});
109+
formData.append('path', path);
110+
111+
return fetch(config.url_upload, {
112+
method: 'POST',
113+
body: formData,
114+
headers: {
115+
// a workaround for node connector, passing the path by header
116+
path: path
117+
}
118+
});
119+
};

src/Api/ApiHandler.js

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { list, createDirectory, getFileContent, remove, move, copy } from './Api.js';
1+
import { list, createDirectory, getFileContent, remove, move, copy, upload } from './Api.js';
22
import config from './../config.js';
33

44
const messageTranslation = {
@@ -151,6 +151,22 @@ export const copyFile = (path, destination, filenames) => {
151151
})
152152
};
153153

154+
/**
155+
* Wrap API response for upload files
156+
* @param {String} path
157+
* @param {Object<FileList>} fileList
158+
* @returns {Object}
159+
*/
160+
export const uploadFiles = (path, fileList) => {
161+
path = fixPath(path);
162+
163+
return new Promise((resolve, reject) => {
164+
return upload(path, fileList)
165+
.then(handleFetch(resolve, reject).xthen)
166+
.catch(handleFetch(resolve, reject).xcatch)
167+
})
168+
};
169+
154170
/**
155171
* Calculate available actions for a file
156172
* @param {String} filename

src/App.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const mapDispatchToProps = (dispatch) => {
5555
dispatch(refreshFileList());
5656
return {
5757
handleHideContextMenu: (event) => {
58-
if (! (event.target.tagName == 'INPUT' || /label/i.test(event.target.className))) {
58+
if (! (event.target.tagName === 'INPUT' || /label/i.test(event.target.className))) {
5959
event.preventDefault();
6060
}
6161
dispatch(setContextMenuVisible(false));

src/Components/Dialogs/UploadFile/UploadFile.jsx

+21-9
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import Dialog from '@material-ui/core/Dialog';
44
import DialogActions from '@material-ui/core/DialogActions';
55
import DialogContent from '@material-ui/core/DialogContent';
66
import DialogTitle from '@material-ui/core/DialogTitle';
7+
import LinearProgress from '@material-ui/core/LinearProgress';
78
import { connect } from 'react-redux';
8-
import { setVisibleModalUploadFile, uploadFiles } from '../../../Actions/Actions.js';
9+
import { resetFileUploader, uploadFiles, setFileUploadList } from '../../../Actions/Actions.js';
910
import FileUploader from '../../FileUploader/FileUploader.jsx';
1011

1112
class FormDialog extends Component {
1213

1314
render() {
14-
const { handleClose, handleUpload, open, canUpload } = this.props;
15+
const { handleClose, handleReset, handleUpload, open, canUpload, fileUploadProgress, fileUploadList, handleSelectedFiles } = this.props;
1516

1617
return (
1718
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
@@ -20,7 +21,8 @@ class FormDialog extends Component {
2021
Upload files
2122
</DialogTitle>
2223
<DialogContent>
23-
<FileUploader handleUpload={handleUpload} />
24+
<FileUploader handleUpload={handleUpload} fileUploadList={fileUploadList} handleSelectedFiles={handleSelectedFiles} handleReset={handleReset}/>
25+
<LinearProgress variant="determinate" value={fileUploadProgress} />
2426
</DialogContent>
2527
<DialogActions>
2628
<Button onClick={handleClose} color="primary" type="button">
@@ -37,21 +39,31 @@ class FormDialog extends Component {
3739
}
3840

3941
const mapStateToProps = (state) => {
40-
// prevent moving to same folder
41-
const canMove = state.path.join('') !== state.pathSublist.join('') + (state.selectedFolderSublist ? state.selectedFolderSublist.name : '');
42-
4342
return {
4443
open: state.visibleModalUploadFile,
45-
canUpload: true,
44+
canUpload: state.fileUploadList.length,
45+
fileUploadList: state.fileUploadList,
46+
fileUploadProgress: state.fileUploadProgress
4647
};
4748
};
4849

4950
const mapDispatchToProps = (dispatch, ownProps) => {
5051
return {
5152
handleClose: (event) => {
52-
dispatch(setVisibleModalUploadFile(false));
53+
dispatch(resetFileUploader());
54+
},
55+
handleUpload: (event) => {
56+
event.preventDefault();
57+
const files = event.currentTarget.form.querySelector('input[type=file]').files;
58+
dispatch(uploadFiles(files));
59+
},
60+
handleSelectedFiles: (event) => {
61+
dispatch(setFileUploadList(
62+
[...event.target.files].map(f => ({name: f.name, size: f.size}))
63+
));
5364
},
54-
handleUpload: (event, selectedFiles) => {
65+
handleReset: (event) => {
66+
dispatch(setFileUploadList([]));
5567
}
5668
};
5769
};
+26-45
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,48 @@
11
import React, { Component } from 'react';
2+
import PropTypes from 'prop-types';
23
import Button from '@material-ui/core/Button';
3-
import Dialog from '@material-ui/core/Dialog';
4-
import DialogActions from '@material-ui/core/DialogActions';
5-
import DialogContent from '@material-ui/core/DialogContent';
6-
import DialogTitle from '@material-ui/core/DialogTitle';
7-
import { connect } from 'react-redux';
8-
import { } from '../../Actions/Actions.js';
4+
import UploadFileList from './UploadFileList';
95

106
class FileUploader extends Component {
117

12-
state = { selectedFiles: [] }
13-
14-
humanReadableFileSize(bytes) {
15-
const e = (Math.log(bytes) / Math.log(1e3)) | 0;
16-
return +(bytes / Math.pow(1e3, e)).toFixed(2) + ' ' + ('kMGTPEZY'[e - 1] || '') + 'B';
17-
}
18-
19-
handleSelectedFile = event => {
20-
this.setState({
21-
selectedFiles: [...event.target.files]
22-
});
8+
handleReset(event) {
9+
this.refs.inputfile.value = '';
10+
this.props.handleReset(event);
2311
}
2412

2513
render() {
26-
const { classes } = this.props;
27-
const filesComp = this.state.selectedFiles.map(f =>
28-
<li key={f.name}>
29-
<span>
30-
{f.name} ({this.humanReadableFileSize(f.size)})
31-
</span>
32-
</li>
33-
);
14+
const { fileUploadList, handleSelectedFiles } = this.props;
15+
const styles = {
16+
inputfile: {
17+
display: 'none'
18+
}, inputreset: {
19+
display: fileUploadList.length ? 'inline-flex' : 'none'
20+
}
21+
}
3422

3523
return (
3624
<div>
37-
<input
38-
style={{display:'none'}} id="contained-button-file"
39-
multiple type="file" onChange={this.handleSelectedFile}
40-
/>
41-
42-
<label htmlFor="contained-button-file">
43-
<Button component="span">
25+
<label htmlFor="button-file">
26+
<input style={styles.inputfile} id="button-file" ref="inputfile" multiple type="file" onChange={handleSelectedFiles} />
27+
<Button component="span" variant="contained" color="primary">
4428
Select Files
4529
</Button>
4630
</label>
4731

48-
<ul>
49-
{ filesComp }
50-
</ul>
32+
<Button style={styles.inputreset} component="span" type="reset" onClick={this.handleReset.bind(this)}>
33+
Clear
34+
</Button>
35+
36+
<UploadFileList files={fileUploadList} />
5137
</div>
5238
);
5339
}
5440
}
5541

56-
const mapStateToProps = (state) => {
57-
return {
58-
uploadProgress: state.uploadProgress || 0
59-
};
60-
};
61-
62-
const mapDispatchToProps = (dispatch, ownProps) => {
63-
return {
64-
};
42+
FileUploader.propTypes = {
43+
fileUploadList: PropTypes.array.isRequired,
44+
handleReset: PropTypes.func.isRequired,
45+
handleSelectedFiles: PropTypes.func.isRequired,
6546
};
6647

67-
export default connect(mapStateToProps, mapDispatchToProps)(FileUploader);
48+
export default FileUploader;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import List from '@material-ui/core/List';
4+
import ListItem from '@material-ui/core/ListItem';
5+
import ListItemIcon from '@material-ui/core/ListItemIcon';
6+
import ListItemText from '@material-ui/core/ListItemText';
7+
import FileIcon from '@material-ui/icons/InsertDriveFile';
8+
9+
function humanFileSize(bytes) {
10+
const e = (Math.log(bytes) / Math.log(1e3)) | 0;
11+
return +(bytes / Math.pow(1e3, e)).toFixed(2) + ' ' + ('kMGTPEZY'[e - 1] || '') + 'B';
12+
}
13+
14+
function UploadFileList(props) {
15+
const { files } = props;
16+
const list = files.map((f, i) =>
17+
<ListItem dense key={i}>
18+
<ListItemIcon>
19+
<FileIcon />
20+
</ListItemIcon>
21+
<ListItemText primary={`${f.name} (${humanFileSize(f.size)})`} />
22+
</ListItem>
23+
);
24+
25+
return (
26+
<div>
27+
<List component="nav">
28+
{list}
29+
</List>
30+
</div>
31+
);
32+
}
33+
34+
UploadFileList.propTypes = {
35+
files: PropTypes.array.isRequired
36+
};
37+
38+
export default UploadFileList;

src/Components/Navbar/ThreeDotsMenu.jsx

-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import React from 'react';
22
import Menu from '@material-ui/core/Menu';
3-
import MenuItem from '@material-ui/core/MenuItem';
43
import IconButton from '@material-ui/core/IconButton';
54
import MoreVertIcon from '@material-ui/icons/MoreVert';
65
import { connect } from 'react-redux';
7-
import { setVisibleModalCreateFolder } from '../../Actions/Actions.js';
86
import CreateFolderAction from '../ContextMenu/ContextMenuActions/CreateFolderAction.jsx';
97
import UploadFileAction from '../ContextMenu/ContextMenuActions/UploadFileAction.jsx';
108

@@ -23,7 +21,6 @@ class ThreeDotsMenu extends React.Component {
2321

2422
render() {
2523
const { anchorEl } = this.state;
26-
const { handleOpenCreateFolder } = this.props;
2724
return (
2825
<div style={{marginLeft:'1em'}}>
2926
<IconButton color="inherit"

0 commit comments

Comments
 (0)