import React, { useState, useEffect } from 'react'; import { deleteObject, uploadBytesResumable, ref, listAll, getDownloadURL } from 'firebase/storage'; import { storage } from '../firebaseInit'; import { HStack, Stack, VStack } from './Stack'; import { copyToClipboard } from '../utils'; import { Trans } from '@lingui/macro'; import { ProBadge } from './ProBadge'; import { LoaderWithText } from './Loader'; import { Text } from './Text'; import { Icon } from './Icons'; function getFileType(url) { // get extension from a url using URL API const ext = new URL(url).pathname.split('.').pop(); if (['jpg', 'jpeg', 'png', 'gif', 'svg'].includes(ext)) { return 'image'; } return ext; } const Assets = ({ onProBtnClick, onLoginBtnClick }) => { const [files, setFiles] = useState([]); const [isFetchingFiles, setIsFetchingFiles] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const [filteredFiles, setFilteredFiles] = useState([]); const [isUploading, setIsUploading] = useState(false); const [uploadProgress, setUploadProgress] = useState(); const [listType, setListType] = useState('grid'); const uploadFile = file => { if (file.size > 1024 * 1024) { // 1MB limit alert('File size must be less than 1MB'); return; } setIsUploading(true); const metadata = { cacheControl: 'public, max-age=3600' // 1 hr }; const task = uploadBytesResumable( ref(storage, `assets/${window.user?.uid}/${file.name}`), file, metadata ); task.on( 'state_changed', snapshot => { // Observe state change events such as progress, pause, and resume // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; // console.log('Upload is ' + progress + '% done'); }, error => { // Handle unsuccessful uploads setIsUploading(false); // console.log('File upload error:', error); alertsService.add('⚠️ File upload failed'); }, () => { // uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => { // console.log('File available at', downloadURL); // }); alertsService.add('File uploaded successfully'); fetchFiles(); setIsUploading(false); } ); }; // Function to handle file upload const handleFileUpload = e => { const file = e.target.files[0]; uploadFile(file); }; // Function to fetch existing files const fetchFiles = () => { setIsFetchingFiles(true); listAll(ref(storage, `assets/${window.user?.uid}`)) .then(result => { const filePromises = result.items.map(item => { return getDownloadURL(item).then(url => { return { name: item.name, url }; }); }); Promise.all(filePromises).then(files => { files.forEach(f => (f.ext = getFileType(f.url))); setFiles(files); }); setIsFetchingFiles(false); }) .catch(error => { console.error('File fetch error:', error); setIsFetchingFiles(false); }); }; // Function to handle search input change const handleSearchChange = e => { const term = e.target.value; setSearchTerm(term); }; useEffect(() => { if (window.user?.isPro) { fetchFiles(); } }, []); useEffect(() => { if (searchTerm) { setFilteredFiles( files.filter(file => file.name.toLowerCase().includes(searchTerm.toLowerCase()) ) ); } else { setFilteredFiles(files); } }, [files, searchTerm]); const [isDropTarget, setIsDropTarget] = useState(false); const handleDragDropEvent = e => { if (e.type === 'dragover') { // required for drop to work e.preventDefault(); } else if (e.type === 'dragleave') { e.preventDefault(); // so that individual nested elements don't trigger dragleave if (e.currentTarget.contains(e.target)) return; setIsDropTarget(false); } else if (e.type === 'dragenter') { setIsDropTarget(true); } }; const handleDrop = e => { e.preventDefault(); setIsDropTarget(false); if (e.dataTransfer.items) { const file = e.dataTransfer.items[0].getAsFile(); uploadFile(file); } }; const [lastCopiedFile, setLastCopiedFile] = useState({ name: '', count: 0 }); const copyFileUrl = url => { let copyContent = url; if (lastCopiedFile.name === url) { lastCopiedFile.count = (lastCopiedFile.count + 1) % 3; } else { lastCopiedFile.count = 0; lastCopiedFile.name = url; } switch (lastCopiedFile.count) { case 0: copyContent = url; break; case 1: copyContent = ``; break; case 2: copyContent = `url("${url}")`; break; } setLastCopiedFile({ ...lastCopiedFile }); copyToClipboard(copyContent).then(() => { switch (lastCopiedFile.count) { case 0: alertsService.add('File URL copied'); break; case 1: alertsService.add('File URL copied as tag'); break; case 2: alertsService.add('File URL copied as CSS image URL'); break; } }); }; const removeFileHandler = index => { const file = files[index]; const answer = confirm(`Are you sure you want to delete "${file.name}"?`); if (!answer) return; const fileRef = ref(storage, file.url); deleteObject(fileRef) .then(() => { alertsService.add('File deleted successfully'); setFiles(files.filter((_, i) => i !== index)); }) .catch(error => { console.error('File delete error:', error); }); }; if (!window.user?.isPro) { return (

Assets feature is available in PRO plan.

); } return (

Assets

{isUploading ?
: null}
{isFetchingFiles && Fetching files...} {!isFetchingFiles && !files.length ? ( No files uploaded yet ) : null} {files.length ? ( ) : null}
); }; export { Assets };