SELF-702: Refactor navigation structure and dev utilities (#994)

* Refactor navigation and dev screens

* refactor: rename passport screens to document

* fixes

* add missing header

* fixes

* type files
This commit is contained in:
Justin Hernandez
2025-08-30 21:00:21 -07:00
committed by GitHub
parent e3b5e2ebe4
commit 520c05d4fc
47 changed files with 220 additions and 333 deletions

View File

@@ -83,7 +83,7 @@ class MockFirebaseRemoteConfig implements RemoteConfigBackend {
getAll(): Record<string, RemoteConfigValue> {
const result: Record<string, RemoteConfigValue> = {};
for (const [key, value] of Object.entries(this.config)) {
for (const [key, _value] of Object.entries(this.config)) {
result[key] = this.getValue(key);
}
return result;

View File

@@ -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 (

View File

@@ -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,

View File

@@ -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,
},

View File

@@ -5,28 +5,35 @@
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 DevHapticFeedbackScreen = lazy(
() => import('@/screens/dev/DevHapticFeedback'),
() => import('@/screens/dev/DevHapticFeedbackScreen'),
);
const DevSettingsScreen = lazy(() => import('@/screens/dev/DevSettingsScreen'));
const MockDataScreen = lazy(() => import('@/screens/dev/MockDataScreen'));
const MockDataScreenDeepLink = lazy(
() => import('@/screens/dev/MockDataScreenDeepLink'),
);
const DevPrivateKeyScreen = lazy(
() => import('@/screens/dev/DevPrivateKeyScreen'),
const CreateMockScreen = lazy(() => import('@/screens/dev/CreateMockScreen'));
const CreateMockScreenDeepLink = lazy(
() => import('@/screens/dev/CreateMockScreenDeepLink'),
);
const devHeaderOptions: NativeStackNavigationOptions = {
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: white,
},
headerBackTitle: 'close',
};
const devScreens = {
CreateMock: {
screen: MockDataScreen,
screen: CreateMockScreen,
options: {
...devHeaderOptions,
title: 'Mock Document',
headerStyle: {
backgroundColor: black,
@@ -37,7 +44,7 @@ const devScreens = {
} as NativeStackNavigationOptions,
},
MockDataDeepLink: {
screen: MockDataScreenDeepLink,
screen: CreateMockScreenDeepLink,
options: {
headerShown: false,
} as NativeStackNavigationOptions,
@@ -51,14 +58,8 @@ const devScreens = {
DevSettings: {
screen: DevSettingsScreen,
options: {
...devHeaderOptions,
title: 'Dev Mode',
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: white,
},
headerBackTitle: 'close',
} as NativeStackNavigationOptions,
},
DevFeatureFlags: {
@@ -70,18 +71,6 @@ const devScreens = {
},
} as NativeStackNavigationOptions,
},
DevPrivateKey: {
screen: DevPrivateKeyScreen,
options: {
title: 'Private Key',
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: white,
},
} as NativeStackNavigationOptions,
},
};
export default devScreens;

View File

@@ -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',
@@ -55,24 +55,24 @@ 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,
@@ -80,8 +80,8 @@ const passportScreens = {
passportData: null,
},
},
PassportNFCMethodSelection: {
screen: NFCMethodSelectionScreen,
DocumentNFCMethodSelection: {
screen: DocumentNFCMethodSelectionScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
@@ -89,4 +89,4 @@ const passportScreens = {
},
};
export default passportScreens;
export default documentScreens;

View File

@@ -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,

View File

@@ -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 { lazy, type LazyExoticComponent } 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>>(
factory: () => Promise<{ default: T }>,
) {
const Component = lazy(factory) as LazyExoticComponent<T> & {
preload: () => Promise<{ default: T }>;
};
Component.preload = factory;
return Component;
}

View File

@@ -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',

View File

@@ -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'),
);
const RecoverWithPhraseScreen = lazy(
() => import('@/screens/recovery/RecoverWithPhraseScreen'),
@@ -46,8 +46,8 @@ const recoveryScreens = {
animation: 'slide_from_bottom',
} as NativeStackNavigationOptions,
},
PassportDataNotFound: {
screen: PassportDataNotFound,
DocumentDataNotFound: {
screen: DocumentDataNotFound,
options: {
headerShown: false,
gestureEnabled: false,

View File

@@ -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'),
);
const recoveryScreens = {
PassportDataNotFound: {
screen: PassportDataNotFound,
DocumentDataNotFound: {
screen: DocumentDataNotFound,
options: {
headerShown: false,
gestureEnabled: false,

View File

@@ -13,8 +13,8 @@ const CloudBackupScreen = lazy(
const ManageDocumentsScreen = lazy(
() => import('@/screens/settings/ManageDocumentsScreen'),
);
const PassportDataInfoScreen = lazy(
() => import('@/screens/settings/PassportDataInfoScreen'),
const DocumentDataInfoScreen = lazy(
() => import('@/screens/settings/DocumentDataInfoScreen'),
);
const SettingsScreen = lazy(() => import('@/screens/settings/SettingsScreen'));
const ShowRecoveryPhraseScreen = lazy(
@@ -46,10 +46,10 @@ const settingsScreens = {
},
} as NativeStackNavigationOptions,
},
PassportDataInfo: {
screen: PassportDataInfoScreen,
DocumentDataInfo: {
screen: DocumentDataInfoScreen,
options: {
title: 'Passport Data Info',
title: 'Document Data Info',
headerStyle: {
backgroundColor: white,
},

View File

@@ -10,8 +10,8 @@ import { black, white } from '@/utils/colors';
const ManageDocumentsScreen = lazy(
() => import('@/screens/settings/ManageDocumentsScreen'),
);
const PassportDataInfoScreen = lazy(
() => import('@/screens/settings/PassportDataInfoScreen'),
const DocumentDataInfoScreen = lazy(
() => import('@/screens/settings/DocumentDataInfoScreen'),
);
const SettingsScreen = lazy(() => import('@/screens/settings/SettingsScreen'));
@@ -28,10 +28,10 @@ const settingsScreens = {
},
} as NativeStackNavigationOptions,
},
PassportDataInfo: {
screen: PassportDataInfoScreen,
DocumentDataInfo: {
screen: DocumentDataInfoScreen,
options: {
title: 'Passport Data Info',
title: 'Document Data Info',
headerStyle: {
backgroundColor: white,
},

View File

@@ -9,17 +9,17 @@ import type { NativeStackNavigationOptions } from '@react-navigation/native-stac
// Important: SplashScreen is imported directly and not lazy-loaded.
// This is because it's used as a fallback for the Suspense boundary in the root navigator,
// ensuring it's immediately available at startup.
import SplashScreen from '@/screens/misc/SplashScreen';
import SplashScreen from '@/screens/system/SplashScreen';
import { black } from '@/utils/colors';
const LaunchScreen = lazy(() => import('@/screens/misc/LaunchScreen'));
const LoadingScreen = lazy(() => import('@/screens/misc/LoadingScreen'));
const ModalScreen = lazy(() => import('@/screens/misc/ModalScreen'));
const LaunchScreen = lazy(() => import('@/screens/system/LaunchScreen'));
const LoadingScreen = lazy(() => import('@/screens/system/Loading'));
const ModalScreen = lazy(() => import('@/screens/system/ModalScreen'));
const DeferredLinkingInfoScreen = lazy(
() => import('@/screens/misc/DeferredLinkingInfoScreen'),
() => import('@/screens/system/DeferredLinkingInfoScreen'),
);
const miscScreens = {
const systemScreens = {
Launch: {
screen: LaunchScreen,
options: {
@@ -27,7 +27,7 @@ const miscScreens = {
navigationBarColor: black,
},
},
LoadingScreen: {
Loading: {
screen: LoadingScreen,
options: {
headerShown: false,
@@ -59,4 +59,4 @@ const miscScreens = {
},
};
export default miscScreens;
export default systemScreens;

View File

@@ -26,9 +26,9 @@ import Scan from '@/images/icons/passport_camera_scan.svg';
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
import { black, slate100, white } from '@/utils/colors';
const PassportOnboardingScreen: React.FC = () => {
const DocumentOnboardingScreen: React.FC = () => {
const client = useSelfClient();
const handleCameraPress = useHapticNavigation('PassportCamera');
const handleCameraPress = useHapticNavigation('DocumentCamera');
const navigateToLaunch = useHapticNavigation('Launch', {
action: 'cancel',
});
@@ -113,7 +113,7 @@ const PassportOnboardingScreen: React.FC = () => {
);
};
export default PassportOnboardingScreen;
export default DocumentOnboardingScreen;
const styles = StyleSheet.create({
animation: {

View File

@@ -156,7 +156,7 @@ const FormSection: React.FC<FormSectionProps> = ({
);
};
const MockDataScreen: React.FC = () => {
const CreateMockScreen: React.FC = () => {
const { trackEvent } = useSelfClient();
const navigation = useNavigation();
const {
@@ -191,7 +191,7 @@ const MockDataScreen: React.FC = () => {
selectedDocumentType,
});
await storePassportData(parsedMockData);
navigation.navigate('ConfirmBelongingScreen', {});
navigation.navigate('ConfirmBelonging', {});
} catch (error) {
console.error('Error during mock data generation:', error);
} finally {
@@ -689,4 +689,4 @@ const MockDataScreen: React.FC = () => {
);
};
export default MockDataScreen;
export default CreateMockScreen;

View File

@@ -25,7 +25,7 @@ import useUserStore from '@/stores/userStore';
import { black, borderColor, white } from '@/utils/colors';
import { extraYPadding } from '@/utils/constants';
const MockDataScreenDeepLink: React.FC = () => {
const CreateMockScreenDeepLink: React.FC = () => {
const navigation = useNavigation();
const [selectedCountry, setSelectedCountry] = useState('USA');
@@ -56,7 +56,7 @@ const MockDataScreenDeepLink: React.FC = () => {
};
const passportData = genMockIdDocAndInitDataParsing(idDocInput);
await storePassportData(passportData);
navigation.navigate('ConfirmBelongingScreen', {});
navigation.navigate('ConfirmBelonging', {});
useUserStore.getState().clearDeepLinkUserDetails();
}, [navigation]);
@@ -204,4 +204,4 @@ const MockDataScreenDeepLink: React.FC = () => {
);
};
export default MockDataScreenDeepLink;
export default CreateMockScreenDeepLink;

View File

@@ -33,7 +33,7 @@ const StyledButton = styled(Button, {
touchAction: 'manipulation',
});
const DevHapticFeedback = () => {
const DevHapticFeedbackScreen = () => {
const [loadingProgressEnabled, setLoadingProgressEnabled] = useState(true);
return (
@@ -79,4 +79,4 @@ const styles = StyleSheet.create({
},
});
export default DevHapticFeedback;
export default DevHapticFeedbackScreen;

View File

@@ -1,123 +0,0 @@
// 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 { useCallback, useEffect, useState } from 'react';
import { Button, Text, XStack, YStack } from 'tamagui';
import Clipboard from '@react-native-clipboard/clipboard';
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
import { black, slate50, slate200, teal500, white } from '@/utils/colors';
import { confirmTap } from '@/utils/haptic';
const DevPrivateKeyScreen: React.FC = () => {
const [privateKey, setPrivateKey] = useState<string | null>(
'Loading private key…',
);
const [isPrivateKeyRevealed, setIsPrivateKeyRevealed] = useState(false);
const [copied, setCopied] = useState(false);
const selfClient = useSelfClient();
useEffect(() => {
let mounted = true;
selfClient
.getPrivateKey()
.then(key => {
if (!mounted) return;
setPrivateKey(key || 'No private key found');
})
.catch(() => {
if (!mounted) return;
setPrivateKey('No private key found');
});
return () => {
mounted = false;
};
}, [selfClient]);
const handleRevealPrivateKey = useCallback(() => {
confirmTap();
if (!isPrivateKeyRevealed) {
setIsPrivateKeyRevealed(true);
}
if (isPrivateKeyRevealed) {
Clipboard.setString(privateKey || '');
setCopied(true);
setTimeout(() => setCopied(false), 1500);
}
}, [isPrivateKeyRevealed, privateKey]);
const getRedactedPrivateKey = useCallback(() => {
if (
!privateKey ||
privateKey === 'Loading private key…' ||
privateKey === 'No private key found'
) {
return privateKey;
}
// If it starts with 0x, show 0x followed by asterisks for the rest
if (privateKey.startsWith('0x')) {
const restLength = privateKey.length - 2;
return '0x' + '*'.repeat(restLength);
}
// Otherwise, show asterisks for the entire length
return '*'.repeat(privateKey.length);
}, [privateKey]);
return (
<YStack padding="$4">
<YStack position="relative" alignItems="stretch" gap={0}>
<XStack
borderColor={slate200}
backgroundColor={slate50}
borderWidth="$1"
borderBottomWidth={0}
borderTopLeftRadius="$5"
borderTopRightRadius="$5"
gap={12}
paddingHorizontal={26}
paddingVertical={28}
flexWrap="wrap"
>
<Text>
{isPrivateKeyRevealed ? privateKey : getRedactedPrivateKey()}
</Text>
</XStack>
<XStack
borderTopColor={slate200}
borderTopWidth="$1"
justifyContent="center"
alignItems="stretch"
>
<Button
unstyled
color={isPrivateKeyRevealed ? (copied ? black : white) : black}
borderColor={
isPrivateKeyRevealed ? (copied ? teal500 : black) : slate200
}
backgroundColor={
isPrivateKeyRevealed ? (copied ? teal500 : black) : slate50
}
borderWidth="$1"
borderTopWidth={0}
borderBottomLeftRadius="$5"
borderBottomRightRadius="$5"
paddingVertical="$2"
onPress={handleRevealPrivateKey}
width="100%"
textAlign="center"
>
{isPrivateKeyRevealed
? `${copied ? 'COPIED' : 'COPY'} TO CLIPBOARD`
: 'TAP TO REVEAL'}
</Button>
</XStack>
</YStack>
</YStack>
);
};
export default DevPrivateKeyScreen;

View File

@@ -128,28 +128,28 @@ const items = [
'DevHapticFeedback',
'Splash',
'Launch',
'PassportOnboarding',
'PassportCamera',
'PassportNFCScan',
'PassportDataInfo',
'LoadingScreen',
'DocumentOnboarding',
'DocumentCamera',
'DocumentNFCScan',
'DocumentDataInfo',
'Loading',
'AccountVerifiedSuccess',
'ConfirmBelongingScreen',
'ConfirmBelonging',
'CreateMock',
'Home',
'Disclaimer',
'QRCodeViewFinder',
'ProveScreen',
'ProofRequestStatusScreen',
'Prove',
'ProofRequestStatus',
'Settings',
'AccountRecovery',
'SaveRecoveryPhrase',
'RecoverWithPhrase',
'ShowRecoveryPhrase',
'CloudBackupSettings',
'UnsupportedPassport',
'PassportCameraTrouble',
'PassportNFCTrouble',
'UnsupportedDocument',
'DocumentCameraTrouble',
'DocumentNFCTrouble',
] satisfies (keyof RootStackParamList)[];
const ScreenSelector = ({}) => {
@@ -304,7 +304,7 @@ const DevSettingsScreen: React.FC<DevSettingsScreenProps> = ({}) => {
{
label: 'Scan new ID Document',
onPress: () => {
navigation.navigate('PassportOnboarding');
navigation.navigate('DocumentOnboarding');
},
},
].map(({ label, onPress }) => (
@@ -349,16 +349,6 @@ const DevSettingsScreen: React.FC<DevSettingsScreenProps> = ({}) => {
darkMode={true}
>
{[
// Only show private key button in development
...(__DEV__
? [
{
label: 'Display your private key',
onPress: () => navigation.navigate('DevPrivateKey'),
dangerTheme: false,
},
]
: []),
{
label: 'Delete your private key',
onPress: handleClearSecretsPress,

View File

@@ -33,7 +33,7 @@ import { checkScannedInfo } from '@/utils/utils';
const { trackEvent } = analytics();
const PassportCameraScreen: React.FC = () => {
const DocumentCameraScreen: React.FC = () => {
const client = useSelfClient();
const navigation = useNavigation();
const isFocused = useIsFocused();
@@ -98,7 +98,7 @@ const PassportCameraScreen: React.FC = () => {
dateOfExpiryLength: formattedDateOfExpiry.length,
duration_seconds: parseFloat(scanDurationSeconds),
});
navigation.navigate('PassportCameraTrouble');
navigation.navigate('DocumentCameraTrouble');
return;
}
@@ -114,7 +114,7 @@ const PassportCameraScreen: React.FC = () => {
duration_seconds: parseFloat(scanDurationSeconds),
});
navigation.navigate('PassportNFCScan');
navigation.navigate('DocumentNFCScan');
},
[store, navigation],
);
@@ -183,7 +183,7 @@ const PassportCameraScreen: React.FC = () => {
);
};
export default PassportCameraScreen;
export default DocumentCameraScreen;
const styles = StyleSheet.create({
animation: {

View File

@@ -47,8 +47,8 @@ const tips: TipProps[] = [
},
];
const PassportCameraTrouble: React.FC = () => {
const go = useHapticNavigation('PassportCamera', { action: 'cancel' });
const DocumentCameraTroubleScreen: React.FC = () => {
const go = useHapticNavigation('DocumentCamera', { action: 'cancel' });
// error screen, flush analytics
useEffect(() => {
@@ -76,4 +76,4 @@ const PassportCameraTrouble: React.FC = () => {
);
};
export default PassportCameraTrouble;
export default DocumentCameraTroubleScreen;

View File

@@ -88,7 +88,7 @@ const NFC_METHODS = [
// },
];
const NFCMethodSelectionScreen: React.FC = () => {
const DocumentNFCMethodSelectionScreen: React.FC = () => {
const navigation = useNavigation();
const [selectedMethod, setSelectedMethod] = useState('standard');
const [canValue, setCanValue] = useState('');
@@ -129,7 +129,7 @@ const NFCMethodSelectionScreen: React.FC = () => {
if (selectedMethod === 'can') {
params.canNumber = canValue;
}
navigation.navigate('PassportNFCScan', params as never);
navigation.navigate('DocumentNFCScan', params as never);
};
return (
@@ -218,4 +218,4 @@ const NFCMethodSelectionScreen: React.FC = () => {
);
};
export default NFCMethodSelectionScreen;
export default DocumentNFCMethodSelectionScreen;

View File

@@ -67,7 +67,7 @@ const emitter =
? new NativeEventEmitter(NativeModules.nativeModule)
: null;
type PassportNFCScanRouteParams = {
type DocumentNFCScanRouteParams = {
usePacePolling?: boolean;
canNumber?: string;
useCan?: boolean;
@@ -76,17 +76,17 @@ type PassportNFCScanRouteParams = {
extendedMode?: boolean;
};
type PassportNFCScanRoute = RouteProp<
Record<string, PassportNFCScanRouteParams>,
type DocumentNFCScanRoute = RouteProp<
Record<string, DocumentNFCScanRouteParams>,
string
>;
const PassportNFCScanScreen: React.FC = () => {
const DocumentNFCScanScreen: React.FC = () => {
const selfClient = useSelfClient();
const { trackEvent } = selfClient;
const navigation = useNavigation();
const route = useRoute<PassportNFCScanRoute>();
const route = useRoute<DocumentNFCScanRoute>();
const { showModal } = useFeedback();
useFeedbackAutoHide();
const {
@@ -123,9 +123,9 @@ const PassportNFCScanScreen: React.FC = () => {
}, []);
const goToNFCMethodSelection = useHapticNavigation(
'PassportNFCMethodSelection',
'DocumentNFCMethodSelection',
);
const goToNFCTrouble = useHapticNavigation('PassportNFCTrouble');
const goToNFCTrouble = useHapticNavigation('DocumentNFCTrouble');
// 5-taps with a single finger
const devModeTap = Gesture.Tap()
@@ -329,7 +329,7 @@ const PassportNFCScanScreen: React.FC = () => {
if (scanCancelledRef.current) {
return;
}
navigation.navigate('ConfirmBelongingScreen', {});
navigation.navigate('ConfirmBelonging', {});
} catch (e: unknown) {
// Check if scan was cancelled by timeout
if (scanCancelledRef.current) {
@@ -597,7 +597,7 @@ const PassportNFCScanScreen: React.FC = () => {
);
};
export default PassportNFCScanScreen;
export default DocumentNFCScanScreen;
const styles = StyleSheet.create({
title: {

View File

@@ -21,7 +21,7 @@ import NFC_IMAGE from '@/images/nfc.png';
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
import { black, slate100, white } from '@/utils/colors';
const PassportNFCScanScreen: React.FC = () => {
const DocumentNFCScanScreen: React.FC = () => {
const selfClient = useSelfClient();
const navigateToLaunch = useHapticNavigation('Launch', {
action: 'cancel',
@@ -74,4 +74,4 @@ const PassportNFCScanScreen: React.FC = () => {
);
};
export default PassportNFCScanScreen;
export default DocumentNFCScanScreen;

View File

@@ -44,10 +44,10 @@ const tips: TipProps[] = [
},
];
const PassportNFCTrouble: React.FC = () => {
const go = useHapticNavigation('PassportNFCScan', { action: 'cancel' });
const DocumentNFCTroubleScreen: React.FC = () => {
const go = useHapticNavigation('DocumentNFCScan', { action: 'cancel' });
const goToNFCMethodSelection = useHapticNavigation(
'PassportNFCMethodSelection',
'DocumentNFCMethodSelection',
);
useFeedbackAutoHide();
@@ -109,4 +109,4 @@ const PassportNFCTrouble: React.FC = () => {
);
};
export default PassportNFCTrouble;
export default DocumentNFCTroubleScreen;

View File

@@ -23,9 +23,9 @@ import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
import { black, slate100, white } from '@/utils/colors';
import { impactLight } from '@/utils/haptic';
const PassportOnboardingScreen: React.FC = () => {
const DocumentOnboardingScreen: React.FC = () => {
const navigation = useNavigation();
const handleCameraPress = useHapticNavigation('PassportCamera');
const handleCameraPress = useHapticNavigation('DocumentCamera');
const animationRef = useRef<LottieView>(null);
const onCancelPress = () => {
@@ -86,7 +86,7 @@ const PassportOnboardingScreen: React.FC = () => {
);
};
export default PassportOnboardingScreen;
export default DocumentOnboardingScreen;
const styles = StyleSheet.create({
animation: {

View File

@@ -36,20 +36,20 @@ type CountryFlagComponent = React.ComponentType<{
}>;
type CountryFlagsRecord = Record<string, CountryFlagComponent>;
type UnsupportedPassportScreenRouteProp = RouteProp<
type UnsupportedDocumentScreenRouteProp = RouteProp<
{
UnsupportedPassport: {
UnsupportedDocument: {
passportData: PassportData | null;
};
},
'UnsupportedPassport'
'UnsupportedDocument'
>;
interface UnsupportedPassportScreenProps {
route: UnsupportedPassportScreenRouteProp;
interface UnsupportedDocumentScreenProps {
route: UnsupportedDocumentScreenRouteProp;
}
const UnsupportedPassportScreen: React.FC<UnsupportedPassportScreenProps> = ({
const UnsupportedDocumentScreen: React.FC<UnsupportedDocumentScreenProps> = ({
route,
}) => {
const selfClient = useSelfClient();
@@ -213,4 +213,4 @@ const UnsupportedPassportScreen: React.FC<UnsupportedPassportScreenProps> = ({
);
};
export default UnsupportedPassportScreen;
export default UnsupportedDocumentScreen;

View File

@@ -35,7 +35,7 @@ type ConfirmBelongingScreenProps = StaticScreenProps<Record<string, never>>;
const ConfirmBelongingScreen: React.FC<ConfirmBelongingScreenProps> = () => {
const selfClient = useSelfClient();
const { trackEvent } = selfClient;
const navigate = useHapticNavigation('LoadingScreen', {
const navigate = useHapticNavigation('Loading', {
params: {},
});
const [_requestingPermission, setRequestingPermission] = useState(false);

View File

@@ -147,7 +147,7 @@ const ProveScreen: React.FC = () => {
userIdType: selectedApp?.userIdType,
});
setTimeout(() => {
navigate('ProofRequestStatusScreen');
navigate('ProofRequestStatus');
}, 100);
}

View File

@@ -36,7 +36,7 @@ const QRCodeViewFinderScreen: React.FC = () => {
const navigation = useNavigation();
const isFocused = useIsFocused();
const [doneScanningQR, setDoneScanningQR] = useState(false);
const navigateToProveScreen = useHapticNavigation('ProveScreen');
const navigateToProve = useHapticNavigation('Prove');
const navigateToHome = useHapticNavigation('Home');
const onCancelPress = useCallback(() => {
navigateToHome();
@@ -74,7 +74,7 @@ const QRCodeViewFinderScreen: React.FC = () => {
useSelfAppStore.getState().setSelfApp(selfAppJson);
useSelfAppStore.getState().startAppListener(selfAppJson.sessionId);
setTimeout(() => {
navigateToProveScreen();
navigateToProve();
}, 100);
} catch (parseError) {
trackEvent(ProofEvents.QR_SCAN_FAILED, {
@@ -96,7 +96,7 @@ const QRCodeViewFinderScreen: React.FC = () => {
useSelfAppStore.getState().cleanSelfApp();
useSelfAppStore.getState().startAppListener(sessionId);
setTimeout(() => {
navigateToProveScreen();
navigateToProve();
}, 100);
} else {
trackEvent(ProofEvents.QR_SCAN_FAILED, {
@@ -110,7 +110,7 @@ const QRCodeViewFinderScreen: React.FC = () => {
}
}
},
[doneScanningQR, navigation, navigateToProveScreen, trackEvent],
[doneScanningQR, navigation, navigateToProve, trackEvent],
);
const shouldRenderCamera = !connectionModalVisible && !doneScanningQR;

View File

@@ -19,7 +19,7 @@ import { black, slate200, white } from '@/utils/colors';
const { flush: flushAnalytics } = analytics();
const PassportDataNotFound: React.FC = () => {
const DocumentDataNotFoundScreen: React.FC = () => {
const selfClient = useSelfClient();
const navigateToLaunch = useHapticNavigation('Launch');
const navigateToHome = useHapticNavigation('Home');
@@ -63,4 +63,4 @@ const PassportDataNotFound: React.FC = () => {
);
};
export default PassportDataNotFound;
export default DocumentDataNotFoundScreen;

View File

@@ -59,7 +59,7 @@ const InfoRow: React.FC<{
</YStack>
);
const PassportDataInfoScreen: React.FC = () => {
const DocumentDataInfoScreen: React.FC = () => {
const { trackEvent } = useSelfClient();
const { getData } = usePassport();
const [metadata, setMetadata] = useState<PassportMetadata | null>(null);
@@ -117,4 +117,4 @@ const PassportDataInfoScreen: React.FC = () => {
);
};
export default PassportDataInfoScreen;
export default DocumentDataInfoScreen;

View File

@@ -279,7 +279,7 @@ const ManageDocumentsScreen: React.FC = () => {
const handleScanDocument = () => {
impactLight();
trackEvent(DocumentEvents.ADD_NEW_SCAN_SELECTED);
navigation.navigate('PassportOnboarding');
navigation.navigate('DocumentOnboarding');
};
const handleGenerateMock = () => {

View File

@@ -68,7 +68,7 @@ const goToStore = () => {
const routes =
Platform.OS !== 'web'
? ([
[Data, 'View passport info', 'PassportDataInfo'],
[Data, 'View document info', 'DocumentDataInfo'],
[Lock, 'Reveal recovery phrase', 'ShowRecoveryPhrase'],
[Cloud, 'Cloud backup', 'CloudBackupSettings'],
[Feedback, 'Send feedback', 'email_feedback'],
@@ -80,7 +80,7 @@ const routes =
],
] satisfies [React.FC<SvgProps>, string, RouteOption][])
: ([
[Data, 'View passport info', 'PassportDataInfo'],
[Data, 'View document info', 'DocumentDataInfo'],
[Feedback, 'Send feeback', 'email_feedback'],
[
FileText as React.FC<SvgProps>,

View File

@@ -23,7 +23,7 @@ import { advercase, dinot } from '@/utils/fonts';
const LaunchScreen: React.FC = () => {
useConnectionModal();
const onStartPress = useHapticNavigation('PassportOnboarding');
const onStartPress = useHapticNavigation('DocumentOnboarding');
const createMock = useHapticNavigation('CreateMock');
const { bottom } = useSafeAreaInsets();

View File

@@ -81,7 +81,7 @@ export const handleUrl = (uri: string) => {
const selfAppJson = JSON.parse(selfAppStr);
useSelfAppStore.getState().setSelfApp(selfAppJson);
useSelfAppStore.getState().startAppListener(selfAppJson.sessionId);
navigationRef.navigate('ProveScreen');
navigationRef.navigate('Prove');
return;
} catch (error) {
@@ -93,7 +93,7 @@ export const handleUrl = (uri: string) => {
} else if (sessionId && typeof sessionId === 'string') {
useSelfAppStore.getState().cleanSelfApp();
useSelfAppStore.getState().startAppListener(sessionId);
navigationRef.navigate('ProveScreen');
navigationRef.navigate('Prove');
} else if (mock_passport) {
try {
const data = JSON.parse(mock_passport);

View File

@@ -278,7 +278,7 @@ export const useProvingStore = create<ProvingState>((set, get) => {
if (state.value === 'passport_not_supported') {
if (navigationRef.isReady()) {
const currentPassportData = get().passportData;
(navigationRef as any).navigate('UnsupportedPassport', {
(navigationRef as any).navigate('UnsupportedDocument', {
passportData: currentPassportData,
});
}
@@ -290,7 +290,7 @@ export const useProvingStore = create<ProvingState>((set, get) => {
}
if (state.value === 'passport_data_not_found') {
if (navigationRef.isReady()) {
navigationRef.navigate('PassportDataNotFound');
navigationRef.navigate('DocumentDataNotFound');
}
}
if (state.value === 'failure') {

View File

@@ -11,32 +11,32 @@ describe('navigation', () => {
'AccountRecoveryChoice',
'AccountVerifiedSuccess',
'CloudBackupSettings',
'ConfirmBelongingScreen',
'ConfirmBelonging',
'CreateMock',
'DeferredLinkingInfo',
'DevFeatureFlags',
'DevHapticFeedback',
'DevPrivateKey',
'DevSettings',
'Disclaimer',
'DocumentCamera',
'DocumentCameraTrouble',
'DocumentDataInfo',
'DocumentDataNotFound',
'DocumentNFCMethodSelection',
'DocumentNFCScan',
'DocumentNFCTrouble',
'DocumentOnboarding',
'Home',
'Launch',
'LoadingScreen',
'Loading',
'ManageDocuments',
'MockDataDeepLink',
'Modal',
'PassportCamera',
'PassportCameraTrouble',
'PassportDataInfo',
'PassportDataNotFound',
'PassportNFCMethodSelection',
'PassportNFCScan',
'PassportNFCTrouble',
'PassportOnboarding',
'ProofHistory',
'ProofHistoryDetail',
'ProofRequestStatusScreen',
'ProveScreen',
'ProofRequestStatus',
'Prove',
'QRCodeTrouble',
'QRCodeViewFinder',
'RecoverWithPhrase',
@@ -44,7 +44,7 @@ describe('navigation', () => {
'Settings',
'ShowRecoveryPhrase',
'Splash',
'UnsupportedPassport',
'UnsupportedDocument',
]);
});
@@ -56,7 +56,7 @@ describe('navigation', () => {
it('should use regular passport screens when shouldShowAesopRedesign is false', () => {
const navigationScreens = require('@/navigation').navigationScreens;
expect(
navigationScreens.PassportOnboarding.options.title,
navigationScreens.DocumentOnboarding.options.title,
).toBeUndefined();
});
@@ -66,7 +66,7 @@ describe('navigation', () => {
}));
const navigationScreens = require('@/navigation').navigationScreens;
expect(navigationScreens.PassportOnboarding.options.title).toBeDefined();
expect(navigationScreens.DocumentOnboarding.options.title).toBeDefined();
});
});
});

View File

@@ -60,7 +60,7 @@ describe('deeplinks', () => {
expect(setSelfApp).toHaveBeenCalledWith(selfApp);
expect(startAppListener).toHaveBeenCalledWith('abc');
const { navigationRef } = require('@/navigation');
expect(navigationRef.navigate).toHaveBeenCalledWith('ProveScreen');
expect(navigationRef.navigate).toHaveBeenCalledWith('Prove');
});
it('handles sessionId parameter', () => {
@@ -70,7 +70,7 @@ describe('deeplinks', () => {
expect(cleanSelfApp).toHaveBeenCalled();
expect(startAppListener).toHaveBeenCalledWith('123');
const { navigationRef } = require('@/navigation');
expect(navigationRef.navigate).toHaveBeenCalledWith('ProveScreen');
expect(navigationRef.navigate).toHaveBeenCalledWith('Prove');
});
it('handles mock_passport parameter', () => {

View File

@@ -148,7 +148,7 @@ export default defineConfig({
'vendor-state-zustand': ['zustand'],
// Screen-specific chunks - more granular
'screens-passport-core': ['./src/navigation/passport.ts'],
'screens-document-core': ['./src/navigation/document.ts'],
'screens-passport-nfc': ['./src/utils/nfcScanner.ts'],
// Proving - split into even smaller chunks
@@ -173,7 +173,7 @@ export default defineConfig({
// Other screens
'screens-settings': ['./src/navigation/settings.ts'],
'screens-recovery': ['./src/navigation/recovery.ts'],
'screens-dev': ['./src/navigation/devTools.ts'],
'screens-dev': ['./src/navigation/dev.ts'],
'screens-aesop': ['./src/navigation/aesop.ts'],
},
},

View File

@@ -8,7 +8,7 @@ The app uses `@react-navigation/native` with `createStaticNavigation` for type-s
```typescript
// Navigation setup pattern
export const navigationScreens = {
...miscScreens,
...systemScreens,
...passportScreens,
...homeScreens,
...proveScreens,

View File

@@ -151,7 +151,19 @@ function fixLicenseHeader(filePath) {
const lines = content.split('\n');
const headerInfo = findLicenseHeaderIndex(lines);
if (headerInfo.index !== -1 && headerInfo.valid) {
if (headerInfo.index === -1) {
// No header exists - add the canonical header
const newLines = [
...CANONICAL_HEADER_LINES,
'', // Add newline after header
...lines,
];
const fixedContent = newLines.join('\n');
writeFileSync(filePath, fixedContent, 'utf8');
return true;
}
if (headerInfo.valid) {
const headerEndIndex = headerInfo.endIndex;
if (lines[headerEndIndex + 1] !== '') {
// Insert empty line after license header