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;