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 { windowLabel } from "@/lib/timeUtils"; function UsageRow({ label, percent, resetAtSeconds, resetAtISO, }: { label: string; percent: number; resetAtSeconds?: number; resetAtISO?: string; }) { const color = percent >= 90 ? "#ef4444" : percent >= 70 ? "#f59e0b" : "#10a37f"; 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 ); } export default function DashboardTab() { const codex = useCodexUsage(); const claude = useClaudeUsage(); // Fire daily digest reschedule + threshold check whenever fresh data arrives useEffect(() => { if (codex.status === "success" || claude.status === "success") { void onUsageDataLoaded( claude.status === "success" ? claude.usage : null, codex.status === "success" ? codex.usage : null ); } }, [codex.status, claude.status]); useFocusEffect( useCallback(() => { codex.reload(); claude.reload(); }, []) ); const isRefreshing = codex.status === "loading" || claude.status === "loading"; const handleRefresh = () => { codex.refresh(); 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.auth && codex.status === "error" && ( Failed to fetch usage )} {codex.status === "success" && 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.auth && claude.status === "error" && ( Failed to fetch usage )} {claude.status === "success" && 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 && ( )} )} )} ); }