[SEL-154] Step 1: Scan your passport (#511)

* simplify navigation logic

* use aesop design hook

* save wip

* add new aesop redesign screens

* save wip design

* refactor nav bar logic

* fix paths

* save wip

* stub progress navbar and save wip

* save wip progress bar animation

* save wip progress bar, almost done with design

* fix progress bar design

* fix bottom padding

* disable git commit for now

* fix flaky android downloads that causes pipeline to crash

* update lock for ci
This commit is contained in:
Justin Hernandez
2025-04-18 07:35:52 -05:00
committed by GitHub
parent 7297659a30
commit 35221386da
29 changed files with 4328 additions and 2505 deletions

View File

@@ -30,6 +30,8 @@ buildscript {
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io" }
jcenter()
}
configurations.configureEach {

View File

@@ -1,8 +1,8 @@
#Mon Feb 03 16:12:34 CET 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
networkTimeout=10000
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=600000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -1,2 +1,3 @@
SEGMENT_KEY=
SENTRY_DSN=
IS_TEST_BUILD=

View File

@@ -1,379 +0,0 @@
import React from 'react';
import { StatusBar } from 'react-native';
import 'react-native-gesture-handler';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import {
StaticParamList,
createNavigationContainerRef,
createStaticNavigation,
} from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import DefaultNavBar from './components/DefaultNavBar';
import HomeNavBar from './components/HomeNavBar';
import AppLayout from './layouts/AppLayout';
import AccountRecoveryChoiceScreen from './screens/AccountFlow/AccountRecoveryChoiceScreen';
import AccountRecoveryScreen from './screens/AccountFlow/AccountRecoveryScreen';
import AccountVerifiedSuccessScreen from './screens/AccountFlow/AccountVerifiedSuccessScreen';
import RecoverWithPhraseScreen from './screens/AccountFlow/RecoverWithPhraseScreen';
import SaveRecoveryPhraseScreen from './screens/AccountFlow/SaveRecoveryPhraseScreen';
import DisclaimerScreen from './screens/DisclaimerScreen';
import HomeScreen from './screens/HomeScreen';
import LaunchScreen from './screens/LaunchScreen';
import MockDataScreen from './screens/MockDataScreen';
import ConfirmBelongingScreen from './screens/Onboarding/ConfirmBelongingScreen';
import LoadingScreen from './screens/Onboarding/LoadingScreen';
import PassportCameraScreen from './screens/Onboarding/PassportCameraScreen';
import PassportCameraTrouble from './screens/Onboarding/PassportCameraTrouble';
import PassportDataNotFound from './screens/Onboarding/PassportDataNotFound';
import PassportNFCScanScreen from './screens/Onboarding/PassportNFCScanScreen';
import PassportNFCTrouble from './screens/Onboarding/PassportNFCTrouble';
import PassportOnboardingScreen from './screens/Onboarding/PassportOnboardingScreen';
import UnsupportedPassportScreen from './screens/Onboarding/UnsupportedPassport';
import ProofRequestStatusScreen from './screens/ProveFlow/ProofRequestStatusScreen';
import ProveScreen from './screens/ProveFlow/ProveScreen';
import QRCodeTroubleScreen from './screens/ProveFlow/QRCodeTrouble';
import QRCodeViewFinderScreen from './screens/ProveFlow/ViewFinder';
import CloudBackupScreen from './screens/Settings/CloudBackupScreen';
import DevSettingsScreen from './screens/Settings/DevSettingsScreen';
import ModalScreen from './screens/Settings/ModalScreen';
import PassportDataInfoScreen from './screens/Settings/PassportDataInfoScreen';
import ShowRecoveryPhraseScreen from './screens/Settings/ShowRecoveryPhraseScreen';
import SettingsScreen from './screens/SettingsScreen';
import SplashScreen from './screens/SplashScreen';
import { useApp } from './stores/appProvider';
import { useProofInfo } from './stores/proofProvider';
import analytics from './utils/analytics';
import { black, slate300, white } from './utils/colors';
import { setupUniversalLinkListenerInNavigation } from './utils/qrCodeNew';
const AppNavigation = createNativeStackNavigator({
initialRouteName: 'Splash',
orientation: 'portrait_up',
screenOptions: {
header: DefaultNavBar,
navigationBarColor: white,
},
layout: AppLayout,
screens: {
/**
* STATIC SCREENS
*/
Splash: {
screen: SplashScreen,
options: {
header: () => (
<StatusBar barStyle="light-content" backgroundColor={black} />
),
navigationBarColor: black,
},
},
Launch: {
screen: LaunchScreen,
options: {
headerShown: false,
gestureEnabled: false,
},
},
Modal: {
screen: ModalScreen,
options: {
headerShown: false,
presentation: 'transparentModal',
animation: 'fade',
},
},
/**
* SCAN PASSPORT FLOW
*/
PassportOnboarding: {
screen: PassportOnboardingScreen,
options: {
animation: 'slide_from_bottom',
// presentation: 'modal' wanted to do this but seems to break stuff
headerShown: false,
},
},
PassportCameraTrouble: {
screen: PassportCameraTrouble,
options: {
headerShown: false,
animation: 'slide_from_bottom',
presentation: 'modal',
},
},
PassportNFCTrouble: {
screen: PassportNFCTrouble,
options: {
headerShown: false,
animation: 'slide_from_bottom',
presentation: 'modal',
},
},
PassportCamera: {
screen: PassportCameraScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
},
},
PassportNFCScan: {
screen: PassportNFCScanScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
},
initialParams: {
passportNumber: '',
dateOfBirth: '',
dateOfExpiry: '',
},
},
ConfirmBelongingScreen: {
screen: ConfirmBelongingScreen,
options: {
headerShown: false,
},
},
UnsupportedPassport: {
screen: UnsupportedPassportScreen,
options: {
headerShown: false,
},
},
LoadingScreen: {
screen: LoadingScreen,
options: {
headerShown: false,
navigationBarColor: black,
},
},
CreateMock: {
screen: MockDataScreen,
options: {
title: 'Mock Passport',
},
},
/**
* HOME SECTION
*/
Home: {
screen: HomeScreen,
options: {
title: 'Self',
header: HomeNavBar,
navigationBarColor: black,
presentation: 'card',
},
},
Disclaimer: {
screen: DisclaimerScreen,
options: {
title: 'Disclaimer',
headerShown: false,
},
},
/**
* QR CODE SCANNING + PROVE FLOW
*/
QRCodeViewFinder: {
screen: QRCodeViewFinderScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
// presentation: 'modal',
},
},
QRCodeTrouble: {
screen: QRCodeTroubleScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
presentation: 'modal',
},
},
PassportDataNotFound: {
screen: PassportDataNotFound,
options: {
headerShown: false,
gestureEnabled: false,
animation: 'slide_from_bottom',
// presentation: 'modal',
},
},
ProveScreen: {
screen: ProveScreen,
options: {
title: 'Request Proof',
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: white,
},
},
},
ProofRequestStatusScreen: {
screen: ProofRequestStatusScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
},
},
/**
* CREATE OR RECOVER ACCOUNT
*/
AccountRecovery: {
screen: AccountRecoveryScreen,
options: {
headerShown: false,
},
},
AccountRecoveryChoice: {
screen: AccountRecoveryChoiceScreen,
options: {
headerShown: false,
},
},
SaveRecoveryPhrase: {
screen: SaveRecoveryPhraseScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
},
},
RecoverWithPhrase: {
screen: RecoverWithPhraseScreen,
options: {
headerTintColor: black,
title: 'Enter Recovery Phrase',
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: slate300,
},
headerBackTitle: 'close',
},
},
AccountVerifiedSuccess: {
screen: AccountVerifiedSuccessScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
},
},
/**
* SETTINGS
*/
Settings: {
screen: SettingsScreen,
options: {
animation: 'slide_from_bottom',
title: 'Settings',
headerStyle: {
backgroundColor: white,
},
headerTitleStyle: {
color: black,
},
navigationBarColor: black,
},
config: {
screens: {},
},
},
ShowRecoveryPhrase: {
screen: ShowRecoveryPhraseScreen,
options: {
title: 'Recovery Phrase',
headerStyle: {
backgroundColor: white,
},
},
},
PassportDataInfo: {
screen: PassportDataInfoScreen,
options: {
title: 'Passport Data Info',
headerStyle: {
backgroundColor: white,
},
},
},
DevSettings: {
screen: DevSettingsScreen,
options: {
title: 'Developer Settings',
headerStyle: {
backgroundColor: white,
},
},
},
CloudBackupSettings: {
screen: CloudBackupScreen,
options: {
title: 'Cloud backup',
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: slate300,
},
},
},
},
});
export type RootStackParamList = StaticParamList<typeof AppNavigation>;
declare global {
namespace ReactNavigation {
interface RootParamList extends RootStackParamList {}
}
}
// Create a ref that we can use to access the navigation state
export const navigationRef = createNavigationContainerRef();
const { trackScreenView } = analytics();
const Navigation = createStaticNavigation(AppNavigation);
const NavigationWithTracking = () => {
const trackScreen = () => {
const currentRoute = navigationRef.getCurrentRoute();
if (currentRoute) {
console.log(`Screen View: ${currentRoute.name}`);
trackScreenView(`${currentRoute.name}`, {
screenName: currentRoute.name,
});
}
};
// Add these hooks to get access to the necessary functions
const { setSelectedApp, cleanSelfApp } = useProofInfo();
const { startAppListener } = useApp();
// Setup universal link handling at the navigation level
React.useEffect(() => {
const cleanup = setupUniversalLinkListenerInNavigation(
navigationRef,
setSelectedApp,
cleanSelfApp,
startAppListener,
);
return () => {
cleanup();
};
}, [setSelectedApp, cleanSelfApp, startAppListener]);
return (
<GestureHandlerRootView>
<Navigation ref={navigationRef} onStateChange={trackScreen} />
</GestureHandlerRootView>
);
};
export default NavigationWithTracking;

View File

@@ -0,0 +1,27 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import { HomeNavBar } from '../components/NavBar';
import DisclaimerScreen from '../screens/DisclaimerScreen';
import HomeScreen from '../screens/HomeScreen';
import { black } from '../utils/colors';
const accountScreens = {
Home: {
screen: HomeScreen,
options: {
title: 'Self',
header: HomeNavBar,
navigationBarColor: black,
presentation: 'card',
} as NativeStackNavigationOptions,
},
Disclaimer: {
screen: DisclaimerScreen,
options: {
title: 'Disclaimer',
headerShown: false,
} as NativeStackNavigationOptions,
},
};
export default accountScreens;

View File

@@ -0,0 +1,67 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import { ProgressNavBar } from '../components/NavBar';
import { shouldShowAesopRedesign } from '../hooks/useAesopRedesign';
import PassportOnboardingScreen from '../screens/_Aesop/PassportOnboardingScreen';
import { white } from '../utils/colors';
const aesopScreens = {
PassportOnboarding: {
screen: PassportOnboardingScreen,
options: {
animation: 'slide_from_bottom',
header: ProgressNavBar,
title: 'Scan your passport',
headerStyle: {
backgroundColor: white,
},
headerCurrentStep: 1,
headerTotalSteps: 4,
} as NativeStackNavigationOptions,
},
// stub the rest of the steps. will update in future pr
PassportCamera: {
screen: PassportOnboardingScreen,
options: {
animation: 'slide_from_bottom',
header: ProgressNavBar,
title: 'Take a photo',
headerStyle: {
backgroundColor: white,
},
headerCurrentStep: 2,
headerTotalSteps: 4,
} as NativeStackNavigationOptions,
},
PassportNFC: {
screen: PassportOnboardingScreen,
options: {
animation: 'slide_from_bottom',
header: ProgressNavBar,
title: 'Scan NFC',
headerStyle: {
backgroundColor: white,
},
headerCurrentStep: 3,
headerTotalSteps: 4,
} as NativeStackNavigationOptions,
},
PassportComplete: {
screen: PassportOnboardingScreen,
options: {
animation: 'slide_from_bottom',
header: ProgressNavBar,
title: 'Complete',
headerStyle: {
backgroundColor: white,
},
headerCurrentStep: 4,
headerTotalSteps: 4,
} as NativeStackNavigationOptions,
},
};
export default shouldShowAesopRedesign() ? aesopScreens : {};

View File

@@ -0,0 +1,27 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import HomeNavBar from '../components/NavBar/HomeNavBar';
import DisclaimerScreen from '../screens/DisclaimerScreen';
import HomeScreen from '../screens/HomeScreen';
import { black } from '../utils/colors';
const homeScreens = {
Home: {
screen: HomeScreen,
options: {
title: 'Self',
header: HomeNavBar,
navigationBarColor: black,
presentation: 'card',
} as NativeStackNavigationOptions,
},
Disclaimer: {
screen: DisclaimerScreen,
options: {
title: 'Disclaimer',
headerShown: false,
} as NativeStackNavigationOptions,
},
};
export default homeScreens;

View File

@@ -0,0 +1,96 @@
import React from 'react';
import 'react-native-gesture-handler';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import {
StaticParamList,
createNavigationContainerRef,
createStaticNavigation,
} from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { DefaultNavBar } from '../components/NavBar';
import AppLayout from '../layouts/AppLayout';
import { useApp } from '../stores/appProvider';
import { useProofInfo } from '../stores/proofProvider';
import analytics from '../utils/analytics';
import { white } from '../utils/colors';
import { setupUniversalLinkListenerInNavigation } from '../utils/qrCodeNew';
import accountScreens from './account';
import aesopScreens from './aesop';
import homeScreens from './home';
import passportScreens from './passport';
import proveScreens from './prove';
import settingsScreens from './settings';
import staticScreens from './static';
const AppNavigation = createNativeStackNavigator({
initialRouteName: 'Splash',
orientation: 'portrait_up',
screenOptions: {
header: DefaultNavBar,
navigationBarColor: white,
},
layout: AppLayout,
screens: {
...staticScreens,
...passportScreens,
...homeScreens,
...proveScreens,
...accountScreens,
...settingsScreens,
...aesopScreens,
},
});
export type RootStackParamList = StaticParamList<typeof AppNavigation>;
declare global {
namespace ReactNavigation {
interface RootParamList extends RootStackParamList {}
}
}
// Create a ref that we can use to access the navigation state
export const navigationRef = createNavigationContainerRef();
const { trackScreenView } = analytics();
const Navigation = createStaticNavigation(AppNavigation);
const NavigationWithTracking = () => {
const trackScreen = () => {
const currentRoute = navigationRef.getCurrentRoute();
if (currentRoute) {
console.log(`Screen View: ${currentRoute.name}`);
trackScreenView(`${currentRoute.name}`, {
screenName: currentRoute.name,
});
}
};
// Add these hooks to get access to the necessary functions
const { setSelectedApp, cleanSelfApp } = useProofInfo();
const { startAppListener } = useApp();
// Setup universal link handling at the navigation level
React.useEffect(() => {
const cleanup = setupUniversalLinkListenerInNavigation(
navigationRef,
setSelectedApp,
cleanSelfApp,
startAppListener,
);
return () => {
cleanup();
};
}, [setSelectedApp, cleanSelfApp, startAppListener]);
return (
<GestureHandlerRootView>
<Navigation ref={navigationRef} onStateChange={trackScreen} />
</GestureHandlerRootView>
);
};
export default NavigationWithTracking;

View File

@@ -0,0 +1,85 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import MockDataScreen from '../screens/MockDataScreen';
import ConfirmBelongingScreen from '../screens/Onboarding/ConfirmBelongingScreen';
import LoadingScreen from '../screens/Onboarding/LoadingScreen';
import PassportCameraScreen from '../screens/Onboarding/PassportCameraScreen';
import PassportCameraTrouble from '../screens/Onboarding/PassportCameraTrouble';
import PassportNFCScanScreen from '../screens/Onboarding/PassportNFCScanScreen';
import PassportNFCTrouble from '../screens/Onboarding/PassportNFCTrouble';
import PassportOnboardingScreen from '../screens/Onboarding/PassportOnboardingScreen';
import UnsupportedPassportScreen from '../screens/Onboarding/UnsupportedPassport';
import { black } from '../utils/colors';
const passportScreens = {
PassportOnboarding: {
screen: PassportOnboardingScreen,
options: {
animation: 'slide_from_bottom',
// presentation: 'modal' wanted to do this but seems to break stuff
headerShown: false,
} as NativeStackNavigationOptions,
},
PassportCameraTrouble: {
screen: PassportCameraTrouble,
options: {
headerShown: false,
animation: 'slide_from_bottom',
presentation: 'modal',
} as NativeStackNavigationOptions,
},
PassportNFCTrouble: {
screen: PassportNFCTrouble,
options: {
headerShown: false,
animation: 'slide_from_bottom',
presentation: 'modal',
} as NativeStackNavigationOptions,
},
PassportCamera: {
screen: PassportCameraScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
} as NativeStackNavigationOptions,
},
PassportNFCScan: {
screen: PassportNFCScanScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
} as NativeStackNavigationOptions,
initialParams: {
passportNumber: '',
dateOfBirth: '',
dateOfExpiry: '',
},
},
ConfirmBelongingScreen: {
screen: ConfirmBelongingScreen,
options: {
headerShown: false,
} as NativeStackNavigationOptions,
},
UnsupportedPassport: {
screen: UnsupportedPassportScreen,
options: {
headerShown: false,
} as NativeStackNavigationOptions,
},
LoadingScreen: {
screen: LoadingScreen,
options: {
headerShown: false,
navigationBarColor: black,
} as NativeStackNavigationOptions,
},
CreateMock: {
screen: MockDataScreen,
options: {
title: 'Mock Passport',
} as NativeStackNavigationOptions,
},
};
export default passportScreens;

View File

@@ -0,0 +1,57 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import PassportDataNotFound from '../screens/Onboarding/PassportDataNotFound';
import ProofRequestStatusScreen from '../screens/ProveFlow/ProofRequestStatusScreen';
import ProveScreen from '../screens/ProveFlow/ProveScreen';
import QRCodeTroubleScreen from '../screens/ProveFlow/QRCodeTrouble';
import QRCodeViewFinderScreen from '../screens/ProveFlow/ViewFinder';
import { black, white } from '../utils/colors';
const proveScreens = {
QRCodeViewFinder: {
screen: QRCodeViewFinderScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
// presentation: 'modal',
} as NativeStackNavigationOptions,
},
QRCodeTrouble: {
screen: QRCodeTroubleScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
presentation: 'modal',
} as NativeStackNavigationOptions,
},
PassportDataNotFound: {
screen: PassportDataNotFound,
options: {
headerShown: false,
gestureEnabled: false,
animation: 'slide_from_bottom',
// presentation: 'modal',
} as NativeStackNavigationOptions,
},
ProveScreen: {
screen: ProveScreen,
options: {
title: 'Request Proof',
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: white,
},
} as NativeStackNavigationOptions,
},
ProofRequestStatusScreen: {
screen: ProofRequestStatusScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
} as NativeStackNavigationOptions,
},
};
export default proveScreens;

View File

@@ -0,0 +1,53 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import AccountRecoveryChoiceScreen from '../screens/AccountFlow/AccountRecoveryChoiceScreen';
import AccountRecoveryScreen from '../screens/AccountFlow/AccountRecoveryScreen';
import AccountVerifiedSuccessScreen from '../screens/AccountFlow/AccountVerifiedSuccessScreen';
import RecoverWithPhraseScreen from '../screens/AccountFlow/RecoverWithPhraseScreen';
import SaveRecoveryPhraseScreen from '../screens/AccountFlow/SaveRecoveryPhraseScreen';
import { black, slate300 } from '../utils/colors';
const recoveryScreens = {
AccountRecovery: {
screen: AccountRecoveryScreen,
options: {
headerShown: false,
} as NativeStackNavigationOptions,
},
AccountRecoveryChoice: {
screen: AccountRecoveryChoiceScreen,
options: {
headerShown: false,
} as NativeStackNavigationOptions,
},
SaveRecoveryPhrase: {
screen: SaveRecoveryPhraseScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
} as NativeStackNavigationOptions,
},
RecoverWithPhrase: {
screen: RecoverWithPhraseScreen,
options: {
headerTintColor: black,
title: 'Enter Recovery Phrase',
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: slate300,
},
headerBackTitle: 'close',
} as NativeStackNavigationOptions,
},
AccountVerifiedSuccess: {
screen: AccountVerifiedSuccessScreen,
options: {
headerShown: false,
animation: 'slide_from_bottom',
} as NativeStackNavigationOptions,
},
};
export default recoveryScreens;

View File

@@ -0,0 +1,69 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import CloudBackupScreen from '../screens/Settings/CloudBackupScreen';
import DevSettingsScreen from '../screens/Settings/DevSettingsScreen';
import PassportDataInfoScreen from '../screens/Settings/PassportDataInfoScreen';
import ShowRecoveryPhraseScreen from '../screens/Settings/ShowRecoveryPhraseScreen';
import SettingsScreen from '../screens/SettingsScreen';
import { black, slate300, white } from '../utils/colors';
const settingsScreens = {
Settings: {
screen: SettingsScreen,
options: {
animation: 'slide_from_bottom',
title: 'Settings',
headerStyle: {
backgroundColor: white,
},
headerTitleStyle: {
color: black,
},
navigationBarColor: black,
} as NativeStackNavigationOptions,
config: {
screens: {},
},
},
ShowRecoveryPhrase: {
screen: ShowRecoveryPhraseScreen,
options: {
title: 'Recovery Phrase',
headerStyle: {
backgroundColor: white,
},
} as NativeStackNavigationOptions,
},
PassportDataInfo: {
screen: PassportDataInfoScreen,
options: {
title: 'Passport Data Info',
headerStyle: {
backgroundColor: white,
},
} as NativeStackNavigationOptions,
},
DevSettings: {
screen: DevSettingsScreen,
options: {
title: 'Developer Settings',
headerStyle: {
backgroundColor: white,
},
} as NativeStackNavigationOptions,
},
CloudBackupSettings: {
screen: CloudBackupScreen,
options: {
title: 'Cloud backup',
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: slate300,
},
} as NativeStackNavigationOptions,
},
};
export default settingsScreens;

View File

@@ -0,0 +1,38 @@
import React from 'react';
import { StatusBar } from 'react-native';
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import LaunchScreen from '../screens/LaunchScreen';
import ModalScreen from '../screens/Settings/ModalScreen';
import SplashScreen from '../screens/SplashScreen';
import { black } from '../utils/colors';
const staticScreens = {
Splash: {
screen: SplashScreen,
options: {
header: () => (
<StatusBar barStyle="light-content" backgroundColor={black} />
),
navigationBarColor: black,
},
},
Launch: {
screen: LaunchScreen,
options: {
headerShown: false,
gestureEnabled: false,
},
},
Modal: {
screen: ModalScreen,
options: {
headerShown: false,
presentation: 'transparentModal',
animation: 'fade',
} as NativeStackNavigationOptions,
},
};
export default staticScreens;

View File

@@ -1,6 +1,8 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { shouldShowAesopRedesign } from '../hooks/useAesopRedesign';
interface ButtonsContainerProps {
children: React.ReactNode;
}
@@ -14,7 +16,7 @@ export default ButtonsContainer;
const styles = StyleSheet.create({
buttonsContainer: {
display: 'flex',
flexDirection: 'column',
flexDirection: shouldShowAesopRedesign() ? 'row' : 'column',
justifyContent: 'center',
alignItems: 'center',
gap: 10,

View File

@@ -11,7 +11,7 @@ import {
XStackProps,
} from 'tamagui';
import { Title } from './typography/Title';
import { Title } from '../typography/Title';
interface NavBarProps extends XStackProps {
children: React.ReactNode;

View File

@@ -4,11 +4,11 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { NativeStackHeaderProps } from '@react-navigation/native-stack';
import { TextStyle, ViewStyle } from 'tamagui';
import { white } from '../utils/colors';
import { buttonTap } from '../utils/haptic';
import { NavBar } from './NavBar';
import { white } from '../../utils/colors';
import { buttonTap } from '../../utils/haptic';
import { NavBar } from './BaseNavBar';
const DefaultNavBar = (props: NativeStackHeaderProps) => {
export const DefaultNavBar = (props: NativeStackHeaderProps) => {
const { goBack, canGoBack } = props.navigation;
const { options } = props;
const headerStyle = (options.headerStyle || {}) as ViewStyle;
@@ -43,5 +43,3 @@ const DefaultNavBar = (props: NativeStackHeaderProps) => {
</NavBar.Container>
);
};
export default DefaultNavBar;

View File

@@ -4,13 +4,13 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { NativeStackHeaderProps } from '@react-navigation/native-stack';
import { Button } from 'tamagui';
import ActivityIcon from '../images/icons/activity.svg';
import SettingsIcon from '../images/icons/settings.svg';
import { black, neutral400, white } from '../utils/colors';
import { buttonTap } from '../utils/haptic';
import { NavBar } from './NavBar';
import ActivityIcon from '../../images/icons/activity.svg';
import SettingsIcon from '../../images/icons/settings.svg';
import { black, neutral400, white } from '../../utils/colors';
import { buttonTap } from '../../utils/haptic';
import { NavBar } from './BaseNavBar';
const HomeNavBar = (props: NativeStackHeaderProps) => {
export const HomeNavBar = (props: NativeStackHeaderProps) => {
const insets = useSafeAreaInsets();
return (
<NavBar.Container
@@ -61,5 +61,3 @@ const HomeNavBar = (props: NativeStackHeaderProps) => {
</NavBar.Container>
);
};
export default HomeNavBar;

View File

@@ -0,0 +1,109 @@
import React from 'react';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import {
NativeStackHeaderProps,
NativeStackNavigationOptions,
} from '@react-navigation/native-stack';
import { TextStyle, ViewStyle, XStack, YStack } from 'tamagui';
import { cyan300, slate200, white } from '../../utils/colors';
import { buttonTap } from '../../utils/haptic';
import { NavBar } from './BaseNavBar';
interface ProgressNavBarProps extends NativeStackHeaderProps {
currentStep?: number;
totalSteps?: number;
}
interface ProgressNavigationOptions extends NativeStackNavigationOptions {
headerCurrentStep?: number;
headerTotalSteps?: number;
}
export const ProgressNavBar = (props: NativeStackHeaderProps) => {
const { goBack, canGoBack } = props.navigation;
const { options } = props;
const headerStyle = (options.headerStyle || {}) as ViewStyle;
const insets = useSafeAreaInsets();
const progressOptions = options as ProgressNavigationOptions;
const currentStep =
progressOptions.headerCurrentStep ||
(props as ProgressNavBarProps).currentStep ||
1;
const totalSteps =
progressOptions.headerTotalSteps ||
(props as ProgressNavBarProps).totalSteps ||
1;
const segments = Array.from({ length: totalSteps }, (_, index) => index + 1);
return (
<YStack>
<NavBar.Container
gap={14}
paddingHorizontal={20}
paddingTop={Math.max(insets.top, 12)}
paddingBottom={14}
backgroundColor={headerStyle.backgroundColor as string}
barStyle={
options.headerTintColor === white ||
(options.headerTitleStyle as TextStyle)?.color === white
? 'light-content'
: 'dark-content'
}
>
<XStack width="100%" alignItems="center">
<XStack width={50} justifyContent="flex-start">
<NavBar.LeftAction
component={
options.headerBackTitle || (canGoBack() ? 'back' : undefined)
}
onPress={() => {
buttonTap();
goBack();
}}
{...(options.headerTitleStyle as ViewStyle)}
/>
</XStack>
<XStack flex={1} justifyContent="center" alignItems="center">
<NavBar.Title {...(options.headerTitleStyle as ViewStyle)}>
{props.options.title}
</NavBar.Title>
</XStack>
<XStack width={50} />
</XStack>
</NavBar.Container>
<YStack
backgroundColor={
(props.options.headerStyle as ViewStyle)?.backgroundColor || white
}
paddingHorizontal={20}
paddingBottom={20}
>
<XStack width="100%" height={4} gap={4}>
{segments.map((step, index) => (
<YStack
key={step}
flex={1}
height={5}
backgroundColor={step <= currentStep ? cyan300 : slate200}
borderRadius={0}
borderTopLeftRadius={index === 0 ? 4 : 0}
borderBottomLeftRadius={index === 0 ? 4 : 0}
borderTopRightRadius={index === segments.length - 1 ? 4 : 0}
borderBottomRightRadius={index === segments.length - 1 ? 4 : 0}
overflow="hidden"
/>
))}
</XStack>
</YStack>
</YStack>
);
};

View File

@@ -0,0 +1,3 @@
export { DefaultNavBar } from './DefaultNavBar';
export { HomeNavBar } from './HomeNavBar';
export { ProgressNavBar } from './ProgressNavBar';

View File

@@ -1,12 +1,13 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { StyleSheet, View, ViewStyle } from 'react-native';
interface TextsContainerProps {
children: React.ReactNode;
style?: ViewStyle;
}
const TextsContainer = ({ children }: TextsContainerProps) => {
return <View style={styles.textsContainer}>{children}</View>;
const TextsContainer = ({ children, style }: TextsContainerProps) => {
return <View style={[styles.textsContainer, style]}>{children}</View>;
};
export default TextsContainer;

View File

@@ -3,6 +3,7 @@ import { StyleSheet, ViewStyle } from 'react-native';
import { Button, Text, ViewProps } from 'tamagui';
import { shouldShowAesopRedesign } from '../../hooks/useAesopRedesign';
import { dinot } from '../../utils/fonts';
import { pressedStyle } from './pressedStyle';
@@ -58,7 +59,7 @@ const styles = StyleSheet.create({
flexDirection: 'row',
flexGrow: 0,
flexShrink: 0,
width: '100%',
width: shouldShowAesopRedesign() ? '48%' : '100%',
display: 'flex',
alignItems: 'center',
rowGap: 12,

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { StyleSheet, Text, TextProps } from 'react-native';
import { shouldShowAesopRedesign } from '../../hooks/useAesopRedesign';
import { slate400 } from '../../utils/colors';
import { dinot } from '../../utils/fonts';
@@ -24,5 +25,10 @@ const styles = StyleSheet.create({
color: slate400,
marginTop: 10,
fontFamily: dinot,
textTransform: 'none',
...(shouldShowAesopRedesign() && {
fontSize: 11.5,
textTransform: 'uppercase',
}),
},
});

View File

@@ -3,6 +3,7 @@ import { StyleSheet } from 'react-native';
import { Text, TextProps } from 'tamagui';
import { shouldShowAesopRedesign } from '../../hooks/useAesopRedesign';
import { slate500 } from '../../utils/colors';
import { dinot } from '../../utils/fonts';
@@ -29,5 +30,9 @@ const styles = StyleSheet.create({
lineHeight: 23,
textAlign: 'center',
fontFamily: dinot,
...(shouldShowAesopRedesign() && {
textAlign: 'left',
fontSize: 16,
}),
},
});

View File

@@ -0,0 +1,9 @@
import { Text, styled } from 'tamagui';
import { dinot } from '../../utils/fonts';
export const DescriptionTitle = styled(Text, {
fontSize: 18,
lineHeight: 35,
fontFamily: dinot,
});

View File

@@ -1,17 +1,26 @@
import { StyleProp, TextStyle } from 'react-native';
import { Text, styled } from 'tamagui';
import { advercase } from '../../utils/fonts';
export const Title = styled(Text, {
fontSize: 28,
lineHeight: 35,
fontFamily: advercase,
variants: {
size: {
large: {
fontSize: 38,
lineHeight: 47,
export const Title = styled(
Text,
{
fontSize: 28,
lineHeight: 35,
fontFamily: advercase,
variants: {
size: {
large: {
fontSize: 38,
lineHeight: 47,
},
},
},
},
});
{
acceptsClassName: true,
style: (props: { style?: StyleProp<TextStyle> }) => props.style,
},
);

View File

@@ -0,0 +1,9 @@
import { IS_TEST_BUILD } from '@env';
export const shouldShowAesopRedesign = (): boolean => {
return IS_TEST_BUILD === 'true';
};
export const useAesopRedesign = (): boolean => {
return shouldShowAesopRedesign();
};

View File

@@ -0,0 +1,109 @@
import React, { useEffect, useRef } from 'react';
import { StatusBar, StyleSheet, View } from 'react-native';
import LottieView from 'lottie-react-native';
import passportOnboardingAnimation from '../../assets/animations/passport_onboarding.json';
import ButtonsContainer from '../../components/ButtonsContainer';
import TextsContainer from '../../components/TextsContainer';
import { PrimaryButton } from '../../components/buttons/PrimaryButton';
import { SecondaryButton } from '../../components/buttons/SecondaryButton';
import Additional from '../../components/typography/Additional';
import Description from '../../components/typography/Description';
import { DescriptionTitle } from '../../components/typography/DescriptionTitle';
import useHapticNavigation from '../../hooks/useHapticNavigation';
import Scan from '../../images/icons/passport_camera_scan.svg';
import { ExpandableBottomLayout } from '../../layouts/ExpandableBottomLayout';
import { black, slate100, white } from '../../utils/colors';
interface PassportOnboardingScreenProps {}
const PassportOnboardingScreen: React.FC<
PassportOnboardingScreenProps
> = ({}) => {
const handleCameraPress = useHapticNavigation('PassportCamera');
const onCancelPress = useHapticNavigation('Launch', { action: 'cancel' });
const animationRef = useRef<LottieView>(null);
useEffect(() => {
animationRef.current?.play();
}, []);
return (
<ExpandableBottomLayout.Layout backgroundColor={white}>
<StatusBar barStyle="light-content" backgroundColor={white} />
<ExpandableBottomLayout.TopSection backgroundColor={white}>
<LottieView
ref={animationRef}
autoPlay={false}
loop={false}
onAnimationFinish={() => {
setTimeout(() => {
animationRef.current?.play();
}, 5000); // Pause 5 seconds before playing again
}}
source={passportOnboardingAnimation}
style={styles.animation}
cacheComposition={true}
renderMode="HARDWARE"
/>
</ExpandableBottomLayout.TopSection>
<ExpandableBottomLayout.BottomSection
style={styles.bottomSection}
backgroundColor={white}
>
<TextsContainer style={styles.textContainer}>
<View style={styles.textIconWrapper}>
<Scan
style={styles.scanIcon}
height={40}
width={40}
color={black}
/>
<View>
<DescriptionTitle>Open to the photograph page</DescriptionTitle>
<Description textBreakStrategy="balanced">
Lay the Passport flat and position the machine readable text in
the viewfinder.
</Description>
</View>
</View>
</TextsContainer>
<TextsContainer>
<Additional textBreakStrategy="balanced">
Self will not capture an image of your passport.
</Additional>
</TextsContainer>
<ButtonsContainer>
<PrimaryButton onPress={handleCameraPress}>Open Camera</PrimaryButton>
<SecondaryButton onPress={onCancelPress}>Cancel</SecondaryButton>
</ButtonsContainer>
</ExpandableBottomLayout.BottomSection>
</ExpandableBottomLayout.Layout>
);
};
export default PassportOnboardingScreen;
const styles = StyleSheet.create({
animation: {
backgroundColor: slate100,
width: '115%',
height: '115%',
},
textIconWrapper: {
width: '100%',
flexDirection: 'row',
alignItems: 'center',
gap: 20,
},
scanIcon: {
marginRight: 10,
},
textContainer: {
marginBottom: 10,
},
bottomSection: {
paddingBottom: 32,
},
});

View File

@@ -15,6 +15,7 @@ export const slate800 = '#1E293B';
export const sky500 = '#0EA5E9';
export const green500 = '#22C55E';
export const red500 = '#EF4444';
export const cyan300 = '#67E8F9';
export const teal300 = '#5EEAD4';
export const teal500 = '#5EEAD4';
export const neutral400 = '#A3A3A3';