import { DriveInfo, FileManagerBase } from '@solarpunkltd/file-manager-lib' import { ReactElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react' import { AdminStatusBar } from '../../modules/filemanager/components/AdminStatusBar/AdminStatusBar' import { Button } from '../../modules/filemanager/components/Button/Button' import { ConfirmModal } from '../../modules/filemanager/components/ConfirmModal/ConfirmModal' import { ErrorModal } from '../../modules/filemanager/components/ErrorModal/ErrorModal' import { FileBrowser } from '../../modules/filemanager/components/FileBrowser/FileBrowser' import { FormbricksIntegration } from '../../modules/filemanager/components/FormbricksIntegration/FormbricksIntegration' import { Header } from '../../modules/filemanager/components/Header/Header' import { InitialModal } from '../../modules/filemanager/components/InitialModal/InitialModal' import { PrivateKeyModal } from '../../modules/filemanager/components/PrivateKeyModal/PrivateKeyModal' import { Sidebar } from '../../modules/filemanager/components/Sidebar/Sidebar' import { getSignerPk, removeSignerPk } from '../../modules/filemanager/utils/common' import { CheckState, Context as BeeContext } from '../../providers/Bee' import { Context as FMContext } from '../../providers/FileManager' import { BrowserPlatform, cacheClearUrls, detectBrowser } from '../../providers/Platform' import { SearchProvider } from './SearchContext' import { ViewProvider } from './ViewContext' import './FileManager.scss' function PrivateKeyModalBlock({ onSaved }: { onSaved: () => void }) { return (
) } function InitializationErrorBlock({ onOk }: { onOk: () => void }) { return (
Failed to initialize File Manager, reload and try again
) } function ResetModalBlock({ cacheHelpUrl, onConfirm }: { cacheHelpUrl: string; onConfirm: () => void }) { return (
Your File Manager state appears invalid. Please{' '} clear the browser cache {' '} and reload the page. Then you can reset the File Manager to continue. } confirmLabel="Continue" onConfirm={onConfirm} background={false} />
) } function InitialModalBlock(props: { resetState: boolean handleShowError: (flag: boolean, error?: string) => void setIsCreationInProgress: (isCreating: boolean) => void }) { return (
) } function LoadingBlock() { return (
) } function ChainSyncingBlock() { return (
) } function ErrorModalBlock({ onClick, label }: { onClick: () => void; label: string }) { return } function FileManagerMainContent(props: { fm: FileManagerBase | null showConnectionError: boolean setShowConnectionError: (v: boolean) => void isFormbricksActive: boolean errorMessage: string setErrorMessage: (msg: string) => void loading: boolean adminDrive: DriveInfo | null isCreationInProgress: boolean }) { const { fm, showConnectionError, setShowConnectionError, isFormbricksActive, errorMessage, setErrorMessage, loading, adminDrive, isCreationInProgress, } = props return (
{showConnectionError && fm && ( setShowConnectionError(false)} /> )}
) } enum PageState { Connecting = 'connecting', // still warming up — show nothing / loader NoPrivateKey = 'no-pk', // private key not set Loading = 'loading', // bee ready, pk present, FM init in progress Reset = 'reset', // STATE_INVALID emitted and user has not yet acknowledged InitError = 'init-error', // FM init completed with an error (non-reset case) ChainSyncing = 'chain-syncing', // bee node is still syncing postage batch state from chain Initial = 'initial', // FM ready but no admin stamp/drive → show InitialModal AdminError = 'admin-error', // drive creation failed Ready = 'ready', // fully operational } export function FileManagerPage(): ReactElement { const isMountedRef = useRef(true) const [hasPk, setHasPk] = useState(getSignerPk() !== undefined) const [showAdminErrorModal, setAdminShowErrorModal] = useState(false) const [errorMessage, setErrorMessage] = useState('') const [resetAcknowledged, setResetAcknowledged] = useState(false) const [isCreationInProgress, setIsCreationInProgress] = useState(false) const [connectionErrorDismissed, setConnectionErrorDismissed] = useState(false) const [cacheHelpUrl, setCacheHelpUrl] = useState(cacheClearUrls[BrowserPlatform.Chrome]) const { status, chainState } = useContext(BeeContext) const { fm, initDone, shallReset, adminDrive, initializationError, notifyPkSaved } = useContext(FMContext) useEffect(() => { isMountedRef.current = true const getBrowserPlatform = async () => { const browserPlatform = await detectBrowser() setCacheHelpUrl(cacheClearUrls[browserPlatform]) } getBrowserPlatform() return () => { isMountedRef.current = false } }, []) const { isBeeReady, isConnectionError } = useMemo(() => { const isConnecting = status.all === CheckState.CONNECTING const isApiOk = status.apiConnection.isEnabled && status.apiConnection.checkState === CheckState.OK return { isBeeReady: !isConnecting && isApiOk, isConnectionError: !isConnecting && !isApiOk && Boolean(fm), } }, [status, fm]) useEffect(() => { if (!isConnectionError) { setConnectionErrorDismissed(false) } }, [isConnectionError]) const pageState = useMemo((): PageState => { const isChainSyncing = chainState === null if (!isBeeReady && !initDone) return PageState.Connecting if (!hasPk) return PageState.NoPrivateKey if (!initDone) return PageState.Loading if (shallReset && !resetAcknowledged) return PageState.Reset if (initializationError && !shallReset) return PageState.InitError const hasAdminStamp = Boolean(fm?.adminStamp) const hasAdminDrive = Boolean(adminDrive) const setupIncomplete = !hasAdminStamp && !hasAdminDrive if (setupIncomplete && isChainSyncing) return PageState.ChainSyncing if (showAdminErrorModal) return PageState.AdminError if (setupIncomplete && !isCreationInProgress) return PageState.Initial return PageState.Ready }, [ isBeeReady, hasPk, initDone, shallReset, resetAcknowledged, initializationError, showAdminErrorModal, fm, adminDrive, isCreationInProgress, chainState, ]) const handlePrivateKeySaved = useCallback(() => { if (!isMountedRef.current) return setHasPk(true) if (fm) return notifyPkSaved() }, [fm, notifyPkSaved]) const loading = !fm?.adminStamp || !adminDrive const isFormbricksActive = Boolean(fm && fm.adminStamp && adminDrive && !loading) if (pageState === PageState.Connecting || pageState === PageState.Loading) { return } if (pageState === PageState.ChainSyncing) { return } if (pageState === PageState.NoPrivateKey) { return } if (pageState === PageState.InitError) { return ( { removeSignerPk() setHasPk(false) }} /> ) } if (pageState === PageState.Reset) { return setResetAcknowledged(true)} /> } if (pageState === PageState.Initial) { return ( { setAdminShowErrorModal(flag) if (error) setErrorMessage(error) }} setIsCreationInProgress={(isCreating: boolean) => setIsCreationInProgress(isCreating)} /> ) } if (pageState === PageState.AdminError) { const adminErrorLabel = chainState === null ? 'Your Bee node is still syncing the postage batch state from the chain. Please wait for the sync to complete and try again.' : errorMessage || 'Error creating Admin Drive. Please try again. Possible causes include insufficient xDAI balance or a lost connection to the RPC.' return ( { setAdminShowErrorModal(false) setErrorMessage('') }} /> ) } return ( setConnectionErrorDismissed(!show)} isFormbricksActive={isFormbricksActive} errorMessage={errorMessage} setErrorMessage={setErrorMessage} loading={loading} adminDrive={adminDrive} isCreationInProgress={isCreationInProgress} /> ) }