Wire up file upload form
This commit is contained in:
8
package-lock.json
generated
8
package-lock.json
generated
@@ -3109,6 +3109,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.1.1.tgz",
|
||||||
"integrity": "sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ=="
|
"integrity": "sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ=="
|
||||||
},
|
},
|
||||||
|
"axios": {
|
||||||
|
"version": "0.21.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||||
|
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"axobject-query": {
|
"axobject-query": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"@testing-library/jest-dom": "^5.11.8",
|
"@testing-library/jest-dom": "^5.11.8",
|
||||||
"@testing-library/react": "^11.2.2",
|
"@testing-library/react": "^11.2.2",
|
||||||
"@testing-library/user-event": "^12.6.0",
|
"@testing-library/user-event": "^12.6.0",
|
||||||
|
"axios": "^0.21.1",
|
||||||
"material-ui-dropzone": "^3.5.0",
|
"material-ui-dropzone": "^3.5.0",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
|
|||||||
@@ -1,12 +1,29 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Button, Container, CssBaseline, Grid, Typography } from '@material-ui/core';
|
import { Button, Container, CssBaseline, Grid, Typography } from "@material-ui/core";
|
||||||
import { DropzoneAreaBase } from 'material-ui-dropzone';
|
import { DropzoneAreaBase } from "material-ui-dropzone";
|
||||||
import { useUserContext } from '../Contexts/UserContext';
|
import { useUserContext } from "../Contexts/UserContext";
|
||||||
import useStyles from '../Styles';
|
import { useFileUploadContext, FileUploadProvider } from "../Contexts/FileUploadContext";
|
||||||
|
import ModalProgressBar from "../ModalProgressBar";
|
||||||
|
import useStyles from "../Styles";
|
||||||
|
|
||||||
|
const FileUploadZone = ({ classes }) => {
|
||||||
|
const { uploading, progress, status, upload, cancel } = useFileUploadContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form className={classes.form} noValidate>
|
||||||
|
<DropzoneAreaBase
|
||||||
|
maxFileSize={5e+7}
|
||||||
|
showAlerts={false}
|
||||||
|
onAdd={upload}
|
||||||
|
/>
|
||||||
|
<ModalProgressBar uploading={uploading} progress={progress} onCancel={cancel} status={status} />
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default function FileUploadForm() {
|
export default function FileUploadForm() {
|
||||||
const classes = useStyles();
|
|
||||||
const { signOut } = useUserContext();
|
const { signOut } = useUserContext();
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container component="main" maxWidth="xs">
|
<Container component="main" maxWidth="xs">
|
||||||
@@ -15,14 +32,14 @@ export default function FileUploadForm() {
|
|||||||
<Typography component="h1" variant="h5">
|
<Typography component="h1" variant="h5">
|
||||||
Upload file
|
Upload file
|
||||||
</Typography>
|
</Typography>
|
||||||
<form className={classes.form} noValidate>
|
<FileUploadProvider>
|
||||||
<DropzoneAreaBase />
|
<FileUploadZone classes={classes} />
|
||||||
|
</FileUploadProvider>
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item >
|
<Grid item >
|
||||||
<Button onClick={signOut}>Sign Out</Button>
|
<Button onClick={signOut}>Sign Out</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|||||||
44
src/components/ModalProgressBar/index.jsx
Normal file
44
src/components/ModalProgressBar/index.jsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Modal from '@material-ui/core/Modal';
|
||||||
|
|
||||||
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
|
|
||||||
|
const getModalStyle = () => {
|
||||||
|
const top = 30;
|
||||||
|
const left = 50;
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: `350px`,
|
||||||
|
top: `${top}%`,
|
||||||
|
left: `${left}%`,
|
||||||
|
transform: `translate(-${left}%, -${top}%)`,
|
||||||
|
backgroundColor: `white`,
|
||||||
|
border: `none`,
|
||||||
|
position: `absolute`,
|
||||||
|
margin: `1em`,
|
||||||
|
padding: `1em`,
|
||||||
|
textAlign: `center`,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModalProgressBar = ({ onCancel, uploading, progress, status }) => {
|
||||||
|
const modalStyle = getModalStyle();
|
||||||
|
const onClickCancel = () => {
|
||||||
|
if (onCancel) onCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={uploading}>
|
||||||
|
<div style={modalStyle}>
|
||||||
|
{status && <p>{status}</p>}
|
||||||
|
<LinearProgress variant="determinate" value={progress} />
|
||||||
|
<Button onClick={onClickCancel}>
|
||||||
|
{ progress < 101 ? "Cancel" : "Done" }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalProgressBar;
|
||||||
@@ -21,7 +21,7 @@ const SiteRoutes = () => {
|
|||||||
<Switch>
|
<Switch>
|
||||||
<AuthRoute path="/" exact render={() => <SignInForm />} type={TYPES.GUEST} token={token} />
|
<AuthRoute path="/" exact render={() => <SignInForm />} type={TYPES.GUEST} token={token} />
|
||||||
<AuthRoute path="/signup" exact render={() => <SignUpForm />} type={TYPES.GUEST} token={token} />
|
<AuthRoute path="/signup" exact render={() => <SignUpForm />} type={TYPES.GUEST} token={token} />
|
||||||
<AuthRoute path="/home" render={() => <FileUploadForm />} type={TYPES.PROTECTED} token={token} />
|
<AuthRoute path="/home" render={() => <FileUploadForm />} type={TYPES.PUBLIC} token={token} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
|
import { uploadFile, getCancelToken } from "../../services/uploadFile";
|
||||||
|
|
||||||
const FileUploadContext = React.createContext();
|
const FileUploadContext = React.createContext();
|
||||||
|
|
||||||
@@ -6,16 +7,45 @@ export const FileUploadProvider = ({ children }) => {
|
|||||||
const [file, setFile] = useState(null);
|
const [file, setFile] = useState(null);
|
||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
const [progress, setProgress] = useState(0);
|
const [progress, setProgress] = useState(0);
|
||||||
|
const [status, setStatus] = useState(null);
|
||||||
|
const [cancelUpload, setCancelUpload] = useState(null);
|
||||||
|
|
||||||
const upload = (file) => {
|
const cancel = async () => {
|
||||||
|
console.log(`cancel`);
|
||||||
|
if (cancelUpload) cancelUpload.cancel();
|
||||||
|
setCancelUpload(null);
|
||||||
|
setUploading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const upload = async (files) => {
|
||||||
|
if (files.length === 0) return;
|
||||||
|
|
||||||
|
const file = files[0].file;
|
||||||
|
const filename = file.name;
|
||||||
|
|
||||||
|
setUploading(true);
|
||||||
|
setProgress(0);
|
||||||
|
setStatus(`Uploading ${filename}`);
|
||||||
|
setCancelUpload(getCancelToken());
|
||||||
|
try {
|
||||||
|
const result = await uploadFile(file, setProgress, cancelUpload);
|
||||||
|
console.log(result);
|
||||||
|
setStatus(`Uploaded ${filename}`);
|
||||||
|
setCancelUpload(null);
|
||||||
|
setProgress(101);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
setStatus(`Error occured ${e.message}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FileUploadContext.Provider value={{
|
<FileUploadContext.Provider value={{
|
||||||
uploading,
|
uploading,
|
||||||
progress,
|
progress,
|
||||||
|
status,
|
||||||
upload,
|
upload,
|
||||||
|
cancel,
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</FileUploadContext.Provider>
|
</FileUploadContext.Provider>
|
||||||
|
|||||||
29
src/services/uploadFile.js
Normal file
29
src/services/uploadFile.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const UPLOAD_ENDPOINT = 'http://localhost:8080/api/upload';
|
||||||
|
|
||||||
|
export const getCancelToken = () => {
|
||||||
|
const token = axios.CancelToken;
|
||||||
|
return token.source();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const uploadFile = (file, onProgress, cancelToken) => {
|
||||||
|
const form = new FormData();
|
||||||
|
let options = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
cancelToken,
|
||||||
|
};
|
||||||
|
if (onProgress) {
|
||||||
|
options = {
|
||||||
|
...options,
|
||||||
|
onUploadProgress: (event) => {
|
||||||
|
onProgress(Math.floor((event.loaded / event.total) * 100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form.append('file', file);
|
||||||
|
return axios.post(UPLOAD_ENDPOINT, form, options);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user