Skip to content

Commit 3d3872c

Browse files
perf(mobile): usage screens compile with the React Compiler
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 7f7d639 commit 3d3872c

5 files changed

Lines changed: 91 additions & 127 deletions

File tree

‎apps/mobile/src/features/usage/UsageDailyChart.ios.tsx‎

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Chart, Host, type ChartDataPoint } from "@expo/ui/swift-ui";
22
import { frame } from "@expo/ui/swift-ui/modifiers";
3-
import { useMemo } from "react";
43

54
import type { DailyTotals } from "@t3tools/shared/usageMerge";
65

@@ -17,15 +16,13 @@ export interface UsageDailyChartProps {
1716
export function UsageDailyChart({ days, daily, metric, height }: UsageDailyChartProps) {
1817
const colors = useProviderColors();
1918

20-
const data = useMemo((): ChartDataPoint[] => {
21-
return buildChartDays(days, daily, metric).flatMap((day) =>
22-
day.values.map((entry) => ({
23-
x: day.day,
24-
y: entry.value,
25-
color: colors[entry.provider],
26-
})),
27-
);
28-
}, [days, daily, metric, colors]);
19+
const data: ChartDataPoint[] = buildChartDays(days, daily, metric).flatMap((day) =>
20+
day.values.map((entry) => ({
21+
x: day.day,
22+
y: entry.value,
23+
color: colors[entry.provider],
24+
})),
25+
);
2926

3027
return (
3128
<Host style={{ height, width: "100%" }}>

‎apps/mobile/src/features/usage/UsageDailyChart.tsx‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { useMemo } from "react";
21
import { View } from "react-native";
32

43
import type { DailyTotals } from "@t3tools/shared/usageMerge";
@@ -15,7 +14,7 @@ export interface UsageDailyChartProps {
1514

1615
export function UsageDailyChart({ days, daily, metric, height }: UsageDailyChartProps) {
1716
const colors = useProviderColors();
18-
const chartDays = useMemo(() => buildChartDays(days, daily, metric), [days, daily, metric]);
17+
const chartDays = buildChartDays(days, daily, metric);
1918
const max = chartDays.reduce((peak, day) => Math.max(peak, day.total), 0);
2019

2120
return (

‎apps/mobile/src/features/usage/UsageLimitsSection.tsx‎

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -297,40 +297,34 @@ export function useRefreshLimits(
297297
presentation.connection.phase === "connected" &&
298298
(selectedEnvironmentIds === null || selectedEnvironmentIds.has(environmentId)),
299299
);
300-
try {
301-
await Promise.all(
302-
connected.map(async ([environmentId, presentation]) => {
303-
const result = await refreshUsageLimits(
304-
environmentId,
305-
() => refreshProviders({ environmentId, input: {} }),
306-
automatic,
307-
afterPending,
308-
);
309-
if (result === undefined) return;
310-
setFailedEnvironments((previous) => [
311-
...previous.filter((failed) => failed.environmentId !== environmentId),
312-
...(result._tag === "Failure"
313-
? [{ environmentId, label: presentation.entry.target.label }]
314-
: []),
315-
]);
316-
}),
317-
);
318-
} finally {
319-
setNow(Date.now());
320-
}
300+
await Promise.all(
301+
connected.map(async ([environmentId, presentation]) => {
302+
const result = await refreshUsageLimits(
303+
environmentId,
304+
() => refreshProviders({ environmentId, input: {} }),
305+
automatic,
306+
afterPending,
307+
);
308+
if (result === undefined) return;
309+
setFailedEnvironments((previous) => [
310+
...previous.filter((failed) => failed.environmentId !== environmentId),
311+
...(result._tag === "Failure"
312+
? [{ environmentId, label: presentation.entry.target.label }]
313+
: []),
314+
]);
315+
}),
316+
).finally(() => setNow(Date.now()));
321317
};
322318
// Always toggles `refreshing`, even with nothing to probe: Android's
323319
// RefreshControl keeps its spinner up until it sees true then false.
324320
const refreshManually = async () => {
325321
if (refreshingRef.current) return;
326322
refreshingRef.current = true;
327323
setRefreshing(true);
328-
try {
329-
await refresh();
330-
} finally {
324+
await refresh().finally(() => {
331325
refreshingRef.current = false;
332326
setRefreshing(false);
333-
}
327+
});
334328
};
335329
const connectedLimitsEnvironments = [...presentations]
336330
.filter(

‎apps/mobile/src/features/usage/UsageRouteScreen.tsx‎

Lines changed: 64 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
formatUsd,
2828
makeWindow,
2929
} from "@t3tools/shared/usageFormat";
30-
import { useCallback, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from "react";
30+
import { useLayoutEffect, useRef, useState, type ReactNode } from "react";
3131
import { Platform, Pressable, RefreshControl, View } from "react-native";
3232
import Animated, {
3333
FadeIn,
@@ -135,29 +135,19 @@ export function UsageRouteScreen() {
135135
(environment) => environment.canReadDiagnostics,
136136
);
137137

138-
const days = useMemo(
139-
() => enumerateDays(window.sinceDay, window.untilDay),
140-
[window.sinceDay, window.untilDay],
141-
);
142-
const chartDays = useMemo(
143-
() =>
144-
isPast24Hours && window.sinceTime !== undefined && window.untilTime !== undefined
145-
? enumerateHourStarts(window.sinceTime, window.untilTime)
146-
: days,
147-
[days, isPast24Hours, window.sinceTime, window.untilTime],
148-
);
149-
const chartTotals = useMemo(
150-
(): readonly DailyTotals[] =>
151-
isPast24Hours
152-
? merged.hourly.map((hour) => ({
153-
day: hour.hourStart,
154-
costUsd: hour.costUsd,
155-
totalTokens: hour.totalTokens,
156-
byProvider: hour.byProvider,
157-
}))
158-
: merged.daily,
159-
[isPast24Hours, merged.daily, merged.hourly],
160-
);
138+
const days = enumerateDays(window.sinceDay, window.untilDay);
139+
const chartDays =
140+
isPast24Hours && window.sinceTime !== undefined && window.untilTime !== undefined
141+
? enumerateHourStarts(window.sinceTime, window.untilTime)
142+
: days;
143+
const chartTotals: readonly DailyTotals[] = isPast24Hours
144+
? merged.hourly.map((hour) => ({
145+
day: hour.hourStart,
146+
costUsd: hour.costUsd,
147+
totalTokens: hour.totalTokens,
148+
byProvider: hour.byProvider,
149+
}))
150+
: merged.daily;
161151

162152
const [refreshingUsage, setRefreshingUsage] = useState(false);
163153
const refreshingRef = useRef(false);
@@ -196,62 +186,52 @@ export function UsageRouteScreen() {
196186
selectedEnvironmentIds === null
197187
? "line.3.horizontal.decrease"
198188
: "line.3.horizontal.decrease.circle.fill";
199-
const environmentActions = useMemo(
200-
() => [
201-
{
202-
id: "all",
203-
title: "All environments",
204-
subtitle: undefined,
205-
state: selectedEnvironmentIds === null ? ("on" as const) : ("off" as const),
206-
},
207-
...environments.map((environment) => ({
208-
id: environment.environmentId,
209-
title: environment.label,
210-
subtitle: usageEnvironmentStatus(environment, refreshingUsage),
211-
state:
212-
selectedEnvironmentIds === null || selectedEnvironmentIds.has(environment.environmentId)
213-
? ("on" as const)
214-
: ("off" as const),
215-
})),
216-
],
217-
[environments, refreshingUsage, selectedEnvironmentIds],
218-
);
219-
const selectEnvironment = useCallback(
220-
(value: string) => {
221-
if (value === "all") {
222-
setSelectedEnvironmentIds(null);
223-
return;
224-
}
225-
const id = EnvironmentId.make(value);
226-
setSelectedEnvironmentIds((selected) => toggleUsageEnvironment(selected, environments, id));
189+
const environmentActions = [
190+
{
191+
id: "all",
192+
title: "All environments",
193+
subtitle: undefined,
194+
state: selectedEnvironmentIds === null ? ("on" as const) : ("off" as const),
227195
},
228-
[environments],
229-
);
230-
const environmentFilter = useMemo(
231-
() =>
232-
showEnvironmentFilter ? (
233-
<Menu
234-
accessible
235-
accessibilityRole="button"
236-
accessibilityLabel="Filter usage environments"
237-
title="Environments"
238-
actions={environmentActions}
239-
onPressAction={({ nativeEvent }) => selectEnvironment(nativeEvent.event)}
240-
>
241-
<Pressable
242-
accessibilityRole="button"
243-
accessibilityLabel="Filter usage environments"
244-
className={cn(
245-
"items-center justify-center rounded-full",
246-
Platform.OS === "ios" ? "size-[28px]" : "size-[44px]",
247-
)}
248-
>
249-
<SymbolView name={filterIcon} size={22} tintColorClassName="accent-icon" />
250-
</Pressable>
251-
</Menu>
252-
) : null,
253-
[showEnvironmentFilter, environmentActions, selectEnvironment, filterIcon],
254-
);
196+
...environments.map((environment) => ({
197+
id: environment.environmentId,
198+
title: environment.label,
199+
subtitle: usageEnvironmentStatus(environment, refreshingUsage),
200+
state:
201+
selectedEnvironmentIds === null || selectedEnvironmentIds.has(environment.environmentId)
202+
? ("on" as const)
203+
: ("off" as const),
204+
})),
205+
];
206+
const selectEnvironment = (value: string) => {
207+
if (value === "all") {
208+
setSelectedEnvironmentIds(null);
209+
return;
210+
}
211+
const id = EnvironmentId.make(value);
212+
setSelectedEnvironmentIds((selected) => toggleUsageEnvironment(selected, environments, id));
213+
};
214+
const environmentFilter = showEnvironmentFilter ? (
215+
<Menu
216+
accessible
217+
accessibilityRole="button"
218+
accessibilityLabel="Filter usage environments"
219+
title="Environments"
220+
actions={environmentActions}
221+
onPressAction={({ nativeEvent }) => selectEnvironment(nativeEvent.event)}
222+
>
223+
<Pressable
224+
accessibilityRole="button"
225+
accessibilityLabel="Filter usage environments"
226+
className={cn(
227+
"items-center justify-center rounded-full",
228+
Platform.OS === "ios" ? "size-[28px]" : "size-[44px]",
229+
)}
230+
>
231+
<SymbolView name={filterIcon} size={22} tintColorClassName="accent-icon" />
232+
</Pressable>
233+
</Menu>
234+
) : null;
255235

256236
useLayoutEffect(() => {
257237
if (Platform.OS === "ios") {
@@ -440,15 +420,11 @@ function CursorEnableAction({
440420
const [pending, setPending] = useState(false);
441421
const enable = async () => {
442422
setPending(true);
443-
try {
444-
const result = await updateSettings({
445-
environmentId,
446-
input: { patch: { cursorKeychainUsageEnabled: true } },
447-
});
448-
if (result._tag === "Success") onEnabled();
449-
} finally {
450-
setPending(false);
451-
}
423+
const result = await updateSettings({
424+
environmentId,
425+
input: { patch: { cursorKeychainUsageEnabled: true } },
426+
}).finally(() => setPending(false));
427+
if (result._tag === "Success") onEnabled();
452428
};
453429
return (
454430
<Pressable

‎apps/mobile/src/reactCompilerCoverage.test.ts‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ const KNOWN_BAILOUTS: Record<string, string> = {
9393
"src/features/threads/thread-work-log.tsx": "manual memo not preserved",
9494
"src/features/threads/use-composer-command-menu.ts": "manual memo not preserved",
9595
"src/features/threads/use-thread-list-v2-shelf-preferences.ts": "ref read during render",
96-
"src/features/usage/UsageLimitsSection.tsx": "try without catch",
97-
"src/features/usage/UsageRouteScreen.tsx": "try without catch",
9896
"src/features/voice-input/VoiceInputProvider.tsx": "ref read during render",
9997
"src/features/voice-input/useVoiceInputController.ts": "ref read during render",
10098
"src/lib/attachmentDocument.ts": "try/finally",

0 commit comments

Comments
 (0)