import PropTypes from 'prop-types' import { Card, Flex } from 'antd' import { ApiServerContext } from '../context/ApiServerContext' import { useCallback, useContext, useEffect, useState, memo, useRef } from 'react' import GCodePreview from './GCodePreview' import ThreeDPreview from './ThreeDPreview' import PDFPreview from './PDFPreview' import ProgressDisplay from './ProgressDisplay' import { AuthContext } from '../context/AuthContext' const hasExplicitPreviewHeight = (height) => typeof height === 'number' || (typeof height === 'string' && height !== '100%' && height !== 'auto') const FilePreview = ({ file, style = {} }) => { const { token } = useContext(AuthContext) const { fetchFileContent } = useContext(ApiServerContext) const [fileObjectUrl, setFileObjectUrl] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [downloadProgress, setDownloadProgress] = useState({ progress: 0, message: 'Loading file preview...' }) const currentId = useRef(null) const extension = (file?.extension || '').toLowerCase() const isGcode = ['.g', '.gcode'].includes(extension) const is3DModel = ['.stl', '.3mf'].includes(extension) const isImage = file?.type?.startsWith('image/') || false const isPdf = file?.type?.startsWith('application/pdf') || extension === '.pdf' const fetchPreview = useCallback(async () => { if (error != null) { return } const requestId = file._id setLoading(true) setDownloadProgress({ progress: 0, message: 'Starting download...' }) const objectUrl = await fetchFileContent(file, false, (progress) => { if (currentId.current !== requestId) { return } setDownloadProgress(progress) }) if (currentId.current !== requestId) { return } if (objectUrl == null) { setLoading(false) setDownloadProgress(null) console.error('Failed to fetch file content', file) setError('Failed to fetch file content') return } setFileObjectUrl(objectUrl) setDownloadProgress(null) setLoading(false) }, [file, fetchFileContent, error]) useEffect(() => { if (file._id !== currentId.current) { setFileObjectUrl(null) setError(null) setLoading(true) setDownloadProgress({ progress: 0, message: 'Loading file preview...' }) } if (file?.type && token != null && file._id !== currentId.current) { currentId.current = file._id fetchPreview() } }, [file._id, file?.type, fetchPreview, token]) const showProgressOverlay = downloadProgress != null const wrapperHeight = hasExplicitPreviewHeight(style.height) ? style.height : isPdf ? '72vh' : style.height const renderPreview = () => { if (isPdf) { return ( ) } if (loading == true || !file?.type) { return null } if (isGcode && fileObjectUrl) { return ( ) } if (is3DModel && fileObjectUrl) { return ( ) } if (isImage && fileObjectUrl) { return } return null } if (error != null) { return
{error}
} return (
{showProgressOverlay ? ( {downloadProgress.message || 'Downloading...'} ) : null}
{renderPreview()}
) } FilePreview.propTypes = { file: PropTypes.object.isRequired, style: PropTypes.object } // Custom comparison function to only re-render when file._id changes const areEqual = (prevProps, nextProps) => { return ( prevProps.file?._id === nextProps.file?._id && prevProps.file?.type === nextProps.file?.type && prevProps.file?.size === nextProps.file?.size && JSON.stringify(prevProps.style) === JSON.stringify(nextProps.style) ) } export default memo(FilePreview, areEqual)