Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ This project requires **Node.js 22.x**. Use the included `.nvmrc` to match the v
Run `yarn install` to bootstrap dependencies and husky hooks.
Gitleaks will scan staged changes on each commit via `yarn gitleaks`.

### Debugging screens

Set `DEBUG_SCREEN` to preload and open a specific screen on startup:

```bash
DEBUG_SCREEN=PassportOnboarding yarn start
```

This bypasses lazy loading for the chosen screen, making it easier to debug in isolation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Fix outdated DEBUG_SCREEN example (route was renamed).

Example still points to PassportOnboarding; navigation now uses Document* naming. Update the example to the current route key to avoid confusion.

Proposed change:

-DEBUG_SCREEN=PassportOnboarding yarn start
+DEBUG_SCREEN=DocumentOnboarding yarn start

Run to confirm the actual route key:


🏁 Script executed:

#!/bin/bash
# Find candidate route names
rg -nP --type=ts --type=tsx -C2 '\b(DocumentOnboarding|PassportOnboarding)\b|DEBUG_SCREEN'

Length of output: 120


Here’s a refined set of searches to pinpoint the current debug‐screen key and route definitions:


🏁 Script executed:

#!/bin/bash
# 1. Locate all DEBUG_SCREEN usages (in MD or TS/TSX)
rg -n 'DEBUG_SCREEN' -g '*.md' -g '*.ts' -g '*.tsx' -C2

# 2. Find the navigation parameter list that defines available routes
rg -n 'RootStackParamList' -g '*.ts' -g '*.tsx' -C3

# 3. Search for any Onboarding routes (Passport vs. Document)
rg -n 'Onboarding' -g '*.ts' -g '*.tsx' -C2

Length of output: 21535


Update README debug example to use DocumentOnboarding route
In README.md (around line 80), replace

-DEBUG_SCREEN=PassportOnboarding yarn start
+DEBUG_SCREEN=DocumentOnboarding yarn start

to reflect the current navigation key.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Debugging screens
Set `DEBUG_SCREEN` to preload and open a specific screen on startup:
```bash
DEBUG_SCREEN=PassportOnboarding yarn start
```
This bypasses lazy loading for the chosen screen, making it easier to debug in isolation.
### Debugging screens
Set `DEBUG_SCREEN` to preload and open a specific screen on startup:
🤖 Prompt for AI Agents
In README.md around lines 75 to 83, the DEBUG_SCREEN example uses the outdated
route name "PassportOnboarding"; update the example to use the current
navigation key "DocumentOnboarding" instead so the command reads
DEBUG_SCREEN=DocumentOnboarding yarn start and matches the app's current routes.


## Development Documentation

For detailed development patterns and conventions, see:
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/NavBar/HomeNavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const HomeNavBar = (props: NativeStackHeaderProps) => {
try {
Clipboard.setString('');
} catch {}
props.navigation.navigate('ProveScreen');
props.navigation.navigate('Prove');
} catch (error) {
console.error('Error consuming token:', error);
if (
Expand Down
2 changes: 1 addition & 1 deletion app/src/hooks/useModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { useCallback, useRef, useState } from 'react';
import { useNavigation } from '@react-navigation/native';

import type { ModalParams } from '@/screens/misc/ModalScreen';
import type { ModalParams } from '@/screens/system/ModalScreen';
import {
getModalCallbacks,
registerModalCallbacks,
Expand Down
10 changes: 5 additions & 5 deletions app/src/navigation/aesop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import { ProgressNavBar } from '@/components/NavBar';
import { shouldShowAesopRedesign } from '@/hooks/useAesopRedesign';
import { white } from '@/utils/colors';

const PassportOnboardingScreen = lazy(
() => import('@/screens/aesop/PassportOnboardingScreen'),
const DocumentOnboardingScreen = lazy(
() => import('@/screens/aesop/DocumentOnboardingScreen'),
);

const aesopScreens = {
PassportOnboarding: {
screen: PassportOnboardingScreen,
DocumentOnboarding: {
screen: DocumentOnboardingScreen,
options: {
animation: 'slide_from_bottom',
header: ProgressNavBar,
title: 'Scan your passport',
title: 'Scan your document',
headerStyle: {
backgroundColor: white,
},
Expand Down
49 changes: 22 additions & 27 deletions app/src/navigation/devTools.ts → app/src/navigation/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,34 @@
import { lazy } from 'react';
import type { NativeStackNavigationOptions } from '@react-navigation/native-stack';

// DevPrivateKeyScreen is loaded lazily to avoid bundling in production
import { black, white } from '@/utils/colors';

const DevFeatureFlagsScreen = lazy(
() => import('@/screens/dev/DevFeatureFlagsScreen'),
);
const DevFeatureFlagsScreen = lazy(() => import('@/screens/dev/feature-flags'));
const DevHapticFeedbackScreen = lazy(
() => import('@/screens/dev/DevHapticFeedback'),
);
const DevSettingsScreen = lazy(() => import('@/screens/dev/DevSettingsScreen'));
const MockDataScreen = lazy(() => import('@/screens/dev/MockDataScreen'));
const MockDataScreenDeepLink = lazy(
() => import('@/screens/dev/MockDataScreenDeepLink'),
() => import('@/screens/dev/haptic-feedback'),
);
const DevPrivateKeyScreen = lazy(
() => import('@/screens/dev/DevPrivateKeyScreen'),
const DevSettingsScreen = lazy(() => import('@/screens/dev/settings'));
const CreateMockScreen = lazy(() => import('@/screens/dev/create-mock'));
const CreateMockScreenDeepLink = lazy(
() => import('@/screens/dev/create-mock/CreateMockScreenDeepLink'),
);
const DevPrivateKeyScreen = lazy(() => import('@/screens/dev/private-key'));

const devHeaderOptions: NativeStackNavigationOptions = {
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: white,
},
headerBackTitle: 'close',
};
Comment on lines +22 to +30
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Back arrow and icon tint may be invisible on dark headers

You set a black header and white title but didn’t set headerTintColor, so back chevron and header buttons may be low-contrast depending on theme/platform. Set headerTintColor to white in the shared options.

 const devHeaderOptions: NativeStackNavigationOptions = {
   headerStyle: {
     backgroundColor: black,
   },
   headerTitleStyle: {
     color: white,
   },
+  headerTintColor: white,
   headerBackTitle: 'close',
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const devHeaderOptions: NativeStackNavigationOptions = {
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: white,
},
headerBackTitle: 'close',
};
const devHeaderOptions: NativeStackNavigationOptions = {
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: white,
},
headerTintColor: white,
headerBackTitle: 'close',
};
🤖 Prompt for AI Agents
In app/src/navigation/dev.ts around lines 21 to 29, the shared devHeaderOptions
sets a black header and white title but omits headerTintColor so the back
chevron and header buttons can be low-contrast; update devHeaderOptions to
include headerTintColor: white so icons and back arrow are tinted white and
remain visible against the dark header.


const devScreens = {
CreateMock: {
screen: MockDataScreen,
screen: CreateMockScreen,
options: {
...devHeaderOptions,
title: 'Mock Document',
headerStyle: {
backgroundColor: black,
Expand All @@ -37,7 +43,7 @@ const devScreens = {
} as NativeStackNavigationOptions,
},
MockDataDeepLink: {
screen: MockDataScreenDeepLink,
screen: CreateMockScreenDeepLink,
options: {
headerShown: false,
} as NativeStackNavigationOptions,
Expand All @@ -51,14 +57,8 @@ const devScreens = {
DevSettings: {
screen: DevSettingsScreen,
options: {
...devHeaderOptions,
title: 'Dev Mode',
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: white,
},
headerBackTitle: 'close',
} as NativeStackNavigationOptions,
},
DevFeatureFlags: {
Expand All @@ -73,13 +73,8 @@ const devScreens = {
DevPrivateKey: {
screen: DevPrivateKeyScreen,
options: {
...devHeaderOptions,
title: 'Private Key',
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: white,
},
} as NativeStackNavigationOptions,
},
};
Expand Down
60 changes: 30 additions & 30 deletions app/src/navigation/passport.ts → app/src/navigation/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,46 @@
import { lazy } from 'react';
import type { NativeStackNavigationOptions } from '@react-navigation/native-stack';

const PassportCameraScreen = lazy(
() => import('@/screens/passport/PassportCameraScreen'),
const DocumentCameraScreen = lazy(
() => import('@/screens/document/DocumentCameraScreen'),
);
const PassportCameraTrouble = lazy(
() => import('@/screens/passport/PassportCameraTroubleScreen'),
const DocumentCameraTroubleScreen = lazy(
() => import('@/screens/document/DocumentCameraTroubleScreen'),
);
const PassportNFCScanScreen = lazy(
() => import('@/screens/passport/PassportNFCScanScreen'),
const DocumentNFCScanScreen = lazy(
() => import('@/screens/document/DocumentNFCScanScreen'),
);
const PassportNFCTrouble = lazy(
() => import('@/screens/passport/PassportNFCTroubleScreen'),
const DocumentNFCTroubleScreen = lazy(
() => import('@/screens/document/DocumentNFCTroubleScreen'),
);
const PassportOnboardingScreen = lazy(
() => import('@/screens/passport/PassportOnboardingScreen'),
const DocumentOnboardingScreen = lazy(
() => import('@/screens/document/DocumentOnboardingScreen'),
);
const UnsupportedPassportScreen = lazy(
() => import('@/screens/passport/UnsupportedPassportScreen'),
const UnsupportedDocumentScreen = lazy(
() => import('@/screens/document/UnsupportedDocumentScreen'),
);
const NFCMethodSelectionScreen = lazy(
() => import('@/screens/passport/NFCMethodSelectionScreen'),
const DocumentNFCMethodSelectionScreen = lazy(
() => import('@/screens/document/DocumentNFCMethodSelectionScreen'),
);

const passportScreens = {
PassportCamera: {
screen: PassportCameraScreen,
const documentScreens = {
DocumentCamera: {
screen: DocumentCameraScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
} as NativeStackNavigationOptions,
},
PassportCameraTrouble: {
screen: PassportCameraTrouble,
DocumentCameraTrouble: {
screen: DocumentCameraTroubleScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
presentation: 'modal',
} as NativeStackNavigationOptions,
},
PassportNFCScan: {
screen: PassportNFCScanScreen,
DocumentNFCScan: {
screen: DocumentNFCScanScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
Expand All @@ -55,38 +55,38 @@ const passportScreens = {
dateOfExpiry: '',
},
},
PassportNFCTrouble: {
screen: PassportNFCTrouble,
DocumentNFCTrouble: {
screen: DocumentNFCTroubleScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
presentation: 'modal',
} as NativeStackNavigationOptions,
},
PassportOnboarding: {
screen: PassportOnboardingScreen,
DocumentOnboarding: {
screen: DocumentOnboardingScreen,
options: {
animation: 'slide_from_bottom',
// presentation: 'modal' wanted to do this but seems to break stuff
headerShown: false,
} as NativeStackNavigationOptions,
},
UnsupportedPassport: {
screen: UnsupportedPassportScreen,
UnsupportedDocument: {
screen: UnsupportedDocumentScreen,
options: {
headerShown: false,
} as NativeStackNavigationOptions,
initialParams: {
passportData: null,
},
},
PassportNFCMethodSelection: {
screen: NFCMethodSelectionScreen,
DocumentNFCMethodSelection: {
screen: DocumentNFCMethodSelectionScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
} as NativeStackNavigationOptions,
},
};

export default passportScreens;
export default documentScreens;
10 changes: 5 additions & 5 deletions app/src/navigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { DefaultNavBar } from '@/components/NavBar';
import AppLayout from '@/layouts/AppLayout';
import { getAesopScreens } from '@/navigation/aesop';
import devScreens from '@/navigation/devTools';
import devScreens from '@/navigation/dev';
import documentScreens from '@/navigation/document';
import homeScreens from '@/navigation/home';
import miscScreens from '@/navigation/misc';
import passportScreens from '@/navigation/passport';
import proveScreens from '@/navigation/prove';
import recoveryScreens from '@/navigation/recovery';
import settingsScreens from '@/navigation/settings';
import systemScreens from '@/navigation/system';
import analytics from '@/utils/analytics';
import { white } from '@/utils/colors';
import { setupUniversalLinkListenerInNavigation } from '@/utils/deeplinks';

export const navigationScreens = {
...miscScreens,
...passportScreens,
...systemScreens,
...documentScreens,
...homeScreens,
...proveScreens,
...settingsScreens,
Expand Down
19 changes: 19 additions & 0 deletions app/src/navigation/lazyWithPreload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
// SPDX-License-Identifier: BUSL-1.1
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.

import React from 'react';

// Helper around React.lazy that exposes the underlying dynamic import
// so callers can manually preload a screen when debugging or profiling.
// Prefer using React.lazy directly and opt into this only when you need
// to eagerly load a component.
export function lazyWithPreload<T extends React.ComponentType<any>>(

Check warning on line 11 in app/src/navigation/lazyWithPreload.ts

View workflow job for this annotation

GitHub Actions / build-deps

Unexpected any. Specify a different type
factory: () => Promise<{ default: T }>,
) {
Comment on lines +11 to +13
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove any and add explicit return type for stronger type-safety

Eliminate any, align with static analysis, and make the API surface explicit. Also type preload via typeof factory to prevent drift.

-export function lazyWithPreload<T extends React.ComponentType<any>>(
-  factory: () => Promise<{ default: T }>,
-) {
+export function lazyWithPreload<T extends ComponentType<unknown>>(
+  factory: () => Promise<{ default: T }>,
+): LazyExoticComponent<T> & { preload: typeof factory } {

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Check: build-deps

[warning] 11-11:
Unexpected any. Specify a different type

const Component = React.lazy(factory) as React.LazyExoticComponent<T> & {

Check warning on line 14 in app/src/navigation/lazyWithPreload.ts

View workflow job for this annotation

GitHub Actions / build-deps

Caution: `React` also has a named export `lazy`. Check if you meant to write `import {lazy} from 'react'` instead
preload: () => Promise<{ default: T }>;
};
Component.preload = factory;
return Component;
}
8 changes: 4 additions & 4 deletions app/src/navigation/prove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,24 @@ const QRCodeTroubleScreen = lazy(
() => import('@/screens/prove/QRCodeTroubleScreen'),
);
const QRCodeViewFinderScreen = lazy(
() => import('@/screens/prove/ViewFinderScreen'),
() => import('@/screens/prove/QRCodeViewFinderScreen'),
);

const proveScreens = {
ConfirmBelongingScreen: {
ConfirmBelonging: {
screen: ConfirmBelongingScreen,
options: {
headerShown: false,
} as NativeStackNavigationOptions,
},
ProofRequestStatusScreen: {
ProofRequestStatus: {
screen: ProofRequestStatusScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
} as NativeStackNavigationOptions,
},
ProveScreen: {
Prove: {
screen: ProveScreen,
options: {
title: 'Request Proof',
Expand Down
8 changes: 4 additions & 4 deletions app/src/navigation/recovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const AccountRecoveryScreen = lazy(
const AccountVerifiedSuccessScreen = lazy(
() => import('@/screens/recovery/AccountVerifiedSuccessScreen'),
);
const PassportDataNotFound = lazy(
() => import('@/screens/recovery/PassportDataNotFoundScreen'),
const DocumentDataNotFound = lazy(
() => import('@/screens/recovery/DocumentDataNotFoundScreen'),
);
Comment on lines +19 to 21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Preload recovery fallback screen and verify route/key propagation

  • Swap to lazyWithPreload so DEBUG_SCREEN or error paths can warm this screen.
  • Check that linking and tests reference DocumentDataNotFound everywhere.

Apply within these hunks:

-const DocumentDataNotFound = lazy(
+const DocumentDataNotFound = lazyWithPreload(
  () => import('@/screens/recovery/DocumentDataNotFoundScreen'),
);

Add the helper import:

import { lazyWithPreload } from '@/utils/lazyWithPreload';

Quick checks:

Also applies to: 49-57


🏁 Script executed:

#!/bin/bash
rg -nP "PassportDataNotFound\b|navigate\\(['\"]PassportDataNotFound['\"]|name:\\s*['\"]PassportDataNotFound['\"]" -S

Length of output: 124


🏁 Script executed:

#!/bin/bash
rg -nP "DocumentDataNotFound\b|navigate\(['\"]DocumentDataNotFound['\"]\)|name:\s*['\"]DocumentDataNotFound['\"]" -S

Length of output: 699


Preload recovery fallback screens

  • Replace lazy with lazyWithPreload for DocumentDataNotFound in both
    app/src/navigation/recovery.ts (lines 19, 49–57)
    app/src/navigation/recovery.web.ts (lines 8, 13–14)
  • Add import { lazyWithPreload } from '@/utils/lazyWithPreload'; to both files
  • No changes needed for tests (app/tests/src/navigation.test.ts) or provingMachine.ts, which already reference DocumentDataNotFound
🤖 Prompt for AI Agents
In app/src/navigation/recovery.ts (around lines 19 and 49–57) and
app/src/navigation/recovery.web.ts (around lines 8 and 13–14), replace the use
of React.lazy for the DocumentDataNotFound screen with the project's
lazyWithPreload helper: add the import "import { lazyWithPreload } from
'@/utils/lazyWithPreload';" at the top of both files, change the
DocumentDataNotFound lazy initialization to use lazyWithPreload(...) instead of
lazy(...), and ensure exported references remain the same; do not modify tests
or provingMachine.ts.

const RecoverWithPhraseScreen = lazy(
() => import('@/screens/recovery/RecoverWithPhraseScreen'),
Expand Down Expand Up @@ -46,8 +46,8 @@ const recoveryScreens = {
animation: 'slide_from_bottom',
} as NativeStackNavigationOptions,
},
PassportDataNotFound: {
screen: PassportDataNotFound,
DocumentDataNotFound: {
screen: DocumentDataNotFound,
options: {
headerShown: false,
gestureEnabled: false,
Expand Down
8 changes: 4 additions & 4 deletions app/src/navigation/recovery.web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
import { lazy } from 'react';
import type { NativeStackNavigationOptions } from '@react-navigation/native-stack';

const PassportDataNotFound = lazy(
() => import('@/screens/recovery/PassportDataNotFoundScreen'),
const DocumentDataNotFound = lazy(
() => import('@/screens/recovery/DocumentDataNotFoundScreen'),
);
Comment on lines +8 to 10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Web parity: consider lazyWithPreload here too

Keep recovery.web.ts consistent with native for predictable chunking/preload behavior.

-const DocumentDataNotFound = lazy(
+const DocumentDataNotFound = lazyWithPreload(
  () => import('@/screens/recovery/DocumentDataNotFoundScreen'),
);

Add:

import { lazyWithPreload } from '@/utils/lazyWithPreload';

Also applies to: 13-15

🤖 Prompt for AI Agents
In app/src/navigation/recovery.web.ts around lines 8-10 and 13-15, the file uses
React.lazy for screens but the native code uses lazyWithPreload; add the import
"import { lazyWithPreload } from '@/utils/lazyWithPreload';" near other imports
and replace React.lazy calls for the listed screens with lazyWithPreload so the
web build matches native chunking/preload behavior and supports preloading the
same way.


const recoveryScreens = {
PassportDataNotFound: {
screen: PassportDataNotFound,
DocumentDataNotFound: {
screen: DocumentDataNotFound,
options: {
headerShown: false,
gestureEnabled: false,
Expand Down
Loading
Loading