Skip to content

Commit

Permalink
#1133 | WIP: Correctly handle upload of single/multiple image or vide…
Browse files Browse the repository at this point in the history
…o upload in forms
  • Loading branch information
himeshr committed Apr 3, 2024
1 parent 4f438e8 commit 013f999
Showing 1 changed file with 108 additions and 91 deletions.
199 changes: 108 additions & 91 deletions src/dataEntryApp/components/MediaUploader.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { Fragment, useEffect, useState } from "react";
import { get, isEmpty, includes, lowerCase } from "lodash";
import { get, isEmpty, includes, lowerCase, isArrayLikeObject } from "lodash";
import { Box, Button, Grid, makeStyles, Typography } from "@material-ui/core";
import FormControl from "@material-ui/core/FormControl";
import http from "../../common/utils/httpClient";
Expand All @@ -9,7 +9,6 @@ import Audiotrack from "@material-ui/icons/Audiotrack";
import CloudUploadIcon from "@material-ui/icons/CloudUpload";
import CustomizedBackdrop from "./CustomizedBackdrop";
import CloseIcon from "@material-ui/icons/Close";
import ReactImageVideoLightbox from "react-image-video-lightbox";
import { Concept } from "avni-models";
import { FilePreview } from "./FilePreview";
import MediaService from "../../adminApp/service/MediaService";
Expand Down Expand Up @@ -56,134 +55,145 @@ const isValidType = (formElement, type, isFile) => {
: includes(type, lowerCase(formElement.getType()));
};

function updatePreviewWithObsResults(observationValue, setPreview) {
MediaService.getMedia(observationValue).then(res =>
setPreview(oldPreview => ({
...oldPreview,
[observationValue]: res
}))
);
}

function addMediaUrlToLocalObsValue(setLocalObsValue, isMultiSelect, localObsValue, r) {
if (isMultiSelect) {
if (isArrayLikeObject(localObsValue)) {
setLocalObsValue(locObsValue => [...locObsValue, r.data]);
} else {
setLocalObsValue(locObsValue => [locObsValue, r.data].filter(ele => !isEmpty(ele)));
}
} else {
setLocalObsValue(r.data);
}
}

export const MediaUploader = ({ label, obsValue, mediaType, update, formElement }) => {
const classes = useStyles();
const Icon = iconMap[mediaType];
const [value, setValue] = useState("");
const [file, setFile] = useState();
const [preview, setPreview] = useState();
const [localObsValue, setLocalObsValue] = useState(obsValue);
const [preview, setPreview] = useState({});
const [uploadButtonClicked, setUploadButtonClicked] = useState(0);
const [uploading, setUploading] = useState(false);
const [openImage, setOpenImage] = useState(false);
const isFileDataType = formElement.getType() === Concept.dataType.File;
const supportedMIMEType = isFileDataType ? getFileMimeType(formElement) : `${mediaType}/*`;
const isMultiSelect = formElement.isMultiSelect();

useEffect(() => {
if (!file) {
setPreview();
return;
}
const objectUrl = URL.createObjectURL(file);
setPreview(objectUrl);
// free memory when ever this component is unmounted
return () => URL.revokeObjectURL(objectUrl);
}, [file]);
// Push consolidated changes to ObservationHolder, doing it directly without state messes up the multi-select flows
update(localObsValue);
}, [localObsValue]);

useEffect(() => {
if (!isEmpty(obsValue)) {
MediaService.getMedia(obsValue).then(res => setPreview(res));
if (isArrayLikeObject(obsValue)) {
obsValue.forEach(obsItrValue => updatePreviewWithObsResults(obsItrValue, setPreview));
} else {
updatePreviewWithObsResults(obsValue, setPreview);
}
}
}, [obsValue]);

useEffect(() => {
if (openImage) {
const LightBoxContainer = document.querySelector("div.imagePreviewContainer");
LightBoxContainer.firstChild.style.zIndex = 1;
}
}, [openImage]);
setUploading(uploadButtonClicked > 0);
}, [uploadButtonClicked]);

const onMediaSelect = event => {
const fileReader = new FileReader();
event.target.files[0] && fileReader.readAsText(event.target.files[0]);
const file = event.target.files[0];
if (!isValidType(formElement, file.type, isFileDataType)) {
alert("Selected file type not supported. Please choose proper file.");
return;
}
if (isFileDataType && isBiggerFile(formElement, file.size)) {
const oneMBInBytes = 1000000;
alert(
`Selected file size ${file.size /
oneMBInBytes} MB is more than the set max file size ${formElement.allowedMaxSize /
oneMBInBytes} MB.`
);
const alerts = [];
const etFiles = Array.from(event.target.files);
etFiles.forEach(file => {
if (!isValidType(formElement, file.type, isFileDataType)) {
alerts.push(
`Selected files type not supported for file ${file.name}. Please choose proper files.\n`
);
}
if (isFileDataType && isBiggerFile(formElement, file.size)) {
const oneMBInBytes = 1000000;
alerts.push(
`Selected file size ${file.size /
oneMBInBytes} MB is more than the set max file size ${formElement.allowedMaxSize /
oneMBInBytes} MB for file ${file.name}.\n`
);
}
});
if (!isEmpty(alerts)) {
alert(alerts);
return;
}
setValue(event.target.value);
fileReader.onloadend = event => {
setFile(file);
setUploading(true);
http
.uploadFile("/web/uploadMedia", file)
.then(r => {
setUploading(false);
update(r.data);
})
.catch(r => {
const error = `${get(r, "response.data") ||
get(r, "message") ||
"Unknown error occurred while uploading media"}`;
setUploading(false);
onDelete();
alert(error);
});
};
etFiles.forEach(file => {
const fileReader = new FileReader();
file && fileReader.readAsText(file);
setUploadButtonClicked(oldValue => oldValue + 1);
fileReader.onloadend = event => {
http
.uploadFile("/web/uploadMedia", file)
.then(r => {
setUploadButtonClicked(oldValue => oldValue - 1);
addMediaUrlToLocalObsValue(setLocalObsValue, isMultiSelect, localObsValue, r);
})
.catch(r => {
const error = `${get(r, "response.data") ||
get(r, "message") ||
"Unknown error occurred while uploadButtonClicked media"}`;
setUploadButtonClicked(oldValue => oldValue - 1);
onDelete(file.name);
alert(error);
});
};
});
};

const onDelete = () => {
setFile();
setPreview();
update();
preview && URL.revokeObjectURL(preview);
const onDelete = fileName => {
if (isMultiSelect && isArrayLikeObject(localObsValue)) {
const filteredObsValue = localObsValue.filter(item => item != fileName);
setLocalObsValue([...filteredObsValue]);
} else {
if (localObsValue === fileName) setLocalObsValue(); //Remove previous value
}
preview[fileName] && URL.revokeObjectURL(preview[fileName]);
setPreview(oldPreview => ({
...oldPreview,
[fileName]: null
}));
};

const mediaPreviewMap = {
image: (
<img src={preview} alt={label} width={200} height={200} onClick={() => setOpenImage(true)} />
),
//TODO ONCLICK open in new tab
const mediaPreviewMap = fileToPreview => ({
image: <img src={fileToPreview} alt={label} width={200} height={200} />,
video: (
<video preload="auto" controls width={200} height={200} controlsList="nodownload">
<source src={preview} type="video/mp4" />
<source src={fileToPreview} type="video/mp4" />
Sorry, your browser doesn't support embedded videos.
</video>
),
audio: <audio preload="auto" controls src={preview} controlsList="nodownload" />,
file: <FilePreview url={preview} obsValue={obsValue} />
};
audio: <audio preload="auto" controls src={fileToPreview} controlsList="nodownload" />,
file: <FilePreview url={fileToPreview} obsValue={obsValue} />
});

const renderMedia = () => {
const renderMedia = fileName => {
return (
<Box
display={"flex"}
flexDirection={"row"}
alignItems={"flex-start"}
className={classes.boxStyle}
>
{mediaPreviewMap[mediaType]}
<Button style={{ float: "left", color: "red" }} onClick={onDelete}>
{mediaPreviewMap(preview[fileName])[mediaType]}
<Button style={{ float: "left", color: "red" }} onClick={() => onDelete(fileName)}>
<CloseIcon />
</Button>
</Box>
);
};

const previewImage = () => {
return (
<div className="imagePreviewContainer">
<ReactImageVideoLightbox
data={[
{
url: preview,
type: "photo",
altTag: label
}
]}
startIndex={0}
onCloseCallback={() => setOpenImage(false)}
/>
</div>
);
};

return (
<Fragment>
<FormControl>
Expand All @@ -202,7 +212,7 @@ export const MediaUploader = ({ label, obsValue, mediaType, update, formElement
className={classes.input}
id="contained-button-file"
type="file"
value={value}
multiple={isMultiSelect}
onChange={onMediaSelect}
style={{ display: "none" }}
onClick={event => (event.target.value = null)}
Expand All @@ -211,8 +221,15 @@ export const MediaUploader = ({ label, obsValue, mediaType, update, formElement
</Grid>
</Grid>
</FormControl>
<Grid item>{preview ? renderMedia() : null}</Grid>
{openImage && previewImage()}
<Grid container direction="row" alignItems="center" spacing={1}>
{Object.keys(preview).map(fileName => {
return (
<Grid item key={fileName}>
{preview[fileName] ? renderMedia(fileName) : null}
</Grid>
);
})}
</Grid>
<CustomizedBackdrop load={!uploading} />
</Fragment>
);
Expand Down

0 comments on commit 013f999

Please sign in to comment.