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...
)}
)}
);
}