authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-13 19:45:37-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-13 19:45:37-07:00
log564719165e9131ab2aa55e9c805b860a415690da
tree5bf3194ed5f24237c47b2c36170368b4662bd9f4
parent5e961d3d7fc07ff1ba998bfd20bb366c2ab8c9a9
signature Signed by PGP key B5690EEEBB952194

fix(mobile): keep usage widget rows consistently sized (#11669)


1 files changed, 29 insertions(+), 30 deletions(-)

apps/mobile/src/widgets/SubscriptionUsage.tsx+29-30
...@@ -2,12 +2,12 @@ import { HStack, ProgressView, Spacer, Text, VStack } from "@expo/ui/swift-ui";...@@ -2,12 +2,12 @@ import { HStack, ProgressView, Spacer, Text, VStack } from "@expo/ui/swift-ui";
2import {2import {
3 accessibilityElement,3 accessibilityElement,
4 accessibilityLabel,4 accessibilityLabel,
5 fixedSize,
5 font,6 font,
6 foregroundStyle,7 foregroundStyle,
7 frame,8 frame,
8 layoutPriority,9 layoutPriority,
9 lineLimit,10 lineLimit,
10 minimumScaleFactor,
11 progressViewStyle,11 progressViewStyle,
12 tint,12 tint,
13 widgetURL,13 widgetURL,
...@@ -33,6 +33,8 @@ function SubscriptionUsage(...@@ -33,6 +33,8 @@ function SubscriptionUsage(
33 const accessory = family === "accessoryRectangular";33 const accessory = family === "accessoryRectangular";
34 const compact =34 const compact =
35 family === "systemSmall" || accessory || environment.levelOfDetail === "simplified";35 family === "systemSmall" || accessory || environment.levelOfDetail === "simplified";
36 // Budget short cards for two quotas per provider, including their secondary text.
37 const dense = family === "systemSmall" || family === "systemMedium";
36 const limit = family === "systemExtraLarge" ? 6 : family === "systemLarge" ? 4 : 2;38 const limit = family === "systemExtraLarge" ? 6 : family === "systemLarge" ? 4 : 2;
37 const monochrome =39 const monochrome =
38 environment.widgetRenderingMode !== "fullColor" || environment.isLuminanceReduced;40 environment.widgetRenderingMode !== "fullColor" || environment.isLuminanceReduced;
...@@ -77,6 +79,7 @@ function SubscriptionUsage(...@@ -77,6 +79,7 @@ function SubscriptionUsage(
77 : provider.detail;79 : provider.detail;
78 const barModifiers = [80 const barModifiers = [
79 progressViewStyle("linear"),81 progressViewStyle("linear"),
82 frame({ height: 4 }),
80 ...(monochrome ? [] : [tint(provider.name === "Claude" ? "#d97757" : "#8e8e93")]),83 ...(monochrome ? [] : [tint(provider.name === "Claude" ? "#d97757" : "#8e8e93")]),
81 ];84 ];
82 if (accessory) {85 if (accessory) {
...@@ -99,7 +102,6 @@ function SubscriptionUsage(...@@ -99,7 +102,6 @@ function SubscriptionUsage(
99 modifiers={[102 modifiers={[
100 font({ textStyle: "caption", weight: "semibold" }),103 font({ textStyle: "caption", weight: "semibold" }),
101 lineLimit(1),104 lineLimit(1),
102 minimumScaleFactor(0.75),
103 foregroundStyle("primary"),105 foregroundStyle("primary"),
104 ]}106 ]}
105 >107 >
...@@ -132,35 +134,37 @@ function SubscriptionUsage(...@@ -132,35 +134,37 @@ function SubscriptionUsage(
132 <VStack134 <VStack
133 key={provider.name}135 key={provider.name}
134 alignment="leading"136 alignment="leading"
135 spacing={compact ? 2 : 4}137 spacing={dense ? 1 : compact ? 2 : 4}
136 modifiers={[frame({ maxWidth: Infinity, alignment: "leading" })]}138 modifiers={[
139 frame({ maxWidth: Infinity, alignment: "leading" }),
140 fixedSize({ horizontal: false, vertical: true }),
141 ]}
137 >142 >
138 <Text143 <Text
139 modifiers={[144 modifiers={[
140 font({ textStyle: compact ? "caption" : "headline", weight: "bold" }),145 font({ textStyle: compact ? "caption" : "headline", weight: "bold" }),
141 lineLimit(1),146 lineLimit(1),
142 minimumScaleFactor(0.75),
143 foregroundStyle("primary"),147 foregroundStyle("primary"),
144 ]}148 ]}
145 >149 >
146 {provider.name}150 {provider.name}
147 </Text>151 </Text>
148 {(!compact || shown.length === 0) && detail !== "Subscription remaining" ? (152 {!compact || shown.length === 0 ? (
149 <Text153 <Text
150 modifiers={[154 modifiers={[
151 font({ textStyle: "caption2" }),155 font({ textStyle: "caption2" }),
152 foregroundStyle("secondary"),156 foregroundStyle("secondary"),
153 lineLimit(compact ? 1 : 2),157 lineLimit(1),
154 ]}158 ]}
155 >159 >
156 {detail}160 {detail === "Subscription remaining" ? " " : detail}
157 </Text>161 </Text>
158 ) : null}162 ) : null}
159 {shown.map((window) => (163 {shown.map((window) => (
160 <VStack164 <VStack
161 key={window.label}165 key={window.label}
162 alignment="leading"166 alignment="leading"
163 spacing={2}167 spacing={dense ? 1 : 2}
164 modifiers={[168 modifiers={[
165 accessibilityElement("ignore"),169 accessibilityElement("ignore"),
166 accessibilityLabel(170 accessibilityLabel(
...@@ -171,10 +175,9 @@ function SubscriptionUsage(...@@ -171,10 +175,9 @@ function SubscriptionUsage(
171 <HStack spacing={4}>175 <HStack spacing={4}>
172 <Text176 <Text
173 modifiers={[177 modifiers={[
174 font({ textStyle: compact ? "caption2" : "caption" }),178 font({ textStyle: compact || dense ? "caption2" : "caption" }),
175 foregroundStyle("secondary"),179 foregroundStyle("secondary"),
176 lineLimit(1),180 lineLimit(1),
177 minimumScaleFactor(0.75),
178 ]}181 ]}
179 >182 >
180 {window.label}183 {window.label}
...@@ -182,9 +185,11 @@ function SubscriptionUsage(...@@ -182,9 +185,11 @@ function SubscriptionUsage(
182 <Spacer />185 <Spacer />
183 <Text186 <Text
184 modifiers={[187 modifiers={[
185 font({ textStyle: compact ? "caption2" : "caption", weight: "semibold" }),188 font({
189 textStyle: compact || dense ? "caption2" : "caption",
190 weight: "semibold",
191 }),
186 lineLimit(1),192 lineLimit(1),
187 minimumScaleFactor(0.75),
188 layoutPriority(1),193 layoutPriority(1),
189 foregroundStyle(194 foregroundStyle(
190 window.remaining <= 10 && !monochrome195 window.remaining <= 10 && !monochrome
...@@ -200,14 +205,7 @@ function SubscriptionUsage(...@@ -200,14 +205,7 @@ function SubscriptionUsage(
200 </HStack>205 </HStack>
201 <ProgressView value={window.remaining / 100} modifiers={barModifiers} />206 <ProgressView value={window.remaining / 100} modifiers={barModifiers} />
202 {!compact ? (207 {!compact ? (
203 <Text208 <Text modifiers={[font({ size: 10 }), foregroundStyle("secondary"), lineLimit(1)]}>
204 modifiers={[
205 font({ textStyle: "caption2" }),
206 foregroundStyle("secondary"),
207 lineLimit(1),
208 minimumScaleFactor(0.75),
209 ]}
210 >
211 {window.reset}209 {window.reset}
212 </Text>210 </Text>
213 ) : null}211 ) : null}
...@@ -216,7 +214,13 @@ function SubscriptionUsage(...@@ -216,7 +214,13 @@ function SubscriptionUsage(
216 {!compact &&214 {!compact &&
217 !stale &&215 !stale &&
218 (period === "auto" ? (provider.totalWindows ?? windows.length) : windows.length) > limit ? (216 (period === "auto" ? (provider.totalWindows ?? windows.length) : windows.length) > limit ? (
219 <Text modifiers={[font({ textStyle: "caption2" }), foregroundStyle("secondary")]}>217 <Text
218 modifiers={[
219 font({ textStyle: "caption2" }),
220 foregroundStyle("secondary"),
221 lineLimit(1),
222 ]}
223 >
220 {(period === "auto" ? (provider.totalWindows ?? windows.length) : windows.length) -224 {(period === "auto" ? (provider.totalWindows ?? windows.length) : windows.length) -
221 limit}{" "}225 limit}{" "}
222 more in T3226 more in T3
...@@ -228,11 +232,11 @@ function SubscriptionUsage(...@@ -228,11 +232,11 @@ function SubscriptionUsage(
228 return (232 return (
229 <VStack233 <VStack
230 alignment="leading"234 alignment="leading"
231 spacing={accessory ? 2 : 6}235 spacing={accessory || dense ? 2 : 6}
232 modifiers={props.url ? [widgetURL(props.url)] : []}236 modifiers={props.url ? [widgetURL(props.url)] : []}
233 >237 >
234 {compact ? (238 {compact ? (
235 <VStack alignment="leading" spacing={accessory ? 4 : 8}>239 <VStack alignment="leading" spacing={accessory || dense ? 4 : 8}>
236 {columns}240 {columns}
237 </VStack>241 </VStack>
238 ) : (242 ) : (
...@@ -243,12 +247,7 @@ function SubscriptionUsage(...@@ -243,12 +247,7 @@ function SubscriptionUsage(
243 {!accessory ? <Spacer /> : null}247 {!accessory ? <Spacer /> : null}
244 {!accessory ? (248 {!accessory ? (
245 <Text249 <Text
246 modifiers={[250 modifiers={[font({ textStyle: "caption2" }), foregroundStyle("secondary"), lineLimit(1)]}
247 font({ textStyle: "caption2" }),
248 foregroundStyle("secondary"),
249 lineLimit(1),
250 minimumScaleFactor(0.75),
251 ]}
252 >251 >
253 {props.checkedAt252 {props.checkedAt
254 ? `As of ${new Date(props.checkedAt).toLocaleString(undefined, { hour: "numeric", minute: "2-digit", month: "short", day: "numeric" })}`253 ? `As of ${new Date(props.checkedAt).toLocaleString(undefined, { hour: "numeric", minute: "2-digit", month: "short", day: "numeric" })}`