Aller au contenu

Navigation

L'arbre des navigateurs, les noms de routes, la garde d'authentification et les conventions de navigation.

Stack

React Navigation v7. Navigateurs utilisés :

Package Rôle
@react-navigation/native NavigationContainer
@react-navigation/native-stack toutes les stacks (createNativeStackNavigator)
@react-navigation/bottom-tabs la barre d'onglets racine
@react-navigation/material-top-tabs les onglets dans l'écran de détail Projet
@react-navigation/stack types uniquement (pour StackNavigationProp) ; aucun createStackNavigator n'est instancié

Pas de drawer navigator. enableScreens(false) est appelé dans src/App.tsx (native screens désactivés).

Où il est monté

L'unique <NavigationContainer> vit dans src/components/Navigation/NavigationProvider.tsx, monté en bas de l'arbre des providers (voir architecture.md). Deux factories sont créées au niveau du module :

const Tab = createBottomTabNavigator();
export const Stack = createNativeStackNavigator();  // réutilisée par les sous-stacks partagées commonScreen

Arbre des navigateurs

NavigationContainer
└── Tab.Navigator (bottom-tabs, headerShown:false)          NavigationProvider.tsx
    │   tabBar masquée quand il n'y a pas de token.access_token
    ├── Home                → HomeController
    ├── ProjectsStack       → ProjectsStackScreen   (rend UnregistrerStackScreen si pas de token)
    │   ├── ProjectsTab      → ProjectsTabController
    │   ├── ProjectDetail    → ProjectScreenTabParent
    │   │     └── Tab.Navigator (material-top-tabs)          ProjectScreenTabParent.tsx
    │   │         ├── ProjectSensorsTab
    │   │         ├── ProjectPlansTab
    │   │         └── ProjectParametersTab   (→ UnitPreference, ShareProject, Thresholds, Alerts…)
    │   ├── AddProjectsScreen / EditProjectScreen
    │   ├── AddModule / AddTechnicalModule / AddDeformationModuleForm / AddModuleSensor
    │   ├── SensorMecaDetails / AddMeasure / AddPlan / PlanScreen
    │   ├── AddSensorStack / ElecSensorStack / GatewayStack   (sous-stacks partagées imbriquées, ci-dessous)
    │   ├── ListGatewaySensors / ScanGateways / SaveGateway
    │   ├── SyncManager
    │   ├── ModuleDetail / ModuleConfiguration / ModuleThresholds / …
    │   ├── ModuleSensorsResync              (presentation: 'modal')
    │   ├── ModuleSensorsChangeInterval      (presentation: 'transparentModal', fade)
    │   ├── ModulePutSensorsInStock          (transparentModal, fade)
    │   ├── ModuleSensorSAVRepport           (transparentModal, fade)
    │   ├── ModuleExportCsv                  (transparentModal, fade)
    │   ├── Map                              (presentation: 'modal')
    │   ├── Archive* / ProjectThresholds / ShareProject / ProjectAlerts
    │   └── UnitPreference                   (feature : features/unitPreference)
    │
    ├── SensorsStack        → SoleSensorsStackScreen (rend UnregistrerStackScreen si pas de token)
    │   ├── SoleSensors      → SoleSensorController
    │   ├── AddSensor / ScanSensors / SaveSensor / AddSoleSensorToProject
    │   ├── AddSensorStack / ElecSensorStack   (sous-stacks partagées imbriquées)
    │   ├── SyncManager
    │   └── Map                              (presentation: 'modal')
    │
    └── [4e onglet conditionnel]
        ├── (token)  MyAccountStack   → MyAccountStackScreen
        │   ├── MyAccount / MyInformations / MyNotifications / MyPreferences
        │   ├── Wallet / BuyFeatures / Cart
        │   ├── OurOffers / OffersMyCart / MyCurrentOffers
        │   ├── ValidateCode / PurchaseToken / HelpContact / SyncManager
        │   └── Swap                         (presentation: 'modal')
        │
        └── (pas de token) UnregistrerStack → UnregistrerStackScreen
            ├── Welcome  (headerShown:false)
            ├── Register / Login / ForgotPassword
            └── AddSuccess

Native-stacks imbriquées partagées (construites depuis la factory Stack exportée, headerShown:false) :
  AddSensorStack   → AddSensor, ScanSensors, SaveSensor, ScanGateways, SaveGateway
  ElecSensorStack  → SensorElecDetail, SensorConfiguration, SensorConfigureThresholds,
                     SensorDistantActions, LiveView, ActionsPage
  GatewayStack     → GatewayDetail, GatewayConfiguration, GatewayPlannedActions, ListGatewaySensors

Warning

Certains noms de routes se répètent entre stacks (SyncManager, Map, AddSensor, ScanSensors, SaveGateway, AddSuccess). La résolution est relative au navigateur auquel appartient l'écran appelant.

Listes de paramètres typées

Les listes de paramètres vivent dans src/components/Navigation/types/ (alias NavigationTypes), un fichier par stack :

Fichier Liste(s) de paramètres
mainNav.ts RootStackParamList (niveau onglets) + RootNavigationProp
Projects.ts ProjectsStackParamList (~40 routes)
Sensors.ts SoleSensorsStackParamList
MyAccount.ts UnregisterStackParamList + RegisterStackParamList
commonScreen/{AddSensor,ElecSensor,Gateway}.ts les trois sous-stacks partagées
theme.ts hook useAppTheme() (pas une liste de paramètres)
// mainNav.ts
type RootStackParamList = {
  Home: undefined;
  ProjectsStack: NavigatorScreenParams<ProjectsStackParamList>;
  SensorsStack: NavigatorScreenParams<SoleSensorsStackParamList>;
  MyAccountStack: NavigatorScreenParams<RegisterStackParamList>;
  UnregistrerStack: NavigatorScreenParams<UnregisterStackParamList>;
};

Convention par stack : export type XStackNavigationProp = StackNavigationProp<XStackParamList> et un alias RouteProp par écran. Les écrans les consomment via des hooks typés :

const navigation = useNavigation<ProjectsStackNavigationProp>();
const route = useRoute<ProjectDetailScreenRouteProp>();

Warning

Les navigateurs eux-mêmes sont créés non typés (createNativeStackNavigator() sans générique), donc les types de paramètres ne sont imposés qu'aux sites d'appel via les génériques du hook — pas à l'enregistrement des <Stack.Screen>. Attention à la dérive (quelques routes typées n'ont pas d'écran enregistré et inversement).

Garde d'authentification

Il n'y a aucun basculement impératif de navigateur à la connexion — l'authentification est purement réactive sur le token Redux (store/ducks/token, persisté). Chaque garde vérifie !isNil(token?.access_token) :

  • NavigationProvider masque la barre d'onglets et échange le 4e onglet (MyAccountStack vs UnregistrerStack).
  • ProjectsStackScreen / SoleSensorsStackScreen rendent <UnregistrerStackScreen /> quand il n'y a pas de token.
  • Login.tsx dispatche setToken(...) ; le changement Redux re-rend les gardes et révèle l'UI authentifiée (pas de navigation.reset).

Modales & overlays

Pas de navigateur de modale dédié — les modales utilisent les options presentation de native-stack :

  • presentation: 'modal'Map, ModuleSensorsResync, Swap.
  • presentation: 'transparentModal' + animation: 'fade' — les overlays Module*.

Les overlays globaux sont rendus comme frères du navigateur à l'intérieur de NavigationProvider (pas des écrans) : FirmwareUpdate, MessageAlert, HandleSyncMutationOnline, InfoDialog, LoadingAlert, plus le Portal.Host de Paper.

Deep linking

Aucun configuré. <NavigationContainer> n'a pas de prop linking. Le seul Linking.openURL est la redirection de mise à jour du store dans components/VersionChecker. Les notifications push Firebase ne routent pas vers la navigation.

Conventions

  • Le hook useNavigation() est le pattern principal (forme générique typée). Aucun service/ref de navigation personnalisé n'existe.
  • La ref du NavigationContainer sert uniquement à enregistrer l'instrumentation de routing Sentry (Sentry.reactNavigationIntegration()), pas à de la navigation impérative.
  • La navigation inter-stacks utilise la forme objet : navigation.navigate('ProjectsStack', { screen: 'ProjectsTab' }).
  • Le flux « succès » après ajout utilise CommonActions.reset pour atterrir sur l'écran de détail du nouvel appareil.
  • La plupart des stacks authentifiées injectent <SyncManagerButton /> en headerRight via GET_SCREEN_OPTIONS(colors) (Stack/constant.ts) ; MyAccountStack préfixe aussi <CartButton />.
  • Les onglets inactifs sont démontés de force via le wrapper de layout UnmountOnBlur.
  • Le thème des en-têtes découle de useAppTheme() + themeMode Redux, combinant les thèmes Paper et React Navigation.

Fichiers clés

  • src/components/Navigation/NavigationProvider.tsx — container + onglets
  • src/components/Navigation/Stack/*StackScreen.tsx — les quatre stacks principales
  • src/components/Navigation/Stack/commonScreen/*.tsx — sous-stacks partagées
  • src/components/Navigation/Stack/constant.ts — options d'écran partagées
  • src/components/Navigation/types/* — listes de paramètres (alias NavigationTypes)