400 lines
10 KiB
JavaScript
400 lines
10 KiB
JavaScript
import { useActionSheet } from "@expo/react-native-action-sheet";
|
|
import * as DocumentPicker from "expo-document-picker";
|
|
import * as ImagePicker from "expo-image-picker";
|
|
import moment from "moment";
|
|
import { Image, Pressable, Text, View } from "react-native";
|
|
import Compressor from "react-native-compressor";
|
|
import useMinuit from "react-native-minuit/src/hooks/useMinuit";
|
|
import React, { useCallback, useEffect, useRef, useState } from "reactn";
|
|
|
|
import { arrayUnion } from "../config/firebase";
|
|
|
|
import { icons } from "../assets";
|
|
import { Fonts, Palette } from "../styles";
|
|
import Style, { gutterConstant, mainBorderRadius } from "../styles/Style";
|
|
|
|
import { uploadFileToFirebase } from "../helpers/uploadToFirebase";
|
|
import useLayoutType from "../hooks/useLayoutType";
|
|
|
|
const NATIVE_OPTIONS = [
|
|
"Importer depuis la galerie",
|
|
"Ajouter un fichier",
|
|
"Prendre une photo ou une vidéo",
|
|
"Annuler",
|
|
];
|
|
|
|
const DocumentDropZone = ({
|
|
containerStyle = {},
|
|
|
|
documentID = null,
|
|
documentExists = false,
|
|
|
|
collectionRef = null,
|
|
|
|
setFiles = null,
|
|
customElement = null,
|
|
shouldReturnObject = false,
|
|
}) => {
|
|
const dropRef = useRef(null);
|
|
|
|
const [isDragging, setIsDragging] = useState(false);
|
|
|
|
const { setIsLoading, setTooltip } = useMinuit();
|
|
const { isDesktop, isWeb, isNative } = useLayoutType();
|
|
const { showActionSheetWithOptions } = useActionSheet();
|
|
|
|
useEffect(() => {
|
|
if (isWeb && dropRef.current) {
|
|
const el = dropRef.current;
|
|
|
|
const handleDragIn = (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
setIsDragging(true);
|
|
};
|
|
|
|
const handleDragOut = (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if (!dropRef.current.contains(e.relatedTarget)) {
|
|
console.log("drag left");
|
|
setIsDragging(false);
|
|
}
|
|
};
|
|
|
|
const handleDrop = (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
setIsDragging(false);
|
|
|
|
let files = [...e.dataTransfer.files];
|
|
|
|
if (files.length > 0) {
|
|
for (const file of files) {
|
|
handleWebFile({ file });
|
|
}
|
|
}
|
|
};
|
|
|
|
el.addEventListener("dragenter", handleDragIn);
|
|
el.addEventListener("dragleave", handleDragOut);
|
|
el.addEventListener("dragover", (e) => e.preventDefault());
|
|
el.addEventListener("drop", handleDrop);
|
|
|
|
return () => {
|
|
el.removeEventListener("dragenter", handleDragIn);
|
|
el.removeEventListener("dragleave", handleDragOut);
|
|
el.removeEventListener("dragover", (e) => e.preventDefault());
|
|
el.removeEventListener("drop", handleDrop);
|
|
};
|
|
}
|
|
}, [dropRef?.current]);
|
|
|
|
const handleWebFile = ({ file }) => {
|
|
try {
|
|
const { type = "" } = file;
|
|
const reader = new FileReader();
|
|
|
|
reader.onloadend = () => {
|
|
const base64 = reader.result.split(",")[1]; // Vous pourriez avoir besoin de cette valeur base64 pour un upload direct
|
|
const uri = reader.result; // URI en base64 du fichier
|
|
|
|
console.log("file", file);
|
|
|
|
onUploadDocument({ files: [{ name: file.name, uri, type }] });
|
|
};
|
|
|
|
reader.onerror = (err) => {
|
|
console.error("FileReader error", err);
|
|
};
|
|
|
|
reader.readAsDataURL(file); // Lire le fichier et déclencher reader.onloadend lorsque c'est fait
|
|
} catch (error) {
|
|
console.log(error);
|
|
setTooltip({ text: error.message, type: "error" });
|
|
}
|
|
};
|
|
|
|
const onAddFile = async ({} = {}) => {
|
|
try {
|
|
if (isNative) {
|
|
const cancelButtonIndex = NATIVE_OPTIONS.length - 1;
|
|
|
|
showActionSheetWithOptions(
|
|
{
|
|
options: NATIVE_OPTIONS,
|
|
cancelButtonIndex,
|
|
userInterfaceStyle: "dark",
|
|
...Style.actionSheet,
|
|
},
|
|
async (selectedIndex) => {
|
|
if (selectedIndex !== cancelButtonIndex) {
|
|
switch (selectedIndex) {
|
|
case 0:
|
|
onChooseLibrary();
|
|
break;
|
|
case 1:
|
|
onChooseDocumentPicker();
|
|
break;
|
|
case 2:
|
|
onTakePicture();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
);
|
|
} else {
|
|
onChooseDocumentPicker();
|
|
}
|
|
} catch (error) {
|
|
console.log(error);
|
|
setTooltip({ text: error.message, type: "error" });
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const onChooseLibrary = async ({} = {}) => {
|
|
const result = await ImagePicker.launchImageLibraryAsync({
|
|
mediaTypes: ImagePicker.MediaTypeOptions.All,
|
|
allowsEditing: false,
|
|
quality: 3,
|
|
});
|
|
|
|
if (result?.assets?.[0]?.uri) {
|
|
const { uri, fileName = "" } = result?.assets?.[0];
|
|
|
|
onUploadDocument({
|
|
files: [
|
|
{
|
|
name: getAssetName({ fileName, uri }),
|
|
uri,
|
|
type: "IMAGE",
|
|
},
|
|
],
|
|
});
|
|
} else {
|
|
throw new Error("Aucune image sélectionnée");
|
|
}
|
|
};
|
|
|
|
const onChooseDocumentPicker = async ({} = {}) => {
|
|
const result = await DocumentPicker.getDocumentAsync({
|
|
type: "*/*",
|
|
copyToCacheDirectory: false,
|
|
});
|
|
|
|
if (result?.assets?.length > 0) {
|
|
const { name = "", uri = null } = result?.assets[0] || {};
|
|
|
|
if (!uri) {
|
|
throw new Error("Erreur lors de l'ajout du document");
|
|
}
|
|
|
|
onUploadDocument({
|
|
files: [
|
|
{
|
|
name: name || getDefaultFileName(),
|
|
uri,
|
|
},
|
|
],
|
|
});
|
|
} else {
|
|
console.log(result);
|
|
throw new Error("Erreur lors de l'ajout du document");
|
|
}
|
|
};
|
|
|
|
const onTakePicture = useCallback(async () => {
|
|
const { status } = await ImagePicker.requestCameraPermissionsAsync();
|
|
|
|
if (status !== "granted") {
|
|
throw new Error("Permissions caméra non accordées");
|
|
}
|
|
|
|
const result = await ImagePicker.launchCameraAsync({
|
|
mediaTypes: ImagePicker.MediaTypeOptions.All,
|
|
allowsEditing: true,
|
|
quality: 1,
|
|
});
|
|
|
|
if (result?.assets?.[0]?.uri) {
|
|
const { uri, fileName = "" } = result?.assets?.[0];
|
|
|
|
onUploadDocument({
|
|
files: [
|
|
{
|
|
name: getAssetName({ fileName, uri }),
|
|
uri,
|
|
type: "IMAGE",
|
|
},
|
|
],
|
|
});
|
|
} else {
|
|
throw new Error("Aucune image sélectionnée");
|
|
}
|
|
}, []);
|
|
|
|
const getDefaultFileName = () => {
|
|
const randomID = Math.random().toString(36).substring(7);
|
|
return `${moment().format(`DD_MM_YYYY_HH_mm_ss`)}_${randomID}`;
|
|
};
|
|
|
|
const getAssetName = ({ fileName = "", uri = "" }) => {
|
|
let name = fileName;
|
|
|
|
if (!name && uri?.startsWith("file://")) {
|
|
const splittedArray = uri?.split("/") || [];
|
|
name = splittedArray?.[splittedArray?.length - 1] || "";
|
|
}
|
|
|
|
if (!name?.length) {
|
|
let extension = uri?.split(";")?.[0]?.split("/")?.[1] || "";
|
|
|
|
const defaultFileName = getDefaultFileName();
|
|
|
|
if (extension?.length) {
|
|
name = `${defaultFileName}.${extension}`;
|
|
} else {
|
|
name = `${defaultFileName}`;
|
|
}
|
|
}
|
|
|
|
return name;
|
|
};
|
|
|
|
const onUploadDocument = async ({ files = [] } = {}) => {
|
|
try {
|
|
setIsLoading(true);
|
|
|
|
const filesToUpdate = [];
|
|
|
|
await Promise.all(
|
|
files.map(async (file) => {
|
|
const { name = "", uri = null } = file;
|
|
|
|
console.log(file);
|
|
|
|
if (!uri) {
|
|
throw new Error("Erreur lors de l'ajout du document");
|
|
}
|
|
|
|
const fileName = name || getDefaultFileName();
|
|
let type = "FILE";
|
|
|
|
if (name?.toLowerCase()?.match(/\.(jpeg|jpg|gif|png)$/) != null) {
|
|
type = "IMAGE";
|
|
}
|
|
|
|
if (name?.toLowerCase()?.match(/\.(mp4|mov|avi|mkv)$/) != null) {
|
|
type = "VIDEO";
|
|
}
|
|
|
|
let compressedURI = uri;
|
|
let thumbnailURI = null;
|
|
|
|
if (isNative) {
|
|
if (type === "IMAGE") {
|
|
compressedURI = await Compressor.Image.compress(uri, {
|
|
compressionMethod: "manual",
|
|
maxWidth: 1000,
|
|
quality: 0.8,
|
|
});
|
|
} else if (type === "VIDEO") {
|
|
compressedURI = await Compressor.Video.compress(uri);
|
|
}
|
|
}
|
|
|
|
const { resultURI = null } = await uploadFileToFirebase({
|
|
uri: compressedURI,
|
|
path: `documents/${documentID}/files/${fileName}`,
|
|
});
|
|
|
|
if (resultURI) {
|
|
if (shouldReturnObject) {
|
|
filesToUpdate.push({
|
|
name: fileName,
|
|
uri: resultURI,
|
|
type,
|
|
thumbnailURI,
|
|
});
|
|
} else {
|
|
filesToUpdate.push(resultURI);
|
|
}
|
|
} else {
|
|
console.log("resultURI is null");
|
|
}
|
|
})
|
|
);
|
|
|
|
if (filesToUpdate.length > 0) {
|
|
if (documentExists) {
|
|
await collectionRef.doc(documentID).update({
|
|
files: arrayUnion(...filesToUpdate),
|
|
});
|
|
}
|
|
|
|
if (setFiles) {
|
|
setFiles((prev) => [...prev, ...filesToUpdate]);
|
|
}
|
|
}
|
|
|
|
setTooltip({ text: "Document(s) ajouté(s) avec succès" });
|
|
} catch (error) {
|
|
console.log(error);
|
|
setTooltip({ text: error.message, type: "error" });
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
if (!documentID) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<View ref={dropRef}>
|
|
<Pressable onPress={onAddFile}>
|
|
{customElement?.() || (
|
|
<View
|
|
style={{
|
|
...Style.containerCenter,
|
|
backgroundColor: isDragging
|
|
? Palette.transparentGreen
|
|
: Palette.transparentPrimary,
|
|
borderRadius: mainBorderRadius,
|
|
borderStyle: "dashed",
|
|
borderWidth: 2,
|
|
borderColor: isDragging ? Palette.green : Palette.primary,
|
|
...containerStyle,
|
|
}}
|
|
>
|
|
<Image
|
|
source={icons.screenshot}
|
|
style={{
|
|
...Style.iconLarge,
|
|
marginBottom: gutterConstant,
|
|
}}
|
|
/>
|
|
<Text
|
|
style={{
|
|
...Fonts({ type: "default" }),
|
|
textAlign: "center",
|
|
color: Palette.white,
|
|
}}
|
|
>
|
|
{isWeb && isDesktop
|
|
? `Glissez-déposez ici\nles fichiers, images et vidéos\nà ajouter.`
|
|
: "Ajouter une image,\nun fichier ou une vidéo."}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</Pressable>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
export default DocumentDropZone;
|