diff --git a/.changeset/quiet-pots-crash.md b/.changeset/quiet-pots-crash.md new file mode 100644 index 000000000..4054d2d28 --- /dev/null +++ b/.changeset/quiet-pots-crash.md @@ -0,0 +1,7 @@ +--- +'@siafoundation/renterd-js': patch +'@siafoundation/renterd-react': patch +'@siafoundation/renterd-types': patch +--- + +Fixed missing fields and structure of bus and autopilot state types. diff --git a/.changeset/thirty-countries-cover.md b/.changeset/thirty-countries-cover.md new file mode 100644 index 000000000..412cc048a --- /dev/null +++ b/.changeset/thirty-countries-cover.md @@ -0,0 +1,5 @@ +--- +'renterd': minor +--- + +The app was refactored to assume there is a singular autopilot and its API always exists, even if it is disabled. diff --git a/apps/renterd/components/CmdRoot/ConfigCmdGroup.tsx b/apps/renterd/components/CmdRoot/ConfigCmdGroup.tsx index 96ad39360..153d465ad 100644 --- a/apps/renterd/components/CmdRoot/ConfigCmdGroup.tsx +++ b/apps/renterd/components/CmdRoot/ConfigCmdGroup.tsx @@ -4,7 +4,6 @@ import { useDialog } from '../../contexts/dialog' import { CommandGroup, CommandItemNav, CommandItemSearch } from './Item' import { Page } from './types' import { useConfig } from '../../contexts/config' -import { useApp } from '../../contexts/app' const commandPage = { namespace: 'configuration', @@ -21,7 +20,6 @@ export function ConfigCmdGroup({ currentPage, parentPage, pushPage }: Props) { const router = useRouter() const { configViewMode } = useConfig() const { closeDialog } = useDialog() - const { autopilotInfo } = useApp() return ( Open configuration - {autopilotInfo.data?.isAutopilotEnabled && ( - { - router.push(routes.config.storage) - closeDialog() - }} - > - Configure storage - - )} + { + router.push(routes.config.storage) + closeDialog() + }} + > + Configure storage + {configViewMode === 'advanced' && ( <> - {autopilotInfo.data?.isAutopilotEnabled && ( - <> - { - router.push(routes.config.hosts) - closeDialog() - }} - > - Configure hosts - - { - router.push(routes.config.wallet) - closeDialog() - }} - > - Configure wallet - - - )} + { + router.push(routes.config.hosts) + closeDialog() + }} + > + Configure hosts + + { + router.push(routes.config.wallet) + closeDialog() + }} + > + Configure wallet + { const { form } = useConfig() - const { isAutopilotEnabled } = useApp() const storageTB = form.watch('storageTB') const downloadTBMonth = form.watch('downloadTBMonth') const uploadTBMonth = form.watch('uploadTBMonth') @@ -99,40 +97,35 @@ function useSpendingDerivedPricingFromEnabledFields({ }) const { rate } = useFormExchangeRate(form) const values = useMemo(() => { - if (isAutopilotEnabled) { - const derivedPricing = derivePricingFromSpendingEstimate({ - estimatedSpendingPerMonth, - maxPricingFactor, - storageTB, - downloadTBMonth, - uploadTBMonth, - redundancyMultiplier, - storageWeight, - downloadWeight, - uploadWeight, - }) - if (!derivedPricing) { - return undefined - } - // Convert derived siacoin prices to pinned fiat prices. - const pinnedPricing = rate - ? { - maxStoragePriceTBMonthPinned: - derivedPricing?.maxStoragePriceTBMonth.times(rate), - maxDownloadPriceTBPinned: - derivedPricing?.maxDownloadPriceTB.times(rate), - maxUploadPriceTBPinned: - derivedPricing?.maxUploadPriceTB.times(rate), - } - : undefined - return { - ...derivedPricing, - ...pinnedPricing, - } + const derivedPricing = derivePricingFromSpendingEstimate({ + estimatedSpendingPerMonth, + maxPricingFactor, + storageTB, + downloadTBMonth, + uploadTBMonth, + redundancyMultiplier, + storageWeight, + downloadWeight, + uploadWeight, + }) + if (!derivedPricing) { + return undefined + } + // Convert derived siacoin prices to pinned fiat prices. + const pinnedPricing = rate + ? { + maxStoragePriceTBMonthPinned: + derivedPricing?.maxStoragePriceTBMonth.times(rate), + maxDownloadPriceTBPinned: + derivedPricing?.maxDownloadPriceTB.times(rate), + maxUploadPriceTBPinned: derivedPricing?.maxUploadPriceTB.times(rate), + } + : undefined + return { + ...derivedPricing, + ...pinnedPricing, } - return undefined }, [ - isAutopilotEnabled, estimatedSpendingPerMonth, storageTB, downloadTBMonth, diff --git a/apps/renterd/components/Config/Recommendations.tsx b/apps/renterd/components/Config/Recommendations.tsx index 116d1562a..b4d7a9632 100644 --- a/apps/renterd/components/Config/Recommendations.tsx +++ b/apps/renterd/components/Config/Recommendations.tsx @@ -12,14 +12,12 @@ import { CheckmarkFilled20, PendingFilled20, } from '@siafoundation/react-icons' -import { useApp } from '../../contexts/app' import { routes } from '../../config/routes' import { useConfig } from '../../contexts/config' import { pluralize } from '@siafoundation/units' import { HangingNavItem } from './HangingNavItem' export function Recommendations() { - const { autopilotInfo } = useApp() const { form, fields, evaluation } = useConfig() const { hostMargin50, @@ -33,10 +31,6 @@ export function Recommendations() { usableHostsAfterRecommendation, } = evaluation - if (!autopilotInfo.data?.isAutopilotEnabled) { - return null - } - const tip = (
diff --git a/apps/renterd/components/Files/checks/useAutopilotNotConfigured.tsx b/apps/renterd/components/Files/checks/useAutopilotNotConfigured.tsx index 457a1de8d..1525f2b73 100644 --- a/apps/renterd/components/Files/checks/useAutopilotNotConfigured.tsx +++ b/apps/renterd/components/Files/checks/useAutopilotNotConfigured.tsx @@ -1,10 +1,8 @@ -import { useApp } from '../../../contexts/app' +import { useAutopilotState } from '@siafoundation/renterd-react' export function useAutopilotNotConfigured() { - const { autopilotInfo } = useApp() + const autopilotState = useAutopilotState() return { - active: - autopilotInfo.data?.isAutopilotEnabled && - !autopilotInfo.data?.state?.configured, + active: !autopilotState.data?.configured, } } diff --git a/apps/renterd/components/Hosts/HostsCmd/HostsFilterCmd/HostsFilterCmdGroups/index.tsx b/apps/renterd/components/Hosts/HostsCmd/HostsFilterCmd/HostsFilterCmdGroups/index.tsx index 2679ac41d..fd005a0b7 100644 --- a/apps/renterd/components/Hosts/HostsCmd/HostsFilterCmd/HostsFilterCmdGroups/index.tsx +++ b/apps/renterd/components/Hosts/HostsCmd/HostsFilterCmd/HostsFilterCmdGroups/index.tsx @@ -4,7 +4,6 @@ import { AllowBlockCmdGroup } from './AllowBlock' import { ServerFilterItem } from '@siafoundation/design-system' import { ContractsCmdGroup } from './Contracts' import { UsableCmdGroup } from './Usable' -import { useApp } from '../../../../../contexts/app' import { PublicKeyCmdGroup } from './PublicKey' type Props = { @@ -14,12 +13,9 @@ type Props = { } export function ContractFilterCmdGroups({ currentPage, select }: Props) { - const { isAutopilotEnabled } = useApp() return ( <> - {isAutopilotEnabled && ( - - )} + diff --git a/apps/renterd/components/Hosts/HostsCmd/HostsFilterCmd/HostsFilterNav/index.tsx b/apps/renterd/components/Hosts/HostsCmd/HostsFilterCmd/HostsFilterNav/index.tsx index e0b1a2403..7c322e969 100644 --- a/apps/renterd/components/Hosts/HostsCmd/HostsFilterCmd/HostsFilterNav/index.tsx +++ b/apps/renterd/components/Hosts/HostsCmd/HostsFilterCmd/HostsFilterNav/index.tsx @@ -6,7 +6,6 @@ import { ServerFilterItem } from '@siafoundation/design-system' import { hostsFilterContractsPage } from '../HostsFilterCmdGroups/Contracts' import { hostsFilterUsablePage } from '../HostsFilterCmdGroups/Usable' import { PublicKeyCmdNav } from '../HostsFilterCmdGroups/PublicKey' -import { useApp } from '../../../../../contexts/app' export const commandPage = { namespace: 'hosts', @@ -26,21 +25,18 @@ export function HostsFilterNav({ pushPage, select, }: Props) { - const { isAutopilotEnabled } = useApp() return ( <> - {isAutopilotEnabled && ( - { - pushPage(hostsFilterUsablePage) - }} - > - {hostsFilterUsablePage.label} - - )} + { + pushPage(hostsFilterUsablePage) + }} + > + {hostsFilterUsablePage.label} + ( @@ -43,7 +42,7 @@ export function OnboardingBar() { const notEnoughContracts = useNotEnoughContracts() const { estimatedSpendingPerMonth } = useSpendingEstimate() - if (!isUnlockedAndAuthedRoute || !isAutopilotEnabled) { + if (!isUnlockedAndAuthedRoute) { return null } @@ -51,7 +50,7 @@ export function OnboardingBar() { wallet.data ? wallet.data.confirmed + wallet.data.unconfirmed : 0 ) - const step1Configured = autopilotInfo.data?.state?.configured + const step1Configured = autopilotState.data?.configured const step2Synced = syncStatus.isSynced const step3Funded = walletBalance.gt(0) const step4Contracts = !notEnoughContracts.active diff --git a/apps/renterd/contexts/app/index.tsx b/apps/renterd/contexts/app/index.tsx index ad7eb313b..ebad71e5f 100644 --- a/apps/renterd/contexts/app/index.tsx +++ b/apps/renterd/contexts/app/index.tsx @@ -1,16 +1,11 @@ import React, { createContext, useContext } from 'react' -import { useAutopilotInfo } from './useAutopilotInfo' import { useBusSdk } from './useBusSdk' function useAppMain() { - const autopilotInfo = useAutopilotInfo() const bus = useBusSdk() - const isAutopilotEnabled = !!autopilotInfo.data?.isAutopilotEnabled return { bus, - autopilotInfo, - isAutopilotEnabled, } } diff --git a/apps/renterd/contexts/app/useAutopilotInfo.tsx b/apps/renterd/contexts/app/useAutopilotInfo.tsx deleted file mode 100644 index f07a68102..000000000 --- a/apps/renterd/contexts/app/useAutopilotInfo.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { secondsInMilliseconds } from '@siafoundation/units' -import { useAutopilotState } from '@siafoundation/renterd-react' -import { useMemo } from 'react' - -export type AutopilotInfo = ReturnType['data'] - -export function useAutopilotInfo() { - const state = useAutopilotState({ - config: { - swr: { - dedupingInterval: secondsInMilliseconds(5), - revalidateOnFocus: false, - refreshInterval: (data) => - !data ? secondsInMilliseconds(1) : secondsInMilliseconds(60), - }, - }, - }) - - const status = useMemo(() => { - if (state.error) { - return 'off' - } else if (state.data) { - // This check is required because the API currently returns html when the - // endpoint does not exist. - const validResponse = typeof state.data === 'object' - return validResponse ? 'on' : 'off' - } - return 'loading' - }, [state]) - - return { - ...state, - data: state.data - ? { - state: state.data, - status, - isAutopilotEnabled: status === 'on', - } - : undefined, - } -} diff --git a/apps/renterd/contexts/config/fields.tsx b/apps/renterd/contexts/config/fields.tsx index 53effbd3f..36328770d 100644 --- a/apps/renterd/contexts/config/fields.tsx +++ b/apps/renterd/contexts/config/fields.tsx @@ -35,11 +35,9 @@ export const scDecimalPlaces = 6 type GetFields = { advancedDefaults?: AdvancedDefaults - isAutopilotEnabled: boolean configViewMode: ConfigViewMode recommendations: Partial> validationContext: { - isAutopilotEnabled: boolean configViewMode: ConfigViewMode } } @@ -49,7 +47,6 @@ export type Fields = ReturnType export function getFields({ advancedDefaults, recommendations, - isAutopilotEnabled, configViewMode, validationContext, }: GetFields): ConfigFields { @@ -61,11 +58,8 @@ export function getFields({ title: 'Expected storage', description: <>The amount of storage you would like to rent in TB., units: 'TB', - hidden: !isAutopilotEnabled, validation: { - validate: { - required: requiredIfAutopilot(validationContext), - }, + required: 'required', }, }, uploadTBMonth: { @@ -76,11 +70,8 @@ export function getFields({ <>The amount of upload bandwidth you plan to use each month in TB. ), units: 'TB/month', - hidden: !isAutopilotEnabled, validation: { - validate: { - required: requiredIfAutopilot(validationContext), - }, + required: 'required', }, }, downloadTBMonth: { @@ -91,11 +82,8 @@ export function getFields({ <>The amount of download bandwidth you plan to use each month in TB. ), units: 'TB/month', - hidden: !isAutopilotEnabled, validation: { - validate: { - required: requiredIfAutopilot(validationContext), - }, + required: 'required', }, }, periodWeeks: { @@ -106,10 +94,10 @@ export function getFields({ units: 'weeks', suggestion: advancedDefaults?.periodWeeks, suggestionTip: `Typically ${advancedDefaults?.periodWeeks} weeks.`, - hidden: !isAutopilotEnabled || configViewMode === 'basic', + hidden: configViewMode === 'basic', validation: { validate: { - required: requiredIfAutopilotAndAdvanced(validationContext), + required: requiredIfAdvanced(validationContext), }, }, }, @@ -127,10 +115,10 @@ export function getFields({ decimalsLimit: 6, suggestion: advancedDefaults?.renewWindowWeeks, suggestionTip: `Typically ${advancedDefaults?.renewWindowWeeks} weeks.`, - hidden: !isAutopilotEnabled || configViewMode === 'basic', + hidden: configViewMode === 'basic', validation: { validate: { - required: requiredIfAutopilotAndAdvanced(validationContext), + required: requiredIfAdvanced(validationContext), }, }, }, @@ -143,10 +131,10 @@ export function getFields({ decimalsLimit: 0, suggestion: advancedDefaults?.amountHosts, suggestionTip: `Typically ${advancedDefaults?.amountHosts} hosts.`, - hidden: !isAutopilotEnabled || configViewMode === 'basic', + hidden: configViewMode === 'basic', validation: { validate: { - required: requiredIfAutopilotAndAdvanced(validationContext), + required: requiredIfAdvanced(validationContext), }, }, }, @@ -170,7 +158,7 @@ export function getFields({ The default value is {advancedDefaults?.prune}. ), - hidden: !isAutopilotEnabled || configViewMode === 'basic', + hidden: configViewMode === 'basic', validation: {}, }, @@ -189,7 +177,7 @@ export function getFields({ suggestionTip: `Defaults to ${ advancedDefaults?.allowRedundantIPs ? 'on' : 'off' }.`, - hidden: !isAutopilotEnabled || configViewMode === 'basic', + hidden: configViewMode === 'basic', validation: {}, }, maxDowntimeHours: { @@ -214,10 +202,10 @@ export function getFields({ 1 )} days.` : undefined, - hidden: !isAutopilotEnabled || configViewMode === 'basic', + hidden: configViewMode === 'basic', validation: { validate: { - required: requiredIfAutopilotAndAdvanced(validationContext), + required: requiredIfAdvanced(validationContext), }, }, }, @@ -237,10 +225,10 @@ export function getFields({ suggestionTip: advancedDefaults?.maxConsecutiveScanFailures ? `Defaults to ${advancedDefaults?.maxConsecutiveScanFailures.toNumber()}.` : undefined, - hidden: !isAutopilotEnabled || configViewMode === 'basic', + hidden: configViewMode === 'basic', validation: { validate: { - required: requiredIfAutopilotAndAdvanced(validationContext), + required: requiredIfAdvanced(validationContext), }, }, }, @@ -256,11 +244,11 @@ export function getFields({ ), suggestion: advancedDefaults?.minProtocolVersion, suggestionTip: `Defaults to ${advancedDefaults?.minProtocolVersion}.`, - hidden: !isAutopilotEnabled || configViewMode === 'basic', + hidden: configViewMode === 'basic', validation: { validate: { - required: requiredIfAutopilotAndAdvanced(validationContext), - version: requiredIfAutopilotAndAdvanced( + required: requiredIfAdvanced(validationContext), + version: requiredIfAdvanced( validationContext, (value: Maybe) => { const regex = /^\d+\.\d+\.\d+$/ @@ -836,49 +824,6 @@ function requiredIfAdvanced( } } -function requiredIfAutopilot( - context: { isAutopilotEnabled: boolean }, - method?: ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value: any, - values: InputValues - ) => string | boolean -) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (value: any, values: InputValues) => { - if (context.isAutopilotEnabled) { - if (method) { - return method(value, values) - } - return !!value || 'required' - } - return true - } -} - -function requiredIfAutopilotAndAdvanced( - context: { - isAutopilotEnabled: boolean - configViewMode: ConfigViewMode - }, - method?: ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value: any, - values: InputValues - ) => string | boolean -) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (value: any, values: InputValues) => { - if (context.isAutopilotEnabled && context.configViewMode === 'advanced') { - if (method) { - return method(value, values) - } - return !!value || 'required' - } - return true - } -} - type ShouldPinField = | 'shouldPinMaxStoragePrice' | 'shouldPinMaxUploadPrice' diff --git a/apps/renterd/contexts/config/index.tsx b/apps/renterd/contexts/config/index.tsx index ad9fcda77..d999b32c2 100644 --- a/apps/renterd/contexts/config/index.tsx +++ b/apps/renterd/contexts/config/index.tsx @@ -17,11 +17,10 @@ import { checkIfAllResourcesLoaded, checkIfAnyResourcesErrored, } from './useResources' -import { useApp } from '../app' export function useConfigMain() { const { - autopilotInfo, + autopilotState, autopilot, gouging, pinned, @@ -46,8 +45,7 @@ export function useConfigMain() { return undefined } return transformDown({ - autopilotID: loaded.autopilotInfo.data?.state?.id, - hasBeenConfigured: !!loaded.autopilotInfo.data?.state?.configured, + hasBeenConfigured: !!loaded.autopilotState.data?.configured, autopilot: loaded.autopilot.data, gouging: loaded.gouging.data, pinned: loaded.pinned.data, @@ -61,22 +59,20 @@ export function useConfigMain() { [resources] ) - const { isAutopilotEnabled } = useApp() const revalidateAndResetForm = useCallback(async () => { // these do not seem to throw on errors, just return undefined - const _autopilotInfo = await autopilotInfo.mutate() - const _autopilot = isAutopilotEnabled ? await autopilot.mutate() : undefined + const _autopilotState = await autopilotState.mutate() + const _autopilot = await autopilot.mutate() const _gouging = await gouging.mutate() const _pinned = await pinned.mutate() const _upload = await upload.mutate() - if (!_autopilotInfo || !_gouging || !_upload || !_pinned) { + if (!_autopilotState || !_gouging || !_upload || !_pinned) { triggerErrorToast({ title: 'Error fetching settings' }) return undefined } form.reset( transformDown({ - autopilotID: _autopilotInfo.id, - hasBeenConfigured: _autopilotInfo.configured, + hasBeenConfigured: _autopilotState.configured, autopilot: _autopilot, gouging: _gouging, pinned: _pinned, @@ -84,16 +80,7 @@ export function useConfigMain() { averages: averages.data, }) ) - }, [ - form, - autopilotInfo, - isAutopilotEnabled, - autopilot, - gouging, - pinned, - upload, - averages.data, - ]) + }, [form, autopilotState, autopilot, gouging, pinned, upload, averages.data]) useFormInit({ form, diff --git a/apps/renterd/contexts/config/transform.spec.ts b/apps/renterd/contexts/config/transform.spec.ts index a0bf5db92..2d86796c5 100644 --- a/apps/renterd/contexts/config/transform.spec.ts +++ b/apps/renterd/contexts/config/transform.spec.ts @@ -17,12 +17,12 @@ import { import { CurrencyId } from '@siafoundation/react-core' import { AutopilotConfig, + AutopilotState, SettingsGouging, SettingsPinned, SettingsUpload, } from '@siafoundation/renterd-types' import { merge } from '@technically/lodash' -import { AutopilotInfo } from '../app/useAutopilotInfo' describe('tansforms', () => { describe('down', () => { @@ -31,7 +31,6 @@ describe('tansforms', () => { expect( transformDown({ hasBeenConfigured: true, - autopilotID: 'autopilot', autopilot: { ...autopilot, hosts: { @@ -84,7 +83,6 @@ describe('tansforms', () => { const values = transformDown({ hasBeenConfigured: false, autopilot: undefined, - autopilotID: 'autopilot', pinned, gouging, upload: merge(upload, { @@ -112,7 +110,6 @@ describe('tansforms', () => { const values = transformDown({ hasBeenConfigured: false, autopilot: undefined, - autopilotID: 'autopilot', gouging, pinned, upload: merge(upload, { @@ -351,8 +348,7 @@ describe('tansforms', () => { otherNewValue: '77777777777', foobar: 'value', // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any, - 'autopilot' + } as any ) ).toEqual({ currency: 'usd' as CurrencyId, @@ -420,12 +416,12 @@ describe('tansforms', () => { describe('down up down - ensure converting back and forth results in the same values', () => { it('converts ap download down up down', () => { - const { autopilotInfo, autopilot, gouging, pinned, upload } = + const { autopilotState, autopilot, gouging, pinned, upload } = buildAllResponses() const transformUpMocks = { resources: { - autopilotInfo: { - data: autopilotInfo, + autopilotState: { + data: autopilotState, }, autopilot: {}, gouging: { data: gouging }, @@ -441,12 +437,9 @@ describe('tansforms', () => { renterdState: { network: 'mainnet' as const, }, - autopilotID: 'autopilot', - isAutopilotEnabled: true, } const down = transformDown({ hasBeenConfigured: true, - autopilotID: 'autopilot', autopilot: { ...autopilot, contracts: { @@ -469,7 +462,6 @@ describe('tansforms', () => { }) const downUpDown = transformDown({ hasBeenConfigured: true, - autopilotID: 'autopilot', ...downUp.payloads, }) @@ -526,29 +518,23 @@ describe('tansforms', () => { function buildAllResponses() { return { - autopilotInfo: { - state: { - id: 'autopilot', - configured: true, - migrating: true, - migratingLastStart: new Date().toISOString(), - scanning: true, - scanningLastStart: new Date().toISOString(), - synced: true, - uptimeMS: '333', - network: 'mainnet' as const, - version: '0.0.0', - commit: 'commit', - OS: 'os', - buildTime: new Date().getTime(), - explorer: { - enabled: true, - url: 'https://api.siascan.com', - }, - startTime: new Date().getTime(), - }, - status: 'on', - } as AutopilotInfo, + autopilotState: { + id: 'autopilot', + configured: true, + migrating: true, + migratingLastStart: new Date().toISOString(), + scanning: true, + scanningLastStart: new Date().toISOString(), + pruning: true, + pruningLastStart: new Date().toISOString(), + uptimeMS: '333', + network: 'mainnet' as const, + version: '0.0.0', + commit: 'commit', + OS: 'os', + buildTime: new Date().getTime(), + startTime: new Date().getTime(), + } as AutopilotState, autopilot: { hosts: { allowRedundantIPs: false, diff --git a/apps/renterd/contexts/config/transformDown.ts b/apps/renterd/contexts/config/transformDown.ts index c3da34e71..ad12ef694 100644 --- a/apps/renterd/contexts/config/transformDown.ts +++ b/apps/renterd/contexts/config/transformDown.ts @@ -200,7 +200,6 @@ export function transformDownUpload(u: SettingsUpload): ValuesUpload { export type RemoteData = { hasBeenConfigured: boolean - autopilotID: string | undefined autopilot: AutopilotConfig | undefined gouging: SettingsGouging pinned: SettingsPinned @@ -216,7 +215,6 @@ export type RemoteData = { export function transformDown({ hasBeenConfigured, - autopilotID, autopilot, gouging, pinned, diff --git a/apps/renterd/contexts/config/transformUp.ts b/apps/renterd/contexts/config/transformUp.ts index dbbbdce68..429d898a7 100644 --- a/apps/renterd/contexts/config/transformUp.ts +++ b/apps/renterd/contexts/config/transformUp.ts @@ -118,8 +118,7 @@ export function transformUpGouging( export function transformUpPinned( values: SubmitValuesPinned & { periodWeeks?: BigNumber }, - existingValues: SettingsPinned, - autopilotID = 'autopilot' + existingValues: SettingsPinned ): SettingsPinned { const v = applyDefaultToAnyEmptyValues( values, @@ -171,28 +170,19 @@ export function transformUpUpload( export function transformUp({ resources, renterdState, - isAutopilotEnabled, values, }: { resources: ResourcesRequiredLoaded renterdState: { network: 'mainnet' | 'zen' | 'anagami' } - isAutopilotEnabled: boolean values: SubmitValues }) { - const autopilot = isAutopilotEnabled - ? transformUpAutopilot( - renterdState.network, - values, - resources.autopilot.data - ) - : undefined - - const gouging = transformUpGouging(values, resources.gouging.data) - const pinned = transformUpPinned( + const autopilot = transformUpAutopilot( + renterdState.network, values, - resources.pinned.data, - resources.autopilotInfo.data?.state?.id + resources.autopilot.data ) + const gouging = transformUpGouging(values, resources.gouging.data) + const pinned = transformUpPinned(values, resources.pinned.data) const upload = transformUpUpload(values, resources.upload.data) return { diff --git a/apps/renterd/contexts/config/useAutopilotEvaluations.tsx b/apps/renterd/contexts/config/useAutopilotEvaluations.tsx index 092f78e46..83ebb6e3a 100644 --- a/apps/renterd/contexts/config/useAutopilotEvaluations.tsx +++ b/apps/renterd/contexts/config/useAutopilotEvaluations.tsx @@ -2,7 +2,6 @@ import { objectEntries } from '@siafoundation/design-system' import { currencyOptions } from '@siafoundation/react-core' import { useAutopilotConfigEvaluate, - useAutopilotState, useBusState, } from '@siafoundation/renterd-react' import { @@ -36,21 +35,16 @@ import { AutopilotConfigEvaluatePayload } from '@siafoundation/renterd-types' export function useAutopilotEvaluations({ form, resources, - isAutopilotEnabled, }: { form: UseFormReturn resources: ResourcesMaybeLoaded - isAutopilotEnabled: boolean }) { const values = form.watch() const renterdState = useBusState() - const autopilotState = useAutopilotState() const hasDataToEvaluate = useMemo(() => { - if (!isAutopilotEnabled) { - return false - } - if (!checkIfAllResourcesLoaded(resources)) { + const loaded = checkIfAllResourcesLoaded(resources) + if (!loaded) { return false } if (!form.formState.isValid) { @@ -59,17 +53,11 @@ export function useAutopilotEvaluations({ if (!renterdState.data) { return false } - if (!autopilotState.data?.configured) { + if (!loaded.autopilotState.data.configured) { return false } return true - }, [ - isAutopilotEnabled, - form.formState.isValid, - resources, - renterdState.data, - autopilotState.data, - ]) + }, [form.formState.isValid, resources, renterdState.data]) // Convert any pinned fields over to siacoin values. const pricing = useEnabledPricingValuesInSiacoin({ @@ -89,9 +77,9 @@ export function useAutopilotEvaluations({ // the transformUp calculations that assume all data is valid and present. return mergeValuesWithDefaultsOrZeroValues( valuesWithPinnedOverrides, - resources.autopilotInfo.data?.state?.network + renterdState.data?.network ) - }, [values, pricing, resources.autopilotInfo.data?.state?.network]) + }, [values, pricing, renterdState.data?.network]) const payloads = useMemo(() => { if (!hasDataToEvaluate || !renterdState.data) { @@ -101,7 +89,6 @@ export function useAutopilotEvaluations({ const { payloads } = transformUp({ resources: resources as ResourcesRequiredLoaded, renterdState: renterdState.data, - isAutopilotEnabled, values: currentValuesWithPinnedOverridesAndDefaults, }) return payloads @@ -109,7 +96,6 @@ export function useAutopilotEvaluations({ currentValuesWithPinnedOverridesAndDefaults, resources, renterdState, - isAutopilotEnabled, hasDataToEvaluate, ]) @@ -415,10 +401,8 @@ function pricesToPinnedPrices({ // We just need some of the static metadata so pass in dummy values. const fields = getFields({ validationContext: { - isAutopilotEnabled: true, configViewMode: 'basic', }, - isAutopilotEnabled: true, configViewMode: 'basic', recommendations: {}, }) diff --git a/apps/renterd/contexts/config/useForm.tsx b/apps/renterd/contexts/config/useForm.tsx index c1c36ff27..3009d7e43 100644 --- a/apps/renterd/contexts/config/useForm.tsx +++ b/apps/renterd/contexts/config/useForm.tsx @@ -3,7 +3,6 @@ import { ConfigViewMode, inputValues, getAdvancedDefaults } from './types' import { useForm as useHookForm } from 'react-hook-form' import { useBusState } from '@siafoundation/renterd-react' import { getFields } from './fields' -import { useApp } from '../app' import useLocalStorageState from 'use-local-storage-state' import { useAutopilotEvaluations } from './useAutopilotEvaluations' import { ResourcesMaybeLoaded } from './useResources' @@ -27,7 +26,6 @@ export function useForm({ resources }: { resources: ResourcesMaybeLoaded }) { [minShards, totalShards] ) - const { isAutopilotEnabled } = useApp() const [configViewMode, setConfigViewMode] = useLocalStorageState('v0/config/mode', { defaultValue: 'basic', @@ -51,7 +49,6 @@ export function useForm({ resources }: { resources: ResourcesMaybeLoaded }) { const evaluation = useAutopilotEvaluations({ form, resources, - isAutopilotEnabled, }) const renterdState = useBusState() @@ -59,13 +56,11 @@ export function useForm({ resources }: { resources: ResourcesMaybeLoaded }) { // Field validation is only re-applied on re-mount, // so we pass a ref with latest data that can be used interally. const validationContext = useRef({ - isAutopilotEnabled, configViewMode, }) useEffect(() => { - validationContext.current.isAutopilotEnabled = isAutopilotEnabled validationContext.current.configViewMode = configViewMode - }, [isAutopilotEnabled, configViewMode]) + }, [configViewMode]) const fields = useMemo(() => { const advancedDefaults = renterdState.data @@ -79,17 +74,11 @@ export function useForm({ resources }: { resources: ResourcesMaybeLoaded }) { }, {}) return getFields({ validationContext: validationContext.current, - isAutopilotEnabled, configViewMode, advancedDefaults, recommendations, }) - }, [ - isAutopilotEnabled, - configViewMode, - renterdState.data, - evaluation.recommendations, - ]) + }, [configViewMode, renterdState.data, evaluation.recommendations]) return { form, diff --git a/apps/renterd/contexts/config/useOnValid.tsx b/apps/renterd/contexts/config/useOnValid.tsx index 742146d04..305c1ce44 100644 --- a/apps/renterd/contexts/config/useOnValid.tsx +++ b/apps/renterd/contexts/config/useOnValid.tsx @@ -4,23 +4,18 @@ import { } from '@siafoundation/design-system' import { delay, useMutate } from '@siafoundation/react-core' import { - useAutopilotConfigUpdate, useAutopilotTrigger, useBusState, + useAutopilotConfigUpdate, useSettingsGougingUpdate, useSettingsPinnedUpdate, useSettingsUploadUpdate, } from '@siafoundation/renterd-react' import { busHostsRoute } from '@siafoundation/renterd-types' import { useCallback } from 'react' -import { - ResourcesMaybeLoaded, - ResourcesRequiredLoaded, - checkIfAllResourcesLoaded, -} from './useResources' +import { ResourcesMaybeLoaded, checkIfAllResourcesLoaded } from './useResources' import { transformUp } from './transformUp' import { InputValues, SubmitValues } from './types' -import { useApp } from '../app' export function useOnValid({ resources, @@ -29,8 +24,6 @@ export function useOnValid({ resources: ResourcesMaybeLoaded revalidateAndResetForm: () => Promise }) { - const app = useApp() - const isAutopilotEnabled = !!app.autopilotInfo.data?.isAutopilotEnabled const autopilotTrigger = useAutopilotTrigger() const autopilotUpdate = useAutopilotConfigUpdate() const settingsGougingUpdate = useSettingsGougingUpdate() @@ -45,56 +38,45 @@ export function useOnValid({ if (!loaded || !renterdState.data) { return } - const firstTimeSettingConfig = - isAutopilotEnabled && !resources.autopilot.data + const firstTimeSettingConfig = !loaded.autopilotState.data?.configured const { payloads } = transformUp({ - resources: resources as ResourcesRequiredLoaded, + resources: loaded, renterdState: renterdState.data, - isAutopilotEnabled, values: values as SubmitValues, }) - const autopilotResponse = payloads.autopilot - ? await autopilotUpdate.put({ - payload: payloads.autopilot, - }) - : undefined - - const [gougingResponse, pinnedResponse, uploadResponse] = - await Promise.all([ - settingsGougingUpdate.put({ - payload: payloads.gouging, - }), - settingsPinnedUpdate.put({ - payload: payloads.pinned, - }), - settingsUploadUpdate.put({ - payload: payloads.upload, - }), - ]) + const promises = [ + autopilotUpdate.put({ + payload: payloads.autopilot, + }), + settingsGougingUpdate.put({ + payload: payloads.gouging, + }), + settingsPinnedUpdate.put({ + payload: payloads.pinned, + }), + settingsUploadUpdate.put({ + payload: payloads.upload, + }), + ] - const error = - autopilotResponse?.error || - gougingResponse.error || - pinnedResponse.error || - uploadResponse.error - if (error) { + const results = await Promise.all(promises) + const err = results.find((result) => result.error) + if (err) { triggerErrorToast({ title: 'Error updating configuration', - body: error, + body: err.error, }) return } - if (isAutopilotEnabled && payloads.autopilot) { - // Trigger the autopilot loop with new settings applied. - autopilotTrigger.post({ - payload: { - forceScan: true, - }, - }) - } + // Trigger the autopilot loop with new settings applied. + autopilotTrigger.post({ + payload: { + forceScan: true, + }, + }) triggerSuccessToast({ title: 'Configuration has been saved' }) @@ -115,7 +97,6 @@ export function useOnValid({ [ resources, renterdState.data, - isAutopilotEnabled, autopilotUpdate, settingsGougingUpdate, settingsPinnedUpdate, diff --git a/apps/renterd/contexts/config/useResources.tsx b/apps/renterd/contexts/config/useResources.tsx index f06029b0e..58c2485f0 100644 --- a/apps/renterd/contexts/config/useResources.tsx +++ b/apps/renterd/contexts/config/useResources.tsx @@ -1,12 +1,14 @@ import { useAppSettings, SWRError } from '@siafoundation/react-core' import { AutopilotConfig, + AutopilotState, SettingsGouging, SettingsPinned, SettingsUpload, } from '@siafoundation/renterd-types' import { useAutopilotConfig, + useAutopilotState, useSettingsGouging, useSettingsPinned, useSettingsUpload, @@ -14,12 +16,16 @@ import { import { useSiaCentralHostsNetworkAverages } from '@siafoundation/sia-central-react' import { SiaCentralHostsNetworkAveragesResponse } from '@siafoundation/sia-central-types' import { minutesInMilliseconds } from '@siafoundation/units' -import { useApp } from '../app' import { useMemo } from 'react' -import { AutopilotInfo } from '../app/useAutopilotInfo' export function useResources() { - const { autopilotInfo } = useApp() + const autopilotState = useAutopilotState({ + config: { + swr: { + refreshInterval: minutesInMilliseconds(1), + }, + }, + }) // Settings that 404 when empty. const autopilot = useAutopilotConfig({ config: { @@ -64,9 +70,9 @@ export function useResources() { // Resources required to intialize form. const resources: ResourcesMaybeLoaded = useMemo( () => ({ - autopilotInfo: { - data: autopilotInfo.data, - error: autopilotInfo.error, + autopilotState: { + data: autopilotState.data, + error: autopilotState.error, }, autopilot: { data: autopilot.data, @@ -95,8 +101,8 @@ export function useResources() { }, }), [ - autopilotInfo.data, - autopilotInfo.error, + autopilotState.data, + autopilotState.error, autopilot.data, autopilot.error, gouging.data, @@ -113,7 +119,7 @@ export function useResources() { return { resources, - autopilotInfo, + autopilotState, autopilot, gouging, pinned, @@ -124,12 +130,12 @@ export function useResources() { } export function checkIfAllResourcesLoaded(resources: ResourcesMaybeLoaded) { - const { autopilotInfo, autopilot, gouging, pinned, upload } = resources + const { autopilotState, autopilot, gouging, pinned, upload } = resources const loaded = !!( // These settings have initial daemon values. ( - autopilotInfo.data && - !autopilotInfo.error && + autopilotState.data && + !autopilotState.error && gouging.data && !gouging.error && pinned.data && @@ -160,8 +166,8 @@ export function checkIfAnyResourcesErrored({ } export type ResourcesMaybeLoaded = { - autopilotInfo: { - data?: AutopilotInfo + autopilotState: { + data?: AutopilotState error?: SWRError } autopilot: { @@ -192,8 +198,8 @@ export type ResourcesMaybeLoaded = { } export type ResourcesRequiredLoaded = { - autopilotInfo: { - data?: AutopilotInfo + autopilotState: { + data: AutopilotState error?: SWRError } autopilot: { diff --git a/apps/renterd/contexts/hosts/index.tsx b/apps/renterd/contexts/hosts/index.tsx index 6cef62539..841152e50 100644 --- a/apps/renterd/contexts/hosts/index.tsx +++ b/apps/renterd/contexts/hosts/index.tsx @@ -7,6 +7,7 @@ import { } from '@siafoundation/design-system' import { useAppSettings } from '@siafoundation/react-core' import { + useAutopilotState, useHostsAllowlist, useHostsBlocklist, useHosts as useHostsSearch, @@ -30,7 +31,6 @@ import { import { Commands, emptyCommands } from '../../components/Hosts/HostMap/Globe' import { defaultDatasetRefreshInterval } from '../../config/swr' import { useSiascanUrl } from '../../hooks/useSiascanUrl' -import { useApp } from '../app' import { useContracts } from '../contracts' import { columns } from './columns' import { useDataset } from './dataset' @@ -53,7 +53,7 @@ function useHostsMain() { useServerFilters() const { dataset: allContracts } = useContracts() - const { autopilotInfo, isAutopilotEnabled } = useApp() + const autopilotState = useAutopilotState() const keyIn = useMemo(() => { let keyIn: string[] = [] @@ -78,15 +78,8 @@ function useHostsMain() { addressContains: filters.find((f) => f.id === 'addressContains')?.value, keyIn, } - if ( - isAutopilotEnabled && - autopilotInfo.data?.state?.configured && - autopilotInfo.data?.state?.id - ) { - p.autopilotID = autopilotInfo.data?.state?.id - } return p - }, [autopilotInfo.data, filters, isAutopilotEnabled, keyIn, limit, offset]) + }, [filters, keyIn, limit, offset]) const response = useHostsSearch({ payload, @@ -186,7 +179,7 @@ function useHostsMain() { const dataset = useDataset({ response, allContracts, - autopilotID: autopilotInfo.data?.state?.id, + autopilotID: autopilotState.data?.id, allowlist, blocklist, isAllowlistActive, @@ -194,11 +187,6 @@ function useHostsMain() { onHostSelect: onHostListClick, }) - const disabledCategories = useMemo( - () => (autopilotInfo.data?.status === 'off' ? ['autopilot'] : []), - [autopilotInfo.data?.status] - ) - const { configurableColumns, enabledColumns, @@ -214,7 +202,6 @@ function useHostsMain() { } = useTableState('renterd/v0/hosts', { columns, columnsDefaultVisible, - disabledCategories, }) const filteredTableColumns = useMemo( @@ -227,7 +214,7 @@ function useHostsMain() { const dataState = useDatasetEmptyState(dataset, isValidating, error, filters) const siascanUrl = useSiascanUrl() - const isAutopilotConfigured = !!autopilotInfo.data?.state?.configured + const isAutopilotConfigured = !!autopilotState.data?.configured const tableContext: HostContext = useMemo( () => ({ isAutopilotConfigured, diff --git a/apps/renterd/dialogs/DebugDialog.tsx b/apps/renterd/dialogs/DebugDialog.tsx index 2107777cc..802ac394a 100644 --- a/apps/renterd/dialogs/DebugDialog.tsx +++ b/apps/renterd/dialogs/DebugDialog.tsx @@ -21,7 +21,6 @@ import { useCallback, useMemo } from 'react' import { useForm } from 'react-hook-form' import JSZip from 'jszip' import { saveAs } from 'file-saver' -import { useApp } from '../contexts/app' function getDefaultValues() { return { @@ -84,7 +83,6 @@ type Props = { } export function DebugDialog({ trigger, open, onOpenChange }: Props) { - const { isAutopilotEnabled } = useApp() const defaultValues = useMemo(() => getDefaultValues(), []) const contracts = useContracts() @@ -93,9 +91,7 @@ export function DebugDialog({ trigger, open, onOpenChange }: Props) { limit: 1000, }, }) - const autopilot = useAutopilotConfig({ - disabled: !isAutopilotEnabled, - }) + const autopilot = useAutopilotConfig() const gouging = useSettingsGouging() const upload = useSettingsUpload() const pinned = useSettingsPinned() @@ -123,7 +119,7 @@ export function DebugDialog({ trigger, open, onOpenChange }: Props) { if (values.contracts) { zip.file('contracts.json', JSON.stringify(contracts.data, null, 2)) } - if (isAutopilotEnabled && values.autopilot) { + if (values.autopilot) { zip.file('autopilot.json', JSON.stringify(autopilot.data, null, 2)) } if (values.gouging) { @@ -150,7 +146,6 @@ export function DebugDialog({ trigger, open, onOpenChange }: Props) { } }, [ - isAutopilotEnabled, contracts.data, alerts.data, autopilot.data, @@ -209,14 +204,12 @@ export function DebugDialog({ trigger, open, onOpenChange }: Props) { Configuration
- {isAutopilotEnabled && ( - - )} + { mutate((key) => key === autopilotConfigRoute) - // might need a delay before revalidating status which returns whether - // or not autopilot is configured + // Might need a delay before revalidating status which returns whether + // or not autopilot is configured. const func = async () => { await delay(1000) mutate((key) => key === autopilotStateRoute) diff --git a/libs/renterd-types/src/autopilot.ts b/libs/renterd-types/src/autopilot.ts index 127742715..1c2672be2 100644 --- a/libs/renterd-types/src/autopilot.ts +++ b/libs/renterd-types/src/autopilot.ts @@ -1,5 +1,5 @@ +import { BuildState } from './bus' import { AutopilotConfig, SettingsGouging, SettingsRedundancy } from './types' -import { BusStateResponse } from './bus' export const autopilotStateRoute = '/autopilot/state' export const autopilotConfigRoute = '/autopilot/config' @@ -10,13 +10,15 @@ type AutopilotStatus = { configured: boolean migrating: boolean migratingLastStart: string + pruning: boolean + pruningLastStart: string scanning: boolean scanningLastStart: string - synced: boolean uptimeMS: string + startTime: number } -export type AutopilotState = AutopilotStatus & BusStateResponse +export type AutopilotState = AutopilotStatus & BuildState export type AutopilotStateParams = void export type AutopilotStatePayload = void diff --git a/libs/renterd-types/src/bus.ts b/libs/renterd-types/src/bus.ts index 4decddfac..577982b0e 100644 --- a/libs/renterd-types/src/bus.ts +++ b/libs/renterd-types/src/bus.ts @@ -90,22 +90,22 @@ export const busMultipartPartRoute = '/bus/multipart/part' // state -type BuildState = { - network: 'mainnet' | 'zen' | 'anagami' +export type BuildState = { version: string commit: string OS: string buildTime: number - explorer: { - enabled: boolean - url: string - } } export type BusStateParams = void export type BusStatePayload = void export type BusStateResponse = BuildState & { + network: 'mainnet' | 'zen' | 'anagami' startTime: number + explorer: { + enabled: boolean + url: string + } } // autopilots