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 ( {customElement?.() || ( {isWeb && isDesktop ? `Glissez-déposez ici\nles fichiers, images et vidéos\nà ajouter.` : 'Ajouter une image,\nun fichier ou une vidéo.'} )} ) } export default DocumentDropZone