fix: filemanager state handling (#232)
* fix: filemanager state handling * refactor: fm provider and fm page * fix: detect bee warmup and wait more for syncing * refactor: optimize bee provider to avoid rerenders --------- Co-authored-by: Roland Seres <roland.seres90@gmail.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement, useLayoutEffect, useState } from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
|
|
||||||
import { Button } from '../Button/Button'
|
import { Button } from '../Button/Button'
|
||||||
@@ -35,7 +35,11 @@ export function ConfirmModal({
|
|||||||
onMinimize,
|
onMinimize,
|
||||||
background = true,
|
background = true,
|
||||||
}: ConfirmModalProps): ReactElement {
|
}: ConfirmModalProps): ReactElement {
|
||||||
const modalRoot = document.querySelector('.fm-main') || document.body
|
const [modalRoot, setModalRoot] = useState<Element>(() => document.querySelector('.fm-main') || document.body)
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
setModalRoot(document.querySelector('.fm-main') || document.body)
|
||||||
|
}, [])
|
||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<div className={`fm-modal-container fm-confirm-modal ${background ? '' : 'fm-modal-no-background'}`}>
|
<div className={`fm-modal-container fm-confirm-modal ${background ? '' : 'fm-modal-no-background'}`}>
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import './InitialModal.scss'
|
|||||||
|
|
||||||
interface InitialModalProps {
|
interface InitialModalProps {
|
||||||
resetState: boolean
|
resetState: boolean
|
||||||
handleVisibility: (isVisible: boolean) => void
|
|
||||||
handleShowError: (flag: boolean, errorMessage?: string) => void
|
handleShowError: (flag: boolean, errorMessage?: string) => void
|
||||||
setIsCreationInProgress: (isCreating: boolean) => void
|
setIsCreationInProgress: (isCreating: boolean) => void
|
||||||
}
|
}
|
||||||
@@ -65,7 +64,6 @@ const setSecurityLevel = (setter: (value: RedundancyLevel) => void) => {
|
|||||||
export function InitialModal({
|
export function InitialModal({
|
||||||
resetState,
|
resetState,
|
||||||
setIsCreationInProgress,
|
setIsCreationInProgress,
|
||||||
handleVisibility,
|
|
||||||
handleShowError,
|
handleShowError,
|
||||||
}: InitialModalProps): ReactElement {
|
}: InitialModalProps): ReactElement {
|
||||||
const [isCreateEnabled, setIsCreateEnabled] = useState(false)
|
const [isCreateEnabled, setIsCreateEnabled] = useState(false)
|
||||||
@@ -133,7 +131,6 @@ export function InitialModal({
|
|||||||
|
|
||||||
const createAdminDrive = useCallback(async () => {
|
const createAdminDrive = useCallback(async () => {
|
||||||
setIsCreationInProgress?.(true)
|
setIsCreationInProgress?.(true)
|
||||||
handleVisibility(false)
|
|
||||||
|
|
||||||
await handleCreateDrive({
|
await handleCreateDrive({
|
||||||
beeApi,
|
beeApi,
|
||||||
@@ -148,7 +145,6 @@ export function InitialModal({
|
|||||||
resetState,
|
resetState,
|
||||||
existingBatch: selectedBatch,
|
existingBatch: selectedBatch,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
handleVisibility(false)
|
|
||||||
setIsCreationInProgress(false)
|
setIsCreationInProgress(false)
|
||||||
},
|
},
|
||||||
onError: err => {
|
onError: err => {
|
||||||
@@ -164,7 +160,6 @@ export function InitialModal({
|
|||||||
validityEndDate,
|
validityEndDate,
|
||||||
erasureCodeLevel,
|
erasureCodeLevel,
|
||||||
selectedBatch,
|
selectedBatch,
|
||||||
handleVisibility,
|
|
||||||
handleShowError,
|
handleShowError,
|
||||||
setIsCreationInProgress,
|
setIsCreationInProgress,
|
||||||
resetState,
|
resetState,
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import { getSignerPk, removeSignerPk } from '@/modules/filemanager/utils/common'
|
|||||||
import { CheckState, Context as BeeContext } from '@/providers/Bee'
|
import { CheckState, Context as BeeContext } from '@/providers/Bee'
|
||||||
import { Context as FMContext } from '@/providers/FileManager'
|
import { Context as FMContext } from '@/providers/FileManager'
|
||||||
import { BrowserPlatform, cacheClearUrls, detectBrowser } from '@/providers/Platform'
|
import { BrowserPlatform, cacheClearUrls, detectBrowser } from '@/providers/Platform'
|
||||||
import { Context as SettingsContext } from '@/providers/Settings'
|
|
||||||
|
|
||||||
function PrivateKeyModalBlock({ onSaved }: { onSaved: () => void }) {
|
function PrivateKeyModalBlock({ onSaved }: { onSaved: () => void }) {
|
||||||
return (
|
return (
|
||||||
@@ -74,7 +73,6 @@ function ResetModalBlock({ cacheHelpUrl, onConfirm }: { cacheHelpUrl: string; on
|
|||||||
|
|
||||||
function InitialModalBlock(props: {
|
function InitialModalBlock(props: {
|
||||||
resetState: boolean
|
resetState: boolean
|
||||||
handleVisibility: (isVisible: boolean) => void
|
|
||||||
handleShowError: (flag: boolean, error?: string) => void
|
handleShowError: (flag: boolean, error?: string) => void
|
||||||
setIsCreationInProgress: (isCreating: boolean) => void
|
setIsCreationInProgress: (isCreating: boolean) => void
|
||||||
}) {
|
}) {
|
||||||
@@ -153,22 +151,29 @@ function FileManagerMainContent(props: {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
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 {
|
export function FileManagerPage(): ReactElement {
|
||||||
const isMountedRef = useRef(true)
|
const isMountedRef = useRef(true)
|
||||||
const [showInitialModal, setShowInitialModal] = useState(false)
|
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
|
||||||
const [hasAdminDrive, setHasAdminDrive] = useState(false)
|
|
||||||
const [hasPk, setHasPk] = useState<boolean>(getSignerPk() !== undefined)
|
const [hasPk, setHasPk] = useState<boolean>(getSignerPk() !== undefined)
|
||||||
const [showErrorModal, setShowErrorModal] = useState<boolean>(false)
|
const [showAdminErrorModal, setAdminShowErrorModal] = useState<boolean>(false)
|
||||||
const [errorMessage, setErrorMessage] = useState<string>('')
|
const [errorMessage, setErrorMessage] = useState<string>('')
|
||||||
const [showResetModal, setShowResetModal] = useState<boolean>(false)
|
const [resetAcknowledged, setResetAcknowledged] = useState<boolean>(false)
|
||||||
const [isCreationInProgress, setIsCreationInProgress] = useState<boolean>(false)
|
const [isCreationInProgress, setIsCreationInProgress] = useState<boolean>(false)
|
||||||
const [showConnectionError, setShowConnectionError] = useState<boolean>(false)
|
const [connectionErrorDismissed, setConnectionErrorDismissed] = useState<boolean>(false)
|
||||||
const [cacheHelpUrl, setCacheHelpUrl] = useState<string>(cacheClearUrls[BrowserPlatform.Chrome])
|
const [cacheHelpUrl, setCacheHelpUrl] = useState<string>(cacheClearUrls[BrowserPlatform.Chrome])
|
||||||
|
|
||||||
const { status } = useContext(BeeContext)
|
const { status } = useContext(BeeContext)
|
||||||
const { beeApi } = useContext(SettingsContext)
|
const { fm, initDone, shallReset, adminDrive, initializationError, notifyPkSaved } = useContext(FMContext)
|
||||||
const { fm, shallReset, adminDrive, initializationError, init } = useContext(FMContext)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
isMountedRef.current = true
|
isMountedRef.current = true
|
||||||
@@ -185,93 +190,76 @@ export function FileManagerPage(): ReactElement {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
const { isBeeReady, isConnectionError } = useMemo(() => {
|
||||||
const isApiError = status.apiConnection.checkState !== CheckState.OK || !status.apiConnection.isEnabled
|
const isConnecting = status.all === CheckState.CONNECTING
|
||||||
setShowConnectionError(isApiError)
|
const isApiOk = status.apiConnection.isEnabled && status.apiConnection.checkState === CheckState.OK
|
||||||
}, [status.apiConnection])
|
|
||||||
|
return {
|
||||||
|
isBeeReady: !isConnecting && isApiOk,
|
||||||
|
isConnectionError: !isConnecting && !isApiOk && Boolean(fm),
|
||||||
|
}
|
||||||
|
}, [status, fm])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!beeApi) {
|
if (!isConnectionError) {
|
||||||
return
|
setConnectionErrorDismissed(false)
|
||||||
}
|
}
|
||||||
|
}, [isConnectionError])
|
||||||
|
|
||||||
if (!hasPk) {
|
const pageState = useMemo((): PageState => {
|
||||||
setIsLoading(false)
|
if (!isBeeReady && !initDone) return PageState.Connecting
|
||||||
|
|
||||||
return
|
if (!hasPk) return PageState.NoPrivateKey
|
||||||
}
|
|
||||||
|
|
||||||
setShowResetModal(shallReset)
|
if (!initDone) return PageState.Loading
|
||||||
|
|
||||||
if (shallReset) {
|
if (shallReset && !resetAcknowledged) return PageState.Reset
|
||||||
setShowInitialModal(true)
|
|
||||||
|
|
||||||
return
|
if (initializationError && !shallReset) return PageState.InitError
|
||||||
}
|
|
||||||
|
|
||||||
if (initializationError) {
|
if (showAdminErrorModal) return PageState.AdminError
|
||||||
setIsLoading(false)
|
|
||||||
|
|
||||||
return
|
const hasAdminStamp = Boolean(fm?.adminStamp)
|
||||||
}
|
const hasAdminDrive = Boolean(adminDrive)
|
||||||
|
|
||||||
if (fm) {
|
if (!hasAdminStamp && !hasAdminDrive && !isCreationInProgress) return PageState.Initial
|
||||||
const hasAdminStamp = Boolean(fm.adminStamp)
|
|
||||||
const tmpHasAdminDrive = Boolean(adminDrive)
|
|
||||||
setHasAdminDrive(hasAdminStamp || tmpHasAdminDrive)
|
|
||||||
setIsLoading(false)
|
|
||||||
|
|
||||||
setShowInitialModal(!(hasAdminStamp || tmpHasAdminDrive))
|
return PageState.Ready
|
||||||
|
}, [
|
||||||
|
isBeeReady,
|
||||||
|
hasPk,
|
||||||
|
initDone,
|
||||||
|
shallReset,
|
||||||
|
resetAcknowledged,
|
||||||
|
initializationError,
|
||||||
|
showAdminErrorModal,
|
||||||
|
fm,
|
||||||
|
adminDrive,
|
||||||
|
isCreationInProgress,
|
||||||
|
])
|
||||||
|
|
||||||
return
|
const handlePrivateKeySaved = useCallback(() => {
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoading(true)
|
|
||||||
}, [fm, beeApi, hasPk, initializationError, adminDrive, shallReset])
|
|
||||||
|
|
||||||
const handlePrivateKeySaved = useCallback(async () => {
|
|
||||||
if (!isMountedRef.current) return
|
if (!isMountedRef.current) return
|
||||||
|
|
||||||
setHasPk(true)
|
setHasPk(true)
|
||||||
|
|
||||||
if (fm) {
|
if (fm) return
|
||||||
if (!isMountedRef.current) return
|
|
||||||
|
|
||||||
setIsLoading(false)
|
notifyPkSaved()
|
||||||
|
}, [fm, notifyPkSaved])
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoading(true)
|
|
||||||
const manager = await init()
|
|
||||||
|
|
||||||
if (!isMountedRef.current) return
|
|
||||||
|
|
||||||
setIsLoading(false)
|
|
||||||
|
|
||||||
const hasAdminStamp = Boolean(manager?.adminStamp)
|
|
||||||
const tmpHasAdminDrive = Boolean(adminDrive)
|
|
||||||
|
|
||||||
setShowInitialModal(!(hasAdminStamp || tmpHasAdminDrive))
|
|
||||||
}, [fm, adminDrive, init])
|
|
||||||
|
|
||||||
const isEmptyState = useMemo(() => {
|
|
||||||
return showInitialModal && !isLoading && !hasAdminDrive && !isCreationInProgress
|
|
||||||
}, [showInitialModal, isLoading, hasAdminDrive, isCreationInProgress])
|
|
||||||
const isInvalidState = useMemo(
|
|
||||||
() => shallReset && fm && !isCreationInProgress,
|
|
||||||
[shallReset, fm, isCreationInProgress],
|
|
||||||
)
|
|
||||||
|
|
||||||
const loading = !fm?.adminStamp || !adminDrive
|
const loading = !fm?.adminStamp || !adminDrive
|
||||||
|
const isFormbricksActive = Boolean(fm && fm.adminStamp && adminDrive && !loading)
|
||||||
|
|
||||||
const isFormbricksActive = Boolean(fm && fm.adminStamp && adminDrive && !showInitialModal && !loading)
|
if (pageState === PageState.Connecting || pageState === PageState.Loading) {
|
||||||
|
return <LoadingBlock />
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasPk) {
|
if (pageState === PageState.NoPrivateKey) {
|
||||||
return <PrivateKeyModalBlock onSaved={handlePrivateKeySaved} />
|
return <PrivateKeyModalBlock onSaved={handlePrivateKeySaved} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initializationError && !isLoading && !shallReset) {
|
if (pageState === PageState.InitError) {
|
||||||
return (
|
return (
|
||||||
<InitializationErrorBlock
|
<InitializationErrorBlock
|
||||||
onOk={() => {
|
onOk={() => {
|
||||||
@@ -282,17 +270,16 @@ export function FileManagerPage(): ReactElement {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showResetModal) {
|
if (pageState === PageState.Reset) {
|
||||||
return <ResetModalBlock cacheHelpUrl={cacheHelpUrl} onConfirm={() => setShowResetModal(false)} />
|
return <ResetModalBlock cacheHelpUrl={cacheHelpUrl} onConfirm={() => setResetAcknowledged(true)} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!showErrorModal && (isEmptyState || isInvalidState)) {
|
if (pageState === PageState.Initial) {
|
||||||
return (
|
return (
|
||||||
<InitialModalBlock
|
<InitialModalBlock
|
||||||
resetState={shallReset}
|
resetState={shallReset}
|
||||||
handleVisibility={(isVisible: boolean) => setShowInitialModal(isVisible)}
|
|
||||||
handleShowError={(flag: boolean, error?: string) => {
|
handleShowError={(flag: boolean, error?: string) => {
|
||||||
setShowErrorModal(flag)
|
setAdminShowErrorModal(flag)
|
||||||
|
|
||||||
if (error) setErrorMessage(error)
|
if (error) setErrorMessage(error)
|
||||||
}}
|
}}
|
||||||
@@ -301,19 +288,15 @@ export function FileManagerPage(): ReactElement {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fm) {
|
if (pageState === PageState.AdminError) {
|
||||||
return <LoadingBlock />
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showErrorModal) {
|
|
||||||
return (
|
return (
|
||||||
<ErrorModalBlock
|
<ErrorModalBlock
|
||||||
label={
|
label={
|
||||||
|
errorMessage ||
|
||||||
'Error creating Admin Drive. Please try again. Possible causes include insufficient xDAI balance or a lost connection to the RPC.'
|
'Error creating Admin Drive. Please try again. Possible causes include insufficient xDAI balance or a lost connection to the RPC.'
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowErrorModal(false)
|
setAdminShowErrorModal(false)
|
||||||
setShowInitialModal(true)
|
|
||||||
setErrorMessage('')
|
setErrorMessage('')
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -323,8 +306,8 @@ export function FileManagerPage(): ReactElement {
|
|||||||
return (
|
return (
|
||||||
<FileManagerMainContent
|
<FileManagerMainContent
|
||||||
fm={fm}
|
fm={fm}
|
||||||
showConnectionError={showConnectionError}
|
showConnectionError={isConnectionError && !connectionErrorDismissed}
|
||||||
setShowConnectionError={() => setShowConnectionError(false)}
|
setShowConnectionError={(show: boolean) => setConnectionErrorDismissed(!show)}
|
||||||
isFormbricksActive={isFormbricksActive}
|
isFormbricksActive={isFormbricksActive}
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
setErrorMessage={setErrorMessage}
|
setErrorMessage={setErrorMessage}
|
||||||
|
|||||||
+110
-111
@@ -29,7 +29,7 @@ import { useLatestBeeRelease } from '../hooks/apiHooks'
|
|||||||
|
|
||||||
import { Context as SettingsContext } from './Settings'
|
import { Context as SettingsContext } from './Settings'
|
||||||
|
|
||||||
const LAUNCH_GRACE_PERIOD = 15_000
|
const LAUNCH_GRACE_PERIOD = 35_000
|
||||||
const REFRESH_WHEN_OK = 30_000
|
const REFRESH_WHEN_OK = 30_000
|
||||||
const REFRESH_WHEN_ERROR = 5_000
|
const REFRESH_WHEN_ERROR = 5_000
|
||||||
const TIMEOUT = 3_000
|
const TIMEOUT = 3_000
|
||||||
@@ -116,15 +116,22 @@ interface Props {
|
|||||||
children: ReactNode
|
children: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatus(
|
interface StatusProps {
|
||||||
nodeInfo: NodeInfo | null,
|
nodeInfo: NodeInfo | null
|
||||||
apiHealth: boolean,
|
apiHealth: boolean
|
||||||
topology: Topology | null,
|
topology: Topology | null
|
||||||
chequebookAddress: ChequebookAddressResponse | null,
|
isWarmingUp: boolean
|
||||||
chequebookBalance: ChequebookBalanceResponse | null,
|
chequebookAddress: ChequebookAddressResponse | null
|
||||||
error: Error | null,
|
chequebookBalance: ChequebookBalanceResponse | null
|
||||||
startedAt: number,
|
error: Error | null
|
||||||
): Status {
|
startedAt: number
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatus(props: StatusProps): Status {
|
||||||
|
const { nodeInfo, apiHealth, topology, isWarmingUp, chequebookAddress, chequebookBalance, error, startedAt } = {
|
||||||
|
...props,
|
||||||
|
}
|
||||||
|
|
||||||
const status: Status = { ...initialValues.status }
|
const status: Status = { ...initialValues.status }
|
||||||
|
|
||||||
// API connection check
|
// API connection check
|
||||||
@@ -143,15 +150,17 @@ function getStatus(
|
|||||||
|
|
||||||
if (chequebookAddress?.chequebookAddress && chequebookBalance !== null) {
|
if (chequebookAddress?.chequebookAddress && chequebookBalance !== null) {
|
||||||
status.chequebook.checkState = CheckState.OK
|
status.chequebook.checkState = CheckState.OK
|
||||||
} else status.chequebook.checkState = CheckState.OK
|
} else {
|
||||||
|
status.chequebook.checkState = CheckState.WARNING
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status.all = determineOverallStatus(status, startedAt)
|
status.all = determineOverallStatus(status, isWarmingUp, startedAt)
|
||||||
|
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
function determineOverallStatus(status: Status, startedAt: number): CheckState {
|
function determineOverallStatus(status: Status, isWarmingUp: boolean, startedAt: number): CheckState {
|
||||||
const hasErrors = Object.values(status).some(
|
const hasErrors = Object.values(status).some(
|
||||||
({ isEnabled, checkState }) => isEnabled && checkState === CheckState.ERROR,
|
({ isEnabled, checkState }) => isEnabled && checkState === CheckState.ERROR,
|
||||||
)
|
)
|
||||||
@@ -160,15 +169,23 @@ function determineOverallStatus(status: Status, startedAt: number): CheckState {
|
|||||||
)
|
)
|
||||||
const isInGracePeriod = Date.now() - startedAt < LAUNCH_GRACE_PERIOD
|
const isInGracePeriod = Date.now() - startedAt < LAUNCH_GRACE_PERIOD
|
||||||
|
|
||||||
if (hasErrors && isInGracePeriod) {
|
if (isWarmingUp || isInGracePeriod) {
|
||||||
return CheckState.CONNECTING
|
return CheckState.CONNECTING
|
||||||
} else if (hasErrors) {
|
}
|
||||||
|
|
||||||
|
if (hasErrors) {
|
||||||
return CheckState.ERROR
|
return CheckState.ERROR
|
||||||
} else if (hasWarnings) {
|
}
|
||||||
|
|
||||||
|
if (hasWarnings) {
|
||||||
return CheckState.WARNING
|
return CheckState.WARNING
|
||||||
} else {
|
}
|
||||||
|
|
||||||
return CheckState.OK
|
return CheckState.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFulfilledValue<T>(result: PromiseSettledResult<T>): T | null {
|
||||||
|
return result.status === 'fulfilled' ? result.value : null
|
||||||
}
|
}
|
||||||
|
|
||||||
// This does not need to be exposed and works much better as variable than state variable which may trigger some unnecessary re-renders
|
// This does not need to be exposed and works much better as variable than state variable which may trigger some unnecessary re-renders
|
||||||
@@ -179,6 +196,7 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
|
|
||||||
const [beeVersion, setBeeVersion] = useState<string | null>(null)
|
const [beeVersion, setBeeVersion] = useState<string | null>(null)
|
||||||
const [apiHealth, setApiHealth] = useState<boolean>(false)
|
const [apiHealth, setApiHealth] = useState<boolean>(false)
|
||||||
|
const [isWarmingUp, setIsWarmingUp] = useState<boolean>(true)
|
||||||
const [nodeAddresses, setNodeAddresses] = useState<NodeAddresses | null>(null)
|
const [nodeAddresses, setNodeAddresses] = useState<NodeAddresses | null>(null)
|
||||||
const [nodeInfo, setNodeInfo] = useState<NodeInfo | null>(null)
|
const [nodeInfo, setNodeInfo] = useState<NodeInfo | null>(null)
|
||||||
const [topology, setNodeTopology] = useState<Topology | null>(null)
|
const [topology, setNodeTopology] = useState<Topology | null>(null)
|
||||||
@@ -191,7 +209,7 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
const [settlements, setSettlements] = useState<AllSettlements | null>(null)
|
const [settlements, setSettlements] = useState<AllSettlements | null>(null)
|
||||||
const [chainState, setChainState] = useState<ChainState | null>(null)
|
const [chainState, setChainState] = useState<ChainState | null>(null)
|
||||||
const [walletBalance, setWalletBalance] = useState<WalletBalance | null>(null)
|
const [walletBalance, setWalletBalance] = useState<WalletBalance | null>(null)
|
||||||
const [startedAt] = useState(() => Date.now())
|
const [startedAt, setStartedAt] = useState(() => Date.now())
|
||||||
|
|
||||||
const { latestBeeRelease } = useLatestBeeRelease()
|
const { latestBeeRelease } = useLatestBeeRelease()
|
||||||
|
|
||||||
@@ -202,6 +220,15 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
|
|
||||||
const frequencyRef = useRef<number | null>(frequency)
|
const frequencyRef = useRef<number | null>(frequency)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isWarmingUp) return
|
||||||
|
|
||||||
|
setStartedAt(Date.now())
|
||||||
|
const timer = setTimeout(() => setStartedAt(0), LAUNCH_GRACE_PERIOD)
|
||||||
|
|
||||||
|
return () => clearTimeout(timer)
|
||||||
|
}, [isWarmingUp])
|
||||||
|
|
||||||
const refresh = useCallback(async () => {
|
const refresh = useCallback(async () => {
|
||||||
// Don't want to refresh when already refreshing
|
// Don't want to refresh when already refreshing
|
||||||
if (isRefreshing) {
|
if (isRefreshing) {
|
||||||
@@ -215,101 +242,63 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
isRefreshing = true
|
isRefreshing = true
|
||||||
|
|
||||||
|
const [
|
||||||
|
healthResult,
|
||||||
|
statusResult,
|
||||||
|
nodeAddressesResult,
|
||||||
|
nodeInfoResult,
|
||||||
|
topologyResult,
|
||||||
|
peersResult,
|
||||||
|
chequebookAddressResult,
|
||||||
|
peerChequesResult,
|
||||||
|
chainStateResult,
|
||||||
|
walletResult,
|
||||||
|
chequebookBalanceResult,
|
||||||
|
stakeResult,
|
||||||
|
peerBalancesResult,
|
||||||
|
settlementsResult,
|
||||||
|
] = await Promise.allSettled([
|
||||||
|
beeApi.getHealth({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getStatus({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getNodeAddresses({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getNodeInfo({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getTopology({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getPeers({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getChequebookAddress({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getLastCheques({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getChainState({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getWalletBalance({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getChequebookBalance({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getStake({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getAllBalances({ timeout: TIMEOUT }),
|
||||||
|
beeApi.getAllSettlements(),
|
||||||
|
])
|
||||||
|
|
||||||
|
// All setters called synchronously — React 18 batches them into one render.
|
||||||
|
const health = getFulfilledValue(healthResult)
|
||||||
|
setBeeVersion(health?.version ?? null)
|
||||||
|
setApiHealth(Boolean(health))
|
||||||
|
|
||||||
|
setIsWarmingUp(getFulfilledValue(statusResult)?.isWarmingUp ?? false)
|
||||||
|
setNodeAddresses(getFulfilledValue(nodeAddressesResult))
|
||||||
|
setNodeInfo(getFulfilledValue(nodeInfoResult))
|
||||||
|
setNodeTopology(getFulfilledValue(topologyResult))
|
||||||
|
setPeers(getFulfilledValue(peersResult))
|
||||||
|
setChequebookAddress(getFulfilledValue(chequebookAddressResult))
|
||||||
|
setPeerCheques(getFulfilledValue(peerChequesResult))
|
||||||
|
setChainState(getFulfilledValue(chainStateResult))
|
||||||
|
setWalletBalance(getFulfilledValue(walletResult))
|
||||||
|
setChequebookBalance(getFulfilledValue(chequebookBalanceResult))
|
||||||
|
setStake(getFulfilledValue(stakeResult))
|
||||||
|
setPeerBalances(getFulfilledValue(peerBalancesResult)?.balances ?? null)
|
||||||
|
setSettlements(getFulfilledValue(settlementsResult))
|
||||||
setError(null)
|
setError(null)
|
||||||
|
|
||||||
const promises = [
|
|
||||||
// API health
|
|
||||||
beeApi
|
|
||||||
.getHealth({ timeout: TIMEOUT })
|
|
||||||
.then(response => setBeeVersion(response.version))
|
|
||||||
.then(() => setApiHealth(true))
|
|
||||||
.catch(() => {
|
|
||||||
setBeeVersion(null)
|
|
||||||
setApiHealth(false)
|
|
||||||
}),
|
|
||||||
|
|
||||||
// Node Addresses
|
|
||||||
beeApi
|
|
||||||
.getNodeAddresses({ timeout: TIMEOUT })
|
|
||||||
.then(setNodeAddresses)
|
|
||||||
.catch(() => setNodeAddresses(null)),
|
|
||||||
|
|
||||||
// NodeInfo
|
|
||||||
beeApi
|
|
||||||
.getNodeInfo({ timeout: TIMEOUT })
|
|
||||||
.then(setNodeInfo)
|
|
||||||
.catch(() => setNodeInfo(null)),
|
|
||||||
|
|
||||||
// Network Topology
|
|
||||||
beeApi
|
|
||||||
.getTopology({ timeout: TIMEOUT })
|
|
||||||
.then(setNodeTopology)
|
|
||||||
.catch(() => setNodeTopology(null)),
|
|
||||||
|
|
||||||
// Peers
|
|
||||||
beeApi
|
|
||||||
.getPeers({ timeout: TIMEOUT })
|
|
||||||
.then(setPeers)
|
|
||||||
.catch(() => setPeers(null)),
|
|
||||||
|
|
||||||
// Chequebook address
|
|
||||||
beeApi
|
|
||||||
.getChequebookAddress({ timeout: TIMEOUT })
|
|
||||||
.then(setChequebookAddress)
|
|
||||||
.catch(() => setChequebookAddress(null)),
|
|
||||||
|
|
||||||
// Cheques
|
|
||||||
beeApi
|
|
||||||
.getLastCheques({ timeout: TIMEOUT })
|
|
||||||
.then(setPeerCheques)
|
|
||||||
.catch(() => setPeerCheques(null)),
|
|
||||||
|
|
||||||
// Chain state
|
|
||||||
beeApi
|
|
||||||
.getChainState({ timeout: TIMEOUT })
|
|
||||||
.then(setChainState)
|
|
||||||
.catch(() => setChainState(null)),
|
|
||||||
|
|
||||||
// Wallet
|
|
||||||
beeApi
|
|
||||||
.getWalletBalance({ timeout: TIMEOUT })
|
|
||||||
.then(setWalletBalance)
|
|
||||||
.catch(() => setWalletBalance(null)),
|
|
||||||
|
|
||||||
// Chequebook balance
|
|
||||||
beeApi
|
|
||||||
.getChequebookBalance({ timeout: TIMEOUT })
|
|
||||||
.then(setChequebookBalance)
|
|
||||||
.catch(() => setChequebookBalance(null)),
|
|
||||||
|
|
||||||
beeApi
|
|
||||||
.getStake({ timeout: TIMEOUT })
|
|
||||||
.then(stake => setStake(stake))
|
|
||||||
.catch(() => setStake(null)),
|
|
||||||
|
|
||||||
// Peer balances
|
|
||||||
beeApi
|
|
||||||
.getAllBalances({ timeout: TIMEOUT })
|
|
||||||
.then(x => setPeerBalances(x.balances))
|
|
||||||
.catch(() => setPeerBalances(null)),
|
|
||||||
|
|
||||||
// Settlements
|
|
||||||
beeApi
|
|
||||||
.getAllSettlements()
|
|
||||||
.then(setSettlements)
|
|
||||||
.catch(() => setSettlements(null)),
|
|
||||||
]
|
|
||||||
|
|
||||||
await Promise.allSettled(promises)
|
|
||||||
} catch (e) {
|
|
||||||
setError(e as Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
isRefreshing = false
|
|
||||||
setLastUpdate(Date.now())
|
setLastUpdate(Date.now())
|
||||||
|
|
||||||
|
isRefreshing = false
|
||||||
}, [beeApi])
|
}, [beeApi])
|
||||||
|
|
||||||
const start = useCallback(
|
const start = useCallback(
|
||||||
@@ -322,8 +311,18 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
const stop = useCallback(() => setFrequency(null), [])
|
const stop = useCallback(() => setFrequency(null), [])
|
||||||
|
|
||||||
const status = useMemo(
|
const status = useMemo(
|
||||||
() => getStatus(nodeInfo, apiHealth, topology, chequebookAddress, chequebookBalance, error, startedAt),
|
() =>
|
||||||
[nodeInfo, apiHealth, topology, chequebookAddress, chequebookBalance, error, startedAt],
|
getStatus({
|
||||||
|
nodeInfo,
|
||||||
|
apiHealth,
|
||||||
|
topology,
|
||||||
|
isWarmingUp,
|
||||||
|
chequebookAddress,
|
||||||
|
chequebookBalance,
|
||||||
|
error,
|
||||||
|
startedAt,
|
||||||
|
}),
|
||||||
|
[nodeInfo, apiHealth, topology, chequebookAddress, chequebookBalance, error, startedAt, isWarmingUp],
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ import { FILE_MANAGER_EVENTS } from '../modules/filemanager/constants/common'
|
|||||||
import { getUsableStamps } from '../modules/filemanager/utils/bee'
|
import { getUsableStamps } from '../modules/filemanager/utils/bee'
|
||||||
import { getSignerPk } from '../modules/filemanager/utils/common'
|
import { getSignerPk } from '../modules/filemanager/utils/common'
|
||||||
|
|
||||||
|
import { CheckState, Context as BeeContext } from './Bee'
|
||||||
import { Context as SettingsContext } from './Settings'
|
import { Context as SettingsContext } from './Settings'
|
||||||
|
|
||||||
interface ContextInterface {
|
interface ContextInterface {
|
||||||
fm: FileManagerBase | null
|
fm: FileManagerBase | null
|
||||||
|
initDone: boolean
|
||||||
files: FileInfo[]
|
files: FileInfo[]
|
||||||
currentDrive?: DriveInfo
|
currentDrive?: DriveInfo
|
||||||
currentStamp?: PostageBatch
|
currentStamp?: PostageBatch
|
||||||
@@ -24,6 +26,7 @@ interface ContextInterface {
|
|||||||
setCurrentStamp: (s: PostageBatch | undefined) => void
|
setCurrentStamp: (s: PostageBatch | undefined) => void
|
||||||
resync: () => Promise<void>
|
resync: () => Promise<void>
|
||||||
init: () => Promise<FileManagerBase | null>
|
init: () => Promise<FileManagerBase | null>
|
||||||
|
notifyPkSaved: () => void
|
||||||
setShowError: (show: boolean) => void
|
setShowError: (show: boolean) => void
|
||||||
syncDrives: () => Promise<void>
|
syncDrives: () => Promise<void>
|
||||||
refreshStamp: (batchId: string) => Promise<PostageBatch | undefined>
|
refreshStamp: (batchId: string) => Promise<PostageBatch | undefined>
|
||||||
@@ -31,6 +34,7 @@ interface ContextInterface {
|
|||||||
|
|
||||||
const initialValues: ContextInterface = {
|
const initialValues: ContextInterface = {
|
||||||
fm: null,
|
fm: null,
|
||||||
|
initDone: false,
|
||||||
files: [],
|
files: [],
|
||||||
currentDrive: undefined,
|
currentDrive: undefined,
|
||||||
currentStamp: undefined,
|
currentStamp: undefined,
|
||||||
@@ -45,6 +49,7 @@ const initialValues: ContextInterface = {
|
|||||||
resync: async () => {},
|
resync: async () => {},
|
||||||
// eslint-disable-next-line require-await
|
// eslint-disable-next-line require-await
|
||||||
init: async () => null,
|
init: async () => null,
|
||||||
|
notifyPkSaved: () => {},
|
||||||
setShowError: () => {},
|
setShowError: () => {},
|
||||||
syncDrives: async () => {},
|
syncDrives: async () => {},
|
||||||
// eslint-disable-next-line require-await
|
// eslint-disable-next-line require-await
|
||||||
@@ -85,12 +90,18 @@ const findDrives = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Provider({ children }: Props) {
|
export function Provider({ children }: Props) {
|
||||||
const initInProgressRef = useRef(false)
|
const initInProgressRef = useRef<boolean>(false)
|
||||||
const beeInstanceRef = useRef<Bee | null>(null)
|
const isBeeApiInitialized = useRef<boolean>(false)
|
||||||
|
|
||||||
|
const { status } = useContext(BeeContext)
|
||||||
const { apiUrl } = useContext(SettingsContext)
|
const { apiUrl } = useContext(SettingsContext)
|
||||||
|
|
||||||
|
const apiUrlRef = useRef<string>(apiUrl)
|
||||||
|
|
||||||
|
const [pkSaved, setPkSaved] = useState<boolean>(false)
|
||||||
|
const [beeInstance, setBeeInstance] = useState<Bee | null>(null)
|
||||||
const [fm, setFm] = useState<FileManagerBase | null>(null)
|
const [fm, setFm] = useState<FileManagerBase | null>(null)
|
||||||
|
const [initDone, setInitDone] = useState<boolean>(false)
|
||||||
const [shallReset, setShallReset] = useState<boolean>(false)
|
const [shallReset, setShallReset] = useState<boolean>(false)
|
||||||
const [files, setFiles] = useState<FileInfo[]>([])
|
const [files, setFiles] = useState<FileInfo[]>([])
|
||||||
const [drives, setDrives] = useState<DriveInfo[]>([])
|
const [drives, setDrives] = useState<DriveInfo[]>([])
|
||||||
@@ -102,6 +113,8 @@ export function Provider({ children }: Props) {
|
|||||||
const [initializationError, setInitializationError] = useState<boolean>(false)
|
const [initializationError, setInitializationError] = useState<boolean>(false)
|
||||||
const [showError, setShowError] = useState<boolean>(false)
|
const [showError, setShowError] = useState<boolean>(false)
|
||||||
|
|
||||||
|
const notifyPkSaved = useCallback(() => setPkSaved(v => !v), [])
|
||||||
|
|
||||||
const syncFiles = useCallback((manager: FileManagerBase, fi?: FileInfo, remove?: boolean): void => {
|
const syncFiles = useCallback((manager: FileManagerBase, fi?: FileInfo, remove?: boolean): void => {
|
||||||
if (fi) {
|
if (fi) {
|
||||||
if (remove) {
|
if (remove) {
|
||||||
@@ -129,12 +142,13 @@ export function Provider({ children }: Props) {
|
|||||||
setFiles([...manager.fileInfoList])
|
setFiles([...manager.fileInfoList])
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const syncDrives = useCallback(async (manager: FileManagerBase, di?: DriveInfo, remove?: boolean): Promise<void> => {
|
const syncDrives = useCallback(
|
||||||
if (!beeInstanceRef.current) {
|
async (manager: FileManagerBase, di?: DriveInfo, remove?: boolean): Promise<void> => {
|
||||||
|
if (!beeInstance) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const usableStamps = await getUsableStamps(beeInstanceRef.current)
|
const usableStamps = await getUsableStamps(beeInstance)
|
||||||
|
|
||||||
if (di) {
|
if (di) {
|
||||||
const isNotExpired = usableStamps.some(s => s.batchID.toString() === di.batchId.toString())
|
const isNotExpired = usableStamps.some(s => s.batchID.toString() === di.batchId.toString())
|
||||||
@@ -192,7 +206,9 @@ export function Provider({ children }: Props) {
|
|||||||
setAdminDrive(tmpAdminDrive)
|
setAdminDrive(tmpAdminDrive)
|
||||||
setDrives(userDrives)
|
setDrives(userDrives)
|
||||||
setExpiredDrives(expiredDrives)
|
setExpiredDrives(expiredDrives)
|
||||||
}, [])
|
},
|
||||||
|
[beeInstance],
|
||||||
|
)
|
||||||
|
|
||||||
const syncDrivesPublic = useCallback(async () => {
|
const syncDrivesPublic = useCallback(async () => {
|
||||||
if (fm) {
|
if (fm) {
|
||||||
@@ -200,12 +216,13 @@ export function Provider({ children }: Props) {
|
|||||||
}
|
}
|
||||||
}, [fm, syncDrives])
|
}, [fm, syncDrives])
|
||||||
|
|
||||||
const refreshStamp = useCallback(async (batchId: string): Promise<PostageBatch | undefined> => {
|
const refreshStamp = useCallback(
|
||||||
if (!beeInstanceRef.current) {
|
async (batchId: string): Promise<PostageBatch | undefined> => {
|
||||||
|
if (!beeInstance) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const usableStamps = await getUsableStamps(beeInstanceRef.current)
|
const usableStamps = await getUsableStamps(beeInstance)
|
||||||
const refreshedStamp = usableStamps.find(s => s.batchID.toString() === batchId)
|
const refreshedStamp = usableStamps.find(s => s.batchID.toString() === batchId)
|
||||||
|
|
||||||
setCurrentStamp(prev => {
|
setCurrentStamp(prev => {
|
||||||
@@ -217,38 +234,38 @@ export function Provider({ children }: Props) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return refreshedStamp
|
return refreshedStamp
|
||||||
}, [])
|
},
|
||||||
|
[beeInstance],
|
||||||
|
)
|
||||||
|
|
||||||
const init = useCallback(async (): Promise<FileManagerBase | null> => {
|
const init = useCallback(async (): Promise<FileManagerBase | null> => {
|
||||||
const pk = getSignerPk()
|
const pk = getSignerPk()
|
||||||
|
|
||||||
if (!apiUrl || !pk || initInProgressRef.current) return null
|
if (!beeInstance || !pk || initInProgressRef.current) return null
|
||||||
|
|
||||||
initInProgressRef.current = true
|
initInProgressRef.current = true
|
||||||
|
|
||||||
setFm(null)
|
setFm(null)
|
||||||
|
setInitDone(false)
|
||||||
setFiles([])
|
setFiles([])
|
||||||
setDrives([])
|
setDrives([])
|
||||||
setAdminDrive(null)
|
setAdminDrive(null)
|
||||||
setInitializationError(false)
|
setInitializationError(false)
|
||||||
setCurrentDrive(undefined)
|
setCurrentDrive(undefined)
|
||||||
setCurrentStamp(undefined)
|
setCurrentStamp(undefined)
|
||||||
|
setShallReset(false)
|
||||||
|
|
||||||
if (!beeInstanceRef.current) {
|
const manager = new FileManagerBase(beeInstance)
|
||||||
beeInstanceRef.current = new Bee(apiUrl, { signer: pk })
|
|
||||||
}
|
|
||||||
|
|
||||||
const manager = new FileManagerBase(beeInstanceRef.current)
|
|
||||||
|
|
||||||
const handleInitialized = (success: boolean) => {
|
const handleInitialized = (success: boolean) => {
|
||||||
setInitializationError(!success)
|
setInitializationError(!success)
|
||||||
|
setInitDone(true)
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
if (manager.adminStamp && !manager.adminStamp.usable) {
|
if (manager.adminStamp && !manager.adminStamp.usable) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.warn('Admin stamp exists but is not usable')
|
console.warn('Admin stamp exists but is not usable')
|
||||||
setShallReset(true)
|
setShallReset(true)
|
||||||
setInitializationError(true)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -310,11 +327,13 @@ export function Provider({ children }: Props) {
|
|||||||
|
|
||||||
return manager
|
return manager
|
||||||
} catch {
|
} catch {
|
||||||
|
setInitDone(true)
|
||||||
|
|
||||||
return null
|
return null
|
||||||
} finally {
|
} finally {
|
||||||
initInProgressRef.current = false
|
initInProgressRef.current = false
|
||||||
}
|
}
|
||||||
}, [apiUrl, syncDrives, syncFiles])
|
}, [beeInstance, syncDrives, syncFiles])
|
||||||
|
|
||||||
const resync = useCallback(async (): Promise<void> => {
|
const resync = useCallback(async (): Promise<void> => {
|
||||||
const prevDriveId = currentDrive?.id.toString()
|
const prevDriveId = currentDrive?.id.toString()
|
||||||
@@ -322,19 +341,59 @@ export function Provider({ children }: Props) {
|
|||||||
|
|
||||||
const manager = await init()
|
const manager = await init()
|
||||||
|
|
||||||
if (prevDriveId && manager && beeInstanceRef.current) {
|
if (prevDriveId && manager && beeInstance) {
|
||||||
const refreshedDrive = manager.driveList.find(d => d.id.toString() === prevDriveId)
|
const refreshedDrive = manager.driveList.find(d => d.id.toString() === prevDriveId)
|
||||||
setCurrentDrive(refreshedDrive)
|
setCurrentDrive(refreshedDrive)
|
||||||
|
|
||||||
const uStamps: PostageBatch[] = await getUsableStamps(beeInstanceRef.current)
|
const uStamps: PostageBatch[] = await getUsableStamps(beeInstance)
|
||||||
const isValidCurrentStamp = uStamps.find(s => s.batchID.toString() === prevStamp?.batchID.toString())
|
const isValidCurrentStamp = uStamps.find(s => s.batchID.toString() === prevStamp?.batchID.toString())
|
||||||
|
|
||||||
setCurrentStamp(isValidCurrentStamp)
|
setCurrentStamp(isValidCurrentStamp)
|
||||||
}
|
}
|
||||||
}, [currentDrive?.id, currentStamp, init])
|
}, [beeInstance, currentDrive?.id, currentStamp, init])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!apiUrl || initInProgressRef.current) {
|
apiUrlRef.current = apiUrl
|
||||||
|
}, [apiUrl])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const isConnecting = status.all === CheckState.CONNECTING
|
||||||
|
const isApiOk = status.apiConnection.isEnabled && status.apiConnection.checkState === CheckState.OK
|
||||||
|
const currentApiUrl = apiUrlRef.current
|
||||||
|
const pk = getSignerPk()
|
||||||
|
|
||||||
|
if (!currentApiUrl || !pk) {
|
||||||
|
isBeeApiInitialized.current = false
|
||||||
|
setBeeInstance(null)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isConnecting) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBeeApiInitialized.current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isApiOk) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isBeeApiInitialized.current = true
|
||||||
|
setBeeInstance(new Bee(currentApiUrl, { signer: pk }))
|
||||||
|
}, [status.all, status.apiConnection, pkSaved])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
isBeeApiInitialized.current = false
|
||||||
|
setBeeInstance(null)
|
||||||
|
setInitDone(false)
|
||||||
|
initInProgressRef.current = false
|
||||||
|
}, [apiUrl])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!beeInstance || initInProgressRef.current) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,7 +402,7 @@ export function Provider({ children }: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initFromLocalState()
|
initFromLocalState()
|
||||||
}, [apiUrl, init])
|
}, [beeInstance, init])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (fm && drives.length === 0 && !adminDrive) {
|
if (fm && drives.length === 0 && !adminDrive) {
|
||||||
@@ -354,6 +413,7 @@ export function Provider({ children }: Props) {
|
|||||||
const contextValue = useMemo(
|
const contextValue = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
fm,
|
fm,
|
||||||
|
initDone,
|
||||||
files,
|
files,
|
||||||
currentDrive,
|
currentDrive,
|
||||||
currentStamp,
|
currentStamp,
|
||||||
@@ -367,12 +427,14 @@ export function Provider({ children }: Props) {
|
|||||||
setCurrentStamp,
|
setCurrentStamp,
|
||||||
resync,
|
resync,
|
||||||
init,
|
init,
|
||||||
|
notifyPkSaved,
|
||||||
setShowError,
|
setShowError,
|
||||||
syncDrives: syncDrivesPublic,
|
syncDrives: syncDrivesPublic,
|
||||||
refreshStamp,
|
refreshStamp,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
fm,
|
fm,
|
||||||
|
initDone,
|
||||||
files,
|
files,
|
||||||
currentDrive,
|
currentDrive,
|
||||||
currentStamp,
|
currentStamp,
|
||||||
@@ -386,6 +448,7 @@ export function Provider({ children }: Props) {
|
|||||||
setCurrentStamp,
|
setCurrentStamp,
|
||||||
resync,
|
resync,
|
||||||
init,
|
init,
|
||||||
|
notifyPkSaved,
|
||||||
setShowError,
|
setShowError,
|
||||||
syncDrivesPublic,
|
syncDrivesPublic,
|
||||||
refreshStamp,
|
refreshStamp,
|
||||||
|
|||||||
Reference in New Issue
Block a user