156 lines
5.9 KiB
TypeScript
156 lines
5.9 KiB
TypeScript
import { useState, useCallback, useEffect } from "react";
|
|
import { View, Text, TouchableOpacity, ActivityIndicator } from "react-native";
|
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
import { router } from "expo-router";
|
|
import { MaterialIcons } from "@expo/vector-icons";
|
|
import { pickAndReadCodexAuth } from "@/lib/fileReader";
|
|
import { saveCodexAuth, loadCodexAuth } from "@/lib/storage";
|
|
import type { CodexAuth } from "@/types/codex";
|
|
import { COLORS } from "@/lib/constants";
|
|
|
|
export default function OnboardingCodexScreen() {
|
|
const [auth, setAuth] = useState<CodexAuth | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
loadCodexAuth().then((stored) => {
|
|
if (stored) setAuth(stored);
|
|
});
|
|
}, []);
|
|
|
|
const importFile = useCallback(async () => {
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
const parsed = await pickAndReadCodexAuth();
|
|
await saveCodexAuth(parsed);
|
|
setAuth(parsed);
|
|
} catch (e: unknown) {
|
|
const msg = e instanceof Error ? e.message : "UNKNOWN_ERROR";
|
|
if (msg !== "PICKER_CANCELLED") setError(msg);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
return (
|
|
<SafeAreaView className="flex-1 bg-neutral-950">
|
|
<View className="flex-1 px-8 justify-between py-8">
|
|
{/* Top: back + step indicator */}
|
|
<View className="flex-row items-center justify-between">
|
|
<TouchableOpacity
|
|
onPress={() => router.back()}
|
|
className="w-9 h-9 rounded-xl bg-neutral-900 items-center justify-center"
|
|
>
|
|
<MaterialIcons name="arrow-back" size={18} color="#a3a3a3" />
|
|
</TouchableOpacity>
|
|
<View className="flex-row items-center gap-x-2">
|
|
<View className="w-6 h-6 rounded-full items-center justify-center" style={{ backgroundColor: COLORS.codex }}>
|
|
<Text className="text-white text-xs font-bold">1</Text>
|
|
</View>
|
|
<View className="w-12 h-0.5 bg-neutral-800" />
|
|
<View className="w-6 h-6 rounded-full bg-neutral-800 items-center justify-center">
|
|
<Text className="text-neutral-500 text-xs font-bold">2</Text>
|
|
</View>
|
|
</View>
|
|
<View className="w-9" />
|
|
</View>
|
|
|
|
{/* Content */}
|
|
<View>
|
|
<View
|
|
className="w-16 h-16 rounded-2xl items-center justify-center mb-6"
|
|
style={{ backgroundColor: `${COLORS.codex}22` }}
|
|
>
|
|
<MaterialIcons name="auto-awesome" size={32} color={COLORS.codex} />
|
|
</View>
|
|
<Text className="text-3xl font-bold text-white mb-3">
|
|
Connect Codex CLI
|
|
</Text>
|
|
<Text className="text-neutral-400 text-base leading-relaxed mb-8">
|
|
Import your{" "}
|
|
<Text className="text-neutral-200 font-mono text-sm">
|
|
~/.codex/auth.json
|
|
</Text>{" "}
|
|
file to track your rate limits and credits.
|
|
</Text>
|
|
|
|
{/* Status / action */}
|
|
{auth ? (
|
|
<View className="flex-row items-center gap-x-3 bg-neutral-900 rounded-2xl p-4 border border-neutral-800 mb-4">
|
|
<View
|
|
className="w-10 h-10 rounded-xl items-center justify-center"
|
|
style={{ backgroundColor: `${COLORS.codex}22` }}
|
|
>
|
|
<MaterialIcons name="check-circle" size={22} color={COLORS.codex} />
|
|
</View>
|
|
<View className="flex-1">
|
|
<Text className="text-white font-semibold">Connected</Text>
|
|
{auth.accountId && (
|
|
<Text className="text-neutral-500 text-sm font-mono">
|
|
{auth.accountId.slice(0, 12)}…
|
|
</Text>
|
|
)}
|
|
</View>
|
|
<TouchableOpacity onPress={importFile}>
|
|
<Text className="text-neutral-400 text-xs">Re-import</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
) : (
|
|
<View>
|
|
{error && (
|
|
<View className="bg-red-950 border border-red-900 rounded-xl px-4 py-3 mb-4">
|
|
<Text className="text-red-400 text-sm">{error}</Text>
|
|
</View>
|
|
)}
|
|
<TouchableOpacity
|
|
onPress={importFile}
|
|
disabled={loading}
|
|
className="rounded-2xl py-4 items-center mb-3"
|
|
style={{ backgroundColor: loading ? "#1a1a1a" : COLORS.codex }}
|
|
>
|
|
{loading ? (
|
|
<ActivityIndicator color={COLORS.codex} />
|
|
) : (
|
|
<View className="flex-row items-center gap-x-2">
|
|
<MaterialIcons name="upload-file" size={18} color="white" />
|
|
<Text className="text-white font-bold text-base">
|
|
Import auth.json
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</TouchableOpacity>
|
|
<Text className="text-neutral-600 text-xs text-center">
|
|
Located at ~/.codex/auth.json on your Mac
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
|
|
{/* Bottom actions */}
|
|
<View className="gap-y-3">
|
|
<TouchableOpacity
|
|
onPress={() => router.push("/onboarding/claude")}
|
|
className="rounded-2xl py-4 items-center"
|
|
style={{ backgroundColor: auth ? COLORS.codex : "#1a1a1a" }}
|
|
>
|
|
<Text
|
|
className="font-bold text-base"
|
|
style={{ color: auth ? "white" : "#a3a3a3" }}
|
|
>
|
|
Continue
|
|
</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity
|
|
onPress={() => router.push("/onboarding/claude")}
|
|
className="py-3 items-center"
|
|
>
|
|
<Text className="text-neutral-600 text-sm">Skip for now</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|