Files
musicland/src/components/DocumentDropZone.js
T
2025-10-20 12:08:09 +02:00

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;