diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx new file mode 100644 index 0000000..78426a6 --- /dev/null +++ b/src/components/ProgressBar.tsx @@ -0,0 +1,22 @@ +import React, { ReactElement } from 'react' +import LinearProgress, { LinearProgressProps } from '@material-ui/core/LinearProgress' +import Typography from '@material-ui/core/Typography' +import Box from '@material-ui/core/Box' + +interface Props { + linearProgressProps?: LinearProgressProps + value: number +} + +export function LinearProgressWithLabel(props: Props): ReactElement { + return ( + + + + + + {`${Math.round(props.value)}%`} + + + ) +} diff --git a/src/pages/files/AssetSyncing.tsx b/src/pages/files/AssetSyncing.tsx new file mode 100644 index 0000000..736a9eb --- /dev/null +++ b/src/pages/files/AssetSyncing.tsx @@ -0,0 +1,85 @@ +import { Context as SettingsContext } from '../../providers/Settings' +import { Box } from '@material-ui/core' +import { ReactElement, useContext, useEffect, useRef, useState } from 'react' +import { DocumentationText } from '../../components/DocumentationText' +import { LinearProgressWithLabel } from '../../components/ProgressBar' + +interface Props { + reference: string +} + +export function AssetSyncing({ reference }: Props): ReactElement { + const { beeApi } = useContext(SettingsContext) + + const syncTimer = useRef() + const [isRetrieveChecking, setIsRetrieveChecking] = useState(false) + const [syncProgress, setSyncProgress] = useState(0) + + const syncCheck = async () => { + if (!beeApi) { + return + } + + const tags = await beeApi.getAllTags() + const tag = tags.find(t => t.address === reference) + + if (tag) { + const progress = ((tag.seen + tag.synced) / tag.split) * 100 + setSyncProgress(progress) + } + } + + useEffect(() => { + syncTimer.current = setInterval(syncCheck, 2000) + + return () => { + if (syncTimer.current) { + clearInterval(syncTimer.current) + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [reference]) + + useEffect(() => { + if (syncProgress === 100 && syncTimer.current) { + clearInterval(syncTimer.current) + } + }, [syncProgress]) + + useEffect(() => { + /* + There are instances when it seems that the content isn't synchronized, despite being already available. + To ensure it's not due to invalid synchronization data, + verify availability from at least 70% using one of the stewardship endpoints. + + TODO: is 70 a good number? + */ + if (beeApi && !isRetrieveChecking && syncProgress > 10 && syncProgress < 100) { + // It's a long running task make sure only one run occurs at a time. + setIsRetrieveChecking(true) + + beeApi.isReferenceRetrievable(reference).then(isRetriavable => { + if (isRetriavable) { + setSyncProgress(100) + } + + setIsRetrieveChecking(false) + }) + } + }, [syncProgress, isRetrieveChecking, beeApi, reference]) + + return ( + <> + + + Files are not immediately accessible on the Swarm network. Please wait until your upload is synced to the + network.{' '} + Learn more about syncing. + + + + + + + ) +} diff --git a/src/pages/files/Share.tsx b/src/pages/files/Share.tsx index b748c89..c74e586 100644 --- a/src/pages/files/Share.tsx +++ b/src/pages/files/Share.tsx @@ -16,6 +16,7 @@ import { ManifestJs } from '../../utils/manifest' import { AssetPreview } from './AssetPreview' import { AssetSummary } from './AssetSummary' import { DownloadActionBar } from './DownloadActionBar' +import { AssetSyncing } from './AssetSyncing' export function Share(): ReactElement { const { apiUrl, beeApi } = useContext(SettingsContext) @@ -152,6 +153,9 @@ export function Share(): ReactElement { + + + )} - {step === 2 && stamp && } + {step === 2 && stamp && ( + <> + + + + Please do not close the application until your file is uploaded to your local node! + + + + )}