Files
codexbar-mobile/app/onboarding/claude.tsx
T
space ba7d957155
CodexBar Mobile Build / build-android (push) Failing after 1m38s
CodexBar Mobile Build / release (push) Has been skipped
Maximus upgradus
2026-06-24 20:16:34 +02:00

226 lines
8.9 KiB
TypeScript

import { useState, useCallback, useEffect } from "react";
import {
View,
Text,
TouchableOpacity,
TextInput,
KeyboardAvoidingView,
Platform,
ScrollView,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { router } from "expo-router";
import { MaterialIcons } from "@expo/vector-icons";
import {
saveClaudeSessionKey,
saveClaudeLastActiveOrg,
clearClaudeLastActiveOrg,
loadClaudeSessionKey,
loadClaudeLastActiveOrg,
} from "@/lib/storage";
export default function OnboardingClaudeScreen() {
const [savedKey, setSavedKey] = useState<string | null>(null);
const [savedOrg, setSavedOrg] = useState<string | null>(null);
const [pendingKey, setPendingKey] = useState("");
const [pendingOrg, setPendingOrg] = useState("");
const [error, setError] = useState<string | null>(null);
useEffect(() => {
Promise.all([loadClaudeSessionKey(), loadClaudeLastActiveOrg()]).then(
([k, o]) => {
if (k) setSavedKey(k);
if (o) setSavedOrg(o);
}
);
}, []);
const canSave = pendingKey.trim().startsWith("sk-ant-");
const handleSave = useCallback(async () => {
const trimmed = pendingKey.trim();
if (!trimmed.startsWith("sk-ant-")) {
setError("Session key must start with sk-ant-");
return;
}
const orgTrimmed = pendingOrg.trim() || undefined;
await saveClaudeSessionKey(trimmed);
if (orgTrimmed) await saveClaudeLastActiveOrg(orgTrimmed);
else await clearClaudeLastActiveOrg();
setSavedKey(trimmed);
setSavedOrg(orgTrimmed ?? null);
setPendingKey("");
setPendingOrg("");
setError(null);
}, [pendingKey, pendingOrg]);
return (
<SafeAreaView className="flex-1 bg-neutral-950">
<KeyboardAvoidingView
className="flex-1"
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<ScrollView
className="flex-1"
contentContainerStyle={{ flexGrow: 1 }}
keyboardShouldPersistTaps="handled"
>
<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: "#10a37f" }}
>
<MaterialIcons name="check" size={14} color="white" />
</View>
<View className="w-12 h-0.5" style={{ backgroundColor: "#d97706" }} />
<View
className="w-6 h-6 rounded-full items-center justify-center"
style={{ backgroundColor: "#d97706" }}
>
<Text className="text-white 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: "#d9770622" }}
>
<MaterialIcons name="psychology" size={32} color="#d97706" />
</View>
<Text className="text-3xl font-bold text-white mb-3">
Connect Claude.ai
</Text>
<Text className="text-neutral-400 text-base leading-relaxed mb-8">
Enter your session key to monitor your Claude.ai usage windows.
</Text>
{savedKey ? (
<View className="bg-neutral-900 rounded-2xl border border-neutral-800 mb-4 overflow-hidden">
<View className="flex-row items-center gap-x-3 p-4">
<View
className="w-10 h-10 rounded-xl items-center justify-center"
style={{ backgroundColor: "#d9770622" }}
>
<MaterialIcons name="check-circle" size={22} color="#d97706" />
</View>
<View className="flex-1">
<Text className="text-white font-semibold">Connected</Text>
<Text className="text-neutral-500 text-sm font-mono">
{savedKey.slice(0, 16)}
</Text>
</View>
<TouchableOpacity
onPress={() => {
setSavedKey(null);
setSavedOrg(null);
setPendingKey("");
setPendingOrg("");
}}
>
<Text className="text-neutral-400 text-xs">Change</Text>
</TouchableOpacity>
</View>
{savedOrg && (
<View className="border-t border-neutral-800 px-4 py-3">
<Text className="text-neutral-500 text-xs font-mono">
org: {savedOrg.slice(0, 8)}
</Text>
</View>
)}
</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>
)}
<TextInput
value={pendingKey}
onChangeText={(t) => {
setPendingKey(t);
setError(null);
}}
placeholder="sk-ant-… (required)"
placeholderTextColor="#525252"
autoCapitalize="none"
autoCorrect={false}
secureTextEntry
className="border border-neutral-800 rounded-xl px-4 py-3.5 text-sm text-white bg-neutral-900 mb-3"
/>
<TextInput
value={pendingOrg}
onChangeText={setPendingOrg}
placeholder="lastActiveOrg UUID (optional)"
placeholderTextColor="#525252"
autoCapitalize="none"
autoCorrect={false}
className="border border-neutral-800 rounded-xl px-4 py-3.5 text-sm text-white bg-neutral-900 mb-3"
/>
<TouchableOpacity
onPress={handleSave}
disabled={!canSave}
className="rounded-2xl py-4 items-center mb-4"
style={{ backgroundColor: canSave ? "#d97706" : "#1a1a1a" }}
>
<Text
className="font-bold text-base"
style={{ color: canSave ? "white" : "#525252" }}
>
Save Session Key
</Text>
</TouchableOpacity>
<View className="bg-neutral-900 rounded-xl p-4 border border-neutral-800">
<Text className="text-neutral-400 text-xs font-semibold uppercase tracking-widest mb-2">
How to find your credentials
</Text>
<Text className="text-neutral-500 text-sm leading-relaxed">
Open Claude.ai in Chrome DevTools (F12) Application Cookies claude.ai{"\n"}
Copy <Text className="text-neutral-300 font-mono">sessionKey</Text> and optionally <Text className="text-neutral-300 font-mono">lastActiveOrg</Text>
</Text>
</View>
</View>
)}
</View>
{/* Bottom actions */}
<View className="gap-y-3">
<TouchableOpacity
onPress={() => router.push("/onboarding/done")}
className="rounded-2xl py-4 items-center"
style={{ backgroundColor: savedKey ? "#d97706" : "#1a1a1a" }}
>
<Text
className="font-bold text-base"
style={{ color: savedKey ? "white" : "#a3a3a3" }}
>
Continue
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => router.push("/onboarding/done")}
className="py-3 items-center"
>
<Text className="text-neutral-600 text-sm">Skip for now</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
</SafeAreaView>
);
}