A React Native app for the ultimate thinking partner.
1import React, { useState } from 'react';
2import {
3 View,
4 Text,
5 StyleSheet,
6 TextInput,
7 TouchableOpacity,
8 ActivityIndicator,
9 SafeAreaView,
10 useColorScheme,
11 Linking,
12} from 'react-native';
13import { StatusBar } from 'expo-status-bar';
14import { useFonts, Lexend_400Regular, Lexend_600SemiBold, Lexend_700Bold } from '@expo-google-fonts/lexend';
15import { darkTheme } from './src/theme';
16
17interface CoLoginScreenProps {
18 onLogin: (apiKey: string) => Promise<void>;
19 isLoading: boolean;
20 error: string | null;
21}
22
23export default function CoLoginScreen({ onLogin, isLoading, error }: CoLoginScreenProps) {
24 const colorScheme = useColorScheme();
25 const [apiKey, setApiKey] = useState('');
26
27 const [fontsLoaded] = useFonts({
28 Lexend_400Regular,
29 Lexend_600SemiBold,
30 Lexend_700Bold,
31 });
32
33 if (!fontsLoaded) {
34 return null;
35 }
36
37 const handleLogin = async () => {
38 if (!apiKey.trim()) return;
39 await onLogin(apiKey.trim());
40 };
41
42 return (
43 <SafeAreaView style={styles.container}>
44 <View style={styles.content}>
45 <View style={styles.header}>
46 <Text style={styles.title}>co</Text>
47 </View>
48
49 <View style={styles.form}>
50 <Text style={styles.label}>Letta API Key</Text>
51 <TextInput
52 style={styles.input}
53 placeholder="Enter your Letta API key"
54 placeholderTextColor={darkTheme.colors.text.tertiary}
55 value={apiKey}
56 onChangeText={setApiKey}
57 autoCapitalize="none"
58 autoCorrect={false}
59 autoFocus
60 editable={!isLoading}
61 secureTextEntry
62 />
63
64 {error && (
65 <View style={styles.errorContainer}>
66 <Text style={styles.errorText}>{error}</Text>
67 </View>
68 )}
69
70 <TouchableOpacity
71 style={[styles.button, isLoading && styles.buttonDisabled]}
72 onPress={handleLogin}
73 disabled={isLoading || !apiKey.trim()}
74 >
75 {isLoading ? (
76 <ActivityIndicator size="small" color="#fff" />
77 ) : (
78 <Text style={styles.buttonText}>Connect</Text>
79 )}
80 </TouchableOpacity>
81
82 <Text style={styles.helpText}>
83 Don't have an API key? Visit letta.com to create one.
84 </Text>
85
86 <View style={styles.githubLink}>
87 <Text style={styles.githubText}>
88 co is{' '}
89 <Text
90 style={styles.githubLinkText}
91 onPress={() => Linking.openURL('https://github.com/letta-ai/co')}
92 >
93 open source
94 </Text>
95 {' '}and welcomes contributions
96 </Text>
97 </View>
98 </View>
99 </View>
100 <StatusBar style="auto" />
101 </SafeAreaView>
102 );
103}
104
105const styles = StyleSheet.create({
106 container: {
107 flex: 1,
108 backgroundColor: darkTheme.colors.background.primary,
109 },
110 content: {
111 flex: 1,
112 justifyContent: 'center',
113 paddingHorizontal: 32,
114 },
115 header: {
116 alignItems: 'center',
117 marginBottom: 48,
118 },
119 title: {
120 fontSize: 48,
121 fontFamily: 'Lexend_700Bold',
122 color: darkTheme.colors.text.primary,
123 letterSpacing: -1,
124 },
125 form: {
126 width: '100%',
127 maxWidth: 400,
128 alignSelf: 'center',
129 },
130 label: {
131 fontSize: 14,
132 fontFamily: 'Lexend_600SemiBold',
133 color: darkTheme.colors.text.primary,
134 marginBottom: 8,
135 },
136 input: {
137 height: 48,
138 borderWidth: 1,
139 borderColor: darkTheme.colors.border.primary,
140 borderRadius: 8,
141 paddingHorizontal: 16,
142 fontSize: 16,
143 fontFamily: 'Lexend_400Regular',
144 backgroundColor: darkTheme.colors.background.secondary,
145 color: darkTheme.colors.text.primary,
146 marginBottom: 16,
147 },
148 errorContainer: {
149 backgroundColor: 'rgba(255, 59, 48, 0.1)',
150 borderRadius: 8,
151 padding: 12,
152 marginBottom: 16,
153 },
154 errorText: {
155 color: '#ff3b30',
156 fontSize: 14,
157 fontFamily: 'Lexend_400Regular',
158 textAlign: 'center',
159 },
160 button: {
161 height: 48,
162 backgroundColor: darkTheme.colors.interactive.primary,
163 borderRadius: 8,
164 justifyContent: 'center',
165 alignItems: 'center',
166 marginBottom: 16,
167 },
168 buttonDisabled: {
169 opacity: 0.5,
170 },
171 buttonText: {
172 color: '#fff',
173 fontSize: 16,
174 fontFamily: 'Lexend_600SemiBold',
175 },
176 helpText: {
177 fontSize: 14,
178 fontFamily: 'Lexend_400Regular',
179 color: darkTheme.colors.text.tertiary,
180 textAlign: 'center',
181 lineHeight: 20,
182 },
183 githubLink: {
184 marginTop: 24,
185 alignItems: 'center',
186 },
187 githubText: {
188 fontSize: 13,
189 fontFamily: 'Lexend_400Regular',
190 color: darkTheme.colors.text.tertiary,
191 textAlign: 'center',
192 },
193 githubLinkText: {
194 color: darkTheme.colors.interactive.primary,
195 textDecorationLine: 'underline',
196 },
197});