import { ScrollView, View, Text, TouchableOpacity, RefreshControl, ActivityIndicator, } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { router, useFocusEffect } from "expo-router"; import { useCallback, useEffect } from "react"; import { MaterialIcons } from "@expo/vector-icons"; import { useCodexUsage } from "@/hooks/useCodexUsage"; import { useClaudeUsage } from "@/hooks/useClaudeUsage"; import { onUsageDataLoaded } from "@/lib/notifications"; import { ProgressBar } from "@/components/ProgressBar"; import { ResetCountdown } from "@/components/ResetCountdown"; import { LastUpdated } from "@/components/LastUpdated"; import { ScreenErrorBoundary } from "@/components/ScreenErrorBoundary"; import { COLORS, getUsageColor } from "@/lib/constants"; import { windowLabel } from "@/lib/timeUtils"; function UsageRow({ label, percent, resetAtSeconds, resetAtISO, }: { label: string; percent: number; resetAtSeconds?: number; resetAtISO?: string; }) { const color = getUsageColor(percent); return ( {label} {Math.round(percent)}% used ); } function SetupPrompt({ label }: { label: string }) { return ( {label} router.navigate("/(tabs)/settings")} className="flex-row items-center self-start gap-x-1.5 px-3 py-2 rounded-xl border border-neutral-200 dark:border-neutral-700" > Set up in Settings ); } function DashboardTabContent() { const codex = useCodexUsage(); const claude = useClaudeUsage(); // Fire daily digest reschedule + threshold check whenever fresh data arrives useEffect(() => { if (codex.usage || claude.usage) { void onUsageDataLoaded( claude.usage, codex.usage ); } }, [codex.lastFetchedAt, claude.lastFetchedAt]); useFocusEffect( useCallback(() => { void codex.reloadCredentials(); void claude.reloadCredentials(); }, [codex.reloadCredentials, claude.reloadCredentials]) ); const isRefreshing = codex.status === "loading" || claude.status === "loading"; const handleRefresh = () => { void codex.refresh(); void claude.refresh(); }; const connectedCount = [codex.auth, claude.auth].filter(Boolean).length; return ( } > {/* Header */} Codexbar {connectedCount === 0 ? "No services connected" : connectedCount === 2 ? "2 services connected" : "1 of 2 services connected"} {/* Codex card */} Codex CLI {codex.usage?.plan_type && ( {codex.usage.plan_type} )} {codex.auth && codex.status === "error" && ( )} {codex.status === "success" && !codex.usage?.plan_type && ( )} {!codex.auth && ( )} {codex.auth && codex.status === "loading" && !codex.usage && ( )} {codex.auth && codex.status === "error" && ( Failed to fetch usage )} {codex.usage && ( {!codex.usage.credits.unlimited && codex.usage.credits.has_credits && ( ${Number(codex.usage.credits.balance).toFixed(2)}{" "} credits remaining )} {codex.usage.credits.unlimited && ( Unlimited credits )} )} {/* Claude card */} Claude.ai {claude.auth && claude.status === "error" && ( )} {claude.status === "success" && ( )} {!claude.auth && ( )} {claude.auth && claude.status === "loading" && !claude.usage && ( )} {claude.auth && claude.status === "error" && ( Failed to fetch usage )} {claude.usage && ( {(claude.usage.seven_day_sonnet || claude.usage.seven_day_opus) && ( By Model (7-day) {claude.usage.seven_day_sonnet && ( )} {claude.usage.seven_day_opus && ( )} )} )} ); } export default function DashboardTab() { return ( ); }