import { PostageBatch } from '@ethersphere/bee-js' import { DriveInfo, estimateDriveListMetadataSize } from '@solarpunkltd/file-manager-lib' import { ReactElement, useCallback, useContext, useEffect, useMemo, useState } from 'react' import { Context as FMContext } from '../../../../providers/FileManager' import { Context as SettingsContext } from '../../../../providers/Settings' import { getHumanReadableFileSize } from '../../../../utils/file' import { FILE_MANAGER_EVENTS, POLLING_TIMEOUT_MS } from '../../constants/common' import { TOOLTIPS } from '../../constants/tooltips' import { useStampPolling } from '../../hooks/useStampPolling' import { calculateStampCapacityMetrics, validateStampStillExists } from '../../utils/bee' import { ConfirmModal } from '../ConfirmModal/ConfirmModal' import { ProgressBar } from '../ProgressBar/ProgressBar' import { Tooltip } from '../Tooltip/Tooltip' import { UpgradeDriveModal } from '../UpgradeDriveModal/UpgradeDriveModal' import { UpgradeTimeoutModal } from '../UpgradeTimeoutModal/UpgradeTimeoutModal' import './AdminStatusBar.scss' interface AdminStatusBarProps { adminStamp: PostageBatch | null adminDrive: DriveInfo | null loading: boolean isCreationInProgress: boolean setErrorMessage?: (error: string) => void } export function AdminStatusBar({ adminStamp, adminDrive, loading, isCreationInProgress, setErrorMessage, }: AdminStatusBarProps): ReactElement { const { drives, setShowError, refreshStamp } = useContext(FMContext) const { beeApi } = useContext(SettingsContext) const [isUpgradeDriveModalOpen, setIsUpgradeDriveModalOpen] = useState(false) const [isUpgradeTimeoutModalOpen, setIsUpgradeTimeoutModalOpen] = useState(false) const [isUpgrading, setIsUpgrading] = useState(false) const [actualStamp, setActualStamp] = useState(adminStamp) const [showProgressModal, setShowProgressModal] = useState(true) const handleStampUpdated = useCallback((updatedStamp: PostageBatch) => { setActualStamp(updatedStamp) }, []) const handlePollingStateChange = useCallback((_isPolling: boolean) => { // no-op }, []) const { startPolling } = useStampPolling({ onStampUpdated: handleStampUpdated, onPollingStateChange: handlePollingStateChange, refreshStamp, timeout: POLLING_TIMEOUT_MS, }) useEffect(() => { setShowProgressModal(isCreationInProgress || loading) }, [isCreationInProgress, loading, setShowProgressModal]) useEffect(() => { if (!adminStamp || !actualStamp) { setActualStamp(adminStamp) return } if (actualStamp.batchID.toString() !== adminStamp.batchID.toString()) { setActualStamp(adminStamp) return } const incomingSize = adminStamp.size.toBytes() const currentSize = actualStamp.size.toBytes() const incomingExpiry = adminStamp.duration.toEndDate().getTime() const currentExpiry = actualStamp.duration.toEndDate().getTime() if (incomingSize > currentSize || incomingExpiry > currentExpiry) { setActualStamp(adminStamp) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [adminStamp]) useEffect(() => { if (!adminDrive) return const id = adminDrive.id.toString() const onStart = (e: Event) => { const { driveId } = (e as CustomEvent).detail || {} if (driveId === id) { setIsUpgrading(true) } } const onEnd = (e: Event) => { const { driveId, success, error, updatedStamp } = (e as CustomEvent).detail || {} if (driveId !== id) return if (!success && error) { setIsUpgrading(false) setErrorMessage?.(error) setShowError(true) return } if (updatedStamp) { setActualStamp(updatedStamp) } setIsUpgrading(false) } const onTimeout = (e: Event) => { const { driveId } = (e as CustomEvent).detail || {} if (driveId === id) { setIsUpgradeTimeoutModalOpen(true) } } window.addEventListener(FILE_MANAGER_EVENTS.DRIVE_UPGRADE_START, onStart as EventListener) window.addEventListener(FILE_MANAGER_EVENTS.DRIVE_UPGRADE_END, onEnd as EventListener) window.addEventListener(FILE_MANAGER_EVENTS.DRIVE_UPGRADE_TIMEOUT, onTimeout as EventListener) return () => { window.removeEventListener(FILE_MANAGER_EVENTS.DRIVE_UPGRADE_START, onStart as EventListener) window.removeEventListener(FILE_MANAGER_EVENTS.DRIVE_UPGRADE_END, onEnd as EventListener) window.removeEventListener(FILE_MANAGER_EVENTS.DRIVE_UPGRADE_TIMEOUT, onTimeout as EventListener) } }, [adminDrive, setErrorMessage, setShowError]) const handleTimeoutCancel = useCallback(() => { setIsUpgrading(false) setIsUpgradeTimeoutModalOpen(false) // Restart polling to continue checking for capacity updates if (actualStamp) { startPolling(actualStamp) } }, [actualStamp, startPolling]) const { capacityPct, usedSize, totalSize } = useMemo(() => { if (!actualStamp) { return { capacityPct: 0, usedSize: '—', totalSize: '—', } } const estimatedDlSizeBytes = estimateDriveListMetadataSize(drives) * drives.length const { capacityPct: reportedPct, usedBytes: reportedUsedBytes, stampSizeBytes, } = calculateStampCapacityMetrics(actualStamp, [], adminDrive?.redundancyLevel) const actualUsedSizeBytes = Math.max(reportedUsedBytes, estimatedDlSizeBytes) const actualPct = Math.max(reportedPct, (actualUsedSizeBytes / stampSizeBytes) * 100) return { capacityPct: actualPct, usedSize: getHumanReadableFileSize(actualUsedSizeBytes), totalSize: getHumanReadableFileSize(stampSizeBytes), } }, [actualStamp, adminDrive, drives]) const expiresAt = useMemo( () => (actualStamp ? actualStamp.duration.toEndDate().toLocaleDateString() : '—'), [actualStamp], ) const isBusy = loading || isUpgrading || isCreationInProgress const blurCls = isBusy ? ' is-loading' : '' const statusVerb = isCreationInProgress ? 'Creating' : 'Loading' const statusText = statusVerb + ' admin drive, please do not reload' const renderModalsAndOverlays = () => { return ( <> {isUpgradeDriveModalOpen && actualStamp && adminDrive && ( setIsUpgradeDriveModalOpen(false)} setErrorMessage={setErrorMessage} /> )} {isUpgradeTimeoutModalOpen && adminDrive && actualStamp && ( )} {isUpgrading && (
Upgrading admin drive…
)} {showProgressModal && ( setShowProgressModal(false)} /> )} ) } return (
Capacity {usedSize} / {totalSize}
File Manager Available: Until: {expiresAt}
{renderModalsAndOverlays()}
{ if (!isBusy && actualStamp && adminDrive && beeApi) { const isStampValid = await validateStampStillExists(beeApi, actualStamp.batchID) if (!isStampValid) { setErrorMessage?.('The admin drive has expired. Please clear the browser cache and reload the page.') setShowError(true) return } setIsUpgradeDriveModalOpen(true) } }} aria-disabled={isBusy ? 'true' : 'false'} > {isBusy ? 'Working…' : 'Manage'}
{!showProgressModal && (loading || isCreationInProgress) && (
setShowProgressModal(true)}> {statusText}
)}
) }