mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
enable multi file upload
This commit is contained in:
@@ -23,6 +23,7 @@ import { DATE_TIME_FORMAT } from "@src/constants/dates";
|
||||
import { fieldSx, getFieldId } from "@src/components/SideDrawer/utils";
|
||||
import { projectScope, confirmDialogAtom } from "@src/atoms/projectScope";
|
||||
import { FileValue } from "@src/types/table";
|
||||
import { arrayUnion } from "firebase/firestore";
|
||||
|
||||
export default function File_({
|
||||
column,
|
||||
@@ -37,25 +38,22 @@ export default function File_({
|
||||
const { uploaderState, upload, deleteUpload } = useUploader();
|
||||
|
||||
// Store a preview image locally while uploading
|
||||
const [localFile, setLocalFile] = useState<string>("");
|
||||
const [localFiles, setLocalFiles] = useState<string[]>([]);
|
||||
|
||||
const onDrop = useCallback(
|
||||
(acceptedFiles: File[]) => {
|
||||
const file = acceptedFiles[0];
|
||||
|
||||
if (_rowy_ref && file) {
|
||||
if (acceptedFiles.length > 0) {
|
||||
upload({
|
||||
docRef: _rowy_ref! as any,
|
||||
fieldName: column.key,
|
||||
files: [file],
|
||||
previousValue: value ?? [],
|
||||
onComplete: (newValue) => {
|
||||
onChange(newValue);
|
||||
files: acceptedFiles,
|
||||
onComplete: (newUploads) => {
|
||||
onChange(arrayUnion(newUploads));
|
||||
onSubmit();
|
||||
setLocalFile("");
|
||||
setLocalFiles([]);
|
||||
},
|
||||
});
|
||||
setLocalFile(file.name);
|
||||
setLocalFiles(acceptedFiles.map((file) => file.name));
|
||||
}
|
||||
},
|
||||
[_rowy_ref, value]
|
||||
@@ -71,7 +69,7 @@ export default function File_({
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
onDrop,
|
||||
multiple: false,
|
||||
multiple: true,
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -138,15 +136,18 @@ export default function File_({
|
||||
</Grid>
|
||||
))}
|
||||
|
||||
{localFile && (
|
||||
<Grid item>
|
||||
<Chip
|
||||
icon={<FileIcon />}
|
||||
label={localFile}
|
||||
deleteIcon={<CircularProgressOptical size={20} color="inherit" />}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
{localFiles &&
|
||||
localFiles.map((fileName) => (
|
||||
<Grid item>
|
||||
<Chip
|
||||
icon={<FileIcon />}
|
||||
label={fileName}
|
||||
deleteIcon={
|
||||
<CircularProgressOptical size={20} color="inherit" />
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { IHeavyCellProps } from "@src/components/fields/types";
|
||||
import { useSetAtom } from "jotai";
|
||||
import { findIndex } from "lodash-es";
|
||||
@@ -17,6 +17,7 @@ import useUploader from "@src/hooks/useFirebaseStorageUploader";
|
||||
import { FileIcon } from ".";
|
||||
import { DATE_TIME_FORMAT } from "@src/constants/dates";
|
||||
import { FileValue } from "@src/types/table";
|
||||
import { arrayUnion } from "firebase/firestore";
|
||||
|
||||
export default function File_({
|
||||
column,
|
||||
@@ -28,28 +29,26 @@ export default function File_({
|
||||
}: IHeavyCellProps) {
|
||||
const confirm = useSetAtom(confirmDialogAtom, projectScope);
|
||||
const updateField = useSetAtom(updateFieldAtom, tableScope);
|
||||
|
||||
const [localFiles, setLocalFiles] = useState<string[]>([]);
|
||||
const { uploaderState, upload, deleteUpload } = useUploader();
|
||||
const { progress, isLoading } = uploaderState;
|
||||
|
||||
const onDrop = useCallback(
|
||||
(acceptedFiles: File[]) => {
|
||||
const file = acceptedFiles[0];
|
||||
|
||||
if (file) {
|
||||
if (acceptedFiles.length > 0) {
|
||||
upload({
|
||||
docRef: docRef! as any,
|
||||
fieldName: column.key,
|
||||
files: [file],
|
||||
previousValue: value,
|
||||
onComplete: (newValue) => {
|
||||
files: acceptedFiles,
|
||||
onComplete: (newUploads) => {
|
||||
updateField({
|
||||
path: docRef.path,
|
||||
fieldName: column.key,
|
||||
value: newValue,
|
||||
value: arrayUnion(newUploads),
|
||||
});
|
||||
setLocalFiles([]);
|
||||
},
|
||||
});
|
||||
setLocalFiles(acceptedFiles.map((file) => file.name));
|
||||
}
|
||||
},
|
||||
[value]
|
||||
@@ -65,7 +64,7 @@ export default function File_({
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
onDrop,
|
||||
multiple: false,
|
||||
multiple: true,
|
||||
});
|
||||
|
||||
const dropzoneProps = getRootProps();
|
||||
@@ -135,6 +134,18 @@ export default function File_({
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
))}
|
||||
{localFiles &&
|
||||
localFiles.map((fileName) => (
|
||||
<Grid item>
|
||||
<Chip
|
||||
icon={<FileIcon />}
|
||||
label={fileName}
|
||||
deleteIcon={
|
||||
<CircularProgressOptical size={20} color="inherit" />
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</ChipList>
|
||||
|
||||
{!isLoading ? (
|
||||
|
||||
@@ -26,6 +26,7 @@ import CircularProgressOptical from "@src/components/CircularProgressOptical";
|
||||
import { projectScope, confirmDialogAtom } from "@src/atoms/projectScope";
|
||||
import { IMAGE_MIME_TYPES } from ".";
|
||||
import { fieldSx, getFieldId } from "@src/components/SideDrawer/utils";
|
||||
import { arrayUnion } from "firebase/firestore";
|
||||
|
||||
const imgSx = {
|
||||
position: "relative",
|
||||
@@ -93,25 +94,21 @@ export default function Image_({
|
||||
const { progress } = uploaderState;
|
||||
|
||||
// Store a preview image locally while uploading
|
||||
const [localImage, setLocalImage] = useState<string>("");
|
||||
const [localImages, setLocalImages] = useState<string[]>([]);
|
||||
|
||||
const onDrop = useCallback(
|
||||
(acceptedFiles: File[]) => {
|
||||
const imageFile = acceptedFiles[0];
|
||||
|
||||
if (_rowy_ref && imageFile) {
|
||||
if (_rowy_ref && acceptedFiles.length > 0) {
|
||||
upload({
|
||||
docRef: _rowy_ref! as any,
|
||||
fieldName: column.key,
|
||||
files: [imageFile],
|
||||
previousValue: value ?? [],
|
||||
onComplete: (newValue) => {
|
||||
onChange(newValue);
|
||||
onSubmit();
|
||||
setLocalImage("");
|
||||
files: acceptedFiles,
|
||||
onComplete: (newUploads) => {
|
||||
onChange(arrayUnion(newUploads));
|
||||
setLocalImages([]);
|
||||
},
|
||||
});
|
||||
setLocalImage(URL.createObjectURL(imageFile));
|
||||
setLocalImages(acceptedFiles.map((file) => URL.createObjectURL(file)));
|
||||
}
|
||||
},
|
||||
[_rowy_ref, value]
|
||||
@@ -127,7 +124,7 @@ export default function Image_({
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
onDrop,
|
||||
multiple: false,
|
||||
multiple: true,
|
||||
accept: IMAGE_MIME_TYPES,
|
||||
});
|
||||
|
||||
@@ -237,29 +234,30 @@ export default function Image_({
|
||||
</Grid>
|
||||
))}
|
||||
|
||||
{localImage && (
|
||||
<Grid item>
|
||||
<ButtonBase
|
||||
sx={imgSx}
|
||||
style={{ backgroundImage: `url("${localImage}")` }}
|
||||
className="img"
|
||||
>
|
||||
<Grid
|
||||
container
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
sx={overlaySx}
|
||||
{localImages &&
|
||||
localImages.map((image, i) => (
|
||||
<Grid item>
|
||||
<ButtonBase
|
||||
sx={imgSx}
|
||||
style={{ backgroundImage: `url("${image}")` }}
|
||||
className="img"
|
||||
>
|
||||
<CircularProgressOptical
|
||||
color="inherit"
|
||||
size={48}
|
||||
variant={progress === 0 ? "indeterminate" : "determinate"}
|
||||
value={progress}
|
||||
/>
|
||||
</Grid>
|
||||
</ButtonBase>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid
|
||||
container
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
sx={overlaySx}
|
||||
>
|
||||
<CircularProgressOptical
|
||||
color="inherit"
|
||||
size={48}
|
||||
variant={progress === 0 ? "indeterminate" : "determinate"}
|
||||
value={progress}
|
||||
/>
|
||||
</Grid>
|
||||
</ButtonBase>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -32,6 +32,7 @@ import useUploader from "@src/hooks/useFirebaseStorageUploader";
|
||||
import { IMAGE_MIME_TYPES } from "./index";
|
||||
import { DEFAULT_ROW_HEIGHT } from "@src/components/Table";
|
||||
import { FileValue } from "@src/types/table";
|
||||
import { arrayUnion } from "firebase/firestore";
|
||||
|
||||
// MULTIPLE
|
||||
const imgSx = (rowHeight: number) => ({
|
||||
@@ -99,28 +100,25 @@ export default function Image_({
|
||||
const { progress, isLoading } = uploaderState;
|
||||
|
||||
// Store a preview image locally while uploading
|
||||
const [localImage, setLocalImage] = useState<string>("");
|
||||
const [localImages, setLocalImages] = useState<string[]>([]);
|
||||
|
||||
const onDrop = useCallback(
|
||||
(acceptedFiles: File[]) => {
|
||||
const imageFile = acceptedFiles[0];
|
||||
|
||||
if (imageFile) {
|
||||
if (acceptedFiles.length > 0) {
|
||||
upload({
|
||||
docRef: docRef! as any,
|
||||
fieldName: column.key,
|
||||
files: [imageFile],
|
||||
previousValue: value,
|
||||
onComplete: (newValue) => {
|
||||
files: acceptedFiles,
|
||||
onComplete: (newUploads) => {
|
||||
updateField({
|
||||
path: docRef.path,
|
||||
fieldName: column.key,
|
||||
value: newValue,
|
||||
value: arrayUnion(newUploads),
|
||||
});
|
||||
setLocalImage("");
|
||||
setLocalImages([]);
|
||||
},
|
||||
});
|
||||
setLocalImage(URL.createObjectURL(imageFile));
|
||||
setLocalImages(acceptedFiles.map((file) => URL.createObjectURL(file)));
|
||||
}
|
||||
},
|
||||
[value]
|
||||
@@ -136,7 +134,7 @@ export default function Image_({
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
onDrop,
|
||||
multiple: false,
|
||||
multiple: true,
|
||||
accept: IMAGE_MIME_TYPES,
|
||||
});
|
||||
|
||||
@@ -250,20 +248,21 @@ export default function Image_({
|
||||
</Grid>
|
||||
))}
|
||||
|
||||
{localImage && (
|
||||
<Grid item>
|
||||
<Box
|
||||
sx={[
|
||||
imgSx(rowHeight),
|
||||
{
|
||||
boxShadow: (theme) =>
|
||||
`0 0 0 1px ${theme.palette.divider} inset`,
|
||||
},
|
||||
]}
|
||||
style={{ backgroundImage: `url("${localImage}")` }}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
{localImages &&
|
||||
localImages.map((url) => (
|
||||
<Grid item>
|
||||
<Box
|
||||
sx={[
|
||||
imgSx(rowHeight),
|
||||
{
|
||||
boxShadow: (theme) =>
|
||||
`0 0 0 1px ${theme.palette.divider} inset`,
|
||||
},
|
||||
]}
|
||||
style={{ backgroundImage: `url("${url}")` }}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -33,8 +33,7 @@ export type UploadProps = {
|
||||
docRef: DocumentReference;
|
||||
fieldName: string;
|
||||
files: File[];
|
||||
previousValue?: FileValue[];
|
||||
onComplete?: (values: FileValue[]) => void;
|
||||
onComplete?: (value: FileValue) => void;
|
||||
};
|
||||
|
||||
// TODO: GENERALIZE INTO ATOM
|
||||
@@ -46,13 +45,7 @@ const useFirebaseStorageUploader = () => {
|
||||
...initialState,
|
||||
});
|
||||
|
||||
const upload = ({
|
||||
docRef,
|
||||
fieldName,
|
||||
files,
|
||||
previousValue,
|
||||
onComplete,
|
||||
}: UploadProps) => {
|
||||
const upload = ({ docRef, fieldName, files, onComplete }: UploadProps) => {
|
||||
uploaderDispatch({ isLoading: true });
|
||||
|
||||
files.forEach((file) => {
|
||||
@@ -139,24 +132,20 @@ const useFirebaseStorageUploader = () => {
|
||||
// Upload completed successfully, now we can get the download URL
|
||||
getDownloadURL(uploadTask.snapshot.ref).then(
|
||||
(downloadURL: string) => {
|
||||
const newValue: FileValue[] = Array.isArray(previousValue)
|
||||
? [...previousValue]
|
||||
: [];
|
||||
|
||||
newValue.push({
|
||||
ref: uploadTask.snapshot.ref.fullPath,
|
||||
downloadURL,
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
lastModifiedTS: file.lastModified,
|
||||
});
|
||||
// STore in the document if docRef provided
|
||||
// if (docRef && docRef.update)docRef.update({ [fieldName]: newValue });
|
||||
// Also call callback if it exists
|
||||
// IMPORTANT: SideDrawer form may not update its local values after this
|
||||
// function updates the doc, so you MUST update it manually
|
||||
// using this callback
|
||||
if (onComplete) onComplete(newValue);
|
||||
const obj = {
|
||||
ref: uploadTask.snapshot.ref.fullPath,
|
||||
downloadURL,
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
lastModifiedTS: file.lastModified,
|
||||
};
|
||||
if (onComplete) onComplete(obj);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user