diff --git a/.changeset/chatty-panthers-know.md b/.changeset/chatty-panthers-know.md new file mode 100644 index 000000000..64f1839a8 --- /dev/null +++ b/.changeset/chatty-panthers-know.md @@ -0,0 +1,5 @@ +--- +'hostd': minor +--- + +New users will now see an onboarding wizard that prompts the user to complete the necessary setup steps - it also shows the status and progress of each. diff --git a/apps/hostd/components/OnboardingBar.tsx b/apps/hostd/components/OnboardingBar.tsx new file mode 100644 index 000000000..3f66c9dc7 --- /dev/null +++ b/apps/hostd/components/OnboardingBar.tsx @@ -0,0 +1,264 @@ +import { + Button, + Link, + Logo, + Panel, + ScrollArea, + Text, + Tooltip, +} from '@siafoundation/design-system' +import { + RadioButton16, + CheckmarkFilled16, + Launch16, + PendingFilled16, + Subtract24, +} from '@siafoundation/react-icons' +import { useState } from 'react' +import { useSyncStatus } from '../hooks/useSyncStatus' +import { routes } from '../config/routes' +import { useDialog } from '../contexts/dialog' +import { useSettings, useWallet } from '@siafoundation/react-hostd' +import BigNumber from 'bignumber.js' +import { humanSiacoin, toHastings } from '@siafoundation/sia-js' +import { useAppSettings } from '@siafoundation/react-core' +import { useVolumes } from '../contexts/volumes' + +export function OnboardingBar() { + const { isUnlocked } = useAppSettings() + const { openDialog } = useDialog() + const { dataset: volumes } = useVolumes() + const settings = useSettings() + const wallet = useWallet() + const [maximized, setMaximized] = useState(true) + const syncStatus = useSyncStatus() + + if (!isUnlocked) { + return null + } + + const walletBalance = new BigNumber(wallet.data?.confirmed || 0) + const minimumBalance = toHastings(5_000) + + const step1Funded = wallet.data && walletBalance.gte(minimumBalance) + const step2Volumes = volumes?.length > 0 + const step3Configured = settings.data?.acceptingContracts + const step4Synced = syncStatus.isSynced + // const step5Announced = false + const steps = [ + step1Funded, + step2Volumes, + step3Configured, + step4Synced, + //step5Announced + ] + const totalSteps = steps.length + const completedSteps = steps.filter((step) => step).length + + if (totalSteps === completedSteps) { + return null + } + + if (maximized) { + return ( +
+ + +
+
+ + + Welcome to Sia + +
+ +
+
+ + Get set up by completing the following steps. Once they are + complete, your host is ready to store data. + +
+
openDialog('addressDetails')} + ellipsis + size="14" + underline="hover" + > + Step 1: Fund your wallet + + } + description={`Fund your wallet with at least ${humanSiacoin( + minimumBalance + )} siacoin to cover required contract collateral.${ + syncStatus.isWalletSynced + ? '' + : ' Balance will not be accurate until wallet is finished scanning.' + }`} + action={ + step1Funded ? ( + + + + ) : ( + <> + {!syncStatus.isWalletSynced && ( + + {syncStatus.walletScanPercent}% + + )} + openDialog('addressDetails')} + > + + + + + + + ) + } + /> +
+ Step 2: Add a volume + + } + description={ + 'Add a system volume that will be used to store data.' + } + action={ + step2Volumes ? ( + + + + ) : ( + <> + + + + + + + + ) + } + /> +
+ Step 3: Configure pricing and settings + + } + description={`Configure your host's pricing and settings and start accepting contracts.`} + action={ + step3Configured ? ( + + + + ) : ( + <> + + + + + + + + ) + } + /> +
+ Step 4: Wait for the blockchain to sync + + } + description={ + 'The blockchain will sync in the background, this takes some time. No user action required.' + } + action={ + step4Synced ? ( + + + + ) : ( + <> + + {syncStatus.syncPercent}% + + + + + + ) + } + /> + + +
+ ) + } + return ( +
+ +
+ ) +} + +type SectionProps = { + title: React.ReactNode + action: React.ReactNode + description: React.ReactNode +} + +function Section({ title, action, description }: SectionProps) { + return ( +
+
+
+
{title}
+ {action} +
+
+ + {description} + +
+
+
+ ) +} diff --git a/apps/hostd/config/providers.tsx b/apps/hostd/config/providers.tsx index 163c7c401..c70861a91 100644 --- a/apps/hostd/config/providers.tsx +++ b/apps/hostd/config/providers.tsx @@ -3,6 +3,7 @@ import { MetricsProvider } from '../contexts/metrics' import { DialogProvider, Dialogs } from '../contexts/dialog' import { VolumesProvider } from '../contexts/volumes' import { ConfigProvider } from '../contexts/config' +import { OnboardingBar } from '../components/OnboardingBar' type Props = { children: React.ReactNode @@ -18,6 +19,7 @@ export function Providers({ children }: Props) { {/* this is here so that dialogs can use all the other providers, and the other providers can trigger dialogs */} + {children} diff --git a/apps/renterd/config/providers.tsx b/apps/renterd/config/providers.tsx index ae8fc59a9..c189b91cf 100644 --- a/apps/renterd/config/providers.tsx +++ b/apps/renterd/config/providers.tsx @@ -5,6 +5,8 @@ import { HostsProvider } from '../contexts/hosts' import React from 'react' import { AppProvider } from '../contexts/app' import { ConfigProvider } from '../contexts/config' +import { OnboardingBar } from '../components/OnboardingBar' +import { TransfersBar } from '../components/TransfersBar' type Props = { children: React.ReactNode @@ -20,6 +22,8 @@ export function Providers({ children }: Props) { {/* this is here so that dialogs can use all the other providers, and the other providers can trigger dialogs */} + + {children} diff --git a/apps/renterd/contexts/files/index.tsx b/apps/renterd/contexts/files/index.tsx index 8d3c809a0..83047bb92 100644 --- a/apps/renterd/contexts/files/index.tsx +++ b/apps/renterd/contexts/files/index.tsx @@ -6,7 +6,6 @@ import { } from '@siafoundation/design-system' import { useRouter } from 'next/router' import { createContext, useCallback, useContext, useMemo } from 'react' -import { TransfersBar } from '../../components/TransfersBar' import { columns } from './columns' import { defaultSortField, @@ -18,7 +17,6 @@ import { FullPath, FullPathSegments, pathSegmentsToPath } from './paths' import { useUploads } from './uploads' import { useDownloads } from './downloads' import { useDataset } from './dataset' -import { OnboardingBar } from '../../components/OnboardingBar' function useFilesMain() { const router = useRouter() @@ -184,11 +182,5 @@ type Props = { export function FilesProvider({ children }: Props) { const state = useFilesMain() - return ( - - {children} - - - - ) + return {children} }