()
| 14 | * so monitoring happens continuously, not just when the banner is visible. |
| 15 | */ |
| 16 | export function useUsageMonitor() { |
| 17 | const isChainInProgress = useChatStore((state) => state.isChainInProgress) |
| 18 | const sessionCreditsUsed = useChatStore((state) => state.sessionCreditsUsed) |
| 19 | const setInputMode = useChatStore((state) => state.setInputMode) |
| 20 | const lastWarnedThresholdRef = useRef<number | null>(null) |
| 21 | |
| 22 | // Query usage data - this will refetch when invalidated after message completion |
| 23 | const { data: usageData } = useUsageQuery({ enabled: !IS_FREEBUFF }) |
| 24 | |
| 25 | useEffect(() => { |
| 26 | if (IS_FREEBUFF) return |
| 27 | |
| 28 | // Only show after user has sent at least one message (to avoid overwhelming on app start) |
| 29 | if (sessionCreditsUsed === 0) { |
| 30 | return |
| 31 | } |
| 32 | |
| 33 | const authToken = getAuthToken() |
| 34 | const remainingBalance = usageData?.remainingBalance ?? null |
| 35 | const autoTopupEnabled = usageData?.autoTopupEnabled ?? false |
| 36 | |
| 37 | const decision = shouldAutoShowBanner( |
| 38 | isChainInProgress, |
| 39 | !!authToken, |
| 40 | remainingBalance, |
| 41 | lastWarnedThresholdRef.current, |
| 42 | autoTopupEnabled, |
| 43 | ) |
| 44 | |
| 45 | // Update the last warned threshold |
| 46 | if (decision.newWarningThreshold !== lastWarnedThresholdRef.current) { |
| 47 | lastWarnedThresholdRef.current = decision.newWarningThreshold |
| 48 | } |
| 49 | |
| 50 | // Show the usage banner if we should |
| 51 | if (decision.shouldShow) { |
| 52 | setInputMode('usage') |
| 53 | } |
| 54 | }, [isChainInProgress, usageData, sessionCreditsUsed, setInputMode]) |
| 55 | } |
no test coverage detected