fix(settings): show usage limits approaching exhaustion

Provider status tones could hide utilization risk. Display the strongest signal so high usage reaches warning and danger while provider-specific alerts remain visible.
This commit is contained in:
Mohamed Boudra
2026-07-22 13:16:53 +00:00
parent 42ee5a5949
commit bf4afc3634
4 changed files with 35 additions and 3 deletions

View File

@@ -2,6 +2,7 @@ import { useMemo } from "react";
import { Text, View, type StyleProp, type ViewStyle } from "react-native";
import { StyleSheet } from "react-native-unistyles";
import { clampPct, formatAmount, formatResetLabel } from "./format";
import { resolveTone } from "./tone";
import type { ProviderUsageBalance, ProviderUsageTone } from "./types";
interface ResolvedBalance {
@@ -41,7 +42,7 @@ function fillToneStyle(tone: ProviderUsageTone) {
export function ProviderUsageBalanceBar({ balance }: { balance: ProviderUsageBalance }) {
const { amountText, usedPct } = resolveBalance(balance);
const tone = balance.tone ?? "default";
const tone = resolveTone(balance.tone, usedPct);
const resetLabel = formatResetLabel(balance.resetsAt);
const fillStyle = useMemo<StyleProp<ViewStyle>>(

View File

@@ -0,0 +1,13 @@
import { describe, expect, it } from "vitest";
import { resolveTone } from "./tone";
describe("provider usage tone", () => {
it("shows the strongest risk from provider status and utilization", () => {
expect([
resolveTone("ok", 5),
resolveTone("ok", 70),
resolveTone("ok", 99),
resolveTone("danger", 5),
]).toEqual(["ok", "warning", "danger", "danger"]);
});
});

View File

@@ -1,8 +1,26 @@
import type { ProviderUsageTone } from "./types";
const TONE_RISK: Record<ProviderUsageTone, number> = {
default: 0,
ok: 1,
warning: 2,
danger: 3,
};
export function deriveTone(usedPct: number | null | undefined): ProviderUsageTone {
if (usedPct == null) return "default";
if (usedPct > 90) return "danger";
if (usedPct >= 70) return "warning";
return "default";
}
export function resolveTone(
providerTone: ProviderUsageTone | undefined,
usedPct: number | null | undefined,
): ProviderUsageTone {
const utilizationTone = deriveTone(usedPct);
if (!providerTone || TONE_RISK[utilizationTone] > TONE_RISK[providerTone]) {
return utilizationTone;
}
return providerTone;
}

View File

@@ -2,7 +2,7 @@ import { useMemo } from "react";
import { Text, View, type StyleProp, type ViewStyle } from "react-native";
import { StyleSheet } from "react-native-unistyles";
import { clampPct, formatPct, formatResetLabel } from "./format";
import { deriveTone } from "./tone";
import { resolveTone } from "./tone";
import type { ProviderUsageTone, ProviderUsageWindow } from "./types";
function resolveUsedPct(window: ProviderUsageWindow): number | null {
@@ -26,7 +26,7 @@ function fillToneStyle(tone: ProviderUsageTone) {
export function ProviderUsageWindowBar({ window }: { window: ProviderUsageWindow }) {
const usedPct = resolveUsedPct(window);
const tone = window.tone ?? deriveTone(usedPct);
const tone = resolveTone(window.tone, usedPct);
const fillWidth = clampPct(usedPct ?? 0);
const fillStyle = useMemo<StyleProp<ViewStyle>>(