import { useContext, useEffect, useRef, useState } from 'react' import { Card, Flex, Spin } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import PropTypes from 'prop-types' import merge from 'lodash/merge' import { ApiServerContext } from '../context/ApiServerContext' import { AuthContext } from '../context/AuthContext' import GCodePreview from './GCodePreview' import MissingPlaceholder from './MissingPlaceholder' import ProgressDisplay from './ProgressDisplay' const emptyGcodePosition = { position: [0, 0, 0, 0], filePosition: 0, fileSize: 0, progress: 0, isActive: false, currentLayer: 0, totalLayer: 0 } const PrinterGCodeFilePanel = ({ id, subJobId, offline = true }) => { const { subscribeToObjectEvent, connected, fetchObject, fetchFileContent } = useContext(ApiServerContext) const { token } = useContext(AuthContext) const apiRef = useRef({ fetchObject, fetchFileContent }) apiRef.current = { fetchObject, fetchFileContent } const objectUrlRef = useRef(null) const [gcodePosition, setGcodePosition] = useState(emptyGcodePosition) const [src, setSrc] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [downloadProgress, setDownloadProgress] = useState(null) useEffect(() => { if (offline) { setGcodePosition(emptyGcodePosition) } }, [offline]) useEffect(() => { if (id && connected == true && offline == false) { const gcodePositionUnsubscribe = subscribeToObjectEvent( id?.toLowerCase(), 'printer', 'gcodePosition', (event) => { setGcodePosition((prev) => merge({}, prev, event.data)) } ) return () => { if (gcodePositionUnsubscribe) gcodePositionUnsubscribe() } } }, [id, connected, subscribeToObjectEvent, offline]) useEffect(() => { let cancelled = false const replaceSrc = (nextSrc) => { if (objectUrlRef.current && objectUrlRef.current !== nextSrc) { URL.revokeObjectURL(objectUrlRef.current) } objectUrlRef.current = nextSrc setSrc(nextSrc) } const loadGcodeFile = async () => { replaceSrc(null) setError(null) if (!subJobId || token == null) { setLoading(false) setDownloadProgress(null) return } setLoading(true) setDownloadProgress({ progress: 0, message: 'Loading G-code file...' }) const subJob = await apiRef.current.fetchObject(subJobId, 'subJob') if (cancelled) return const gcodeFileId = subJob?.gcodeFile?._id if (!gcodeFileId) { setLoading(false) setDownloadProgress(null) setError('No G-code file.') return } const gcodeFile = await apiRef.current.fetchObject( gcodeFileId, 'gcodeFile' ) if (cancelled) return const file = gcodeFile?.file if (!file?._id) { setLoading(false) setDownloadProgress(null) setError('No G-code file.') return } const objectUrl = await apiRef.current.fetchFileContent( file, false, (progress) => { if (cancelled) return setDownloadProgress(progress) } ) if (cancelled) { if (objectUrl) URL.revokeObjectURL(objectUrl) return } if (!objectUrl) { setLoading(false) setDownloadProgress(null) setError('Failed to load G-code file.') return } replaceSrc(objectUrl) setDownloadProgress(null) setLoading(false) } loadGcodeFile() return () => { cancelled = true } }, [subJobId, token]) useEffect(() => { return () => { if (objectUrlRef.current) { URL.revokeObjectURL(objectUrlRef.current) objectUrlRef.current = null } } }, []) if (!subJobId) { return ( ) } if (error) { return ( ) } return ( {loading || !src ? ( {downloadProgress ? (
{downloadProgress.message || 'Downloading...'}
) : ( } size='large' /> )}
) : ( )}
) } PrinterGCodeFilePanel.propTypes = { id: PropTypes.string, subJobId: PropTypes.string, offline: PropTypes.bool } export default PrinterGCodeFilePanel