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 (
File manager loading…
Please wait a few seconds
)
}
function ChainSyncingBlock() {
return (
Bee node is syncing…
Your Bee node is still syncing the postage batch state from the chain.
File Manager will be available once the sync is complete.
)
}
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}
/>
)
}