From 213a42e68b569a29241a39c820566897be27ad6e Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 29 Oct 2026 06:53:19 +0100 Subject: [PATCH] refactor(usage): give the sidebar footer sole ownership of its summary --- packages/app/src/components/left-sidebar.tsx | 13 ++++--------- packages/app/src/usage/index.ts | 2 +- packages/app/src/usage/queries.ts | 7 ++----- packages/app/src/usage/sidebar-item.tsx | 14 +++----------- 4 files changed, 10 insertions(+), 26 deletions(-) diff --git a/packages/app/src/components/left-sidebar.tsx b/packages/app/src/components/left-sidebar.tsx index dbc501b5c7..983d142beb 100644 --- a/packages/app/src/components/left-sidebar.tsx +++ b/packages/app/src/components/left-sidebar.tsx @@ -64,12 +64,7 @@ import { useCloseAgentListGesture } from "@/mobile-panels/gestures"; import { MobilePanelOverlay } from "@/mobile-panels/presentation"; import { buildSettingsAddHostRoute, buildSettingsRoute } from "@/utils/host-routes"; import { openHostOverview } from "@/navigation/settings-navigation"; -import { - UsageSidebarItem, - UsageSidebarRoot, - useHasUsageSummary, - useOpenSidebarUsage, -} from "@/usage"; +import { UsageSidebarItem, UsageSidebarRoot, useUsageSummary, useOpenSidebarUsage } from "@/usage"; import { SidebarAgentListSkeleton } from "./sidebar-agent-list-skeleton"; import { SidebarCalloutSlot } from "./sidebar-callout-slot"; import { SidebarWorkspaceList } from "./sidebar-workspace-list"; @@ -480,10 +475,10 @@ function SidebarUsageIcon({ label }: { label: string }) { */ function SidebarFooterRows({ onBeforeNavigate }: { onBeforeNavigate?: () => void }) { const { items } = useSidebarNavItems("footer"); - const hasUsageSummary = useHasUsageSummary(); + const usageSummary = useUsageSummary(); const rowsRef = useRef(null); const visibleItems = items.filter( - (item) => item.visible && (item.kind === "plugin" || hasUsageSummary), + (item) => item.visible && (item.kind === "plugin" || usageSummary.length > 0), ); if (visibleItems.length === 0) return null; return ( @@ -499,7 +494,7 @@ function SidebarFooterRows({ onBeforeNavigate }: { onBeforeNavigate?: () => void onBeforeNavigate={onBeforeNavigate} /> ) : ( - + ), )} diff --git a/packages/app/src/usage/index.ts b/packages/app/src/usage/index.ts index 45599aba2d..64353b8c9d 100644 --- a/packages/app/src/usage/index.ts +++ b/packages/app/src/usage/index.ts @@ -4,6 +4,6 @@ export { useHostReportsUsage } from "./queries"; export { UsageSidebarItem, UsageSidebarRoot, - useHasUsageSummary, + useUsageSummary, useOpenSidebarUsage, } from "./sidebar-item"; diff --git a/packages/app/src/usage/queries.ts b/packages/app/src/usage/queries.ts index 9d84607dd6..524972a010 100644 --- a/packages/app/src/usage/queries.ts +++ b/packages/app/src/usage/queries.ts @@ -168,14 +168,11 @@ const NO_REPORTS: UsageReportEntry[] = []; * The reports of the sidebar's usage host, which is connected and reports usage; none until they * load, or without a host. */ -export function useUsageHostReports( - serverId: string | null, - requestReports = true, -): UsageReportEntry[] { +export function useUsageHostReports(serverId: string | null): UsageReportEntry[] { const setting = useUsageSetting(serverId); const queryClient = useQueryClient(); const query = useFetchQuery({ - enabled: setting.enabled && requestReports, + enabled: setting.enabled, queryKey: usageReportsQueryKey(serverId ?? ""), queryFn: serverId ? () => listReports(queryClient, serverId) : skipToken, dataShape: "list", diff --git a/packages/app/src/usage/sidebar-item.tsx b/packages/app/src/usage/sidebar-item.tsx index 945a82a2ac..437c0a929c 100644 --- a/packages/app/src/usage/sidebar-item.tsx +++ b/packages/app/src/usage/sidebar-item.tsx @@ -34,32 +34,24 @@ import { import { UsageModal } from "./usage-modal"; /** Each summary window with data on the usage host, under its source; empty while none has. */ -function useUsageSummary(requestReports = true): readonly PinnedUsageSource[] { +export function useUsageSummary(): readonly PinnedUsageSource[] { const { t } = useTranslation(); const { inSidebar } = useUsageInSidebar(); const serverId = useUsageHostId(); - const reports = useUsageHostReports(inSidebar ? serverId : null, requestReports); + const reports = useUsageHostReports(inSidebar ? serverId : null); const { preferences } = useUsageDisplay(reports); return useMemo(() => resolvePinnedUsage(reports, preferences, t), [preferences, reports, t]); } -/** Whether the sidebar Usage item has anything to show. */ -export function useHasUsageSummary(): boolean { - return useUsageSummary().length > 0; -} - /** * The sidebar footer's usage entry: each summary window's source icon and percent, and nothing * while no summary window has data, since the footer's Usage icon already opens Usage. * Pressing it opens the Usage modal. */ -export function UsageSidebarItem() { +export function UsageSidebarItem({ sources }: { sources: readonly PinnedUsageSource[] }) { const { t } = useTranslation(); const label = t(builtinSidebarNavLabelKey("usage")); const openUsage = useOpenSidebarUsage(); - // The row mounts after reports arrive; observing without requesting avoids - // the fetch-query policy’s unconditional refetch on mount. - const sources = useUsageSummary(false); if (sources.length === 0) return null; return ; }