Navigation Refactor (#6)

Co-authored-by: Leszek Stachowski <leszek.stachowski@clabs.co>
Co-authored-by: Justin Hernandez <transphorm@gmail.com>
This commit is contained in:
Nicolas Brugneaux
2025-01-29 17:16:12 +01:00
committed by GitHub
parent 112e8ad518
commit 90b0e4a270
35 changed files with 2282 additions and 294 deletions

View File

@@ -6,34 +6,31 @@ import '@ethersproject/shims';
import { Buffer } from 'buffer';
import 'react-native-get-random-values';
import { useToastController } from '@tamagui/toast';
import { YStack } from 'tamagui';
import MainScreen from './src/screens/MainScreen';
import useNavigationStore from './src/stores/navigationStore';
import useUserStore from './src/stores/userStore';
import { bgWhite } from './src/utils/colors';
import { setupUniversalLinkListener } from './src/utils/qrCode'; // Adjust the import path as needed
import AppNavigation from './src/Navigation';
import useUserStore from './src/stores/userStore';
global.Buffer = Buffer;
function App(): React.JSX.Element {
const toast = useToastController();
const setToast = useNavigationStore(state => state.setToast);
// const toast = useToastController();
// const setToast = useNavigationStore(state => state.setToast);
const initUserStore = useUserStore(state => state.initUserStore);
const setSelectedTab = useNavigationStore(state => state.setSelectedTab);
// const setSelectedTab = useNavigationStore(state => state.setSelectedTab);
useEffect(() => {
initUserStore();
}, [initUserStore]);
useEffect(() => {
setToast(toast);
}, [toast, setToast]);
// useEffect(() => {
// setToast(toast);
// }, [toast, setToast]);
useEffect(() => {
setSelectedTab('splash');
}, [setSelectedTab]);
// useEffect(() => {
// setSelectedTab('splash');
// }, [setSelectedTab]);
useEffect(() => {
if (AMPLITUDE_KEY) {
@@ -48,9 +45,7 @@ function App(): React.JSX.Element {
return (
<YStack f={1} bc={bgWhite} h="100%" w="100%">
<YStack h="100%" w="100%">
<MainScreen />
</YStack>
<AppNavigation />
</YStack>
);
}

View File

@@ -39,4 +39,8 @@ class MainActivity : ReactActivity() {
Log.d("MAIN_ACTIVITY", "onNewIntent: " + intent.action)
RNPassportReaderModule.getInstance().receiveIntent(intent)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}

View File

@@ -7,3 +7,10 @@ declare module '*.jpeg' {
const value: string;
export = value;
}
declare module '*.svg' {
import React from 'react';
import { SvgProps } from 'react-native-svg';
const content: React.FC<SvgProps>;
export default content;
}

View File

@@ -1,5 +1,7 @@
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const path = require('path');
const defaultConfig = getDefaultConfig(__dirname);
const { assetExts, sourceExts } = defaultConfig.resolver;
const commonPath = path.join(__dirname, '/../common');
const extraNodeModules = {
@@ -14,10 +16,17 @@ const watchFolders = [path.resolve(commonPath)];
* @type {import('metro-config').MetroConfig}
*/
const config = {
transformer: {
babelTransformerPath: require.resolve(
'react-native-svg-transformer/react-native',
),
},
resolver: {
extraNodeModules,
assetExts: assetExts.filter(ext => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
},
watchFolders,
};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
module.exports = mergeConfig(defaultConfig, config);

View File

@@ -26,6 +26,9 @@
"@react-native-async-storage/async-storage": "^1.23.1",
"@react-native-community/cli": "^14.1.1",
"@react-native-community/netinfo": "^11.3.1",
"@react-navigation/elements": "^2.2.5",
"@react-navigation/native": "^7.0.14",
"@react-navigation/stack": "^7.1.1",
"@tamagui/colors": "1.110.0",
"@tamagui/config": "1.110.0",
"@tamagui/core": "1.110.0",
@@ -49,10 +52,12 @@
"react-native-date-picker": "https://github.com/norman-kapschefsky/react-native-date-picker#07b13884e392f386611248bab0f2f9b1093b2f35",
"react-native-dialog": "^9.3.0",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.22.1",
"react-native-get-random-values": "^1.11.0",
"react-native-keychain": "^8.2.0",
"react-native-nfc-manager": "^3.15.1",
"react-native-passport-reader": "^1.0.3",
"react-native-safe-area-context": "^5.1.0",
"react-native-svg": "13.4.0",
"react-native-zip-archive": "^6.1.0",
"socket.io-client": "^4.7.5",
@@ -83,6 +88,7 @@
"metro-react-native-babel-preset": "0.76.7",
"prettier": "2.8.8",
"react-native-dotenv": "^3.4.11",
"react-native-svg-transformer": "^1.5.0",
"react-test-renderer": "18.3.1",
"typescript": "5.0.4"
},

144
app/src/Navigation.tsx Normal file
View File

@@ -0,0 +1,144 @@
import 'react-native-gesture-handler';
import React from 'react';
import {
createStaticNavigation,
StaticParamList,
} from '@react-navigation/native';
import {
createStackNavigator,
StackHeaderProps,
} from '@react-navigation/stack';
import LaunchScreen from './screens/LaunchScreen';
import StartScreen from './screens/StartScreen';
import PassportOnboardingScreen from './screens/Onboarding/PassportOnboardingScreen';
import PassportCameraScreen from './screens/Onboarding/PassportCameraScreen';
import SettingsScreen from './screens/SettingsScreen';
import { NavBar } from './components/NavBar';
import MockDataScreen from './screens/MockDataScreen';
import NextScreen from './screens/NextScreen';
import { Button, View } from 'tamagui';
import { Clock9, Settings } from '@tamagui/lucide-icons';
import HomeScreen from './screens/HomeScreen';
import { black, white } from './utils/colors';
import PassportNFCScanScreen from './screens/Onboarding/PassportNFCScanScreen';
const DefaultNavBar = (props: StackHeaderProps) => {
const { goBack, canGoBack } = props.navigation;
return (
<NavBar.Container>
<NavBar.LeftAction
component={canGoBack() ? 'back' : undefined}
onPress={goBack}
/>
<NavBar.Title>{props.options.title}</NavBar.Title>
<View />
</NavBar.Container>
);
};
const HomeNavBar = (props: StackHeaderProps) => {
return (
<NavBar.Container bg={black}>
<NavBar.LeftAction
component={
<Button unstyled icon={<Clock9 size="$4" color={white} />} />
}
/>
<NavBar.Title color={white}>{props.options.title}</NavBar.Title>
<NavBar.RightAction
component={
<Button unstyled icon={<Settings size="$4" color={white} />} />
}
onPress={() => props.navigation.navigate('Settings')}
/>
</NavBar.Container>
);
};
const RootStack = createStackNavigator({
initialRouteName: 'Launch',
screenOptions: {
header: DefaultNavBar,
},
screens: {
Launch: {
if: () => true, // TODO: useIsNewUser
screen: LaunchScreen,
options: {
headerShown: false,
},
},
Start: {
if: () => true, // TODO: useIsNewUser
screen: StartScreen,
options: {
headerShown: false,
},
},
PassportOnboarding: {
screen: PassportOnboardingScreen,
options: {
headerShown: false,
},
},
PassportCamera: {
screen: PassportCameraScreen,
options: {
headerShown: false,
},
},
PassportNFCScan: {
screen: PassportNFCScanScreen,
options: {
headerShown: false,
},
initialParams: {
passportNumber: '',
dateOfBirth: '',
dateOfExpiry: '',
},
},
CreateMock: {
screen: MockDataScreen,
options: {
if: () => true, // TODO: dev only
title: 'Mock Passport',
},
},
// TODO: rename ? maybe summary
NextScreen: {
screen: NextScreen,
options: {
title: 'TODO: NextScreen',
},
},
Home: {
screen: HomeScreen,
options: {
title: 'Self ID',
header: HomeNavBar,
},
},
Settings: {
screen: SettingsScreen,
options: {
title: 'Settings',
},
config: {
screens: {},
},
},
},
});
const AppNavigation = createStaticNavigation(RootStack);
type RootStackParamList = StaticParamList<typeof RootStack>;
declare global {
namespace ReactNavigation {
interface RootParamList extends RootStackParamList {}
}
}
export default AppNavigation;

View File

@@ -0,0 +1,22 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
interface ButtonsContainerProps {
children: React.ReactNode;
}
const ButtonsContainer = ({ children }: ButtonsContainerProps) => {
return <View style={styles.buttonsContainer}>{children}</View>;
};
export default ButtonsContainer;
const styles = StyleSheet.create({
buttonsContainer: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
gap: 10,
},
});

View File

@@ -0,0 +1,99 @@
import React, { useMemo } from 'react';
import {
Button,
H1,
TextProps,
View,
ViewProps,
XStack,
XStackProps,
} from 'tamagui';
import { ChevronLeft, X } from '@tamagui/lucide-icons';
interface NavBarProps extends XStackProps {
children: React.ReactNode;
}
interface LeftActionProps extends ViewProps {
component?: 'back' | 'close' | React.ReactNode;
onPress?: () => void;
}
interface RightActionProps extends ViewProps {
component?: React.ReactNode;
onPress?: () => void;
}
interface TitleProps extends TextProps {
children?: React.ReactNode;
}
export const LeftAction: React.FC<LeftActionProps> = ({
component,
onPress,
...props
}) => {
let children: React.ReactNode = useMemo(() => {
switch (component) {
case 'back':
return <Button unstyled icon={<ChevronLeft size="$4" />} />;
case 'close':
return <Button unstyled icon={<X size="$4" />} />;
case undefined:
case null:
return null;
default:
return <Button unstyled>{component}</Button>;
}
}, [component]);
if (!children) {
return null;
}
return (
<View onPress={onPress} {...props}>
{children}
</View>
);
};
export const RightAction: React.FC<RightActionProps> = ({
component,
onPress,
...props
}) => {
if (!component) {
return null;
}
return (
<View onPress={onPress} {...props}>
{component}
</View>
);
};
const Title: React.FC<TitleProps> = ({ children, ...props }) => {
if (!children) {
return null;
}
return typeof children === 'string' ? (
<H1 {...props}>{children}</H1>
) : (
children
);
};
const Container: React.FC<NavBarProps> = ({ children, ...props }) => {
return (
<XStack flexGrow={1} justifyContent="space-between" {...props}>
{children}
</XStack>
);
};
export const NavBar = {
Container,
Title,
LeftAction,
RightAction,
};

View File

@@ -0,0 +1,23 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
interface TextsContainerProps {
children: React.ReactNode;
}
const TextsContainer = ({ children }: TextsContainerProps) => {
return <View style={styles.textsContainer}>{children}</View>;
};
export default TextsContainer;
const styles = StyleSheet.create({
textsContainer: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 20,
gap: 10,
},
});

View File

@@ -66,7 +66,7 @@ const styles = StyleSheet.create({
padding: 16, // plus 4 of border = 20
},
text: {
fontFamily: 'Cochin',
fontFamily: 'DINOT-Medium',
textAlign: 'center',
fontSize: 18,
},

View File

@@ -0,0 +1,24 @@
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import { slate400 } from '../../utils/colors';
interface AdditionalProps {
text: string;
}
const Additional = ({ text }: AdditionalProps) => {
return <Text style={styles.additional}>{text}</Text>;
};
export default Additional;
const styles = StyleSheet.create({
additional: {
fontSize: 14,
lineHeight: 18,
textAlign: 'center',
color: slate400,
marginTop: 10,
fontFamily: 'DINOT-Medium',
},
});

View File

@@ -0,0 +1,23 @@
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import { slate500 } from '../../utils/colors';
interface DescriptionProps {
text: string;
}
const Description = ({ text }: DescriptionProps) => {
return <Text style={styles.description}>{text}</Text>;
};
export default Description;
const styles = StyleSheet.create({
description: {
color: slate500,
fontSize: 18,
lineHeight: 23,
textAlign: 'center',
fontFamily: 'DINOT-Medium',
},
});

View File

@@ -0,0 +1,22 @@
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import { black } from '../../utils/colors';
interface TitleProps {
text: string;
}
const Title = ({ text }: TitleProps) => {
return <Text style={styles.title}>{text}</Text>;
};
export default Title;
const styles = StyleSheet.create({
title: {
fontSize: 28,
lineHeight: 35,
color: black,
fontFamily: 'Advercase-Regular',
},
});

View File

@@ -0,0 +1,3 @@
<svg width="39" height="36" viewBox="0 0 39 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.4858 27.5098C18.0226 27.5098 16.6396 27.2313 15.3369 26.6743C14.0436 26.1079 12.9014 25.3291 11.9102 24.3379C10.9284 23.3467 10.1543 22.2044 9.58789 20.9111C9.03092 19.6178 8.75244 18.2396 8.75244 16.7764C8.75244 15.4736 8.979 14.237 9.43213 13.0664C9.88525 11.8864 10.5177 10.8197 11.3296 9.86621C12.1414 8.91276 13.076 8.12923 14.1333 7.51562V3.80566C14.1333 2.86165 14.4071 2.12061 14.9546 1.58252C15.5021 1.03499 16.2432 0.76123 17.1777 0.76123H21.8364C22.7899 0.76123 23.5356 1.03499 24.0737 1.58252C24.6118 2.12061 24.8809 2.86165 24.8809 3.80566V7.54395C25.9476 8.16699 26.8774 8.95052 27.6704 9.89453C28.4634 10.8385 29.0817 11.8958 29.5254 13.0664C29.9785 14.237 30.2051 15.4736 30.2051 16.7764C30.2051 18.2396 29.9219 19.6178 29.3555 20.9111C28.7985 22.2044 28.0244 23.3467 27.0332 24.3379C26.042 25.3291 24.8997 26.1079 23.6064 26.6743C22.3132 27.2313 20.9396 27.5098 19.4858 27.5098ZM30.7148 7.54395C30.4411 7.81771 30.106 7.95459 29.7095 7.95459C29.3224 7.95459 28.992 7.81771 28.7183 7.54395C28.4539 7.27962 28.3218 6.94922 28.3218 6.55273C28.3218 6.15625 28.4539 5.82585 28.7183 5.56152L30.814 3.46582C31.0877 3.2015 31.4181 3.06934 31.8052 3.06934C32.2017 3.06934 32.5273 3.2015 32.7822 3.46582C33.056 3.73958 33.1929 4.06999 33.1929 4.45703C33.1929 4.84408 33.056 5.17448 32.7822 5.44824L30.7148 7.54395ZM33.9575 18.1782C33.5705 18.1782 33.2401 18.0413 32.9663 17.7676C32.6925 17.4938 32.5557 17.1634 32.5557 16.7764C32.5557 16.3893 32.6925 16.0589 32.9663 15.7852C33.2401 15.5114 33.5705 15.3745 33.9575 15.3745H36.9028C37.2899 15.3745 37.6203 15.5114 37.894 15.7852C38.1678 16.0589 38.3047 16.3893 38.3047 16.7764C38.3047 17.1634 38.1678 17.4938 37.894 17.7676C37.6203 18.0413 37.2899 18.1782 36.9028 18.1782H33.9575ZM28.7183 27.9912C28.4539 27.7174 28.3218 27.387 28.3218 27C28.3218 26.6035 28.4539 26.2684 28.7183 25.9946C28.992 25.7303 29.3224 25.5981 29.7095 25.5981C30.106 25.5981 30.4411 25.7303 30.7148 25.9946L32.7822 28.1045C33.056 28.3688 33.1929 28.6945 33.1929 29.0815C33.1929 29.478 33.056 29.8084 32.7822 30.0728C32.5273 30.3465 32.2017 30.4834 31.8052 30.4834C31.4181 30.4834 31.0877 30.3465 30.814 30.0728L28.7183 27.9912ZM20.916 31.2056V34.165C20.916 34.5426 20.7791 34.8683 20.5054 35.1421C20.2316 35.4159 19.9012 35.5527 19.5142 35.5527C19.1271 35.5527 18.7967 35.4159 18.5229 35.1421C18.2492 34.8683 18.1123 34.5426 18.1123 34.165V31.2056C18.1123 30.828 18.2492 30.4976 18.5229 30.2144C18.7967 29.9406 19.1271 29.8037 19.5142 29.8037C19.9012 29.8037 20.2316 29.9406 20.5054 30.2144C20.7791 30.4976 20.916 30.828 20.916 31.2056ZM10.2534 27.9912L8.17188 30.0728C7.89811 30.3465 7.56771 30.4834 7.18066 30.4834C6.79362 30.4834 6.46322 30.3465 6.18945 30.0728C5.92513 29.8084 5.79297 29.478 5.79297 29.0815C5.79297 28.6945 5.92513 28.3688 6.18945 28.1045L8.28516 25.9946C8.54948 25.7303 8.87988 25.5981 9.27637 25.5981C9.67285 25.5981 9.99854 25.7303 10.2534 25.9946C10.5272 26.2684 10.6641 26.6035 10.6641 27C10.6641 27.387 10.5272 27.7174 10.2534 27.9912ZM5.02832 18.1782H2.08301C1.69596 18.1782 1.36556 18.0413 1.0918 17.7676C0.818034 17.4938 0.681152 17.1634 0.681152 16.7764C0.681152 16.3893 0.818034 16.0589 1.0918 15.7852C1.36556 15.5114 1.69596 15.3745 2.08301 15.3745H5.02832C5.41536 15.3745 5.74577 15.5114 6.01953 15.7852C6.30273 16.0589 6.44434 16.3893 6.44434 16.7764C6.44434 17.1634 6.30273 17.4938 6.01953 17.7676C5.74577 18.0413 5.41536 18.1782 5.02832 18.1782ZM8.28516 7.54395L6.18945 5.44824C5.92513 5.17448 5.79297 4.84408 5.79297 4.45703C5.79297 4.06999 5.92513 3.73958 6.18945 3.46582C6.46322 3.2015 6.79362 3.06934 7.18066 3.06934C7.56771 3.06934 7.89811 3.2015 8.17188 3.46582L10.2534 5.56152C10.5272 5.82585 10.6641 6.15625 10.6641 6.55273C10.6641 6.94922 10.5272 7.27962 10.2534 7.54395C9.99854 7.81771 9.67285 7.95459 9.27637 7.95459C8.87988 7.95459 8.54948 7.81771 8.28516 7.54395Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,3 @@
<svg width="35" height="35" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.8516 27.9321C11.8148 27.9321 11.0229 27.6587 10.4761 27.1118C9.94059 26.5649 9.67285 25.7617 9.67285 24.7021V10.603C9.67285 9.55485 9.94059 8.75732 10.4761 8.21045C11.0229 7.66357 11.8148 7.39014 12.8516 7.39014H16.3892V15.1831C16.3892 15.7756 16.5487 16.237 16.8677 16.5674C17.1981 16.8978 17.6595 17.063 18.252 17.063H26.062V24.7021C26.062 25.7617 25.7886 26.5649 25.2417 27.1118C24.7062 27.6587 23.9144 27.9321 22.8662 27.9321H12.8516ZM18.8159 15.1831C18.4513 15.1831 18.269 15.0008 18.269 14.6362V7.47559C18.4741 7.50977 18.6849 7.60091 18.9014 7.74902C19.1292 7.89714 19.3685 8.09652 19.6191 8.34717L25.0708 13.8159C25.3328 14.0666 25.5379 14.3058 25.686 14.5337C25.8341 14.7502 25.9253 14.9666 25.9595 15.1831H18.8159ZM2.23877 12.1582C1.12223 12.1582 0.563965 11.5885 0.563965 10.4492V6.29639C0.563965 4.47347 1.03678 3.09489 1.98242 2.16064C2.92806 1.2264 4.32373 0.759277 6.16943 0.759277H10.3223C11.4502 0.759277 12.0142 1.31755 12.0142 2.43408C12.0142 3.56201 11.4502 4.12598 10.3223 4.12598H6.37451C5.57699 4.12598 4.96745 4.33105 4.5459 4.74121C4.13574 5.15137 3.93066 5.7723 3.93066 6.604V10.4492C3.93066 11.5885 3.3667 12.1582 2.23877 12.1582ZM32.7441 12.1582C31.6276 12.1582 31.0693 11.5885 31.0693 10.4492V6.604C31.0693 5.7723 30.8529 5.15137 30.4199 4.74121C29.9984 4.33105 29.3945 4.12598 28.6084 4.12598H24.6606C23.5327 4.12598 22.9688 3.56201 22.9688 2.43408C22.9688 1.31755 23.5327 0.759277 24.6606 0.759277H28.8135C30.6706 0.759277 32.0719 1.2264 33.0176 2.16064C33.9632 3.09489 34.436 4.47347 34.436 6.29639V10.4492C34.436 11.5885 33.8721 12.1582 32.7441 12.1582ZM6.16943 34.6143C4.32373 34.6143 2.92806 34.1414 1.98242 33.1958C1.03678 32.2616 0.563965 30.883 0.563965 29.0601V24.9243C0.563965 23.785 1.12223 23.2153 2.23877 23.2153C3.3667 23.2153 3.93066 23.785 3.93066 24.9243V28.7695C3.93066 29.6012 4.13574 30.2222 4.5459 30.6323C4.96745 31.0425 5.57699 31.2476 6.37451 31.2476H10.3223C11.4502 31.2476 12.0142 31.8115 12.0142 32.9395C12.0142 34.056 11.4502 34.6143 10.3223 34.6143H6.16943ZM24.6606 34.6143C23.5327 34.6143 22.9688 34.056 22.9688 32.9395C22.9688 31.8115 23.5327 31.2476 24.6606 31.2476H28.6084C29.3945 31.2476 29.9984 31.0425 30.4199 30.6323C30.8529 30.2222 31.0693 29.6012 31.0693 28.7695V24.9243C31.0693 23.785 31.6276 23.2153 32.7441 23.2153C33.8721 23.2153 34.436 23.785 34.436 24.9243V29.0601C34.436 30.883 33.9632 32.2616 33.0176 33.1958C32.0719 34.1414 30.6706 34.6143 28.8135 34.6143H24.6606Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

10
app/src/images/logo.svg Normal file
View File

@@ -0,0 +1,10 @@
<svg width="313" height="138" viewBox="0 0 313 138" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M145.696 38.2872C145.565 38.418 145.409 38.4858 145.227 38.4906C145.05 38.5001 144.879 38.4466 144.716 38.33C143.835 37.6634 143.051 37.1087 142.365 36.6659C141.68 36.2231 141.055 35.8923 140.49 35.6734C139.921 35.4499 139.373 35.3407 138.845 35.3458C138.318 35.3416 137.774 35.4517 137.214 35.6762C136.654 35.9007 136.039 36.2349 135.372 36.6789C134.699 37.1181 133.931 37.6672 133.067 38.326C132.908 38.4568 132.736 38.5223 132.549 38.5224C132.362 38.5132 132.201 38.441 132.066 38.3058C131.931 38.1706 131.861 38.012 131.856 37.8299C131.852 37.6386 131.913 37.4612 132.039 37.2977C132.852 36.289 133.501 35.4063 133.987 34.6499C134.473 33.884 134.796 33.179 134.955 32.5348C135.119 31.886 135.122 31.2349 134.964 30.5817C134.801 29.9237 134.475 29.2006 133.986 28.4123C133.496 27.624 132.846 26.7028 132.035 25.6487C131.918 25.4855 131.865 25.3152 131.874 25.1378C131.884 24.9605 131.954 24.8064 132.085 24.6756C132.215 24.5448 132.37 24.4747 132.547 24.4652C132.724 24.4557 132.895 24.5092 133.058 24.6258C134.112 25.4369 135.033 26.0872 135.821 26.5766C136.61 27.066 137.33 27.3944 137.984 27.5618C138.637 27.72 139.286 27.7194 139.93 27.5602C140.579 27.3963 141.286 27.0713 142.052 26.5853C142.813 26.0946 143.698 25.4428 144.707 24.6299C144.866 24.5084 145.041 24.4499 145.232 24.4544C145.419 24.4542 145.58 24.5218 145.715 24.657C145.85 24.7922 145.92 24.9555 145.924 25.1469C145.924 25.3336 145.861 25.504 145.735 25.6581C144.927 26.6715 144.28 27.5611 143.794 28.327C143.303 29.0881 142.976 29.7931 142.812 30.442C142.652 31.0862 142.654 31.7372 142.817 32.3951C142.98 33.0437 143.306 33.7622 143.795 34.5505C144.28 35.3342 144.928 36.253 145.739 37.3071C145.856 37.4703 145.909 37.6406 145.9 37.818C145.895 38 145.827 38.1564 145.696 38.2872Z" fill="#FFFBEB"/>
<path d="M163.324 55.9151C163.194 56.0459 163.037 56.1137 162.855 56.1186C162.678 56.128 162.508 56.0745 162.344 55.958C161.463 55.2913 160.679 54.7366 159.994 54.2938C159.308 53.851 158.683 53.5202 158.118 53.3013C157.549 53.0778 157.001 52.9686 156.473 52.9737C155.946 52.9695 155.402 53.0796 154.842 53.3041C154.282 53.5286 153.668 53.8628 153 54.3068C152.327 54.7461 151.559 55.2951 150.695 55.9539C150.537 56.0847 150.364 56.1502 150.177 56.1503C149.99 56.1412 149.829 56.069 149.694 55.9337C149.559 55.7985 149.489 55.6399 149.485 55.4579C149.48 55.2665 149.541 55.0891 149.667 54.9257C150.48 53.9169 151.129 53.0343 151.616 52.2778C152.102 51.512 152.424 50.807 152.583 50.1628C152.747 49.5139 152.75 48.8629 152.592 48.2096C152.429 47.5517 152.103 46.8286 151.614 46.0402C151.124 45.2519 150.474 44.3307 149.663 43.2767C149.546 43.1134 149.493 42.9431 149.502 42.7658C149.512 42.5884 149.582 42.4343 149.713 42.3035C149.844 42.1727 149.998 42.1026 150.175 42.0931C150.352 42.0836 150.523 42.1372 150.686 42.2537C151.74 43.0649 152.661 43.7151 153.45 44.2045C154.238 44.6939 154.959 45.0223 155.612 45.1898C156.265 45.3479 156.914 45.3473 157.558 45.1881C158.207 45.0242 158.914 44.6992 159.68 44.2132C160.441 43.7225 161.326 43.0707 162.335 42.2578C162.494 42.1363 162.669 42.0778 162.86 42.0823C163.047 42.0822 163.208 42.1497 163.343 42.2849C163.478 42.4202 163.548 42.5835 163.553 42.7748C163.552 42.9615 163.489 43.1319 163.363 43.286C162.555 44.2995 161.908 45.1891 161.422 45.9549C160.931 46.716 160.604 47.421 160.44 48.0699C160.281 48.7141 160.282 49.3652 160.445 50.0231C160.608 50.6717 160.934 51.3901 161.423 52.1784C161.908 52.9621 162.556 53.8809 163.367 54.935C163.484 55.0983 163.537 55.2686 163.528 55.4459C163.523 55.6279 163.455 55.7843 163.324 55.9151Z" fill="#FFFBEB"/>
<path d="M163.288 20.6954C163.157 20.8262 163 20.894 162.818 20.8988C162.641 20.9083 162.471 20.8548 162.307 20.7383C161.426 20.0716 160.642 19.5169 159.957 19.0741C159.271 18.6313 158.646 18.3005 158.081 18.0816C157.512 17.8581 156.964 17.7489 156.436 17.754C155.909 17.7498 155.365 17.8599 154.805 18.0844C154.245 18.3089 153.631 18.6431 152.963 19.0871C152.291 19.5263 151.522 20.0754 150.658 20.7342C150.5 20.865 150.327 20.9305 150.14 20.9306C149.954 20.9214 149.793 20.8492 149.657 20.714C149.522 20.5788 149.452 20.4202 149.448 20.2381C149.443 20.0468 149.504 19.8694 149.63 19.7059C150.443 18.6972 151.093 17.8145 151.579 17.0581C152.065 16.2922 152.387 15.5872 152.547 14.943C152.71 14.2942 152.713 13.6431 152.555 12.9899C152.392 12.332 152.066 11.6088 151.577 10.8205C151.088 10.0322 150.437 9.111 149.626 8.05693C149.51 7.89368 149.456 7.72338 149.466 7.54603C149.475 7.36867 149.545 7.2146 149.676 7.08381C149.807 6.95302 149.961 6.88288 150.138 6.87339C150.316 6.86391 150.486 6.91744 150.649 7.03397C151.703 7.84515 152.624 8.49542 153.413 8.98479C154.201 9.47417 154.922 9.80258 155.575 9.97004C156.228 10.1282 156.877 10.1276 157.521 9.96838C158.17 9.80449 158.877 9.47952 159.643 8.99349C160.404 8.50281 161.289 7.851 162.298 7.03807C162.457 6.91659 162.632 6.8581 162.823 6.8626C163.01 6.86245 163.171 6.92998 163.306 7.06521C163.441 7.20044 163.511 7.36373 163.516 7.55508C163.516 7.74176 163.452 7.91216 163.326 8.06628C162.518 9.07973 161.871 9.96935 161.385 10.7352C160.894 11.4963 160.567 12.2013 160.403 12.8502C160.244 13.4944 160.246 14.1454 160.408 14.8033C160.571 15.4519 160.897 16.1704 161.387 16.9587C161.871 17.7424 162.519 18.6612 163.33 19.7153C163.447 19.8785 163.5 20.0488 163.491 20.2262C163.486 20.4082 163.418 20.5646 163.288 20.6954Z" fill="#FFFBEB"/>
<path d="M180.915 38.3243C180.784 38.4551 180.628 38.5229 180.446 38.5277C180.269 38.5372 180.098 38.4837 179.935 38.3672C179.054 37.7005 178.27 37.1458 177.584 36.703C176.899 36.2602 176.274 35.9294 175.709 35.7105C175.14 35.487 174.592 35.3778 174.064 35.3829C173.537 35.3787 172.993 35.4888 172.433 35.7133C171.872 35.9378 171.258 36.272 170.591 36.716C169.918 37.1552 169.15 37.7043 168.286 38.3631C168.127 38.4939 167.955 38.5594 167.768 38.5595C167.581 38.5503 167.42 38.4781 167.285 38.3429C167.15 38.2077 167.08 38.0491 167.075 37.867C167.071 37.6757 167.132 37.4983 167.258 37.3348C168.071 36.3261 168.72 35.4434 169.206 34.687C169.692 33.9211 170.015 33.2161 170.174 32.572C170.338 31.9231 170.341 31.272 170.183 30.6188C170.02 29.9609 169.694 29.2377 169.205 28.4494C168.715 27.6611 168.065 26.7399 167.254 25.6858C167.137 25.5226 167.084 25.3523 167.093 25.1749C167.103 24.9976 167.173 24.8435 167.304 24.7127C167.434 24.5819 167.589 24.5118 167.766 24.5023C167.943 24.4928 168.114 24.5463 168.277 24.6629C169.331 25.4741 170.252 26.1243 171.04 26.6137C171.829 27.1031 172.549 27.4315 173.203 27.5989C173.856 27.7571 174.505 27.7565 175.149 27.5973C175.798 27.4334 176.505 27.1084 177.271 26.6224C178.032 26.1317 178.917 25.4799 179.926 24.667C180.085 24.5455 180.26 24.487 180.451 24.4915C180.638 24.4914 180.799 24.5589 180.934 24.6941C181.069 24.8293 181.139 24.9926 181.143 25.184C181.143 25.3707 181.08 25.5411 180.954 25.6952C180.146 26.7086 179.499 27.5983 179.013 28.3641C178.522 29.1252 178.195 29.8302 178.031 30.4791C177.871 31.1233 177.873 31.7743 178.036 32.4323C178.199 33.0808 178.525 33.7993 179.014 34.5876C179.499 35.3713 180.147 36.2901 180.958 37.3442C181.075 37.5074 181.128 37.6777 181.119 37.8551C181.114 38.0371 181.046 38.1935 180.915 38.3243Z" fill="#FFFBEB"/>
<path d="M156.595 41.2566C156.41 41.2566 156.251 41.1939 156.119 41.0686C155.987 40.9499 155.905 40.7916 155.872 40.5938C155.72 39.4991 155.558 38.5528 155.386 37.7548C155.214 36.9569 155.006 36.2809 154.762 35.727C154.517 35.1664 154.207 34.7015 153.83 34.3322C153.461 33.9563 152.998 33.6497 152.443 33.4122C151.888 33.1748 151.218 32.977 150.432 32.8187C149.646 32.6539 148.714 32.4989 147.637 32.3538C147.433 32.334 147.264 32.2582 147.132 32.1263C147.007 31.9878 146.944 31.8229 146.944 31.6317C146.944 31.4405 147.007 31.2789 147.132 31.147C147.264 31.0085 147.433 30.9261 147.637 30.8997C148.926 30.7612 150.009 30.5963 150.888 30.4051C151.773 30.2073 152.499 29.9369 153.067 29.594C153.642 29.251 154.105 28.7927 154.455 28.219C154.805 27.6386 155.086 26.8968 155.297 25.9933C155.508 25.0898 155.7 23.9786 155.872 22.6597C155.905 22.4619 155.987 22.3036 156.119 22.1849C156.251 22.0662 156.41 22.0068 156.595 22.0068C156.78 22.0068 156.939 22.0662 157.071 22.1849C157.203 22.3036 157.285 22.4619 157.318 22.6597C157.49 23.9786 157.682 25.0898 157.893 25.9933C158.104 26.8968 158.382 27.6386 158.725 28.219C159.075 28.7927 159.535 29.251 160.103 29.594C160.677 29.9369 161.407 30.2073 162.293 30.4051C163.178 30.5963 164.264 30.7612 165.553 30.8997C165.751 30.9261 165.916 31.0085 166.048 31.147C166.18 31.2789 166.246 31.4405 166.246 31.6317C166.246 31.8229 166.18 31.9878 166.048 32.1263C165.916 32.2582 165.751 32.334 165.553 32.3538C164.264 32.4989 163.178 32.6703 162.293 32.8682C161.407 33.0594 160.677 33.3265 160.103 33.6694C159.535 34.0124 159.075 34.474 158.725 35.0543C158.382 35.628 158.104 36.3666 157.893 37.2701C157.682 38.167 157.49 39.2749 157.318 40.5938C157.285 40.7916 157.203 40.9499 157.071 41.0686C156.939 41.1939 156.78 41.2566 156.595 41.2566Z" fill="#FFFBEB"/>
<rect x="137.979" y="30.5322" width="24.8774" height="2.94531" transform="rotate(-45 137.979 30.5322)" fill="#FFFBEB"/>
<rect x="155.607" y="48.1611" width="24.8774" height="2.94531" transform="rotate(-45 155.607 48.1611)" fill="#FFFBEB"/>
<path d="M104.536 94.6362C103.864 89.0682 100.84 85.8522 96.28 85.8522C92.92 85.8522 90.952 87.9162 90.952 91.2282C90.952 100.444 108.184 100.924 108.184 114.796C108.184 121.9 103.624 126.508 96.328 126.508C90.136 126.508 86.44 124.3 86.44 122.236C86.44 121.468 86.632 119.74 86.632 118.012C86.632 116.044 86.44 114.124 86.44 113.74C86.44 112.924 87.016 112.684 87.448 112.684C87.88 112.684 88.264 112.876 88.456 113.644C90.376 121.228 93.112 124.348 97.864 124.348C101.464 124.348 103.576 121.996 103.576 117.964C103.576 106.06 86.44 106.588 86.44 94.3962C86.44 87.9162 90.616 83.6922 97.48 83.6922C101.08 83.6922 106.504 84.9402 106.504 87.1962C106.504 87.8682 106.408 89.5962 106.408 91.3242L106.504 95.0682C106.504 95.8362 106.072 96.0762 105.64 96.0762C105.16 96.0762 104.68 95.8842 104.536 94.6362ZM123.719 105.724C126.791 105.724 128.615 104.188 128.615 101.548C128.615 98.4282 126.743 96.4122 124.055 96.4122C120.551 96.4122 118.343 99.4362 117.623 105.244C117.575 105.58 117.719 105.724 118.007 105.724H123.719ZM111.095 110.764C111.095 100.636 116.039 94.0122 123.623 94.0122C129.479 94.0122 133.319 98.3322 133.319 105.676C133.319 107.5 132.839 108.124 131.735 108.124H117.815C117.527 108.124 117.383 108.268 117.383 108.46L117.335 110.332C117.335 118.252 120.407 123.388 125.639 123.388C128.135 123.388 129.815 122.476 131.735 120.748C132.119 120.412 132.551 120.364 132.887 120.604C133.223 120.844 133.271 121.276 133.079 121.66C131.639 124.588 128.087 126.46 124.055 126.46C116.183 126.46 111.095 120.268 111.095 110.764ZM143.866 120.892C143.866 123.388 145.354 123.916 146.41 124.156C147.034 124.3 147.178 124.588 147.178 124.972C147.178 125.308 146.986 125.74 146.362 125.74C145.834 125.74 143.482 125.452 140.938 125.452C138.49 125.452 136.138 125.74 135.61 125.74C134.986 125.74 134.794 125.308 134.794 124.972C134.794 124.588 134.938 124.3 135.562 124.156C136.618 123.916 138.106 123.388 138.106 120.892V85.5162C138.106 84.1722 137.578 83.7882 136.522 83.5002C135.994 83.3562 135.85 83.0682 135.85 82.6842C135.85 82.3482 135.946 82.0122 136.57 81.9162C138.442 81.6282 141.418 80.8122 142.714 79.5162C143.002 79.2282 143.098 79.1802 143.338 79.1802C143.722 79.1802 143.866 79.3242 143.866 79.6602V120.892ZM163.998 94.7802C164.766 94.7802 165.294 95.2122 165.294 95.9322C165.294 96.7482 164.958 97.1802 163.998 97.1802H159.006C158.286 97.1802 157.95 97.4682 157.95 98.0442V120.892C157.95 123.388 159.438 123.916 160.494 124.156C161.118 124.3 161.262 124.588 161.262 124.972C161.262 125.308 161.07 125.74 160.446 125.74C159.918 125.74 157.566 125.452 155.07 125.452C152.574 125.452 150.222 125.74 149.694 125.74C149.07 125.74 148.878 125.308 148.878 124.972C148.878 124.588 149.021 124.3 149.646 124.156C150.702 123.916 152.19 123.388 152.19 120.892V98.0922C152.19 97.4682 151.902 97.2282 151.326 97.2282L149.982 97.2762C149.502 97.2762 149.118 96.9402 149.118 96.3642C149.118 95.9322 149.262 95.6442 149.79 95.4522L151.422 94.8282C151.95 94.5882 152.19 94.2522 152.19 93.8202V92.0922C152.19 84.6522 156.558 79.2282 162.606 79.2282C166.686 79.2282 169.326 81.8202 169.326 84.7482C169.326 86.1882 168.51 87.1002 167.31 87.1002C166.206 87.1002 165.39 86.6682 164.862 85.2762C163.998 82.9242 163.326 81.3882 161.694 81.3882C159.63 81.3882 157.95 84.1242 157.95 92.3802V93.9162C157.95 94.5402 158.334 94.7802 159.054 94.7802H163.998ZM182.783 89.3082C182.783 86.8122 181.343 86.4282 179.759 86.0442C179.135 85.9002 178.991 85.6122 178.991 85.2282C178.991 84.8922 179.183 84.4602 179.807 84.4602C180.335 84.4602 183.167 84.7482 185.903 84.7482C188.639 84.7482 191.471 84.4602 191.999 84.4602C192.623 84.4602 192.815 84.8922 192.815 85.2282C192.815 85.6122 192.671 85.9002 192.047 86.0442C190.511 86.4282 189.023 86.8122 189.023 89.3082V120.892C189.023 123.388 190.703 123.868 192.047 124.156C192.671 124.3 192.815 124.588 192.815 124.972C192.815 125.308 192.623 125.74 191.999 125.74C191.471 125.74 188.639 125.452 185.903 125.452C183.167 125.452 180.335 125.74 179.807 125.74C179.183 125.74 178.991 125.308 178.991 124.972C178.991 124.588 179.135 124.3 179.759 124.156C181.055 123.868 182.783 123.388 182.783 120.892V89.3082ZM208.544 123.82C216.224 123.82 220.256 117.436 220.256 105.148C220.256 92.8122 216.224 86.3802 208.544 86.3802C205.904 86.3802 204.368 87.0042 204.368 89.7882V120.7C204.368 123.436 206.144 123.82 208.544 123.82ZM195.248 125.74C194.48 125.74 194.288 125.308 194.288 124.972C194.288 124.588 194.432 124.252 195.2 124.108C196.448 123.868 198.128 123.388 198.128 121.852V88.3482C198.128 86.8122 196.448 86.3322 195.2 86.0922C194.432 85.9482 194.288 85.6122 194.288 85.2282C194.288 84.8922 194.48 84.4602 195.248 84.4602C195.92 84.4602 198.32 84.6522 201.728 84.6522C205.04 84.6522 208.304 84.4602 210.608 84.4602C220.928 84.4602 226.976 92.0922 226.976 105.148C226.976 118.156 220.928 125.74 210.608 125.74C208.448 125.74 205.424 125.596 202.448 125.596C198.992 125.596 196.496 125.74 195.248 125.74Z" fill="#FFFBEB"/>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

BIN
app/src/images/map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
app/src/images/passport.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -59,13 +59,16 @@ const styles = StyleSheet.create({
topSection: {
alignSelf: 'stretch',
flexGrow: 1,
flexShrink: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: black,
overflow: 'hidden',
padding: 20, // TODO necessary?
},
bottomSection: {
backgroundColor: white,
paddingTop: 50,
paddingTop: 30,
paddingLeft: 20,
paddingRight: 20,
},

View File

@@ -0,0 +1,67 @@
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import { Image, YStack } from 'tamagui';
import Logo from '../images/logo.png';
import { ExpandableBottomLayout } from '../layouts/ExpandableBottomLayout';
import { PrimaryButton } from '../components/buttons/PrimaryButton';
import { slate50, slate100, slate700, slate500 } from '../utils/colors';
import { useNavigation } from '@react-navigation/native';
interface AccountRecoveryScreenProps {}
const AccountRecoveryScreen: React.FC<AccountRecoveryScreenProps> = ({}) => {
const navigation = useNavigation();
return (
<ExpandableBottomLayout.Layout>
<ExpandableBottomLayout.TopSection>
<Image src={Logo} />
</ExpandableBottomLayout.TopSection>
<ExpandableBottomLayout.BottomSection>
<YStack gap="$2.5">
<Text style={styles.subheader}>
By continuing, you certify that this passport belongs to you and is
not stolen or forged.
</Text>
<PrimaryButton onPress={() => navigation.navigate('TODO: restore')}>
Restore my account
</PrimaryButton>
<PrimaryButton
onPress={() => navigation.navigate('PassportOnboarding')}
>
Create new account
</PrimaryButton>
</YStack>
</ExpandableBottomLayout.BottomSection>
</ExpandableBottomLayout.Layout>
);
};
export default AccountRecoveryScreen;
const styles = StyleSheet.create({
subheader: {
color: slate700,
// fontWeight: '500',
fontSize: 20,
lineHeight: 26,
textAlign: 'center',
},
link: {
textDecorationLine: 'underline',
},
notice: {
paddingTop: 10,
paddingBottom: 10,
paddingLeft: 20,
paddingRight: 20,
backgroundColor: slate50,
borderColor: slate100,
borderWidth: 1,
borderStyle: 'solid',
color: slate500,
textAlign: 'center',
lineHeight: 18,
},
});

View File

@@ -0,0 +1,23 @@
import React from 'react';
import { Image, Text, View, YStack } from 'tamagui';
import MAP from '../images/map.png';
import { black, textBlack } from '../utils/colors';
const HomeScreen: React.FC = () => {
return (
<YStack f={1} px="$4" bg={black}>
<YStack f={1} mt="$6" mb="$2.5" gap="$0" ai="center" jc="space-between">
<View>
<Image src={MAP} />
</View>
<Text textAlign="center" fontSize="$4" color={textBlack}>
No personal information will be shared without your explicit consent.
</Text>
</YStack>
</YStack>
);
};
export default HomeScreen;

View File

@@ -1,22 +1,22 @@
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import { Image, YStack } from 'tamagui';
import { Anchor, YStack } from 'tamagui';
import Logo from '../images/logo.png';
import { ExpandableBottomLayout } from '../components/ExpandableBottomLayout';
import Logo from '../images/logo.svg';
import { ExpandableBottomLayout } from '../layouts/ExpandableBottomLayout';
import { PrimaryButton } from '../components/buttons/PrimaryButton';
import { slate50, slate100, slate700, slate500 } from '../utils/colors';
import useNavigationStore from '../stores/navigationStore';
import { useNavigation } from '@react-navigation/native';
interface LaunchScreenProps {}
const LaunchScreen: React.FC<LaunchScreenProps> = ({}) => {
const { setSelectedTab } = useNavigationStore();
const navigation = useNavigation();
return (
<ExpandableBottomLayout.Layout>
<ExpandableBottomLayout.TopSection>
<Image src={Logo} />
<Logo />
</ExpandableBottomLayout.TopSection>
<ExpandableBottomLayout.BottomSection>
<YStack gap="$2.5">
@@ -27,12 +27,16 @@ const LaunchScreen: React.FC<LaunchScreenProps> = ({}) => {
{/* TODO add linking */}
<Text style={styles.notice}>
By continuing, you agree to the&nbsp;
<Text style={styles.link}>User Terms and Conditions</Text>&nbsp;and
acknowledge the&nbsp;
<Text style={styles.link}>Privacy notice</Text>&nbsp;of Self ID
provided by Self Inc.
<Anchor style={styles.link} href="https://example.com">
User Terms and Conditions
</Anchor>
&nbsp;and acknowledge the&nbsp;
<Anchor style={styles.link} href="https://example.com">
Privacy notice
</Anchor>
&nbsp;of Self ID provided by Self Inc.
</Text>
<PrimaryButton onPress={() => setSelectedTab('app')}>
<PrimaryButton onPress={() => navigation.navigate('Start')}>
Get Started
</PrimaryButton>
</YStack>

View File

@@ -237,29 +237,29 @@ const MainScreen: React.FC = () => {
// }, [modalProofStep]);
const decrementStep = () => {
if (selectedTab === 'scan') {
setSelectedTab('start');
} else if (selectedTab === 'nfc') {
setSelectedTab('scan');
} else if (selectedTab === 'mock') {
setSelectedTab('start');
} else if (selectedTab === 'next') {
if (passportData?.mockUser) {
setSelectedTab('mock');
} else {
setSelectedTab('nfc');
}
} else if (selectedTab === 'app') {
setSelectedTab('next');
} else if (selectedTab === 'prove') {
setSelectedTab('app');
} else if (selectedTab === 'wrong') {
setSelectedTab('app');
} else if (selectedTab === 'valid') {
setSelectedTab('app');
} else if (selectedTab === 'userInfo') {
setSelectedTab('app');
}
// if (selectedTab === 'scan') {
// setSelectedTab('start');
// } else if (selectedTab === 'nfc') {
// setSelectedTab('scan');
// } else if (selectedTab === 'mock') {
// setSelectedTab('start');
// } else if (selectedTab === 'next') {
// if (passportData?.mockUser) {
// setSelectedTab('mock');
// } else {
// setSelectedTab('nfc');
// }
// } else if (selectedTab === 'app') {
// setSelectedTab('next');
// } else if (selectedTab === 'prove') {
// setSelectedTab('app');
// } else if (selectedTab === 'wrong') {
// setSelectedTab('app');
// } else if (selectedTab === 'valid') {
// setSelectedTab('app');
// } else if (selectedTab === 'userInfo') {
// setSelectedTab('app');
// }
};
useEffect(() => {

View File

@@ -1,9 +1,12 @@
import React, { useCallback, useState } from 'react';
import { ChevronDown, Cpu, Minus, Plus } from '@tamagui/lucide-icons';
import { ChevronDown, Cpu, Minus, Plus, X } from '@tamagui/lucide-icons';
import {
Button,
Fieldset,
ScrollView,
Separator,
Sheet,
Spinner,
Switch,
Text,
@@ -17,23 +20,20 @@ import getCountryISO2 from 'country-iso-3-to-2';
import { countryCodes } from '../../../common/src/constants/constants';
import { genMockPassportData } from '../../../common/src/utils/genMockPassportData';
import CustomButton from '../components/CustomButton';
import useNavigationStore from '../stores/navigationStore';
import useUserStore from '../stores/userStore';
import { borderColor, textBlack } from '../utils/colors';
import {
bgWhite,
borderColor,
separatorColor,
textBlack,
} from '../utils/colors';
import { TouchableOpacity } from 'react-native';
import { useNavigation } from '@react-navigation/native';
interface MockDataScreenProps {
onCountryPress: () => void;
onAlgorithmPress: () => void;
selectedCountry: string;
selectedAlgorithm: string;
}
interface MockDataScreenProps {}
const MockDataScreen: React.FC<MockDataScreenProps> = ({
onCountryPress,
onAlgorithmPress,
selectedCountry,
selectedAlgorithm,
}) => {
const MockDataScreen: React.FC<MockDataScreenProps> = ({}) => {
const navigation = useNavigation();
const [age, setAge] = useState(24);
const [expiryYears, setExpiryYears] = useState(5);
const [isGenerating, setIsGenerating] = useState(false);
@@ -48,7 +48,21 @@ const MockDataScreen: React.FC<MockDataScreenProps> = ({
).toString();
};
const { toast } = useNavigationStore();
const [selectedCountry, setSelectedCountry] = useState('USA');
const [selectedAlgorithm, setSelectedAlgorithm] = useState('rsa sha256');
const [isCountrySheetOpen, setCountrySheetOpen] = useState(false);
const [isAlgorithmSheetOpen, setAlgorithmSheetOpen] = useState(false);
const handleCountrySelect = (countryCode: string) => {
setSelectedCountry(countryCode);
setCountrySheetOpen(false);
};
const handleAlgorithmSelect = (algorithm: string) => {
setSelectedAlgorithm(algorithm);
setAlgorithmSheetOpen(false);
};
const signatureAlgorithmToStrictSignatureAlgorithm = {
'rsa sha256': 'rsa_sha256_65537_2048',
'rsa sha1': 'rsa_sha1_65537_2048',
@@ -83,147 +97,58 @@ const MockDataScreen: React.FC<MockDataScreenProps> = ({
}, 0),
);
toast.show('🤖', {
message: 'Passport generated',
customData: {
type: 'success',
},
});
await new Promise(resolve => setTimeout(resolve, 1000));
useNavigationStore.getState().setSelectedTab('next');
navigation.navigate('NextScreen');
}, [selectedAlgorithm, selectedCountry, age, expiryYears, isInOfacList]);
return (
<YStack f={1} gap="$4">
<Text my="$9" textAlign="center" fontSize="$9" color={textBlack}>
Generate passport data
</Text>
<XStack ai="center">
<Text f={1} fontSize="$5">
Encryption
<>
<YStack f={1} gap="$4" px="$4">
<Text my="$9" textAlign="center" fontSize="$9" color={textBlack}>
Generate passport data
</Text>
<Button
onPress={onAlgorithmPress}
p="$2"
px="$3"
bg="white"
borderColor={borderColor}
borderWidth={1}
borderRadius="$4"
>
<XStack ai="center" gap="$2">
<Text fontSize="$4">{selectedAlgorithm}</Text>
<ChevronDown size={20} />
</XStack>
</Button>
</XStack>
<XStack ai="center">
<Text f={1} fontSize="$5">
Nationality
</Text>
<Button
onPress={onCountryPress}
p="$2"
px="$3"
bg="white"
borderColor={borderColor}
borderWidth={1}
borderRadius="$4"
>
<XStack ai="center" gap="$2">
<Text fontSize="$4">
{countryCodes[selectedCountry as keyof typeof countryCodes]}{' '}
{flag(getCountryISO2(selectedCountry))}
</Text>
<ChevronDown size={20} />
</XStack>
</Button>
</XStack>
<XStack ai="center">
<Text f={1} fontSize="$5">
Encryption
</Text>
<Button
onPress={() => setAlgorithmSheetOpen(true)}
p="$2"
px="$3"
bg="white"
borderColor={borderColor}
borderWidth={1}
borderRadius="$4"
>
<XStack ai="center" gap="$2">
<Text fontSize="$4">{selectedAlgorithm}</Text>
<ChevronDown size={20} />
</XStack>
</Button>
</XStack>
<XStack ai="center">
<Text f={1} fontSize="$5">
Nationality
</Text>
<Button
onPress={() => setCountrySheetOpen(true)}
p="$2"
px="$3"
bg="white"
borderColor={borderColor}
borderWidth={1}
borderRadius="$4"
>
<XStack ai="center" gap="$2">
<Text fontSize="$4">
{countryCodes[selectedCountry as keyof typeof countryCodes]}{' '}
{flag(getCountryISO2(selectedCountry))}
</Text>
<ChevronDown size={20} />
</XStack>
</Button>
</XStack>
<Fieldset mt="$2" gap="$2" horizontal>
<Text
color={textBlack}
width={160}
justifyContent="flex-end"
fontSize="$5"
>
Age (🎂)
</Text>
<XStack f={1} />
<Button
h="$3.5"
w="$3.5"
bg="white"
jc="center"
borderColor={borderColor}
borderWidth={1}
borderRadius="$10"
onPress={() => setAge(age - 1)}
disabled={age <= 0}
>
<Minus />
</Button>
<Text textAlign="center" w="$6" color={textBlack} fontSize="$5">
{age} yo
</Text>
<Button
h="$3.5"
w="$3.5"
bg="white"
jc="center"
borderColor={borderColor}
borderWidth={1}
borderRadius="$10"
onPress={() => setAge(age + 1)}
>
<Plus />
</Button>
</Fieldset>
<Fieldset gap="$2" horizontal>
<Text
color={textBlack}
width={160}
justifyContent="flex-end"
fontSize="$5"
>
Passport expires in
</Text>
<XStack f={1} />
<Button
h="$3.5"
w="$3.5"
bg="white"
jc="center"
borderColor={borderColor}
borderWidth={1}
borderRadius="$10"
onPress={() => setExpiryYears(expiryYears - 1)}
disabled={expiryYears <= 0}
>
<Minus />
</Button>
<Text textAlign="center" w="$6" color={textBlack} fontSize="$5">
{expiryYears} years
</Text>
<Button
h="$3.5"
w="$3.5"
bg="white"
jc="center"
borderColor={borderColor}
borderWidth={1}
borderRadius="$10"
onPress={() => setExpiryYears(expiryYears + 1)}
>
<Plus />
</Button>
</Fieldset>
<YStack>
<Fieldset mt="$2" gap="$2" horizontal>
<Text
color={textBlack}
@@ -231,44 +156,212 @@ const MockDataScreen: React.FC<MockDataScreenProps> = ({
justifyContent="flex-end"
fontSize="$5"
>
Is in OFAC list
Age (🎂)
</Text>
<XStack f={1} />
<Switch
size="$3.5"
checked={isInOfacList}
onCheckedChange={() => setIsInOfacList(!isInOfacList)}
bg={isInOfacList ? '$green7Light' : '$gray4'}
<Button
h="$3.5"
w="$3.5"
bg="white"
jc="center"
borderColor={borderColor}
borderWidth={1}
borderRadius="$10"
onPress={() => setAge(age - 1)}
disabled={age <= 0}
>
<Switch.Thumb animation="quick" bc="white" />
</Switch>
<Minus />
</Button>
<Text textAlign="center" w="$6" color={textBlack} fontSize="$5">
{age} yo
</Text>
<Button
h="$3.5"
w="$3.5"
bg="white"
jc="center"
borderColor={borderColor}
borderWidth={1}
borderRadius="$10"
onPress={() => setAge(age + 1)}
>
<Plus />
</Button>
</Fieldset>
<Text
mt="$2"
color="$red10"
justifyContent="flex-end"
fontSize="$3"
style={{ opacity: isInOfacList ? 1 : 0 }}
<Fieldset gap="$2" horizontal>
<Text
color={textBlack}
width={160}
justifyContent="flex-end"
fontSize="$5"
>
Passport expires in
</Text>
<XStack f={1} />
<Button
h="$3.5"
w="$3.5"
bg="white"
jc="center"
borderColor={borderColor}
borderWidth={1}
borderRadius="$10"
onPress={() => setExpiryYears(expiryYears - 1)}
disabled={expiryYears <= 0}
>
<Minus />
</Button>
<Text textAlign="center" w="$6" color={textBlack} fontSize="$5">
{expiryYears} years
</Text>
<Button
h="$3.5"
w="$3.5"
bg="white"
jc="center"
borderColor={borderColor}
borderWidth={1}
borderRadius="$10"
onPress={() => setExpiryYears(expiryYears + 1)}
>
<Plus />
</Button>
</Fieldset>
<YStack>
<Fieldset mt="$2" gap="$2" horizontal>
<Text
color={textBlack}
width={160}
justifyContent="flex-end"
fontSize="$5"
>
Is in OFAC list
</Text>
<XStack f={1} />
<Switch
size="$3.5"
checked={isInOfacList}
onCheckedChange={() => setIsInOfacList(!isInOfacList)}
bg={isInOfacList ? '$green7Light' : '$gray4'}
>
<Switch.Thumb animation="quick" bc="white" />
</Switch>
</Fieldset>
<Text
mt="$2"
color="$red10"
justifyContent="flex-end"
fontSize="$3"
style={{ opacity: isInOfacList ? 1 : 0 }}
>
OFAC list is a list of people who are suspected of being involved in
terrorism or other illegal activities.
</Text>
</YStack>
<YStack f={1} />
<YStack>
<Text mb="$2" textAlign="center" fontSize="$4" color={textBlack}>
These passport data are only for testing purposes.
</Text>
<CustomButton
onPress={handleGenerate}
text="Generate passport data"
Icon={isGenerating ? <Spinner /> : <Cpu color={textBlack} />}
isDisabled={isGenerating}
/>
</YStack>
</YStack>
<Sheet
modal
open={isCountrySheetOpen}
onOpenChange={setCountrySheetOpen}
snapPoints={[60]}
animation="medium"
disableDrag
>
<Sheet.Overlay />
<Sheet.Frame
bg={bgWhite}
borderTopLeftRadius="$9"
borderTopRightRadius="$9"
>
OFAC list is a list of people who are suspected of being involved in
terrorism or other illegal activities.
</Text>
</YStack>
<YStack p="$4">
<XStack ai="center" jc="space-between" mb="$4">
<Text fontSize="$8">Select a country</Text>
<XStack onPress={() => setCountrySheetOpen(false)} p="$2">
<X color={borderColor} size="$1.5" mr="$2" />
</XStack>
</XStack>
<Separator borderColor={separatorColor} mb="$4" />
<ScrollView showsVerticalScrollIndicator={false}>
{Object.keys(countryCodes).map(countryCode => (
<TouchableOpacity
key={countryCode}
onPress={() => {
handleCountrySelect(countryCode);
setCountrySheetOpen(false);
}}
>
<XStack py="$3" px="$2">
<Text fontSize="$4">
{countryCodes[countryCode as keyof typeof countryCodes]}{' '}
{flag(getCountryISO2(countryCode))}
</Text>
</XStack>
</TouchableOpacity>
))}
</ScrollView>
</YStack>
</Sheet.Frame>
</Sheet>
<YStack f={1} />
<YStack>
<Text mb="$2" textAlign="center" fontSize="$4" color={textBlack}>
These passport data are only for testing purposes.
</Text>
<CustomButton
onPress={handleGenerate}
text="Generate passport data"
Icon={isGenerating ? <Spinner /> : <Cpu color={textBlack} />}
isDisabled={isGenerating}
/>
</YStack>
</YStack>
<Sheet
modal
open={isAlgorithmSheetOpen}
onOpenChange={setAlgorithmSheetOpen}
snapPoints={[40]}
animation="medium"
disableDrag
>
<Sheet.Overlay />
<Sheet.Frame
bg={bgWhite}
borderTopLeftRadius="$9"
borderTopRightRadius="$9"
>
<YStack p="$4">
<XStack ai="center" jc="space-between" mb="$4">
<Text fontSize="$8">Select an algorithm</Text>
<XStack onPress={() => setAlgorithmSheetOpen(false)} p="$2">
<X color={borderColor} size="$1.5" mr="$2" />
</XStack>
</XStack>
<Separator borderColor={separatorColor} mb="$4" />
<ScrollView showsVerticalScrollIndicator={false}>
{['rsa sha256', 'rsa sha1', 'rsapss sha256'].map(algorithm => (
<TouchableOpacity
key={algorithm}
onPress={() => {
handleAlgorithmSelect(algorithm);
setAlgorithmSheetOpen(false);
}}
>
<XStack py="$3" px="$2">
<Text fontSize="$4">{algorithm}</Text>
</XStack>
</TouchableOpacity>
))}
</ScrollView>
</YStack>
</Sheet.Frame>
</Sheet>
</>
);
};

View File

@@ -6,20 +6,20 @@ import { Fieldset, Image, Text, useWindowDimensions, YStack } from 'tamagui';
import { attributeToPosition } from '../../../common/src/constants/constants';
import CustomButton from '../components/CustomButton';
import USER_PROFILE from '../images/user_profile.png';
import useNavigationStore from '../stores/navigationStore';
import useUserStore from '../stores/userStore';
import { bgGreen, textBlack } from '../utils/colors';
import { formatAttribute, getFirstName, maskString } from '../utils/utils';
import { useNavigation } from '@react-navigation/native';
const NextScreen: React.FC = () => {
const { height } = useWindowDimensions();
const navigation = useNavigation();
const handleNext = () => {
setRegistered(true);
setSelectedTab('launch');
navigation.navigate('Home');
};
const { hideData, setSelectedTab } = useNavigationStore();
const { passportData, setRegistered } = useUserStore();
const dataHidden = false;
const disclosureOptions: any = {
gender: 'optional',
@@ -28,10 +28,14 @@ const NextScreen: React.FC = () => {
date_of_birth: 'optional',
};
if (!passportData) {
return null;
}
return (
<YStack f={1}>
<YStack f={1} px="$4">
<YStack alignSelf="center" my="$3">
{hideData ? (
{dataHidden ? (
<Image
w={height > 750 ? 150 : 100}
h={height > 750 ? 190 : 80}
@@ -63,7 +67,7 @@ const NextScreen: React.FC = () => {
textDecorationColor: bgGreen,
}}
>
{hideData
{dataHidden
? maskString(getFirstName(passportData.mrz))
: getFirstName(passportData.mrz)}
</Text>
@@ -99,7 +103,7 @@ const NextScreen: React.FC = () => {
{keyFormatted}:
</Text>
<Text color={textBlack} fontSize="$6">
{hideData
{dataHidden
? maskString(mrzAttributeFormatted)
: mrzAttributeFormatted}
</Text>

View File

@@ -0,0 +1,114 @@
import React, { useEffect } from 'react';
import { StyleSheet, Text } from 'react-native';
import { View, XStack, YStack } from 'tamagui';
import Bulb from '../../images/icons/passport_camera_bulb.svg';
import Scan from '../../images/icons/passport_camera_scan.svg';
import { slate400, slate500, black } from '../../utils/colors';
import { SecondaryButton } from '../../components/buttons/SecondaryButton';
import { ExpandableBottomLayout } from '../../layouts/ExpandableBottomLayout';
import { useNavigation } from '@react-navigation/native';
import { startCameraScan } from '../../utils/cameraScanner';
import useUserStore from '../../stores/userStore';
interface PassportNFCScanScreen {}
const PassportCameraScreen: React.FC<PassportNFCScanScreen> = ({}) => {
const navigation = useNavigation();
const store = useUserStore();
useEffect(() => {
const cancelCamera = startCameraScan((error, result) => {
if (error) {
// handle error
console.error(error);
} else {
const { passportNumber, dateOfBirth, dateOfExpiry } = result!;
store.update({ passportNumber, dateOfBirth, dateOfExpiry });
navigation.navigate('PassportNFCScan');
}
});
return cancelCamera;
}, []);
return (
<ExpandableBottomLayout.Layout>
<ExpandableBottomLayout.TopSection>
<View height={400} bg={black} />
</ExpandableBottomLayout.TopSection>
<ExpandableBottomLayout.BottomSection>
<YStack alignItems="center" gap="$2.5">
<YStack alignItems="center" gap="$5" pb="$2.5">
<Text style={styles.title}>Scan your passport</Text>
<XStack gap="$6" alignSelf="flex-start">
<View>
<Scan height={40} width={40} />
</View>
<View
alignItems="flex-start"
justifyContent="flex-start"
maxWidth="70%"
>
<Text style={styles.subheader}>
Open to the photograph page
</Text>
<Text style={styles.description}>
Position all four corners of the first passport page clearly
in the frame.
</Text>
</View>
</XStack>
<XStack gap="$6" alignSelf="flex-start">
<View>
<Bulb height={40} width={40} />
</View>
<View
alignItems="flex-start"
justifyContent="flex-start"
maxWidth="70%"
>
<Text style={styles.subheader}>
Avoid dim lighting or glare{' '}
</Text>
<Text style={styles.description}>
Ensure that the text and photo are clearly readable and well
lit.
</Text>
</View>
</XStack>
</YStack>
<SecondaryButton onPress={() => navigation.navigate('Home')}>
Cancel
</SecondaryButton>
</YStack>
</ExpandableBottomLayout.BottomSection>
</ExpandableBottomLayout.Layout>
);
};
export default PassportCameraScreen;
const styles = StyleSheet.create({
title: {
fontSize: 28,
fontWeight: '400',
lineHeight: 35,
color: black,
},
subheader: {
textAlignVertical: 'center',
color: slate500,
fontWeight: '500',
fontSize: 18,
lineHeight: 23,
textAlign: 'center',
},
description: {
fontSize: 14,
fontWeight: '500',
lineHeight: 18,
color: slate400,
},
});

View File

@@ -0,0 +1,165 @@
import React, { useCallback, useEffect, useState } from 'react';
import {
Linking,
NativeEventEmitter,
NativeModules,
Platform,
Text,
} from 'react-native';
import { Image, View } from 'tamagui';
import { PrimaryButton } from '../../components/buttons/PrimaryButton';
import { black } from '../../utils/colors';
import { SecondaryButton } from '../../components/buttons/SecondaryButton';
import { ExpandableBottomLayout } from '../../layouts/ExpandableBottomLayout';
import { useNavigation } from '@react-navigation/native';
import useUserStore from '../../stores/userStore';
import NfcManager from 'react-native-nfc-manager';
import { scan } from '../../utils/nfcScannerNew';
import NFC_IMAGE from '../../images/nfc.png';
import Title from '../../components/typography/Title';
import Description from '../../components/typography/Description';
import TextsContainer from '../../components/TextsContainer';
import ButtonsContainer from '../../components/ButtonsContainer';
interface PassportNFCScanScreenProps {}
const emitter =
Platform.OS === 'android'
? new NativeEventEmitter(NativeModules.nativeModule)
: null;
const PassportNFCScanScreen: React.FC<PassportNFCScanScreenProps> = ({}) => {
const navigation = useNavigation();
let { passportNumber, dateOfBirth, dateOfExpiry } = useUserStore();
const [dialogMessage, setDialogMessage] = useState('');
const [isNfcSupported, setIsNfcSupported] = useState(true);
const [isNfcEnabled, setIsNfcEnabled] = useState(true);
const [isNfcSheetOpen, setIsNfcSheetOpen] = useState(false);
const [scanningMessage, setScanningMessage] = useState('');
const checkNfcSupport = useCallback(async () => {
const isSupported = await NfcManager.isSupported();
if (isSupported) {
const isEnabled = await NfcManager.isEnabled();
if (!isEnabled) {
setDialogMessage(
'NFC is not enabled. Would you like to enable it in settings?',
);
setIsNfcEnabled(false);
}
setIsNfcSupported(true);
} else {
setDialogMessage(
"Sorry, your device doesn't seem to have an NFC reader.",
);
setIsNfcSupported(false);
}
}, []);
const onPress = useCallback(async () => {
if (isNfcEnabled) {
try {
setIsNfcSheetOpen(true);
await scan({ passportNumber, dateOfBirth, dateOfExpiry });
// Feels better somehow
await new Promise(resolve => setTimeout(resolve, 1000));
navigation.navigate('NextScreen');
} catch (e) {
console.log(e);
} finally {
setIsNfcSheetOpen(false);
}
} else if (isNfcSupported) {
if (Platform.OS === 'ios') {
Linking.openURL('App-Prefs:root=General&path=About');
} else {
Linking.sendIntent('android.settings.NFC_SETTINGS');
}
}
}, [isNfcSupported, isNfcEnabled, passportNumber, dateOfBirth, dateOfExpiry]);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _cancelScanIfRunning = useCallback(async () => {
// // TODO: cancel if scanning
// setIsNfcSheetOpen(false);
}, [isNfcSheetOpen]);
useEffect(() => {
checkNfcSupport();
if (Platform.OS === 'android' && emitter) {
const subscription = emitter.addListener(
'NativeEvent',
(event: string) => {
console.log(event);
setScanningMessage(event);
},
);
return () => {
subscription.remove();
};
}
}, [checkNfcSupport]);
console.log('text?', scanningMessage);
return (
<ExpandableBottomLayout.Layout>
<ExpandableBottomLayout.TopSection>
{/* TODO a placeholder for animation */}
<View height={400} bg={black} />
</ExpandableBottomLayout.TopSection>
<ExpandableBottomLayout.BottomSection>
{isNfcSheetOpen ? (
<>
<TextsContainer>
<Title text="Ready to scan" />
<Description text={scanningMessage} />
</TextsContainer>
<Image
h="$8"
w="$8"
alignSelf="center"
borderRadius={1000}
source={{
uri: NFC_IMAGE,
}}
/>
<Text>
Hold your device near the NFC tag and stop moving when it
vibrates.
</Text>
</>
) : (
<>
<TextsContainer>
<Title text="Verify your passport" />
<Description
text={
isNfcEnabled
? 'Open your passport to the last page to access the NFC chip. Place your phone against the page'
: dialogMessage
}
/>
</TextsContainer>
<ButtonsContainer>
<PrimaryButton onPress={onPress} disabled={!isNfcSupported}>
{isNfcEnabled || !isNfcSupported
? 'Start Scan'
: 'Open settings'}
</PrimaryButton>
<SecondaryButton onPress={() => navigation.navigate('Home')}>
Cancel
</SecondaryButton>
</ButtonsContainer>
</>
)}
</ExpandableBottomLayout.BottomSection>
</ExpandableBottomLayout.Layout>
);
};
export default PassportNFCScanScreen;

View File

@@ -0,0 +1,54 @@
import React from 'react';
import { Image } from 'tamagui';
import Passport from '../../images/passport.png';
import { PrimaryButton } from '../../components/buttons/PrimaryButton';
import { SecondaryButton } from '../../components/buttons/SecondaryButton';
import { ExpandableBottomLayout } from '../../layouts/ExpandableBottomLayout';
import { useNavigation } from '@react-navigation/native';
import Title from '../../components/typography/Title';
import Description from '../../components/typography/Description';
import Additional from '../../components/typography/Additional';
import TextsContainer from '../../components/TextsContainer';
import ButtonsContainer from '../../components/ButtonsContainer';
interface PassportOnboardingScreenProps {}
const PassportOnboardingScreen: React.FC<
PassportOnboardingScreenProps
> = ({}) => {
const navigation = useNavigation();
return (
<ExpandableBottomLayout.Layout>
<ExpandableBottomLayout.TopSection>
<Image
resizeMethod="auto"
source={{ uri: Passport }}
style={{
width: '90%',
height: '90%',
aspectRatio: 0.69,
}}
/>
</ExpandableBottomLayout.TopSection>
<ExpandableBottomLayout.BottomSection>
<TextsContainer>
<Title text="Scan your passport" />
<Description text="Open your passport to the first page to scan it." />
<Additional text="Self ID will not capture an image of your passport. Our system is only reading the fields." />
</TextsContainer>
<ButtonsContainer>
<PrimaryButton onPress={() => navigation.navigate('PassportCamera')}>
Open Camera
</PrimaryButton>
<SecondaryButton onPress={() => navigation.navigate('Home')}>
Cancel
</SecondaryButton>
</ButtonsContainer>
</ExpandableBottomLayout.BottomSection>
</ExpandableBottomLayout.Layout>
);
};
export default PassportOnboardingScreen;

View File

@@ -0,0 +1,122 @@
import React from 'react';
import { Eraser, IterationCw } from '@tamagui/lucide-icons';
import { Button, Fieldset, Label, YStack } from 'tamagui';
import { borderColor, textBlack, textColor2 } from '../utils/colors';
import Dialog from 'react-native-dialog';
interface SettingsScreenProps {}
const SettingsScreen: React.FC<SettingsScreenProps> = ({}) => {
return (
<YStack gap="$2" mt="$2" ai="center">
<Fieldset gap="$4" horizontal>
<Label
color={textBlack}
width={200}
justifyContent="flex-end"
htmlFor="restart"
>
Rescan passport
</Label>
<Button
bg="white"
jc="center"
borderColor={borderColor}
borderWidth={1.2}
size="$3.5"
ml="$2"
// onPress={handleRestart}
>
<IterationCw color={textBlack} />
</Button>
</Fieldset>
<Fieldset gap="$4" mt="$1" horizontal>
<Label
color={textBlack}
width={200}
justifyContent="flex-end"
htmlFor="skip"
>
Delete passport data
</Label>
<Button
bg="white"
jc="center"
borderColor={borderColor}
borderWidth={1.2}
size="$3.5"
ml="$2"
// onPress={clearPassportDataFromStorage}
>
<Eraser color={textBlack} />
</Button>
</Fieldset>
{/* <Fieldset gap="$4" mt="$1" horizontal>
<Label color={textBlack} width={200} justifyContent="flex-end" htmlFor="skip" >
Delete proofs
</Label>
<Button bg="white" jc="center" borderColor={borderColor} borderWidth={1.2} size="$3.5" ml="$2" onPress={clearProofsFromStorage}>
<Eraser color={textBlack} />
</Button>
</Fieldset> */}
{/* <Fieldset horizontal>
<Label color={textBlack} width={225} justifyContent="flex-end" htmlFor="restart" >
Private mode
</Label>
<Switch size="$3.5" checked={hideData} onCheckedChange={handleHideData}>
<Switch.Thumb animation="bouncy" bc={bgColor} />
</Switch>
</Fieldset> */}
<Fieldset gap="$4" mt="$1" horizontal>
<Label
color={textBlack}
width={200}
justifyContent="flex-end"
htmlFor="skip"
>
Delete secret (caution)
</Label>
<Button
bg="white"
jc="center"
borderColor={borderColor}
borderWidth={1.2}
size="$3.5"
ml="$2"
// onPress={() => setDialogDeleteSecretIsOpen(true)}
>
<Eraser color={textColor2} />
</Button>
</Fieldset>
<Dialog.Container visible={false}>
<Dialog.Title>Delete Secret</Dialog.Title>
<Dialog.Description>
You are about to delete your secret. Be careful! You will not be able
to recover your identity.
</Dialog.Description>
<Dialog.Button
// onPress={() => setDialogDeleteSecretIsOpen(false)}
label="Cancel"
/>
<Dialog.Button
// onPress={() => handleDeleteSecret()}
label="Delete secret"
/>
</Dialog.Container>
{/* <Fieldset gap="$4" mt="$1" horizontal>
<Label color={textBlack} width={200} justifyContent="flex-end" htmlFor="skip" >
registered = (!registered)
</Label>
<Button bg="white" jc="center" borderColor={borderColor} borderWidth={1.2} size="$3.5" ml="$2" onPress={() => setRegistered(!registered)}>
<UserPlus color={textColor2} />
</Button>
</Fieldset> */}
</YStack>
);
};
export default SettingsScreen;

View File

@@ -5,11 +5,11 @@ import { Image, Text, YStack } from 'tamagui';
import CustomButton from '../components/CustomButton';
import OPENPASSPORT_LOGO from '../images/openpassport.png';
import useNavigationStore from '../stores/navigationStore';
import { textBlack } from '../utils/colors';
import { useNavigation } from '@react-navigation/native';
const StartScreen: React.FC = () => {
const { setSelectedTab } = useNavigationStore();
const navigation = useNavigation();
return (
<YStack f={1}>
@@ -28,17 +28,23 @@ const StartScreen: React.FC = () => {
Icon={<ArrowRight />}
text="Use my passport"
onPress={() => {
setSelectedTab('scan');
navigation.navigate('PassportOnboarding');
}}
/>
{/* TODO Only display this button during dev mode */}
<CustomButton
bgColor="white"
Icon={<ArrowRight />}
text="Use a mock passport"
onPress={() => {
setSelectedTab('mock');
navigation.navigate('CreateMock');
}}
/>
<CustomButton
text="Cancel"
bgColor="$gray4"
onPress={navigation.goBack}
/>
</YStack>
</YStack>
);

View File

@@ -1,38 +1,49 @@
import * as amplitude from '@amplitude/analytics-react-native';
import { NativeModules, Platform } from 'react-native';
import useNavigationStore from '../stores/navigationStore';
import useUserStore from '../stores/userStore';
import { extractMRZInfo, formatDateToYYMMDD } from './utils';
export const startCameraScan = async () => {
const { toast, setSelectedTab } = useNavigationStore.getState();
type Callback = (
error: Error | null,
result?: {
passportNumber: string;
dateOfBirth: string;
dateOfExpiry: string;
},
) => void;
type CancelScan = () => void;
export const startCameraScan = (callback: Callback): CancelScan => {
if (Platform.OS === 'ios') {
try {
const result = await NativeModules.MRZScannerModule.startScanning();
console.log('Scan result:', result);
console.log(
`Document Number: ${result.documentNumber}, Expiry Date: ${result.expiryDate}, Birth Date: ${result.birthDate}`,
);
NativeModules.MRZScannerModule.startScanning()
.then(
(result: {
documentNumber: string;
birthDate: string;
expiryDate: string;
}) => {
console.log('Scan result:', result);
console.log(
`Document Number: ${result.documentNumber}, Expiry Date: ${result.expiryDate}, Birth Date: ${result.birthDate}`,
);
useUserStore.setState({
passportNumber: result.documentNumber,
dateOfBirth: formatDateToYYMMDD(result.birthDate),
dateOfExpiry: formatDateToYYMMDD(result.expiryDate),
});
setSelectedTab('nfc');
toast.show('✔︎', {
message: 'Scan successful',
customData: {
type: 'success',
callback(null, {
passportNumber: result.documentNumber,
dateOfBirth: formatDateToYYMMDD(result.birthDate),
dateOfExpiry: formatDateToYYMMDD(result.expiryDate),
});
},
)
.catch((e: Error) => {
console.error(e);
amplitude.track('camera_scan_error', { error: e });
callback(e as Error);
});
} catch (e) {
console.error(e);
amplitude.track('camera_scan_error', { error: e });
}
return () => {
// TODO
NativeModules.MRZScannerModule.stopScanning();
};
} else {
NativeModules.CameraActivityModule.startCameraActivity()
.then((mrzInfo: string) => {
@@ -40,27 +51,31 @@ export const startCameraScan = async () => {
const { documentNumber, birthDate, expiryDate } =
extractMRZInfo(mrzInfo);
useUserStore.setState({
callback(null, {
passportNumber: documentNumber,
dateOfBirth: birthDate,
dateOfExpiry: expiryDate,
});
setSelectedTab('nfc');
toast.show('✔︎', {
message: 'Scan successful',
customData: {
type: 'success',
},
} catch (e) {
console.error('Invalid MRZ format:', (e as Error).message);
amplitude.track('invalid_mrz_format', {
error: (e as Error).message,
});
} catch (error: any) {
console.error('Invalid MRZ format:', error.message);
amplitude.track('invalid_mrz_format', { error: error.message });
callback(e as Error);
}
})
.catch((error: any) => {
console.error('Camera Activity Error:', error);
amplitude.track('camera_scan_error', { error: error.message });
.catch((e: Error) => {
console.error('Camera Activity Error:', e);
amplitude.track('camera_scan_error', { error: e.message });
callback(e);
});
return () => {
// TODO
// NativeModules.CameraActivityModule.cancelCameraActivity();
console.log('this would destroy the view');
};
}
};

View File

@@ -7,6 +7,7 @@ export const slate50 = '#F8FAFC';
export const slate100 = '#F1F5F9';
export const slate200 = '#E2E8F0';
export const slate300 = '#CBD5E1';
export const slate400 = '#94A3B8';
export const slate500 = '#64748B';
export const slate600 = '#475569';
export const slate700 = '#334155';

View File

@@ -0,0 +1,289 @@
import { NativeModules, Platform } from 'react-native';
// @ts-ignore
import * as amplitude from '@amplitude/analytics-react-native';
import { Buffer } from 'buffer';
import PassportReader from 'react-native-passport-reader';
import { PassportData } from '../../../common/src/utils/types';
import useNavigationStore from '../stores/navigationStore';
import useUserStore from '../stores/userStore';
import { checkInputs } from '../utils/utils';
import { parsePassportData } from '../../../common/src/utils/parsePassportData';
import {} from '@react-navigation/native';
interface Inputs {
passportNumber: string;
dateOfBirth: string;
dateOfExpiry: string;
}
export const scan = async (inputs: Inputs) => {
const { passportNumber, dateOfBirth, dateOfExpiry } = inputs;
const check = checkInputs(passportNumber, dateOfBirth, dateOfExpiry);
if (!check.success) {
amplitude.track('inputs_invalid', { error: check.message });
return;
}
console.log('scanning...');
if (Platform.OS === 'android') {
await scanAndroid(inputs);
} else {
await scanIOS(inputs);
}
};
const scanAndroid = async (inputs: Inputs) => {
const { passportNumber, dateOfBirth, dateOfExpiry } = inputs;
try {
const response = await PassportReader.scan({
documentNumber: passportNumber,
dateOfBirth: dateOfBirth,
dateOfExpiry: dateOfExpiry,
});
console.log('scanned');
amplitude.track('nfc_scan_successful');
await handleResponseAndroid(response);
} catch (e: any) {
console.log('error during scan:', e);
amplitude.track('nfc_scan_unsuccessful', { error: e.message });
if (e.message.includes('InvalidMRZKey')) {
// toast.show('Error', {
// message:
// 'Go to previous screen and rescan your passport with the camera',
// customData: {
// type: 'error',
// },
// timeout: 5000,
// });
// useNavigationStore.getState().setSelectedTab('scan');
} else {
// toast.show('Error', {
// message: e.message,
// customData: {
// type: 'error',
// },
// });
}
}
};
const scanIOS = async (inputs: Inputs) => {
const { passportNumber, dateOfBirth, dateOfExpiry } = inputs;
console.log('passportNumber', passportNumber);
console.log('dateOfBirth', dateOfBirth);
console.log('dateOfExpiry', dateOfExpiry);
try {
const response = await NativeModules.PassportReader.scanPassport(
passportNumber,
dateOfBirth,
dateOfExpiry,
);
console.log('scanned');
await handleResponseIOS(response);
amplitude.track('nfc_scan_successful');
} catch (e: any) {
console.log('error during scan:', e);
amplitude.track('nfc_scan_unsuccessful', { error: e.message });
if (!e.message.includes('UserCanceled')) {
// handle cancelation
}
if (e.message.includes('InvalidMRZKey')) {
// handle error
} else {
// handle error
}
}
};
const handleResponseIOS = async (response: any) => {
const { toast } = useNavigationStore.getState();
const parsed = JSON.parse(response);
const dgHashesObj = JSON.parse(parsed?.dataGroupHashes);
const dg1HashString = dgHashesObj?.DG1?.sodHash;
const dg1Hash = Array.from(Buffer.from(dg1HashString, 'hex'));
const dg2HashString = dgHashesObj?.DG2?.sodHash;
const dg2Hash = Array.from(Buffer.from(dg2HashString, 'hex'));
const eContentBase64 = parsed?.eContentBase64; // this is what we call concatenatedDataHashes in android world
const signedAttributes = parsed?.signedAttributes; // this is what we call eContent in android world
const mrz = parsed?.passportMRZ;
const signatureBase64 = parsed?.signatureBase64;
console.log('dataGroupsPresent', parsed?.dataGroupsPresent);
console.log('placeOfBirth', parsed?.placeOfBirth);
console.log('activeAuthenticationPassed', parsed?.activeAuthenticationPassed);
console.log('isPACESupported', parsed?.isPACESupported);
console.log(
'isChipAuthenticationSupported',
parsed?.isChipAuthenticationSupported,
);
console.log('residenceAddress', parsed?.residenceAddress);
console.log('passportPhoto', parsed?.passportPhoto.substring(0, 100) + '...');
console.log(
'encapsulatedContentDigestAlgorithm',
parsed?.encapsulatedContentDigestAlgorithm,
);
console.log('documentSigningCertificate', parsed?.documentSigningCertificate);
const pem = JSON.parse(parsed?.documentSigningCertificate).PEM.replace(
/\n/g,
'',
);
console.log('pem', pem);
const eContentArray = Array.from(Buffer.from(signedAttributes, 'base64'));
const signedEContentArray = eContentArray.map(byte =>
byte > 127 ? byte - 256 : byte,
);
const concatenatedDataHashesArray = Array.from(
Buffer.from(eContentBase64, 'base64'),
);
const concatenatedDataHashesArraySigned = concatenatedDataHashesArray.map(
byte => (byte > 127 ? byte - 256 : byte),
);
const encryptedDigestArray = Array.from(
Buffer.from(signatureBase64, 'base64'),
).map(byte => (byte > 127 ? byte - 256 : byte));
// amplitude.track('nfc_response_parsed', {
// dataGroupsPresent: parsed?.dataGroupsPresent,
// eContentLength: signedEContentArray?.length,
// concatenatedDataHashesLength: concatenatedDataHashesArraySigned?.length,
// encryptedDigestLength: encryptedDigestArray?.length,
// activeAuthenticationPassed: parsed?.activeAuthenticationPassed,
// isPACESupported: parsed?.isPACESupported,
// isChipAuthenticationSupported: parsed?.isChipAuthenticationSupported,
// encapsulatedContentDigestAlgorithm: parsed?.encapsulatedContentDigestAlgorithm,
// dsc: pem,
// });
const passportData = {
mrz,
dsc: pem,
dg2Hash: dg2Hash,
dg1Hash: dg1Hash,
dgPresents: parsed?.dataGroupsPresent,
eContent: concatenatedDataHashesArraySigned,
signedAttr: signedEContentArray,
encryptedDigest: encryptedDigestArray,
photoBase64: 'data:image/jpeg;base64,' + parsed.passportPhoto,
mockUser: false,
};
const parsedPassportData = parsePassportData(passportData);
amplitude.track('nfc_response_parsed', parsedPassportData);
try {
await useUserStore.getState().registerPassportData(passportData);
} catch (e: any) {
console.log('error during parsing:', e);
amplitude.track('error_parsing_nfc_response', { error: e.message });
toast.show('Error', {
message: e.message,
customData: {
type: 'error',
},
});
}
};
const handleResponseAndroid = async (response: any) => {
const {
mrz,
eContent,
encryptedDigest,
photo,
digestAlgorithm,
signerInfoDigestAlgorithm,
digestEncryptionAlgorithm,
LDSVersion,
unicodeVersion,
encapContent,
documentSigningCertificate,
dataGroupHashes,
} = response;
const dgHashesObj = JSON.parse(dataGroupHashes);
const dg1HashString = dgHashesObj['1'];
const dg1Hash = Array.from(Buffer.from(dg1HashString, 'hex'));
const dg2Hash = dgHashesObj['2'];
const pem =
'-----BEGIN CERTIFICATE-----' +
documentSigningCertificate +
'-----END CERTIFICATE-----';
const dgPresents = Object.keys(dgHashesObj)
.map(key => parseInt(key)) // eslint-disable-line radix
.filter(num => !isNaN(num))
.sort((a, b) => a - b);
const passportData: PassportData = {
mrz: mrz.replace(/\n/g, ''),
dsc: pem,
dg2Hash,
dg1Hash,
dgPresents,
eContent: JSON.parse(encapContent),
signedAttr: JSON.parse(eContent),
encryptedDigest: JSON.parse(encryptedDigest),
photoBase64: photo.base64,
mockUser: false,
};
console.log(
'passportData',
JSON.stringify(
{
...passportData,
photoBase64: passportData.photoBase64.substring(0, 100) + '...',
},
null,
2,
),
);
console.log('mrz', passportData?.mrz);
console.log('dataGroupHashes', passportData?.eContent);
console.log('eContent', passportData?.eContent);
console.log('encryptedDigest', passportData?.encryptedDigest);
console.log(
'photoBase64',
passportData?.photoBase64.substring(0, 100) + '...',
);
console.log('digestAlgorithm', digestAlgorithm);
console.log('signerInfoDigestAlgorithm', signerInfoDigestAlgorithm);
console.log('digestEncryptionAlgorithm', digestEncryptionAlgorithm);
console.log('LDSVersion', LDSVersion);
console.log('unicodeVersion', unicodeVersion);
console.log('encapContent', encapContent);
console.log('documentSigningCertificate', documentSigningCertificate);
const parsedPassportData = parsePassportData(passportData);
amplitude.track('nfc_response_parsed', parsedPassportData);
// amplitude.track('nfc_response_parsed', {
// dataGroupHashesLength: passportData?.eContent?.length,
// eContentLength: passportData?.eContent?.length,
// encryptedDigestLength: passportData?.encryptedDigest?.length,
// digestAlgorithm: digestAlgorithm,
// signerInfoDigestAlgorithm: signerInfoDigestAlgorithm,
// digestEncryptionAlgorithm: digestEncryptionAlgorithm,
// dsc: pem,
// mockUser: false
// });
try {
await useUserStore.getState().registerPassportData(passportData);
} catch (e: any) {
console.log('error during parsing:', e);
amplitude.track('error_parsing_nfc_response', { error: e.message });
}
};

View File

@@ -111,6 +111,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/compat-data@npm:^7.26.5":
version: 7.26.5
resolution: "@babel/compat-data@npm:7.26.5"
checksum: 10c0/9d2b41f0948c3dfc5de44d9f789d2208c2ea1fd7eb896dfbb297fe955e696728d6f363c600cd211e7f58ccbc2d834fe516bb1e4cf883bbabed8a32b038afc1a0
languageName: node
linkType: hard
"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.16, @babel/core@npm:^7.20.0, @babel/core@npm:^7.23.9":
version: 7.26.0
resolution: "@babel/core@npm:7.26.0"
@@ -134,6 +141,29 @@ __metadata:
languageName: node
linkType: hard
"@babel/core@npm:^7.21.3":
version: 7.26.7
resolution: "@babel/core@npm:7.26.7"
dependencies:
"@ampproject/remapping": "npm:^2.2.0"
"@babel/code-frame": "npm:^7.26.2"
"@babel/generator": "npm:^7.26.5"
"@babel/helper-compilation-targets": "npm:^7.26.5"
"@babel/helper-module-transforms": "npm:^7.26.0"
"@babel/helpers": "npm:^7.26.7"
"@babel/parser": "npm:^7.26.7"
"@babel/template": "npm:^7.25.9"
"@babel/traverse": "npm:^7.26.7"
"@babel/types": "npm:^7.26.7"
convert-source-map: "npm:^2.0.0"
debug: "npm:^4.1.0"
gensync: "npm:^1.0.0-beta.2"
json5: "npm:^2.2.3"
semver: "npm:^6.3.1"
checksum: 10c0/fbd2cd9fc23280bdcaca556e558f715c0a42d940b9913c52582e8e3d24e391d269cb8a9cd6589172593983569021c379e28bba6b19ea2ee08674f6068c210a9d
languageName: node
linkType: hard
"@babel/eslint-parser@npm:^7.20.0":
version: 7.25.9
resolution: "@babel/eslint-parser@npm:7.25.9"
@@ -161,6 +191,19 @@ __metadata:
languageName: node
linkType: hard
"@babel/generator@npm:^7.26.5":
version: 7.26.5
resolution: "@babel/generator@npm:7.26.5"
dependencies:
"@babel/parser": "npm:^7.26.5"
"@babel/types": "npm:^7.26.5"
"@jridgewell/gen-mapping": "npm:^0.3.5"
"@jridgewell/trace-mapping": "npm:^0.3.25"
jsesc: "npm:^3.0.2"
checksum: 10c0/3be79e0aa03f38858a465d12ee2e468320b9122dc44fc85984713e32f16f4d77ce34a16a1a9505972782590e0b8d847b6f373621f9c6fafa1906d90f31416cb0
languageName: node
linkType: hard
"@babel/helper-annotate-as-pure@npm:^7.25.9":
version: 7.25.9
resolution: "@babel/helper-annotate-as-pure@npm:7.25.9"
@@ -183,6 +226,19 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-compilation-targets@npm:^7.26.5":
version: 7.26.5
resolution: "@babel/helper-compilation-targets@npm:7.26.5"
dependencies:
"@babel/compat-data": "npm:^7.26.5"
"@babel/helper-validator-option": "npm:^7.25.9"
browserslist: "npm:^4.24.0"
lru-cache: "npm:^5.1.1"
semver: "npm:^6.3.1"
checksum: 10c0/9da5c77e5722f1a2fcb3e893049a01d414124522bbf51323bb1a0c9dcd326f15279836450fc36f83c9e8a846f3c40e88be032ed939c5a9840922bed6073edfb4
languageName: node
linkType: hard
"@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.25.9":
version: 7.25.9
resolution: "@babel/helper-create-class-features-plugin@npm:7.25.9"
@@ -364,6 +420,16 @@ __metadata:
languageName: node
linkType: hard
"@babel/helpers@npm:^7.26.7":
version: 7.26.7
resolution: "@babel/helpers@npm:7.26.7"
dependencies:
"@babel/template": "npm:^7.25.9"
"@babel/types": "npm:^7.26.7"
checksum: 10c0/37fec398e53a2dbbf24bc2a025c4d571b2556cef18d8116d05d04b153f13ef659cdfbaab96c8eed875e629d39bdf9b3ea5d099ccf80544537de224e2d94f9b11
languageName: node
linkType: hard
"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.25.9, @babel/parser@npm:^7.26.0, @babel/parser@npm:^7.26.3":
version: 7.26.3
resolution: "@babel/parser@npm:7.26.3"
@@ -375,6 +441,17 @@ __metadata:
languageName: node
linkType: hard
"@babel/parser@npm:^7.26.5, @babel/parser@npm:^7.26.7":
version: 7.26.7
resolution: "@babel/parser@npm:7.26.7"
dependencies:
"@babel/types": "npm:^7.26.7"
bin:
parser: ./bin/babel-parser.js
checksum: 10c0/dcb08a4f2878ece33caffefe43b71488d753324bae7ca58d64bca3bc4af34dcfa1b58abdf9972516d76af760fceb25bb9294ca33461d56b31c5059ccfe32001f
languageName: node
linkType: hard
"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.9":
version: 7.25.9
resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.25.9"
@@ -1635,6 +1712,21 @@ __metadata:
languageName: node
linkType: hard
"@babel/traverse@npm:^7.26.7":
version: 7.26.7
resolution: "@babel/traverse@npm:7.26.7"
dependencies:
"@babel/code-frame": "npm:^7.26.2"
"@babel/generator": "npm:^7.26.5"
"@babel/parser": "npm:^7.26.7"
"@babel/template": "npm:^7.25.9"
"@babel/types": "npm:^7.26.7"
debug: "npm:^4.3.1"
globals: "npm:^11.1.0"
checksum: 10c0/b23a36ce40d2e4970741431c45d4f92e3f4c2895c0a421456516b2729bd9e17278846e01ee3d9039b0adf5fc5a071768061c17fcad040e74a5c3e39517449d5b
languageName: node
linkType: hard
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.9, @babel/types@npm:^7.26.0, @babel/types@npm:^7.26.3, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4":
version: 7.26.3
resolution: "@babel/types@npm:7.26.3"
@@ -1645,6 +1737,16 @@ __metadata:
languageName: node
linkType: hard
"@babel/types@npm:^7.21.3, @babel/types@npm:^7.26.5, @babel/types@npm:^7.26.7":
version: 7.26.7
resolution: "@babel/types@npm:7.26.7"
dependencies:
"@babel/helper-string-parser": "npm:^7.25.9"
"@babel/helper-validator-identifier": "npm:^7.25.9"
checksum: 10c0/7810a2bca97b13c253f07a0863a628d33dbe76ee3c163367f24be93bfaf4c8c0a325f73208abaaa050a6b36059efc2950c2e4b71fb109c0f07fa62221d8473d4
languageName: node
linkType: hard
"@bcoe/v8-coverage@npm:^0.2.3":
version: 0.2.3
resolution: "@bcoe/v8-coverage@npm:0.2.3"
@@ -1652,6 +1754,15 @@ __metadata:
languageName: node
linkType: hard
"@egjs/hammerjs@npm:^2.0.17":
version: 2.0.17
resolution: "@egjs/hammerjs@npm:2.0.17"
dependencies:
"@types/hammerjs": "npm:^2.0.36"
checksum: 10c0/dbedc15a0e633f887c08394bd636faf6a3abd05726dc0909a0e01209d5860a752d9eca5e512da623aecfabe665f49f1d035de3103eb2f9022c5cea692f9cc9be
languageName: node
linkType: hard
"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0":
version: 4.4.1
resolution: "@eslint-community/eslint-utils@npm:4.4.1"
@@ -2870,6 +2981,83 @@ __metadata:
languageName: node
linkType: hard
"@react-navigation/core@npm:^7.3.1":
version: 7.3.1
resolution: "@react-navigation/core@npm:7.3.1"
dependencies:
"@react-navigation/routers": "npm:^7.1.2"
escape-string-regexp: "npm:^4.0.0"
nanoid: "npm:3.3.8"
query-string: "npm:^7.1.3"
react-is: "npm:^18.2.0"
use-latest-callback: "npm:^0.2.1"
use-sync-external-store: "npm:^1.2.2"
peerDependencies:
react: ">= 18.2.0"
checksum: 10c0/92a3b60a2a63366639f68a59c1c3dae0c00998bd0eef82d26acc2304a42a18e6ab7589ae43ce9362c723c78336f99f88845cd7ce1b1ec8dd3e5c9eb4d793d393
languageName: node
linkType: hard
"@react-navigation/elements@npm:^2.2.5":
version: 2.2.5
resolution: "@react-navigation/elements@npm:2.2.5"
dependencies:
color: "npm:^4.2.3"
peerDependencies:
"@react-native-masked-view/masked-view": ">= 0.2.0"
"@react-navigation/native": ^7.0.14
react: ">= 18.2.0"
react-native: "*"
react-native-safe-area-context: ">= 4.0.0"
peerDependenciesMeta:
"@react-native-masked-view/masked-view":
optional: true
checksum: 10c0/b80f4e29916360489fd5399d9344dfc64a1fdf77796ba189f5b19324766998fd9723d08ac548b935585feb4643465b07bfcd9a3b453e7693d338c552f97c18c4
languageName: node
linkType: hard
"@react-navigation/native@npm:^7.0.14":
version: 7.0.14
resolution: "@react-navigation/native@npm:7.0.14"
dependencies:
"@react-navigation/core": "npm:^7.3.1"
escape-string-regexp: "npm:^4.0.0"
fast-deep-equal: "npm:^3.1.3"
nanoid: "npm:3.3.8"
use-latest-callback: "npm:^0.2.1"
peerDependencies:
react: ">= 18.2.0"
react-native: "*"
checksum: 10c0/cba91f3d6f75620e5952b53fa6acb4ba9406c54536416e85802b25c99c008ffdf292b0a2f5212d128a5abfaca578df354d6f2e095df80d7881ac194cacd29cb4
languageName: node
linkType: hard
"@react-navigation/routers@npm:^7.1.2":
version: 7.1.2
resolution: "@react-navigation/routers@npm:7.1.2"
dependencies:
nanoid: "npm:3.3.8"
checksum: 10c0/0d47a021cbad2cf8a0fbcb813ec957063720a4e0a83faaee5bfc1d61047d29f56893d4080450a307b12d5e40b7662963bad8fa455791ec98e71523301decb1ae
languageName: node
linkType: hard
"@react-navigation/stack@npm:^7.1.1":
version: 7.1.1
resolution: "@react-navigation/stack@npm:7.1.1"
dependencies:
"@react-navigation/elements": "npm:^2.2.5"
color: "npm:^4.2.3"
peerDependencies:
"@react-navigation/native": ^7.0.14
react: ">= 18.2.0"
react-native: "*"
react-native-gesture-handler: ">= 2.0.0"
react-native-safe-area-context: ">= 4.0.0"
react-native-screens: ">= 4.0.0"
checksum: 10c0/69ed5a26f62d451847e50f02e803f11f153cf723ccb433cbaed55cc41a7d19d1656007c1b21ade393f929839f9c471ba90fb445743e8c783d48b59b15b19c337
languageName: node
linkType: hard
"@sideway/address@npm:^4.1.5":
version: 4.1.5
resolution: "@sideway/address@npm:4.1.5"
@@ -2925,6 +3113,146 @@ __metadata:
languageName: node
linkType: hard
"@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/a50bd0baa34faf16bcba712091f94c7f0e230431fe99a9dfc3401fa92823ad3f68495b86ab9bf9044b53839e8c416cfbb37eb3f246ff33f261e0fa9ee1779c5b
languageName: node
linkType: hard
"@svgr/babel-plugin-remove-jsx-attribute@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-remove-jsx-attribute@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/8a98e59bd9971e066815b4129409932f7a4db4866834fe75677ea6d517972fb40b380a69a4413189f20e7947411f9ab1b0f029dd5e8068686a5a0188d3ccd4c7
languageName: node
linkType: hard
"@svgr/babel-plugin-remove-jsx-empty-expression@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-remove-jsx-empty-expression@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/517dcca75223bd05d3f056a8514dbba3031278bea4eadf0842c576d84f4651e7a4e0e7082d3ee4ef42456de0f9c4531d8a1917c04876ca64b014b859ca8f1bde
languageName: node
linkType: hard
"@svgr/babel-plugin-replace-jsx-attribute-value@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-replace-jsx-attribute-value@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/004bd1892053b7e9c1b0bb14acc44e77634ec393722b87b1e4fae53e2c35122a2dd0d5c15e9070dbeec274e22e7693a2b8b48506733a8009ee92b12946fcb10a
languageName: node
linkType: hard
"@svgr/babel-plugin-svg-dynamic-title@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-svg-dynamic-title@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/80e0a7fcf902f984c705051ca5c82ea6050ccbb70b651a8fea6d0eb5809e4dac274b49ea6be2d87f1eb9dfc0e2d6cdfffe1669ec2117f44b67a60a07d4c0b8b8
languageName: node
linkType: hard
"@svgr/babel-plugin-svg-em-dimensions@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-svg-em-dimensions@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/73e92c8277a89279745c0c500f59f083279a8dc30cd552b22981fade2a77628fb2bd2819ee505725fcd2e93f923e3790b52efcff409a159e657b46604a0b9a21
languageName: node
linkType: hard
"@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0":
version: 8.1.0
resolution: "@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/655ed6bc7a208ceaa4ecff0a54ccc36008c3cb31efa90d11e171cab325ebbb21aa78f09c7b65f9b3ddeda3a85f348c0c862902c48be13c14b4de165c847974e3
languageName: node
linkType: hard
"@svgr/babel-plugin-transform-svg-component@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-transform-svg-component@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/4ac00bb99a3db4ef05e4362f116a3c608ee365a2d26cf7318d8d41a4a5b30a02c80455cce0e62c65b60ed815b5d632bedabac2ccd4b56f998fadef5286e3ded4
languageName: node
linkType: hard
"@svgr/babel-preset@npm:8.1.0":
version: 8.1.0
resolution: "@svgr/babel-preset@npm:8.1.0"
dependencies:
"@svgr/babel-plugin-add-jsx-attribute": "npm:8.0.0"
"@svgr/babel-plugin-remove-jsx-attribute": "npm:8.0.0"
"@svgr/babel-plugin-remove-jsx-empty-expression": "npm:8.0.0"
"@svgr/babel-plugin-replace-jsx-attribute-value": "npm:8.0.0"
"@svgr/babel-plugin-svg-dynamic-title": "npm:8.0.0"
"@svgr/babel-plugin-svg-em-dimensions": "npm:8.0.0"
"@svgr/babel-plugin-transform-react-native-svg": "npm:8.1.0"
"@svgr/babel-plugin-transform-svg-component": "npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/49367d3ad0831f79b1056871b91766246f449d4d1168623af5e283fbaefce4a01d77ab00de6b045b55e956f9aae27895823198493cd232d88d3435ea4517ffc5
languageName: node
linkType: hard
"@svgr/core@npm:^8.1.0":
version: 8.1.0
resolution: "@svgr/core@npm:8.1.0"
dependencies:
"@babel/core": "npm:^7.21.3"
"@svgr/babel-preset": "npm:8.1.0"
camelcase: "npm:^6.2.0"
cosmiconfig: "npm:^8.1.3"
snake-case: "npm:^3.0.4"
checksum: 10c0/6a2f6b1bc79bce39f66f088d468985d518005fc5147ebf4f108570a933818b5951c2cb7da230ddff4b7c8028b5a672b2d33aa2acce012b8b9770073aa5a2d041
languageName: node
linkType: hard
"@svgr/hast-util-to-babel-ast@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/hast-util-to-babel-ast@npm:8.0.0"
dependencies:
"@babel/types": "npm:^7.21.3"
entities: "npm:^4.4.0"
checksum: 10c0/f4165b583ba9eaf6719e598977a7b3ed182f177983e55f9eb55a6a73982d81277510e9eb7ab41f255151fb9ed4edd11ac4bef95dd872f04ed64966d8c85e0f79
languageName: node
linkType: hard
"@svgr/plugin-jsx@npm:^8.1.0":
version: 8.1.0
resolution: "@svgr/plugin-jsx@npm:8.1.0"
dependencies:
"@babel/core": "npm:^7.21.3"
"@svgr/babel-preset": "npm:8.1.0"
"@svgr/hast-util-to-babel-ast": "npm:8.0.0"
svg-parser: "npm:^2.0.4"
peerDependencies:
"@svgr/core": "*"
checksum: 10c0/07b4d9e00de795540bf70556fa2cc258774d01e97a12a26234c6fdf42b309beb7c10f31ee24d1a71137239347b1547b8bb5587d3a6de10669f95dcfe99cddc56
languageName: node
linkType: hard
"@svgr/plugin-svgo@npm:^8.1.0":
version: 8.1.0
resolution: "@svgr/plugin-svgo@npm:8.1.0"
dependencies:
cosmiconfig: "npm:^8.1.3"
deepmerge: "npm:^4.3.1"
svgo: "npm:^3.0.2"
peerDependencies:
"@svgr/core": "*"
checksum: 10c0/bfd25460f23f1548bfb8f6f3bedd6d6972c1a4f8881bd35a4f8c115218da6e999e8f9ac0ef0ed88c4e0b93fcec37f382b94c0322f4ec2b26752a89e5cc8b9d7a
languageName: node
linkType: hard
"@tamagui/accordion@npm:1.110.0":
version: 1.110.0
resolution: "@tamagui/accordion@npm:1.110.0"
@@ -4243,6 +4571,13 @@ __metadata:
languageName: node
linkType: hard
"@trysound/sax@npm:0.2.0":
version: 0.2.0
resolution: "@trysound/sax@npm:0.2.0"
checksum: 10c0/44907308549ce775a41c38a815f747009ac45929a45d642b836aa6b0a536e4978d30b8d7d680bbd116e9dd73b7dbe2ef0d1369dcfc2d09e83ba381e485ecbe12
languageName: node
linkType: hard
"@tsconfig/react-native@npm:^3.0.0":
version: 3.0.5
resolution: "@tsconfig/react-native@npm:3.0.5"
@@ -4350,6 +4685,13 @@ __metadata:
languageName: node
linkType: hard
"@types/hammerjs@npm:^2.0.36":
version: 2.0.46
resolution: "@types/hammerjs@npm:2.0.46"
checksum: 10c0/f3c1cb20dc2f0523f7b8c76065078544d50d8ae9b0edc1f62fed657210ed814266ff2dfa835d2c157a075991001eec3b64c88bf92e3e6e895c0db78d05711d06
languageName: node
linkType: hard
"@types/http-errors@npm:*":
version: 2.0.4
resolution: "@types/http-errors@npm:2.0.4"
@@ -5637,13 +5979,23 @@ __metadata:
languageName: node
linkType: hard
"color-name@npm:~1.1.4":
"color-name@npm:^1.0.0, color-name@npm:~1.1.4":
version: 1.1.4
resolution: "color-name@npm:1.1.4"
checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95
languageName: node
linkType: hard
"color-string@npm:^1.9.0":
version: 1.9.1
resolution: "color-string@npm:1.9.1"
dependencies:
color-name: "npm:^1.0.0"
simple-swizzle: "npm:^0.2.2"
checksum: 10c0/b0bfd74c03b1f837f543898b512f5ea353f71630ccdd0d66f83028d1f0924a7d4272deb278b9aef376cacf1289b522ac3fb175e99895283645a2dc3a33af2404
languageName: node
linkType: hard
"color2k@npm:^2.0.2":
version: 2.0.3
resolution: "color2k@npm:2.0.3"
@@ -5651,6 +6003,16 @@ __metadata:
languageName: node
linkType: hard
"color@npm:^4.2.3":
version: 4.2.3
resolution: "color@npm:4.2.3"
dependencies:
color-convert: "npm:^2.0.1"
color-string: "npm:^1.9.0"
checksum: 10c0/7fbe7cfb811054c808349de19fb380252e5e34e61d7d168ec3353e9e9aacb1802674bddc657682e4e9730c2786592a4de6f8283e7e0d3870b829bb0b7b2f6118
languageName: node
linkType: hard
"colorette@npm:^1.0.7":
version: 1.4.0
resolution: "colorette@npm:1.4.0"
@@ -5681,6 +6043,13 @@ __metadata:
languageName: node
linkType: hard
"commander@npm:^7.2.0":
version: 7.2.0
resolution: "commander@npm:7.2.0"
checksum: 10c0/8d690ff13b0356df7e0ebbe6c59b4712f754f4b724d4f473d3cc5b3fdcf978e3a5dc3078717858a2ceb50b0f84d0660a7f22a96cdc50fb877d0c9bb31593d23a
languageName: node
linkType: hard
"commander@npm:^9.4.1":
version: 9.5.0
resolution: "commander@npm:9.5.0"
@@ -5773,6 +6142,23 @@ __metadata:
languageName: node
linkType: hard
"cosmiconfig@npm:^8.1.3":
version: 8.3.6
resolution: "cosmiconfig@npm:8.3.6"
dependencies:
import-fresh: "npm:^3.3.0"
js-yaml: "npm:^4.1.0"
parse-json: "npm:^5.2.0"
path-type: "npm:^4.0.0"
peerDependencies:
typescript: ">=4.9.5"
peerDependenciesMeta:
typescript:
optional: true
checksum: 10c0/0382a9ed13208f8bfc22ca2f62b364855207dffdb73dc26e150ade78c3093f1cf56172df2dd460c8caf2afa91c0ed4ec8a88c62f8f9cd1cf423d26506aa8797a
languageName: node
linkType: hard
"cosmiconfig@npm:^9.0.0":
version: 9.0.0
resolution: "cosmiconfig@npm:9.0.0"
@@ -5855,6 +6241,26 @@ __metadata:
languageName: node
linkType: hard
"css-tree@npm:^2.3.1":
version: 2.3.1
resolution: "css-tree@npm:2.3.1"
dependencies:
mdn-data: "npm:2.0.30"
source-map-js: "npm:^1.0.1"
checksum: 10c0/6f8c1a11d5e9b14bf02d10717fc0351b66ba12594166f65abfbd8eb8b5b490dd367f5c7721db241a3c792d935fc6751fbc09f7e1598d421477ad9fadc30f4f24
languageName: node
linkType: hard
"css-tree@npm:~2.2.0":
version: 2.2.1
resolution: "css-tree@npm:2.2.1"
dependencies:
mdn-data: "npm:2.0.28"
source-map-js: "npm:^1.0.1"
checksum: 10c0/47e87b0f02f8ac22f57eceb65c58011dd142d2158128882a0bf963cf2eabb81a4ebbc2e3790c8289be7919fa8b83750c7b69272bd66772c708143b772ba3c186
languageName: node
linkType: hard
"css-what@npm:^6.1.0":
version: 6.1.0
resolution: "css-what@npm:6.1.0"
@@ -5862,6 +6268,15 @@ __metadata:
languageName: node
linkType: hard
"csso@npm:^5.0.5":
version: 5.0.5
resolution: "csso@npm:5.0.5"
dependencies:
css-tree: "npm:~2.2.0"
checksum: 10c0/ab4beb1e97dd7e207c10e9925405b45f15a6cd1b4880a8686ad573aa6d476aed28b4121a666cffd26c37a26179f7b54741f7c257543003bfb244d06a62ad569b
languageName: node
linkType: hard
"csstype@npm:^3.0.2":
version: 3.1.3
resolution: "csstype@npm:3.1.3"
@@ -5949,6 +6364,13 @@ __metadata:
languageName: node
linkType: hard
"decode-uri-component@npm:^0.2.2":
version: 0.2.2
resolution: "decode-uri-component@npm:0.2.2"
checksum: 10c0/1f4fa54eb740414a816b3f6c24818fbfcabd74ac478391e9f4e2282c994127db02010ce804f3d08e38255493cfe68608b3f5c8e09fd6efc4ae46c807691f7a31
languageName: node
linkType: hard
"dedent@npm:^1.0.0":
version: 1.5.3
resolution: "dedent@npm:1.5.3"
@@ -5968,7 +6390,7 @@ __metadata:
languageName: node
linkType: hard
"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.0":
"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.0, deepmerge@npm:^4.3.1":
version: 4.3.1
resolution: "deepmerge@npm:4.3.1"
checksum: 10c0/e53481aaf1aa2c4082b5342be6b6d8ad9dfe387bc92ce197a66dea08bd4265904a087e75e464f14d1347cf2ac8afe1e4c16b266e0561cc5df29382d3c5f80044
@@ -6120,6 +6542,16 @@ __metadata:
languageName: node
linkType: hard
"dot-case@npm:^3.0.4":
version: 3.0.4
resolution: "dot-case@npm:3.0.4"
dependencies:
no-case: "npm:^3.0.4"
tslib: "npm:^2.0.3"
checksum: 10c0/5b859ea65097a7ea870e2c91b5768b72ddf7fa947223fd29e167bcdff58fe731d941c48e47a38ec8aa8e43044c8fbd15cd8fa21689a526bc34b6548197cd5b05
languageName: node
linkType: hard
"dotenv@npm:^16.4.5":
version: 16.4.7
resolution: "dotenv@npm:16.4.7"
@@ -6223,7 +6655,7 @@ __metadata:
languageName: node
linkType: hard
"entities@npm:^4.2.0":
"entities@npm:^4.2.0, entities@npm:^4.4.0":
version: 4.5.0
resolution: "entities@npm:4.5.0"
checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250
@@ -6882,6 +7314,13 @@ __metadata:
languageName: node
linkType: hard
"filter-obj@npm:^1.1.0":
version: 1.1.0
resolution: "filter-obj@npm:1.1.0"
checksum: 10c0/071e0886b2b50238ca5026c5bbf58c26a7c1a1f720773b8c7813d16ba93d0200de977af14ac143c5ac18f666b2cfc83073f3a5fe6a4e996c49e0863d5500fccf
languageName: node
linkType: hard
"finalhandler@npm:1.1.2":
version: 1.1.2
resolution: "finalhandler@npm:1.1.2"
@@ -7383,6 +7822,15 @@ __metadata:
languageName: node
linkType: hard
"hoist-non-react-statics@npm:^3.3.0":
version: 3.3.2
resolution: "hoist-non-react-statics@npm:3.3.2"
dependencies:
react-is: "npm:^16.7.0"
checksum: 10c0/fe0889169e845d738b59b64badf5e55fa3cf20454f9203d1eb088df322d49d4318df774828e789898dcb280e8a5521bb59b3203385662ca5e9218a6ca5820e74
languageName: node
linkType: hard
"html-escaper@npm:^2.0.0":
version: 2.0.2
resolution: "html-escaper@npm:2.0.2"
@@ -7582,6 +8030,13 @@ __metadata:
languageName: node
linkType: hard
"is-arrayish@npm:^0.3.1":
version: 0.3.2
resolution: "is-arrayish@npm:0.3.2"
checksum: 10c0/f59b43dc1d129edb6f0e282595e56477f98c40278a2acdc8b0a5c57097c9eff8fe55470493df5775478cf32a4dc8eaf6d3a749f07ceee5bc263a78b2434f6a54
languageName: node
linkType: hard
"is-async-function@npm:^2.0.0":
version: 2.1.0
resolution: "is-async-function@npm:2.1.0"
@@ -8800,6 +9255,15 @@ __metadata:
languageName: node
linkType: hard
"lower-case@npm:^2.0.2":
version: 2.0.2
resolution: "lower-case@npm:2.0.2"
dependencies:
tslib: "npm:^2.0.3"
checksum: 10c0/3d925e090315cf7dc1caa358e0477e186ffa23947740e4314a7429b6e62d72742e0bbe7536a5ae56d19d7618ce998aba05caca53c2902bd5742fdca5fc57fd7b
languageName: node
linkType: hard
"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0":
version: 10.4.3
resolution: "lru-cache@npm:10.4.3"
@@ -8884,6 +9348,20 @@ __metadata:
languageName: node
linkType: hard
"mdn-data@npm:2.0.28":
version: 2.0.28
resolution: "mdn-data@npm:2.0.28"
checksum: 10c0/20000932bc4cd1cde9cba4e23f08cc4f816398af4c15ec81040ed25421d6bf07b5cf6b17095972577fb498988f40f4cb589e3169b9357bb436a12d8e07e5ea7b
languageName: node
linkType: hard
"mdn-data@npm:2.0.30":
version: 2.0.30
resolution: "mdn-data@npm:2.0.30"
checksum: 10c0/a2c472ea16cee3911ae742593715aa4c634eb3d4b9f1e6ada0902aa90df13dcbb7285d19435f3ff213ebaa3b2e0c0265c1eb0e3fb278fda7f8919f046a410cd9
languageName: node
linkType: hard
"memoize-one@npm:^5.0.0":
version: 5.2.1
resolution: "memoize-one@npm:5.2.1"
@@ -9428,6 +9906,15 @@ __metadata:
languageName: node
linkType: hard
"nanoid@npm:3.3.8":
version: 3.3.8
resolution: "nanoid@npm:3.3.8"
bin:
nanoid: bin/nanoid.cjs
checksum: 10c0/4b1bb29f6cfebf3be3bc4ad1f1296fb0a10a3043a79f34fbffe75d1621b4318319211cd420549459018ea3592f0d2f159247a6f874911d6d26eaaadda2478120
languageName: node
linkType: hard
"natural-compare@npm:^1.4.0":
version: 1.4.0
resolution: "natural-compare@npm:1.4.0"
@@ -9463,6 +9950,16 @@ __metadata:
languageName: node
linkType: hard
"no-case@npm:^3.0.4":
version: 3.0.4
resolution: "no-case@npm:3.0.4"
dependencies:
lower-case: "npm:^2.0.2"
tslib: "npm:^2.0.3"
checksum: 10c0/8ef545f0b3f8677c848f86ecbd42ca0ff3cd9dd71c158527b344c69ba14710d816d8489c746b6ca225e7b615108938a0bda0a54706f8c255933703ac1cf8e703
languageName: node
linkType: hard
"nocache@npm:^3.0.1":
version: 3.0.4
resolution: "nocache@npm:3.0.4"
@@ -9752,6 +10249,9 @@ __metadata:
"@react-native/eslint-config": "npm:0.75.4"
"@react-native/metro-config": "npm:0.75.4"
"@react-native/typescript-config": "npm:0.75.4"
"@react-navigation/elements": "npm:^2.2.5"
"@react-navigation/native": "npm:^7.0.14"
"@react-navigation/stack": "npm:^7.1.1"
"@tamagui/colors": "npm:1.110.0"
"@tamagui/config": "npm:1.110.0"
"@tamagui/core": "npm:1.110.0"
@@ -9791,11 +10291,14 @@ __metadata:
react-native-dialog: "npm:^9.3.0"
react-native-dotenv: "npm:^3.4.11"
react-native-fs: "npm:^2.20.0"
react-native-gesture-handler: "npm:^2.22.1"
react-native-get-random-values: "npm:^1.11.0"
react-native-keychain: "npm:^8.2.0"
react-native-nfc-manager: "npm:^3.15.1"
react-native-passport-reader: "npm:^1.0.3"
react-native-safe-area-context: "npm:^5.1.0"
react-native-svg: "npm:13.4.0"
react-native-svg-transformer: "npm:^1.5.0"
react-native-zip-archive: "npm:^6.1.0"
react-test-renderer: "npm:18.3.1"
socket.io-client: "npm:^4.7.5"
@@ -9958,6 +10461,13 @@ __metadata:
languageName: node
linkType: hard
"path-dirname@npm:^1.0.2":
version: 1.0.2
resolution: "path-dirname@npm:1.0.2"
checksum: 10c0/71e59be2bada7c91f62b976245fd421b7cb01fde3207fe53a82d8880621ad04fd8b434e628c9cf4e796259fc168a107d77cd56837725267c5b2c58cefe2c4e1b
languageName: node
linkType: hard
"path-exists@npm:^3.0.0":
version: 3.0.0
resolution: "path-exists@npm:3.0.0"
@@ -10200,6 +10710,18 @@ __metadata:
languageName: node
linkType: hard
"query-string@npm:^7.1.3":
version: 7.1.3
resolution: "query-string@npm:7.1.3"
dependencies:
decode-uri-component: "npm:^0.2.2"
filter-obj: "npm:^1.1.0"
split-on-first: "npm:^1.0.0"
strict-uri-encode: "npm:^2.0.0"
checksum: 10c0/a896c08e9e0d4f8ffd89a572d11f668c8d0f7df9c27c6f49b92ab31366d3ba0e9c331b9a620ee747893436cd1f2f821a6327e2bc9776bde2402ac6c270b801b2
languageName: node
linkType: hard
"queue-microtask@npm:^1.2.2":
version: 1.2.3
resolution: "queue-microtask@npm:1.2.3"
@@ -10242,14 +10764,14 @@ __metadata:
languageName: node
linkType: hard
"react-is@npm:^16.12.0 || ^17.0.0 || ^18.0.0, react-is@npm:^18.0.0, react-is@npm:^18.3.1":
"react-is@npm:^16.12.0 || ^17.0.0 || ^18.0.0, react-is@npm:^18.0.0, react-is@npm:^18.2.0, react-is@npm:^18.3.1":
version: 18.3.1
resolution: "react-is@npm:18.3.1"
checksum: 10c0/f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072
languageName: node
linkType: hard
"react-is@npm:^16.13.1":
"react-is@npm:^16.13.1, react-is@npm:^16.7.0":
version: 16.13.1
resolution: "react-is@npm:16.13.1"
checksum: 10c0/33977da7a5f1a287936a0c85639fec6ca74f4f15ef1e59a6bc20338fc73dc69555381e211f7a3529b8150a1f71e4225525b41b60b52965bda53ce7d47377ada1
@@ -10309,6 +10831,20 @@ __metadata:
languageName: node
linkType: hard
"react-native-gesture-handler@npm:^2.22.1":
version: 2.22.1
resolution: "react-native-gesture-handler@npm:2.22.1"
dependencies:
"@egjs/hammerjs": "npm:^2.0.17"
hoist-non-react-statics: "npm:^3.3.0"
invariant: "npm:^2.2.4"
peerDependencies:
react: "*"
react-native: "*"
checksum: 10c0/f4e1e08a25f4555491d6d2d9428c8f9cda343c6508f2e0f8d212698e5d5dc370cecadf08774243c6502b2c46e85a0d70f7bd426626d3ae27e180950a41415e2f
languageName: node
linkType: hard
"react-native-get-random-values@npm:^1.11.0":
version: 1.11.0
resolution: "react-native-get-random-values@npm:1.11.0"
@@ -10346,6 +10882,31 @@ __metadata:
languageName: node
linkType: hard
"react-native-safe-area-context@npm:^5.1.0":
version: 5.1.0
resolution: "react-native-safe-area-context@npm:5.1.0"
peerDependencies:
react: "*"
react-native: "*"
checksum: 10c0/82e305f5f29607a35471331dc3d86d641e4fc302654d56afb2cbe0e87e6129a845121e8daa29fccdf5298076d0ddbb53666a138492eedf1c2477c5653d9a6556
languageName: node
linkType: hard
"react-native-svg-transformer@npm:^1.5.0":
version: 1.5.0
resolution: "react-native-svg-transformer@npm:1.5.0"
dependencies:
"@svgr/core": "npm:^8.1.0"
"@svgr/plugin-jsx": "npm:^8.1.0"
"@svgr/plugin-svgo": "npm:^8.1.0"
path-dirname: "npm:^1.0.2"
peerDependencies:
react-native: ">=0.59.0"
react-native-svg: ">=12.0.0"
checksum: 10c0/2131b99d8ea88ff8d7e6f55d4091648b754e79a5c263fec555d310e0984f7283b6c3306d8e8fc8e17901f82d4aaf29c091dad26e51e560af8741d47d4b946309
languageName: node
linkType: hard
"react-native-svg@npm:13.4.0":
version: 13.4.0
resolution: "react-native-svg@npm:13.4.0"
@@ -11166,6 +11727,15 @@ __metadata:
languageName: node
linkType: hard
"simple-swizzle@npm:^0.2.2":
version: 0.2.2
resolution: "simple-swizzle@npm:0.2.2"
dependencies:
is-arrayish: "npm:^0.3.1"
checksum: 10c0/df5e4662a8c750bdba69af4e8263c5d96fe4cd0f9fe4bdfa3cbdeb45d2e869dff640beaaeb1ef0e99db4d8d2ec92f85508c269f50c972174851bc1ae5bd64308
languageName: node
linkType: hard
"sisteransi@npm:^1.0.5":
version: 1.0.5
resolution: "sisteransi@npm:1.0.5"
@@ -11198,6 +11768,16 @@ __metadata:
languageName: node
linkType: hard
"snake-case@npm:^3.0.4":
version: 3.0.4
resolution: "snake-case@npm:3.0.4"
dependencies:
dot-case: "npm:^3.0.4"
tslib: "npm:^2.0.3"
checksum: 10c0/ab19a913969f58f4474fe9f6e8a026c8a2142a01f40b52b79368068343177f818cdfef0b0c6b9558f298782441d5ca8ed5932eb57822439fad791d866e62cecd
languageName: node
linkType: hard
"socket.io-client@npm:^4.7.5":
version: 4.8.1
resolution: "socket.io-client@npm:4.8.1"
@@ -11251,6 +11831,13 @@ __metadata:
languageName: node
linkType: hard
"source-map-js@npm:^1.0.1":
version: 1.2.1
resolution: "source-map-js@npm:1.2.1"
checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf
languageName: node
linkType: hard
"source-map-support@npm:0.5.13":
version: 0.5.13
resolution: "source-map-support@npm:0.5.13"
@@ -11285,6 +11872,13 @@ __metadata:
languageName: node
linkType: hard
"split-on-first@npm:^1.0.0":
version: 1.1.0
resolution: "split-on-first@npm:1.1.0"
checksum: 10c0/56df8344f5a5de8521898a5c090023df1d8b8c75be6228f56c52491e0fc1617a5236f2ac3a066adb67a73231eac216ccea7b5b4a2423a543c277cb2f48d24c29
languageName: node
linkType: hard
"sprintf-js@npm:^1.1.3":
version: 1.1.3
resolution: "sprintf-js@npm:1.1.3"
@@ -11347,6 +11941,13 @@ __metadata:
languageName: node
linkType: hard
"strict-uri-encode@npm:^2.0.0":
version: 2.0.0
resolution: "strict-uri-encode@npm:2.0.0"
checksum: 10c0/010cbc78da0e2cf833b0f5dc769e21ae74cdc5d5f5bd555f14a4a4876c8ad2c85ab8b5bdf9a722dc71a11dcd3184085e1c3c0bd50ec6bb85fffc0f28cf82597d
languageName: node
linkType: hard
"string-length@npm:^4.0.1":
version: 4.0.2
resolution: "string-length@npm:4.0.2"
@@ -11560,6 +12161,30 @@ __metadata:
languageName: node
linkType: hard
"svg-parser@npm:^2.0.4":
version: 2.0.4
resolution: "svg-parser@npm:2.0.4"
checksum: 10c0/02f6cb155dd7b63ebc2f44f36365bc294543bebb81b614b7628f1af3c54ab64f7e1cec20f06e252bf95bdde78441ae295a412c68ad1678f16a6907d924512b7a
languageName: node
linkType: hard
"svgo@npm:^3.0.2":
version: 3.3.2
resolution: "svgo@npm:3.3.2"
dependencies:
"@trysound/sax": "npm:0.2.0"
commander: "npm:^7.2.0"
css-select: "npm:^5.1.0"
css-tree: "npm:^2.3.1"
css-what: "npm:^6.1.0"
csso: "npm:^5.0.5"
picocolors: "npm:^1.0.0"
bin:
svgo: ./bin/svgo
checksum: 10c0/a6badbd3d1d6dbb177f872787699ab34320b990d12e20798ecae915f0008796a0f3c69164f1485c9def399e0ce0a5683eb4a8045e51a5e1c364bb13a0d9f79e1
languageName: node
linkType: hard
"tabbable@npm:^6.0.0":
version: 6.2.0
resolution: "tabbable@npm:6.2.0"
@@ -11754,7 +12379,7 @@ __metadata:
languageName: node
linkType: hard
"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.1":
"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.1":
version: 2.8.1
resolution: "tslib@npm:2.8.1"
checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62
@@ -12009,6 +12634,15 @@ __metadata:
languageName: node
linkType: hard
"use-latest-callback@npm:^0.2.1":
version: 0.2.3
resolution: "use-latest-callback@npm:0.2.3"
peerDependencies:
react: ">=16.8"
checksum: 10c0/dc87503f6279ce2980f78e1019231ba20d7509e9d17adac05285babe4d6ba6f68c52f4ef7b5ad777cbc2af9fbaaa09d7adb664ca556da0aebab9f020022880be
languageName: node
linkType: hard
"use-sidecar@npm:^1.1.2":
version: 1.1.3
resolution: "use-sidecar@npm:1.1.3"