diff --git a/apps/mobile/src/features/usage/UsageDailyChart.ios.tsx b/apps/mobile/src/features/usage/UsageDailyChart.ios.tsx index 4fdf9039120b..9f0ebc61df91 100644 --- a/apps/mobile/src/features/usage/UsageDailyChart.ios.tsx +++ b/apps/mobile/src/features/usage/UsageDailyChart.ios.tsx @@ -1,6 +1,5 @@ import { Chart, Host, type ChartDataPoint } from "@expo/ui/swift-ui"; import { frame } from "@expo/ui/swift-ui/modifiers"; -import { useMemo } from "react"; import type { DailyTotals } from "@t3tools/shared/usageMerge"; @@ -17,15 +16,13 @@ export interface UsageDailyChartProps { export function UsageDailyChart({ days, daily, metric, height }: UsageDailyChartProps) { const colors = useProviderColors(); - const data = useMemo((): ChartDataPoint[] => { - return buildChartDays(days, daily, metric).flatMap((day) => - day.values.map((entry) => ({ - x: day.day, - y: entry.value, - color: colors[entry.provider], - })), - ); - }, [days, daily, metric, colors]); + const data: ChartDataPoint[] = buildChartDays(days, daily, metric).flatMap((day) => + day.values.map((entry) => ({ + x: day.day, + y: entry.value, + color: colors[entry.provider], + })), + ); return ( diff --git a/apps/mobile/src/features/usage/UsageDailyChart.tsx b/apps/mobile/src/features/usage/UsageDailyChart.tsx index 5cc8703c420b..e51a13dae160 100644 --- a/apps/mobile/src/features/usage/UsageDailyChart.tsx +++ b/apps/mobile/src/features/usage/UsageDailyChart.tsx @@ -1,4 +1,3 @@ -import { useMemo } from "react"; import { View } from "react-native"; import type { DailyTotals } from "@t3tools/shared/usageMerge"; @@ -15,7 +14,7 @@ export interface UsageDailyChartProps { export function UsageDailyChart({ days, daily, metric, height }: UsageDailyChartProps) { const colors = useProviderColors(); - const chartDays = useMemo(() => buildChartDays(days, daily, metric), [days, daily, metric]); + const chartDays = buildChartDays(days, daily, metric); const max = chartDays.reduce((peak, day) => Math.max(peak, day.total), 0); return ( diff --git a/apps/mobile/src/features/usage/UsageLimitsSection.tsx b/apps/mobile/src/features/usage/UsageLimitsSection.tsx index b72770e7b172..7f0e3908623c 100644 --- a/apps/mobile/src/features/usage/UsageLimitsSection.tsx +++ b/apps/mobile/src/features/usage/UsageLimitsSection.tsx @@ -297,27 +297,23 @@ export function useRefreshLimits( presentation.connection.phase === "connected" && (selectedEnvironmentIds === null || selectedEnvironmentIds.has(environmentId)), ); - try { - await Promise.all( - connected.map(async ([environmentId, presentation]) => { - const result = await refreshUsageLimits( - environmentId, - () => refreshProviders({ environmentId, input: {} }), - automatic, - afterPending, - ); - if (result === undefined) return; - setFailedEnvironments((previous) => [ - ...previous.filter((failed) => failed.environmentId !== environmentId), - ...(result._tag === "Failure" - ? [{ environmentId, label: presentation.entry.target.label }] - : []), - ]); - }), - ); - } finally { - setNow(Date.now()); - } + await Promise.all( + connected.map(async ([environmentId, presentation]) => { + const result = await refreshUsageLimits( + environmentId, + () => refreshProviders({ environmentId, input: {} }), + automatic, + afterPending, + ); + if (result === undefined) return; + setFailedEnvironments((previous) => [ + ...previous.filter((failed) => failed.environmentId !== environmentId), + ...(result._tag === "Failure" + ? [{ environmentId, label: presentation.entry.target.label }] + : []), + ]); + }), + ).finally(() => setNow(Date.now())); }; // Always toggles `refreshing`, even with nothing to probe: Android's // RefreshControl keeps its spinner up until it sees true then false. @@ -325,12 +321,10 @@ export function useRefreshLimits( if (refreshingRef.current) return; refreshingRef.current = true; setRefreshing(true); - try { - await refresh(); - } finally { + await refresh().finally(() => { refreshingRef.current = false; setRefreshing(false); - } + }); }; const connectedLimitsEnvironments = [...presentations] .filter( diff --git a/apps/mobile/src/features/usage/UsageRouteScreen.tsx b/apps/mobile/src/features/usage/UsageRouteScreen.tsx index 001e3fecda25..36d00a73c471 100644 --- a/apps/mobile/src/features/usage/UsageRouteScreen.tsx +++ b/apps/mobile/src/features/usage/UsageRouteScreen.tsx @@ -27,7 +27,7 @@ import { formatUsd, makeWindow, } from "@t3tools/shared/usageFormat"; -import { useCallback, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from "react"; +import { useLayoutEffect, useRef, useState, type ReactNode } from "react"; import { Platform, Pressable, RefreshControl, View } from "react-native"; import Animated, { FadeIn, @@ -135,29 +135,19 @@ export function UsageRouteScreen() { (environment) => environment.canReadDiagnostics, ); - const days = useMemo( - () => enumerateDays(window.sinceDay, window.untilDay), - [window.sinceDay, window.untilDay], - ); - const chartDays = useMemo( - () => - isPast24Hours && window.sinceTime !== undefined && window.untilTime !== undefined - ? enumerateHourStarts(window.sinceTime, window.untilTime) - : days, - [days, isPast24Hours, window.sinceTime, window.untilTime], - ); - const chartTotals = useMemo( - (): readonly DailyTotals[] => - isPast24Hours - ? merged.hourly.map((hour) => ({ - day: hour.hourStart, - costUsd: hour.costUsd, - totalTokens: hour.totalTokens, - byProvider: hour.byProvider, - })) - : merged.daily, - [isPast24Hours, merged.daily, merged.hourly], - ); + const days = enumerateDays(window.sinceDay, window.untilDay); + const chartDays = + isPast24Hours && window.sinceTime !== undefined && window.untilTime !== undefined + ? enumerateHourStarts(window.sinceTime, window.untilTime) + : days; + const chartTotals: readonly DailyTotals[] = isPast24Hours + ? merged.hourly.map((hour) => ({ + day: hour.hourStart, + costUsd: hour.costUsd, + totalTokens: hour.totalTokens, + byProvider: hour.byProvider, + })) + : merged.daily; const [refreshingUsage, setRefreshingUsage] = useState(false); const refreshingRef = useRef(false); @@ -196,62 +186,52 @@ export function UsageRouteScreen() { selectedEnvironmentIds === null ? "line.3.horizontal.decrease" : "line.3.horizontal.decrease.circle.fill"; - const environmentActions = useMemo( - () => [ - { - id: "all", - title: "All environments", - subtitle: undefined, - state: selectedEnvironmentIds === null ? ("on" as const) : ("off" as const), - }, - ...environments.map((environment) => ({ - id: environment.environmentId, - title: environment.label, - subtitle: usageEnvironmentStatus(environment, refreshingUsage), - state: - selectedEnvironmentIds === null || selectedEnvironmentIds.has(environment.environmentId) - ? ("on" as const) - : ("off" as const), - })), - ], - [environments, refreshingUsage, selectedEnvironmentIds], - ); - const selectEnvironment = useCallback( - (value: string) => { - if (value === "all") { - setSelectedEnvironmentIds(null); - return; - } - const id = EnvironmentId.make(value); - setSelectedEnvironmentIds((selected) => toggleUsageEnvironment(selected, environments, id)); + const environmentActions = [ + { + id: "all", + title: "All environments", + subtitle: undefined, + state: selectedEnvironmentIds === null ? ("on" as const) : ("off" as const), }, - [environments], - ); - const environmentFilter = useMemo( - () => - showEnvironmentFilter ? ( - selectEnvironment(nativeEvent.event)} - > - - - - - ) : null, - [showEnvironmentFilter, environmentActions, selectEnvironment, filterIcon], - ); + ...environments.map((environment) => ({ + id: environment.environmentId, + title: environment.label, + subtitle: usageEnvironmentStatus(environment, refreshingUsage), + state: + selectedEnvironmentIds === null || selectedEnvironmentIds.has(environment.environmentId) + ? ("on" as const) + : ("off" as const), + })), + ]; + const selectEnvironment = (value: string) => { + if (value === "all") { + setSelectedEnvironmentIds(null); + return; + } + const id = EnvironmentId.make(value); + setSelectedEnvironmentIds((selected) => toggleUsageEnvironment(selected, environments, id)); + }; + const environmentFilter = showEnvironmentFilter ? ( + selectEnvironment(nativeEvent.event)} + > + + + + + ) : null; useLayoutEffect(() => { if (Platform.OS === "ios") { @@ -440,15 +420,11 @@ function CursorEnableAction({ const [pending, setPending] = useState(false); const enable = async () => { setPending(true); - try { - const result = await updateSettings({ - environmentId, - input: { patch: { cursorKeychainUsageEnabled: true } }, - }); - if (result._tag === "Success") onEnabled(); - } finally { - setPending(false); - } + const result = await updateSettings({ + environmentId, + input: { patch: { cursorKeychainUsageEnabled: true } }, + }).finally(() => setPending(false)); + if (result._tag === "Success") onEnabled(); }; return ( = { "src/features/threads/thread-work-log.tsx": "manual memo not preserved", "src/features/threads/use-composer-command-menu.ts": "manual memo not preserved", "src/features/threads/use-thread-list-v2-shelf-preferences.ts": "ref read during render", - "src/features/usage/UsageLimitsSection.tsx": "try without catch", - "src/features/usage/UsageRouteScreen.tsx": "try without catch", "src/features/voice-input/VoiceInputProvider.tsx": "ref read during render", "src/features/voice-input/useVoiceInputController.ts": "ref read during render", "src/lib/attachmentDocument.ts": "try/finally",