fix(mobile): gate authed routes behind Stack.Protected — cold-start redbox

'/' redirected to /(tabs) unconditionally; tab screens call useApi()
which throws 'not signed in' during render — before AuthGate's effect
redirect could run. Sign-out hit the same throw.

- index redirects based on session state (api ? tabs : login)
- authed screens wrapped in Stack.Protected guard={!!api}
- found by HP-WIN11 emulator visual pass

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-10-09 05:45:44 +02:00
1 parent a0952faf25
commit 6946357a7b
2 files changed
+10 -4

No files matched your search

+6 -3
View File
@@ -19,10 +19,11 @@ function AuthGate() {
useEffect(() => {
if (!ready) return;
const inLogin = segments[0] === 'login';
if (!api && !inLogin) router.replace('/login');
else if (api && inLogin) router.replace('/');
if (api && inLogin) router.replace('/');
}, [ready, api, segments, router]);
if (!ready) return null;
return (
<Stack
screenOptions={{
@@ -31,11 +32,13 @@ function AuthGate() {
contentStyle: { backgroundColor: colors.bg },
}}
>
<Stack.Screen name="login" options={{ headerShown: false }} />
<Stack.Protected guard={!!api}>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="project/[id]" options={{ title: 'Project' }} />
<Stack.Screen name="service/[id]" options={{ title: 'Service' }} />
<Stack.Screen name="service/logs" options={{ title: 'Logs' }} />
</Stack.Protected>
<Stack.Screen name="login" options={{ headerShown: false }} />
</Stack>
);
}
+4 -1
View File
@@ -1,5 +1,8 @@
import { Redirect } from 'expo-router';
import { useSession } from '../session';
export default function Index() {
return <Redirect href="/(tabs)" />;
const { ready, api } = useSession();
if (!ready) return null;
return <Redirect href={api ? '/(tabs)' : '/login'} />;
}