import { useMemo, useState } from 'react'; import { Alert, Pressable, Text, TextInput, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import * as Linking from 'expo-linking'; import { Stack } from 'expo-router'; import * as WebBrowser from 'expo-web-browser'; import { useAuthActions } from '@convex-dev/auth/react'; import { useConvexAuth, useQuery } from 'convex/react'; import { api } from '@spoon/backend/convex/_generated/api.js'; WebBrowser.maybeCompleteAuthSession(); const Stat = ({ label, value }: { label: string; value: number }) => ( {label} {value} ); const Index = () => { const { isAuthenticated, isLoading } = useConvexAuth(); const { signIn, signOut } = useAuthActions(); const user = useQuery(api.auth.getUser, isAuthenticated ? {} : 'skip'); const spoons = useQuery(api.spoons.listMine, isAuthenticated ? {} : 'skip') ?? []; const syncRuns = useQuery( api.syncRuns.listRecent, isAuthenticated ? { limit: 5 } : 'skip', ) ?? []; const threads = useQuery(api.threads.listMine, isAuthenticated ? { limit: 5 } : 'skip') ?? []; const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [submitting, setSubmitting] = useState(false); const redirectTo = useMemo(() => Linking.createURL(''), []); const handlePasswordSignIn = async () => { setSubmitting(true); try { await signIn('password', { email, password, flow: 'signIn' }); } catch (error) { console.error(error); Alert.alert('Sign in failed', 'Check your email and password.'); } finally { setSubmitting(false); } }; const handleAuthentikSignIn = async () => { setSubmitting(true); try { const result = await signIn('authentik', { redirectTo }); if (!result.redirect) return; const authResult = await WebBrowser.openAuthSessionAsync( result.redirect.toString(), redirectTo, ); if (authResult.type !== 'success') return; const parsed = Linking.parse(authResult.url); const code = parsed.queryParams?.code; if (typeof code !== 'string') { Alert.alert('Sign in failed', 'Authentik did not return a code.'); return; } await signIn('authentik', { code }); } catch (error) { console.error(error); Alert.alert('Sign in failed', 'Could not complete Authentik sign in.'); } finally { setSubmitting(false); } }; return ( Spoon Fork freely. Stay close to upstream. {isLoading ? ( Loading... ) : isAuthenticated ? ( Welcome{user?.name ? `, ${user.name}` : ''} Monitor your managed forks from anywhere. Recent Spoons {spoons.length ? ( spoons.slice(0, 4).map((spoon) => ( {spoon.name} - {spoon.status.replaceAll('_', ' ')} )) ) : ( Create your first Spoon from the web dashboard. )} void signOut()} > Sign out ) : ( void handlePasswordSignIn()} > Sign in with password void handleAuthentikSignIn()} > Continue with Authentik Register the native redirect URI based on spoon:// in Authentik. )} ); }; export default Index;