From b57a03c0051c700ad4db573a07565f899a0c01d4 Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Thu, 10 Sep 2026 05:20:03 +0900 Subject: [PATCH] fix(app): refresh provider usage timing --- packages/app/src/usage/balance-bar.tsx | 2 ++ packages/app/src/usage/use-relative-time-tick.ts | 12 ++++++++++++ packages/app/src/usage/window-bar.tsx | 5 +++++ 3 files changed, 19 insertions(+) create mode 100644 packages/app/src/usage/use-relative-time-tick.ts diff --git a/packages/app/src/usage/balance-bar.tsx b/packages/app/src/usage/balance-bar.tsx index 7bfcbbd902..b8339c1038 100644 --- a/packages/app/src/usage/balance-bar.tsx +++ b/packages/app/src/usage/balance-bar.tsx @@ -5,6 +5,7 @@ import { Text, View, type StyleProp, type ViewStyle } from "react-native"; import { StyleSheet } from "react-native-unistyles"; import { clampPct, formatAmount, formatResetLabel } from "./format"; import type { UsageBalance, UsageTone } from "./types"; +import { useRelativeTimeTick } from "./use-relative-time-tick"; interface ResolvedBalance { amountText: string; @@ -51,6 +52,7 @@ function fillToneStyle(tone: UsageTone) { export function UsageBalanceBar({ balance }: { balance: UsageBalance }) { const { t, i18n } = useTranslation(); + useRelativeTimeTick(balance.resetsAt != null); const { amountText, usedPct } = resolveBalance(balance, t, i18n.resolvedLanguage); const tone = balance.tone ?? "default"; const resetLabel = formatResetLabel(balance.resetsAt); diff --git a/packages/app/src/usage/use-relative-time-tick.ts b/packages/app/src/usage/use-relative-time-tick.ts new file mode 100644 index 0000000000..c075612f40 --- /dev/null +++ b/packages/app/src/usage/use-relative-time-tick.ts @@ -0,0 +1,12 @@ +import { useEffect, useReducer } from "react"; +import { subscribeToRelativeTimeTick } from "@/utils/relative-time-ticker"; + +/** Re-renders a mounted usage label when its relative time can change. */ +export function useRelativeTimeTick(enabled: boolean): void { + const [, tick] = useReducer((value: number) => value + 1, 0); + + useEffect(() => { + if (!enabled) return undefined; + return subscribeToRelativeTimeTick("minute", tick); + }, [enabled]); +} diff --git a/packages/app/src/usage/window-bar.tsx b/packages/app/src/usage/window-bar.tsx index 35525e1c36..1f7fc3db28 100644 --- a/packages/app/src/usage/window-bar.tsx +++ b/packages/app/src/usage/window-bar.tsx @@ -16,6 +16,7 @@ import { displayPercent, usageWindowRowLabel } from "./model"; import type { UsageDisplayAs } from "./preferences"; import { windowTone } from "./tone"; import type { UsageTone, UsageWindow } from "./types"; +import { useRelativeTimeTick } from "./use-relative-time-tick"; // Pinned rows carry the pinned surface; hovering an unpinned row previews it at half strength, // so a hover never reads as the selection. Pinned rows do not react to hover. @@ -45,6 +46,10 @@ export function UsageWindowBar({ const tone = windowTone(window); const isAtRisk = window.runsOutAt != null && window.shortfallPct != null; + const relativeTime = isAtRisk ? window.runsOutAt : window.resetsAt; + useRelativeTimeTick(relativeTime != null); + const relativeTime = isAtRisk ? window.runsOutAt : window.resetsAt; + useRelativeTimeTick(relativeTime != null); const trailing = isAtRisk ? formatRunsOutLabel(window.runsOutAt) : formatResetLabel(window.resetsAt);