From e68ce68c24204183dd88f1ebdcbc67815a31d548 Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Wed, 21 Oct 2026 17:41:29 -0300 Subject: [PATCH 1/2] perf(app): load only the active admin tab Each held surface renders inside a React Activity, so a hidden tab keeps its state but makes no RPC calls. Opening a tab loads its data with the existing loading state, and polling already stops while the browser tab is hidden. Refs #288 --- app/src/hooks/useVaultPaused.test.ts | 55 ++++++++++-- app/src/pages/AdminPanel.test.tsx | 124 ++++++++++++++++++++++++--- app/src/pages/AdminPanel.tsx | 22 ++--- 3 files changed, 175 insertions(+), 26 deletions(-) diff --git a/app/src/hooks/useVaultPaused.test.ts b/app/src/hooks/useVaultPaused.test.ts index f45ab008..6b72e97f 100644 --- a/app/src/hooks/useVaultPaused.test.ts +++ b/app/src/hooks/useVaultPaused.test.ts @@ -1,13 +1,30 @@ import { type ContractRead } from "@stellar-scaffold/app-lib" -import { describe, expect, it, vi } from "vitest" -import { classifyPause } from "./useVaultPaused" +import { QueryClient, QueryClientProvider } from "@tanstack/react-query" +import { act, renderHook, waitFor } from "@testing-library/react" +import { createElement, type ReactNode } from "react" +import { afterEach, describe, expect, it, vi } from "vitest" +import { + classifyPause, + PAUSE_REFRESH_MS, + useVaultPaused, +} from "./useVaultPaused" + +const { pausedMock } = vi.hoisted(() => ({ + pausedMock: vi.fn(async () => ({ result: false })), +})) vi.mock("../config/clients", () => ({ - asyncVault: async () => ({ - paused: async () => ({}), - }), + asyncVault: async () => ({ paused: pausedMock }), })) +const setPageVisibility = (state: DocumentVisibilityState) => { + Object.defineProperty(document, "visibilityState", { + configurable: true, + get: () => state, + }) + document.dispatchEvent(new Event("visibilitychange")) +} + const paused: ContractRead = { kind: "value", value: true } const open: ContractRead = { kind: "value", value: false } const contractError: ContractRead = { @@ -26,3 +43,31 @@ describe("classifyPause", () => { expect(classifyPause(read)).toBe(expected) }) }) + +describe("useVaultPaused", () => { + afterEach(() => { + setPageVisibility("visible") + vi.useRealTimers() + }) + + it("stops polling while the browser tab is hidden and resumes once shown", async () => { + vi.useFakeTimers({ shouldAdvanceTime: true }) + const client = new QueryClient({ + defaultOptions: { + queries: { refetchOnWindowFocus: false, retry: false }, + }, + }) + const wrapper = ({ children }: { children: ReactNode }) => + createElement(QueryClientProvider, { client }, children) + renderHook(() => useVaultPaused({ poll: true }), { wrapper }) + await waitFor(() => expect(pausedMock).toHaveBeenCalledTimes(1)) + + act(() => setPageVisibility("hidden")) + await act(() => vi.advanceTimersByTimeAsync(PAUSE_REFRESH_MS * 3)) + expect(pausedMock).toHaveBeenCalledTimes(1) + + act(() => setPageVisibility("visible")) + await act(() => vi.advanceTimersByTimeAsync(PAUSE_REFRESH_MS)) + expect(pausedMock).toHaveBeenCalledTimes(2) + }) +}) diff --git a/app/src/pages/AdminPanel.test.tsx b/app/src/pages/AdminPanel.test.tsx index 6b95d45f..e8906dce 100644 --- a/app/src/pages/AdminPanel.test.tsx +++ b/app/src/pages/AdminPanel.test.tsx @@ -1,4 +1,10 @@ import { + QueryClient, + QueryClientProvider, + useQuery, +} from "@tanstack/react-query" +import { + act, cleanup, fireEvent, render, @@ -7,7 +13,7 @@ import { } from "@testing-library/react" import type React from "react" import { MemoryRouter, Route, Routes } from "react-router-dom" -import { beforeEach, describe, expect, it, vi } from "vitest" +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest" import type * as UseAdminAuthority from "../hooks/useAdminAuthority" import { type AdminAuthority, @@ -16,10 +22,22 @@ import { } from "../hooks/useAdminAuthority" import AdminPanel from "./AdminPanel" -const { useAdminAuthorityMock, useWalletMock } = vi.hoisted(() => ({ - useAdminAuthorityMock: vi.fn(), - useWalletMock: vi.fn(), -})) +const { useAdminAuthorityMock, useWalletMock, readSurfaceMock } = vi.hoisted( + () => ({ + useAdminAuthorityMock: vi.fn(), + useWalletMock: vi.fn(), + readSurfaceMock: vi.fn<(surface: string) => Promise>(), + }), +) + +const SurfaceRead: React.FC<{ surface: string }> = ({ surface }) => { + const { data } = useQuery({ + queryKey: ["surface", surface], + queryFn: () => readSurfaceMock(surface), + refetchInterval: 15_000, + }) + return

{data ?? "Reading…"}

+} vi.mock("../hooks/useAdminAuthority", async (importOriginal) => { const actual = await importOriginal() @@ -40,6 +58,7 @@ vi.mock("../components/admin/CycleSection", () => ({ }) => (

Cycle surface for {grants.map((grant) => grant.role).join(", ")}

+ {contracts}
@@ -66,6 +85,7 @@ vi.mock("../components/admin/ComplianceSection", () => ({

Compliance surface for {grants.map((grant) => grant.role).join(", ")}

+ {contracts} ), @@ -179,17 +199,41 @@ const authority = (overrides: Partial): AdminAuthority => ({ ...overrides, }) +let queryClient = new QueryClient() + const routedPanel = (path: string) => ( - - - } /> - } /> - - + + + + } /> + } /> + + + ) const renderAt = (path: string) => render(routedPanel(path)) +const holdCycleAndCompliance = () => { + const manager = holderOf("vault manager") + const compliance = holderOf("share-token compliance") + useAdminAuthorityMock.mockReturnValue( + authority({ + status: "ready", + surfaces: new Set(["cycle", "compliance"]), + grantedBy: { + ...emptyGrantedBy, + cycle: [manager], + compliance: [compliance], + }, + grants: [manager, compliance], + }), + ) +} + +const readSurfaces = () => + readSurfaceMock.mock.calls.map(([surface]) => surface) + const currentTab = () => screen .getAllByRole("link") @@ -198,6 +242,15 @@ const currentTab = () => describe("AdminPanel", () => { beforeEach(() => { useWalletMock.mockReturnValue({ address: connectedAddress }) + queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }) + readSurfaceMock.mockReset() + readSurfaceMock.mockImplementation(async (surface) => `${surface} read`) + }) + + afterEach(() => { + vi.useRealTimers() }) it("tells a disconnected wallet to connect, with no tabs and no identity line", () => { @@ -770,4 +823,53 @@ describe("AdminPanel", () => { "250", ) }) + + it("reads only the open tab's data on a cold load", async () => { + holdCycleAndCompliance() + renderAt("/admin/cycle") + + expect(await screen.findByText("cycle read")).toBeTruthy() + expect(readSurfaces()).toEqual(["cycle"]) + }) + + it("loads a tab's data when it is opened, showing it as reading until it arrives", async () => { + holdCycleAndCompliance() + let arrive: (value: string) => void = () => undefined + readSurfaceMock.mockImplementation(async (surface) => + surface === "compliance" + ? new Promise((resolve) => { + arrive = resolve + }) + : `${surface} read`, + ) + renderAt("/admin/cycle") + await screen.findByText("cycle read") + expect(readSurfaces()).not.toContain("compliance") + + fireEvent.click(screen.getByRole("link", { name: "Compliance" })) + + const compliance = screen.getByRole("region", { name: "Compliance" }) + expect(within(compliance).getByText("Reading…")).toBeTruthy() + expect(readSurfaces()).toEqual(["cycle", "compliance"]) + + act(() => arrive("compliance read")) + + expect(await within(compliance).findByText("compliance read")).toBeTruthy() + }) + + it("stops reading a tab while another tab is shown", async () => { + vi.useFakeTimers({ shouldAdvanceTime: true }) + holdCycleAndCompliance() + renderAt("/admin/cycle") + await screen.findByText("cycle read") + + fireEvent.click(screen.getByRole("link", { name: "Compliance" })) + await screen.findByText("compliance read") + readSurfaceMock.mockClear() + + await act(() => vi.advanceTimersByTimeAsync(31_000)) + + expect(readSurfaces()).not.toContain("cycle") + expect(readSurfaces()).toContain("compliance") + }) }) diff --git a/app/src/pages/AdminPanel.tsx b/app/src/pages/AdminPanel.tsx index 4f71fcd7..296ebea4 100644 --- a/app/src/pages/AdminPanel.tsx +++ b/app/src/pages/AdminPanel.tsx @@ -1,5 +1,5 @@ import { shortAddress } from "@stellar-scaffold/app-lib" -import React, { useEffect, useRef } from "react" +import React, { Activity, useEffect, useRef } from "react" import { Navigate, useParams } from "react-router-dom" import ComplianceSection from "../components/admin/ComplianceSection" import ConfigurationSection from "../components/admin/ConfigurationSection" @@ -180,15 +180,17 @@ const HeldSurfaces: React.FC<{ > {SURFACE_LABELS[surface]} - + + + ))} From 9f2ad2838fe0b10f06dc1c2eb83cd2ab1887eb6f Mon Sep 17 00:00:00 2001 From: luchobonatti Date: Wed, 21 Oct 2026 17:41:30 -0300 Subject: [PATCH 2/2] refactor(app): drop the admin polling switches Hidden tabs no longer subscribe, so the active and poll arguments that only turned polling on and off are gone. The sections keep active to hide a background tab's transaction modal. Closes #288 --- .../admin/ComplianceSection.test.tsx | 4 ---- .../components/admin/ComplianceSection.tsx | 1 - .../admin/ConfigurationSection.test.tsx | 18 --------------- .../components/admin/ConfigurationSection.tsx | 2 +- .../components/admin/CycleSection.test.tsx | 10 --------- app/src/components/admin/CycleSection.tsx | 2 +- .../admin/EmergencySection.test.tsx | 9 ++------ app/src/components/admin/EmergencySection.tsx | 6 ++--- .../admin/GovernanceSection.test.tsx | 1 - .../components/admin/GovernanceSection.tsx | 1 - app/src/hooks/useComplianceState.ts | 4 +--- app/src/hooks/useConfigurationState.test.ts | 6 ----- app/src/hooks/useConfigurationState.ts | 10 ++++----- app/src/hooks/useCycleState.test.ts | 22 ------------------- app/src/hooks/useCycleState.ts | 8 +++---- app/src/hooks/useGovernanceState.ts | 16 ++++++-------- app/src/hooks/useOracleState.ts | 4 ++-- app/src/hooks/useTokenPaused.ts | 4 ++-- 18 files changed, 28 insertions(+), 100 deletions(-) diff --git a/app/src/components/admin/ComplianceSection.test.tsx b/app/src/components/admin/ComplianceSection.test.tsx index 2782a94c..b4a9f7f6 100644 --- a/app/src/components/admin/ComplianceSection.test.tsx +++ b/app/src/components/admin/ComplianceSection.test.tsx @@ -132,7 +132,6 @@ describe("ComplianceSection", () => { verifier: "CVERIFIERADDRESS", target: TARGET, destination: undefined, - active: true, }) const overview = screen.getByRole("region", { name: "Target" }) expect(within(overview).getByText("Allowlisted")).toBeTruthy() @@ -300,8 +299,5 @@ describe("ComplianceSection", () => { />, ) expect(screen.queryAllByRole("dialog", { hidden: true })).toHaveLength(0) - expect(useComplianceStateMock).toHaveBeenLastCalledWith( - expect.objectContaining({ active: false }), - ) }) }) diff --git a/app/src/components/admin/ComplianceSection.tsx b/app/src/components/admin/ComplianceSection.tsx index 85481696..cee12714 100644 --- a/app/src/components/admin/ComplianceSection.tsx +++ b/app/src/components/admin/ComplianceSection.tsx @@ -112,7 +112,6 @@ const ComplianceSection: React.FC<{ verifier, target, destination: destinationOf(inputs), - active, }) const flows = useComplianceFlows() diff --git a/app/src/components/admin/ConfigurationSection.test.tsx b/app/src/components/admin/ConfigurationSection.test.tsx index 8c9e839b..b001b6fe 100644 --- a/app/src/components/admin/ConfigurationSection.test.tsx +++ b/app/src/components/admin/ConfigurationSection.test.tsx @@ -196,22 +196,4 @@ describe("ConfigurationSection", () => { ) expect(screen.queryAllByRole("dialog", { hidden: true })).toHaveLength(0) }) - - it("reads the parameters with polling only while active", () => { - const { rerender } = render( - , - ) - expect(useConfigurationStateMock).toHaveBeenLastCalledWith({ - active: false, - }) - - rerender( - , - ) - expect(useConfigurationStateMock).toHaveBeenLastCalledWith({ active: true }) - }) }) diff --git a/app/src/components/admin/ConfigurationSection.tsx b/app/src/components/admin/ConfigurationSection.tsx index 7f3846ef..433f32d2 100644 --- a/app/src/components/admin/ConfigurationSection.tsx +++ b/app/src/components/admin/ConfigurationSection.tsx @@ -53,7 +53,7 @@ const ConfigurationSection: React.FC<{ contracts?: React.ReactNode active?: boolean }> = ({ grants, wallet, contracts, active = true }) => { - const { configuration } = useConfigurationState({ active }) + const { configuration } = useConfigurationState() const depositCap = usePendingTransaction(useSetDepositCap()) const custodian = usePendingTransaction(useSetCustodian()) const notice = usePendingTransaction(useSetNotice()) diff --git a/app/src/components/admin/CycleSection.test.tsx b/app/src/components/admin/CycleSection.test.tsx index 6064be2e..519bfc14 100644 --- a/app/src/components/admin/CycleSection.test.tsx +++ b/app/src/components/admin/CycleSection.test.tsx @@ -363,16 +363,6 @@ describe("CycleSection", () => { expect(screen.getAllByRole("dialog", { hidden: true })).toHaveLength(1) }) - it("pauses the cycle polling while the surface is inactive", () => { - const { rerender } = render( - , - ) - expect(useCycleStateMock).toHaveBeenLastCalledWith({ active: false }) - - rerender() - expect(useCycleStateMock).toHaveBeenLastCalledWith({ active: true }) - }) - it("labels the deployment modal with the exact amount that is signed", () => { const { rerender } = render( , diff --git a/app/src/components/admin/CycleSection.tsx b/app/src/components/admin/CycleSection.tsx index 287e4e86..05114960 100644 --- a/app/src/components/admin/CycleSection.tsx +++ b/app/src/components/admin/CycleSection.tsx @@ -41,7 +41,7 @@ const CycleSection: React.FC<{ contracts?: React.ReactNode active?: boolean }> = ({ grants, wallet, contracts, active = true }) => { - const { cycle } = useCycleState({ active }) + const { cycle } = useCycleState() const { history } = useEpochHistory() const { cycleEvents } = useCycleEvents() const { balance } = useDepositBalance() diff --git a/app/src/components/admin/EmergencySection.test.tsx b/app/src/components/admin/EmergencySection.test.tsx index 31e142e0..7af8efe0 100644 --- a/app/src/components/admin/EmergencySection.test.tsx +++ b/app/src/components/admin/EmergencySection.test.tsx @@ -221,15 +221,10 @@ describe("EmergencySection", () => { expect(pauseVaultMock.reset).not.toHaveBeenCalled() }) - it("polls the state only while the surface is active", () => { - const { rerender } = render( + it("polls the vault pause state", () => { + render( , ) - expect(useVaultPausedMock).toHaveBeenLastCalledWith({ poll: false }) - expect(useTokenPausedMock).toHaveBeenLastCalledWith({ poll: false }) - expect(useOracleStateMock).toHaveBeenLastCalledWith({ poll: false }) - - rerender() expect(useVaultPausedMock).toHaveBeenLastCalledWith({ poll: true }) }) }) diff --git a/app/src/components/admin/EmergencySection.tsx b/app/src/components/admin/EmergencySection.tsx index 01622fcb..ba755d02 100644 --- a/app/src/components/admin/EmergencySection.tsx +++ b/app/src/components/admin/EmergencySection.tsx @@ -38,9 +38,9 @@ const EmergencySection: React.FC<{ contracts?: React.ReactNode active?: boolean }> = ({ grants, wallet, contracts, active = true }) => { - const { pause: vault } = useVaultPaused({ poll: active }) - const { pause: token } = useTokenPaused({ poll: active }) - const { oracle } = useOracleState({ poll: active }) + const { pause: vault } = useVaultPaused({ poll: true }) + const { pause: token } = useTokenPaused() + const { oracle } = useOracleState() const pauseVault = usePendingTransaction(usePauseVault()) const unpauseVault = usePendingTransaction(useUnpauseVault()) const raiseRipcord = usePendingTransaction(useRaiseRipcord()) diff --git a/app/src/components/admin/GovernanceSection.test.tsx b/app/src/components/admin/GovernanceSection.test.tsx index a27cc6d4..b6cecb3b 100644 --- a/app/src/components/admin/GovernanceSection.test.tsx +++ b/app/src/components/admin/GovernanceSection.test.tsx @@ -376,7 +376,6 @@ describe("GovernanceSection", () => { ) expect(useGovernanceStateMock).toHaveBeenCalledWith({ contracts: { vault: VAULT, oracle: "CORACLE", token: "CTOKEN" }, - active: true, }) }) diff --git a/app/src/components/admin/GovernanceSection.tsx b/app/src/components/admin/GovernanceSection.tsx index e51a640f..2ea12a05 100644 --- a/app/src/components/admin/GovernanceSection.tsx +++ b/app/src/components/admin/GovernanceSection.tsx @@ -147,7 +147,6 @@ const GovernanceSection: React.FC<{ oracle: addresses.oracle, token: addresses.shareToken, }, - active, }) const flows = useGovernanceFlows() const { inputs, setInputs, onInput } = useRailInputs() diff --git a/app/src/hooks/useComplianceState.ts b/app/src/hooks/useComplianceState.ts index 27fd6921..9dbc5c49 100644 --- a/app/src/hooks/useComplianceState.ts +++ b/app/src/hooks/useComplianceState.ts @@ -54,12 +54,10 @@ export function useComplianceState({ verifier, target, destination, - active = true, }: { verifier: string | null target: string | undefined destination: string | undefined - active?: boolean }): { target: TargetState; destination: Allowance } { const { data } = useQuery({ queryKey: [...complianceReadsKey, verifier, target], @@ -68,7 +66,7 @@ export function useComplianceState({ ? skipToken : () => fetchTargetReads(verifier, target), staleTime: 30_000, - refetchInterval: active ? REFRESH_MS : false, + refetchInterval: REFRESH_MS, }) const { data: destinationAllowance } = useQuery( allowanceQuery(verifier, destination), diff --git a/app/src/hooks/useConfigurationState.test.ts b/app/src/hooks/useConfigurationState.test.ts index 68b96d57..01a3ec0e 100644 --- a/app/src/hooks/useConfigurationState.test.ts +++ b/app/src/hooks/useConfigurationState.test.ts @@ -99,10 +99,4 @@ describe("useConfigurationState", () => { }), ) }) - - it("polls only while active", () => { - renderWithWallet(() => useConfigurationState({ active: false })) - - expect(useCycleReadsMock).toHaveBeenCalledWith(false) - }) }) diff --git a/app/src/hooks/useConfigurationState.ts b/app/src/hooks/useConfigurationState.ts index 2286d35b..67fa52a6 100644 --- a/app/src/hooks/useConfigurationState.ts +++ b/app/src/hooks/useConfigurationState.ts @@ -43,16 +43,16 @@ async function fetchConfigurationReads() { } } -export function useConfigurationState({ - active = true, -}: { active?: boolean } = {}): { configuration: ConfigurationState } { - const cycle = useCycleReads(active) +export function useConfigurationState(): { + configuration: ConfigurationState +} { + const cycle = useCycleReads() const { authorities, isPending } = useVaultAuthorities() const { data: reads } = useQuery({ queryKey: configurationReadsKey, queryFn: fetchConfigurationReads, staleTime: STALE_MS, - refetchInterval: active ? REFRESH_MS : false, + refetchInterval: REFRESH_MS, }) if (cycle === undefined || reads === undefined || isPending) diff --git a/app/src/hooks/useCycleState.test.ts b/app/src/hooks/useCycleState.test.ts index b8591801..ee5f77f5 100644 --- a/app/src/hooks/useCycleState.test.ts +++ b/app/src/hooks/useCycleState.test.ts @@ -363,28 +363,6 @@ describe("useCycleState", () => { } }) - it("stops refetching the cycle reads while inactive and resumes when active again", async () => { - vi.useFakeTimers({ shouldAdvanceTime: true }) - try { - let active = false - const { result, rerender } = renderWithWallet(() => - useCycleState({ active }), - ) - await waitFor(() => expect(result.current.cycle.status).toBe("ready")) - const before = oracleMock.state.mock.calls.length - - await vi.advanceTimersByTimeAsync(31_000) - expect(oracleMock.state.mock.calls).toHaveLength(before) - - active = true - rerender() - await vi.advanceTimersByTimeAsync(16_000) - expect(oracleMock.state.mock.calls.length).toBeGreaterThan(before) - } finally { - vi.useRealTimers() - } - }) - it("reads no balance when no custodian is set", async () => { vaultMock.custodian.mockResolvedValue({ result: null }) diff --git a/app/src/hooks/useCycleState.ts b/app/src/hooks/useCycleState.ts index 1b807a3f..ed3e02e5 100644 --- a/app/src/hooks/useCycleState.ts +++ b/app/src/hooks/useCycleState.ts @@ -262,24 +262,24 @@ async function fetchCustodianBalance( return toAmount(balance) } -export function useCycleReads(active: boolean): CycleReads | undefined { +export function useCycleReads(): CycleReads | undefined { const { data } = useQuery({ queryKey: cycleReadsKey, queryFn: fetchCycleReads, staleTime: STALE_MS, - refetchInterval: active ? REFRESH_MS : false, + refetchInterval: REFRESH_MS, }) return data } -export function useCycleState({ active = true }: { active?: boolean } = {}): { +export function useCycleState(): { cycle: CycleState } { const { history } = useEpochHistory() const { figures, isPending: figuresPending } = useVaultFigures() const { pause } = useVaultPaused() const { authorities, isPending: authoritiesPending } = useVaultAuthorities() - const reads = useCycleReads(active) + const reads = useCycleReads() const custodian = authorities?.custodian ?? { kind: "unreadable" } const custodianAddress = custodian.kind === "set" ? custodian.address : null const { data: custodianBalance, isPending: balancePending } = useQuery({ diff --git a/app/src/hooks/useGovernanceState.ts b/app/src/hooks/useGovernanceState.ts index 8b673c24..815c4835 100644 --- a/app/src/hooks/useGovernanceState.ts +++ b/app/src/hooks/useGovernanceState.ts @@ -58,15 +58,15 @@ async function fetchUpgradeProposal(): Promise { } } -function useUpgrades(active: boolean): Upgrades { - const cycle = useCycleReads(active) - const { configuration } = useConfigurationState({ active }) - const { pause } = useVaultPaused({ poll: active }) +function useUpgrades(): Upgrades { + const cycle = useCycleReads() + const { configuration } = useConfigurationState() + const { pause } = useVaultPaused({ poll: true }) const { data } = useQuery({ queryKey: upgradeProposalKey, queryFn: fetchUpgradeProposal, staleTime: STALE_MS, - refetchInterval: active ? REFRESH_MS : false, + refetchInterval: REFRESH_MS, }) if ( cycle === undefined || @@ -104,16 +104,14 @@ function toHandovers( export function useGovernanceState({ contracts, - active = true, }: { contracts: Record | undefined - active?: boolean }): { handovers: Handovers; upgrades: Upgrades } { const { data, isError } = useQuery({ ...pendingAdminsQuery(contracts), - refetchInterval: active ? REFRESH_MS : false, + refetchInterval: REFRESH_MS, }) - const upgrades = useUpgrades(active) + const upgrades = useUpgrades() let handovers: Handovers = { status: "checking" } if (data !== undefined && contracts !== undefined) handovers = toHandovers(data, contracts) diff --git a/app/src/hooks/useOracleState.ts b/app/src/hooks/useOracleState.ts index df046442..183f39e9 100644 --- a/app/src/hooks/useOracleState.ts +++ b/app/src/hooks/useOracleState.ts @@ -34,14 +34,14 @@ async function fetchOracleState(): Promise { return classifyOracle(read) } -export function useOracleState({ poll = false }: { poll?: boolean } = {}): { +export function useOracleState(): { oracle: OracleStatus } { const { data } = useQuery({ queryKey: oracleStateKey, queryFn: fetchOracleState, staleTime: 30_000, - refetchInterval: poll ? PAUSE_REFRESH_MS : false, + refetchInterval: PAUSE_REFRESH_MS, }) return { oracle: data ?? "checking" } diff --git a/app/src/hooks/useTokenPaused.ts b/app/src/hooks/useTokenPaused.ts index 2a5d42b7..c2bc7835 100644 --- a/app/src/hooks/useTokenPaused.ts +++ b/app/src/hooks/useTokenPaused.ts @@ -14,14 +14,14 @@ async function fetchTokenPause(): Promise { return classifyPause(read) } -export function useTokenPaused({ poll = false }: { poll?: boolean } = {}): { +export function useTokenPaused(): { pause: PauseState } { const { data } = useQuery({ queryKey: tokenPausedKey, queryFn: fetchTokenPause, staleTime: 30_000, - refetchInterval: poll ? PAUSE_REFRESH_MS : false, + refetchInterval: PAUSE_REFRESH_MS, }) return { pause: data ?? "checking" }