import { AttachFile, Image } from '@mui/icons-material' import { Box, CircularProgress, List, ListItem, ListItemButton, Typography, } from '@mui/joy' import { useEffect, useState } from 'react' import EmptyState from '../../../components/common/EmptyState' import ModalActions from '../../../components/common/ModalActions' import { useResponsiveModal } from '../../../hooks/useResponsiveModal' import { GetChoreAttachments } from '../../../utils/Fetcher' import { resolvePhotoURL } from '../../../utils/Helpers' import { cacheChoreImages, getImageSrc } from '../../../utils/ImageCache' import AttachmentViewerModal from './AttachmentViewerModal' import { useTranslation } from 'react-i18next' const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg'] const isImageFile = fileName => { if (!fileName) return false const ext = fileName.split('.').pop().toLowerCase() return IMAGE_EXTENSIONS.includes(ext) } const downloadFile = (url, fileName) => { const a = document.createElement('a') a.href = url a.download = fileName || 'attachment' a.rel = 'noopener' document.body.appendChild(a) a.click() document.body.removeChild(a) } function AttachmentBrowserModal({ choreId, isOpen, onClose }) { const { t } = useTranslation('common') const { ResponsiveModal } = useResponsiveModal() const [attachments, setAttachments] = useState([]) const [isLoading, setIsLoading] = useState(false) const [viewerConfig, setViewerConfig] = useState({ isOpen: false }) useEffect(() => { if (!isOpen || !choreId) return setIsLoading(true) GetChoreAttachments(choreId) .then(async res => { if (!res.ok) throw new Error(t('chores:dataError.attachmentsFailed')) return res.json() }) .then(data => { const list = Array.isArray(data) ? data : [] setAttachments(list) // Fire-and-forget: store attachments so they open offline later cacheChoreImages({ id: choreId, attachments: list }) }) .catch(() => setAttachments([])) .finally(() => setIsLoading(false)) }, [isOpen, choreId, t]) const handleClose = () => { setAttachments([]) onClose?.() } const handleAttachmentClick = async attachment => { const url = await getImageSrc( attachment.file_path, attachment.sign ? resolvePhotoURL(attachment.sign) : null, { choreId: String(choreId), kind: 'attachment' }, ).catch(() => resolvePhotoURL(attachment.sign)) if (isImageFile(attachment.file_name)) { setViewerConfig({ isOpen: true, url, fileName: attachment.file_name, onClose: () => setViewerConfig({ isOpen: false }), }) } else { downloadFile(url, attachment.file_name) } } return ( <> } > {isLoading ? ( ) : attachments.length === 0 ? ( } title='No attachments' description='Photos and files added to this task will show up here.' /> ) : ( {attachments.map((attachment, index) => ( handleAttachmentClick(attachment)} sx={{ borderRadius: 'sm', gap: 1.5, py: 1 }} > {isImageFile(attachment.file_name) ? ( ) : ( )} {attachment.file_name || t('fileNumbered', { index: index + 1 })} {attachment.size_bytes > 0 && ( {(attachment.size_bytes / 1024).toFixed(1)} KB )} ))} )} ) } export default AttachmentBrowserModal