Démarrage de l'app
Que se passe-t-il entre le moment où l'on touche l'icône et le moment où l'écran de pointage s'affiche ?
Étape 1 : package.json
{
"main": "expo-router/entry"
}La clé main dit quel fichier lancer en premier. Ici ce n'est pas un fichier de l'app, mais expo-router lui-même. Il n'y a donc pas de App.tsx : expo-router va lire le dossier src/app/ et construire la navigation tout seul.
Étape 2 : app.json
Ce fichier configure l'application native :
| Clé | Valeur | Effet |
|---|---|---|
name | TimeManager | Le nom sous l'icône |
scheme | timemanagermobile | Les liens timemanagermobile://... ouvrent l'app |
orientation | portrait | Pas de rotation |
userInterfaceStyle | light | Toujours en clair (la maquette n'a pas de mode sombre) |
icon | assets/images/icon.png | L'icône iOS et par défaut |
android.adaptiveIcon | trois images + fond blanc | L'icône Android qui s'adapte au thème du téléphone |
plugins | expo-router, expo-splash-screen, expo-localization, expo-secure-store, expo-sharing | Configuration native de chaque module |
experiments.typedRoutes | true | router.push('/xxx') est vérifié par TypeScript |
experiments.reactCompiler | true | React optimise automatiquement les rendus |
Le splash (écran de lancement) a un fond #f5f5f4 et le logo de 120 px au centre.
Étape 3 : src/app/_layout.tsx
C'est le premier fichier de notre code qui s'exécute. Il enveloppe toute l'application.
3.1 Les langues d'abord
import '@/i18n';Cette ligne est la toute première. Elle configure i18next avec la langue du téléphone, pour que n'importe quel texte affiché ensuite soit déjà traduit.
3.2 Garder le splash
SplashScreen.preventAutoHideAsync();Sans ça, le splash disparaîtrait tout de suite et on verrait un écran blanc pendant le chargement.
3.3 Les providers
export default function RootLayout() {
return (
<SafeAreaProvider>
<OnboardingProvider>
<SessionProvider>
<DirectoryProvider>
<ToastProvider>
<RootStack />
</ToastProvider>
</DirectoryProvider>
</SessionProvider>
</OnboardingProvider>
</SafeAreaProvider>
);
}L'ordre n'est pas au hasard : un provider peut utiliser ceux qui l'entourent. DirectoryProvider a besoin de savoir qui est connecté pour décider quoi charger, il est donc dans SessionProvider.
Dès leur création :
OnboardingProviderlit dans SecureStore si la présentation a été vue ;SessionProviderlit le jeton CSRF enregistré et, s'il existe, demande/auth/mepour retrouver l'utilisateur ;DirectoryProviderattend qu'il y ait un utilisateur, puis charge les utilisateurs et les équipes si c'est un manager ou le GM.
3.4 Attendre que tout soit prêt
function RootStack() {
const { ready, user } = useSession();
const onboarding = useOnboarding();
const [fontsLoaded, fontError] = useFonts(fontAssets);
const loaded = ready && onboarding.ready && (fontsLoaded || fontError);
useEffect(() => {
if (loaded) SplashScreen.hideAsync();
}, [loaded]);
if (!loaded) return null;
// ...
}Trois choses doivent être prêtes : la session (on sait si quelqu'un est connecté), la présentation (on sait si elle a été vue) et les polices. Tant que ce n'est pas le cas, RootStack n'affiche rien et le splash reste. Dès que tout est prêt, le splash disparaît.
Si les polices n'arrivent pas à se charger (fontError), l'app démarre quand même avec la police du système.
3.5 Choisir les écrans autorisés
<Stack>
<Stack.Protected guard={!!user}>
<Stack.Screen name="(tabs)" />
</Stack.Protected>
<Stack.Protected guard={!user && onboarding.seen}>
<Stack.Screen name="sign-in" />
<Stack.Screen name="forgot-password" />
</Stack.Protected>
<Stack.Protected guard={!!user || !onboarding.seen}>
<Stack.Screen name="onboarding" />
</Stack.Protected>
<Stack.Screen name="reset-password" />
<Stack.Screen name="set-password" />
<Stack.Screen name="account-deleted" />
<Stack.Screen name="confirm-email" />
</Stack>Un Stack.Protected rend ses écrans inaccessibles quand sa condition (guard) est fausse. expo-router ouvre alors le premier écran autorisé.
| Situation | Écrans autorisés | Ce qu'on voit |
|---|---|---|
| Premier lancement, personne de connecté | onboarding, écrans publics | La présentation |
| Présentation vue, personne de connecté | sign-in, forgot-password, écrans publics | La connexion |
| Connecté | (tabs), onboarding, écrans publics | L'onglet Clock |
Les quatre derniers écrans sont toujours accessibles : on y arrive par un lien reçu par mail, connecté ou non.
Pas de redirection à la main
Quand la connexion réussit, user passe de null à un objet. La condition !!user devient vraie, celle de sign-in devient fausse, et expo-router bascule tout seul vers les onglets. Pareil à la déconnexion. Aucun écran ne fait router.replace('/sign-in') après un login ou un logout.
Étape 4 : src/app/(tabs)/_layout.tsx
Une fois connecté, on entre dans le groupe (tabs). Son layout crée la barre d'onglets et déclare les 24 écrans internes. Voir Navigation.