| ... | @@ -11,7 +11,7 @@ import { | ... | @@ -11,7 +11,7 @@ import { |
| 11 | formatUsd, | 11 | formatUsd, |
| 12 | makeWindow, | 12 | makeWindow, |
| 13 | } from "@t3tools/shared/usageFormat"; | 13 | } from "@t3tools/shared/usageFormat"; |
| 14 | import { useMemo, useRef, useState } from "react"; | 14 | import { useMemo, useState } from "react"; |
| 15 | import { Platform, Pressable, RefreshControl, ScrollView, View } from "react-native"; | 15 | import { Platform, Pressable, RefreshControl, ScrollView, View } from "react-native"; |
| 16 | import { useSafeAreaInsets } from "react-native-safe-area-context"; | 16 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 17 | | 17 | |
| ... | @@ -96,14 +96,6 @@ export function UsageRouteScreen() { | ... | @@ -96,14 +96,6 @@ export function UsageRouteScreen() { |
| 96 | // environment stays pending forever — neither may pin the spinner on. | 96 | // environment stays pending forever — neither may pin the spinner on. |
| 97 | const refreshingUsage = environments.some((entry) => entry.isPending && entry.summary !== null); | 97 | const refreshingUsage = environments.some((entry) => entry.isPending && entry.summary !== null); |
| 98 | const showingLimits = tab === "limits"; | 98 | const showingLimits = tab === "limits"; |
| 99 | // One ScrollView serves both tabs, so the offset would otherwise carry over | | |
| 100 | // and a short Limits list could open scrolled past its own content. | | |
| 101 | const scrollRef = useRef<ScrollView>(null); | | |
| 102 | const selectTab = (next: UsageTab) => { | | |
| 103 | if (next === tab) return; | | |
| 104 | setTab(next); | | |
| 105 | scrollRef.current?.scrollTo({ y: 0, animated: false }); | | |
| 106 | }; | | |
| 107 | const selectWindow = (days: number) => { | 99 | const selectWindow = (days: number) => { |
| 108 | setWindowSelection({ | 100 | setWindowSelection({ |
| 109 | days, | 101 | days, |
| ... | @@ -133,7 +125,9 @@ export function UsageRouteScreen() { | ... | @@ -133,7 +125,9 @@ export function UsageRouteScreen() { |
| 133 | </> | 125 | </> |
| 134 | ) : null} | 126 | ) : null} |
| 135 | <ScrollView | 127 | <ScrollView |
| 136 | ref={scrollRef} | 128 | // Remount at each tab's native top. Scrolling to y: 0 ignores iOS's |
| | 129 | // automatic header inset and hides the tab bar under the header. |
| | 130 | key={tab} |
| 137 | contentInsetAdjustmentBehavior="automatic" | 131 | contentInsetAdjustmentBehavior="automatic" |
| 138 | showsVerticalScrollIndicator={false} | 132 | showsVerticalScrollIndicator={false} |
| 139 | className="flex-1" | 133 | className="flex-1" |
| ... | @@ -146,7 +140,7 @@ export function UsageRouteScreen() { | ... | @@ -146,7 +140,7 @@ export function UsageRouteScreen() { |
| 146 | /> | 140 | /> |
| 147 | } | 141 | } |
| 148 | > | 142 | > |
| 149 | <SegmentedControl options={TAB_OPTIONS} selected={tab} onSelect={selectTab} role="tab" /> | 143 | <SegmentedControl options={TAB_OPTIONS} selected={tab} onSelect={setTab} role="tab" /> |
| 150 | | 144 | |
| 151 | {showingLimits ? ( | 145 | {showingLimits ? ( |
| 152 | <UsageLimitsSection now={limits.now} failedLabels={limits.failedLabels} /> | 146 | <UsageLimitsSection now={limits.now} failedLabels={limits.failedLabels} /> |