import { useEffect, useCallback, useState, type ComponentType, type ReactNode } from 'react'; import { RefreshCw } from 'lucide-react'; import { cn } from '@/lib/utils'; import { Spinner } from '@/components/ui/spinner'; import { getElectronAPI } from '@/lib/electron'; import { useAppStore } from '@/store/app-store'; import { AnthropicIcon, OpenAIIcon } from '@/components/ui/provider-icon'; interface MobileUsageBarProps { showClaudeUsage: boolean; showCodexUsage: boolean; } // Helper to get progress bar color based on percentage function getProgressBarColor(percentage: number): string { if (percentage >= 80) return 'bg-red-500'; if (percentage >= 50) return 'bg-yellow-500'; return 'bg-green-500'; } // Individual usage bar component function UsageBar({ label, percentage, isStale, }: { label: string; percentage: number; isStale: boolean; }) { return (
{label} = 80 ? 'text-red-500' : percentage >= 50 ? 'text-yellow-500' : 'text-green-500' )} > {Math.round(percentage)}%
); } // Container for a provider's usage info function UsageItem({ icon: Icon, label, isLoading, onRefresh, children, }: { icon: ComponentType<{ className?: string }>; label: string; isLoading: boolean; onRefresh: () => void; children: ReactNode; }) { return (
{label}
{children}
); } export function MobileUsageBar({ showClaudeUsage, showCodexUsage }: MobileUsageBarProps) { const { claudeUsage, claudeUsageLastUpdated, setClaudeUsage } = useAppStore(); const { codexUsage, codexUsageLastUpdated, setCodexUsage } = useAppStore(); const [isClaudeLoading, setIsClaudeLoading] = useState(false); const [isCodexLoading, setIsCodexLoading] = useState(false); // Check if data is stale (older than 2 minutes) const isClaudeStale = !claudeUsageLastUpdated || Date.now() - claudeUsageLastUpdated > 2 * 60 * 1000; const isCodexStale = !codexUsageLastUpdated || Date.now() - codexUsageLastUpdated > 2 * 60 * 1000; const fetchClaudeUsage = useCallback(async () => { setIsClaudeLoading(true); try { const api = getElectronAPI(); if (!api.claude) return; const data = await api.claude.getUsage(); if (!('error' in data)) { setClaudeUsage(data); } } catch { // Silently fail - usage display is optional } finally { setIsClaudeLoading(false); } }, [setClaudeUsage]); const fetchCodexUsage = useCallback(async () => { setIsCodexLoading(true); try { const api = getElectronAPI(); if (!api.codex) return; const data = await api.codex.getUsage(); if (!('error' in data)) { setCodexUsage(data); } } catch { // Silently fail - usage display is optional } finally { setIsCodexLoading(false); } }, [setCodexUsage]); const getCodexWindowLabel = (durationMins: number) => { if (durationMins < 60) return `${durationMins}m Window`; if (durationMins < 1440) return `${Math.round(durationMins / 60)}h Window`; return `${Math.round(durationMins / 1440)}d Window`; }; // Auto-fetch on mount if data is stale useEffect(() => { if (showClaudeUsage && isClaudeStale) { fetchClaudeUsage(); } }, [showClaudeUsage, isClaudeStale, fetchClaudeUsage]); useEffect(() => { if (showCodexUsage && isCodexStale) { fetchCodexUsage(); } }, [showCodexUsage, isCodexStale, fetchCodexUsage]); // Don't render if there's nothing to show if (!showClaudeUsage && !showCodexUsage) { return null; } return (
{showClaudeUsage && ( {claudeUsage ? ( <> ) : (

Loading usage data...

)}
)} {showCodexUsage && ( {codexUsage?.rateLimits ? ( <> {codexUsage.rateLimits.primary && ( )} {codexUsage.rateLimits.secondary && ( )} ) : (

Loading usage data...

)}
)}
); }