Improve UX and DX (#66)

Co-authored-by: Nicolas Brugneaux <nicolas.brugneaux@gmail.com>
This commit is contained in:
Justin Hernandez
2025-02-11 03:08:07 -06:00
committed by GitHub
parent 2fc450e0c9
commit 73421f6a9a
21 changed files with 436 additions and 198 deletions

View File

@@ -1,5 +1,6 @@
import React, { useEffect } from 'react';
import 'react-native-get-random-values';
import Orientation from 'react-native-orientation-locker';
import { createClient } from '@segment/analytics-react-native';
import { Buffer } from 'buffer';
@@ -24,10 +25,6 @@ function App(): React.JSX.Element {
const initUserStore = useUserStore(state => state.initUserStore);
// const setSelectedTab = useNavigationStore(state => state.setSelectedTab);
useEffect(() => {
initUserStore();
}, [initUserStore]);
// useEffect(() => {
// setToast(toast);
// }, [toast, setToast]);
@@ -37,14 +34,18 @@ function App(): React.JSX.Element {
// }, [setSelectedTab]);
useEffect(() => {
const cleanup = setupUniversalLinkListener();
return cleanup;
}, []);
useEffect(() => {
// Initialize segment directly without any tracking checks
// init
initUserStore();
const universalLinkCleanup = setupUniversalLinkListener();
segmentClient = createSegmentClient();
}, []);
Orientation.lockToPortrait();
// cleanup
return () => {
universalLinkCleanup();
Orientation.unlockAllOrientations();
};
}, [initUserStore]);
return (
<YStack f={1} bc={bgWhite} h="100%" w="100%">

View File

@@ -3,6 +3,7 @@ package com.proofofpassportapp
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.content.pm.ActivityInfo
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
@@ -31,5 +32,7 @@ class MainActivity : ReactActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Lock to portrait orientation
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
}

View File

@@ -2,7 +2,6 @@
"name": "react-native-passport-reader",
"version": "1.0.3",
"description": "read the NFC chip in a passport",
"main": "index.android.js",
"keywords": [
"react-native",
"react-component",
@@ -10,6 +9,7 @@
"android",
"scanner"
],
"license": "APLv2",
"author": "Mark Vayngrib <mark@tradle.io> (http://github.com/mvayngrib)",
"license": "APLv2"
"main": "index.android.js"
}

View File

@@ -103,5 +103,9 @@
<string>slkscr.ttf</string>
<string>slkscrb.ttf</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
</dict>
</plist>

View File

@@ -1241,6 +1241,8 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-biometrics (3.0.1):
- React-Core
- react-native-date-picker (4.3.7):
- React-Core
- react-native-get-random-values (1.11.0):
@@ -1249,10 +1251,14 @@ PODS:
- React-Core
- react-native-nfc-manager (3.16.1):
- React-Core
- react-native-orientation-locker (1.7.0):
- React-Core
- react-native-randombytes (3.6.1):
- React-Core
- react-native-safe-area-context (5.1.0):
- react-native-safe-area-context (5.2.0):
- React-Core
- react-native-tracking-transparency (0.1.2):
- React
- React-nativeconfig (0.75.4)
- React-NativeModulesApple (0.75.4):
- glog
@@ -1570,7 +1576,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- RNSVG (13.4.0):
- RNSVG (15.11.1):
- React-Core
- RNZipArchive (6.1.2):
- React-Core
@@ -1632,12 +1638,15 @@ DEPENDENCIES:
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
- react-native-biometrics (from `../node_modules/react-native-biometrics`)
- react-native-date-picker (from `../node_modules/react-native-date-picker`)
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-nfc-manager (from `../node_modules/react-native-nfc-manager`)
- react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`)
- react-native-randombytes (from `../node_modules/react-native-randombytes`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- react-native-tracking-transparency (from `../node_modules/react-native-tracking-transparency`)
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@@ -1764,6 +1773,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
React-microtasksnativemodule:
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
react-native-biometrics:
:path: "../node_modules/react-native-biometrics"
react-native-date-picker:
:path: "../node_modules/react-native-date-picker"
react-native-get-random-values:
@@ -1772,10 +1783,14 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-community/netinfo"
react-native-nfc-manager:
:path: "../node_modules/react-native-nfc-manager"
react-native-orientation-locker:
:path: "../node_modules/react-native-orientation-locker"
react-native-randombytes:
:path: "../node_modules/react-native-randombytes"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-tracking-transparency:
:path: "../node_modules/react-native-tracking-transparency"
React-nativeconfig:
:path: "../node_modules/react-native/ReactCommon"
React-NativeModulesApple:
@@ -1905,12 +1920,15 @@ SPEC CHECKSUMS:
React-logger: d79b704bf215af194f5213a6b7deec50ba8e6a9b
React-Mapbuffer: 42c779748af341935a63ad8831723b8cb1e97830
React-microtasksnativemodule: 744f7e26200ea3976fef8453101cefcc08756008
react-native-biometrics: 352e5a794bfffc46a0c86725ea7dc62deb085bdc
react-native-date-picker: 5637f417bb0c1981bc9d483324d5eb5929a1651c
react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06
react-native-netinfo: f0a9899081c185db1de5bb2fdc1c88c202a059ac
react-native-nfc-manager: 5213321cf6c18d879c8092c0bf56806b771ec5ac
react-native-orientation-locker: 5819fd23ca89cbac0d736fb4314745f62716d517
react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846
react-native-safe-area-context: 04803a01f39f31cc6605a5531280b477b48f8a88
react-native-safe-area-context: 849d7df29ecb2a7155c769c0b76849ba952c2aa3
react-native-tracking-transparency: 25ff1ff866e338c137c818bdec20526bb05ffcc1
React-nativeconfig: 31072ab0146e643594f6959c7f970a04b6c9ddd0
React-NativeModulesApple: 5df767d9a2197ac25f4d8dd2d4ae1af3624022e2
React-perflogger: 59e1a3182dca2cee7b9f1f7aab204018d46d1914
@@ -1944,7 +1962,7 @@ SPEC CHECKSUMS:
RNLocalize: 06991b9c31e7a898a9fa6ddb204ce0f53a967248
RNReactNativeHapticFeedback: cba92e59f56506f6058d261dc85986012b2c5032
RNScreens: 7cdbd2d97472f2838cee0d53171a89e7e0c30991
RNSVG: 07dbd870b0dcdecc99b3a202fa37c8ca163caec2
RNSVG: 669ed128ab9005090c612a0d627dbecb6ab5c76f
RNZipArchive: 6d736ee4e286dbbd9d81206b7a4da355596ca04a
segment-analytics-react-native: d57ed4971cbb995706babf29215ebdbf242ecdab
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d

View File

@@ -73,6 +73,7 @@
"react-native-keychain": "^8.2.0",
"react-native-localize": "^3.4.1",
"react-native-nfc-manager": "^3.15.1",
"react-native-orientation-locker": "^1.7.0",
"react-native-passport-reader": "^1.0.3",
"react-native-randombytes": "^3.6.1",
"react-native-safe-area-context": "^5.2.0",

View File

@@ -35,6 +35,7 @@ import ProveScreen from './screens/ProveFlow/ProveScreen';
import ValidProofScreen from './screens/ProveFlow/ValidProofScreen';
import QRCodeViewFinderScreen from './screens/ProveFlow/ViewFinder';
import WrongProofScreen from './screens/ProveFlow/WrongProofScreen';
import DevSettingsScreen from './screens/Settings/DevSettingsScreen';
import ShowRecoveryPhraseScreen from './screens/Settings/ShowRecoveryPhraseScreen';
import SettingsScreen from './screens/SettingsScreen';
import SplashScreen from './screens/SplashScreen';
@@ -271,6 +272,17 @@ const AppNavigation = createNativeStackNavigator({
},
},
},
DevSettings: {
screen: DevSettingsScreen,
options: {
if: () => __DEV__,
title: 'Developer Settings',
headerStyle: {
backgroundColor: white,
},
},
},
},
});

View File

@@ -1,35 +0,0 @@
import React from 'react';
import { View } from 'react-native';
import { Text, XStack, YStack } from 'tamagui';
import { PrimaryButton } from '../components/buttons/PrimaryButton';
import { SecondaryButton } from '../components/buttons/SecondaryButton';
import { white } from '../utils/colors';
const DevPlayScreen = () => {
return (
<YStack ai="center" f={1} gap="$8" mt="$18" mb="$8">
<View
style={{
padding: 20,
display: 'flex',
alignItems: 'center',
gap: 20,
width: '100%',
backgroundColor: white,
}}
>
<Text fontSize="$9">Hello PASSPORT</Text>
<PrimaryButton>Primary Button</PrimaryButton>
<PrimaryButton disabled>Primary Button Disabled</PrimaryButton>
<SecondaryButton>Secondary Button</SecondaryButton>
<SecondaryButton disabled>Secondary Button Disabled</SecondaryButton>
</View>
<XStack f={1} />
</YStack>
);
};
export default DevPlayScreen;

View File

@@ -10,8 +10,10 @@ import Logo from '../images/logo.svg';
import { ExpandableBottomLayout } from '../layouts/ExpandableBottomLayout';
import { slate50, slate100, slate500, slate700, white } from '../utils/colors';
const LaunchScreen: React.FC = () => {
const onStartPress = useHapticNavigation('Start');
interface LaunchScreenProps {}
const LaunchScreen: React.FC<LaunchScreenProps> = ({}) => {
const onStartPress = useHapticNavigation('PassportCamera');
return (
<ExpandableBottomLayout.Layout>

View File

@@ -0,0 +1,210 @@
import React from 'react';
import Dialog from 'react-native-dialog';
import { useNavigation } from '@react-navigation/native';
import { Check, ChevronDown, Eraser, IterationCw } from '@tamagui/lucide-icons';
import { Adapt, Button, Fieldset, Label, Select, Sheet, YStack } from 'tamagui';
import { RootStackParamList } from '../../Navigation';
import { borderColor, textBlack, textColor2 } from '../../utils/colors';
interface DevSettingsScreenProps {}
const items: (keyof RootStackParamList)[] = [
'DevSettings',
'Splash',
'Launch',
'Start',
'PassportOnboarding',
'PassportCamera',
'PassportNFCScan',
'ConfirmBelongingScreen',
'CreateMock',
'NextScreen',
'Home',
'Disclaimer',
'QRCodeViewFinder',
'ProveScreen',
'ValidProofScreen',
'WrongProofScreen',
'Settings',
'AccountRecovery',
'SaveRecoveryPhrase',
'RecoverWithPhrase',
'AccountVerifiedSuccess',
'ShowRecoveryPhrase',
];
const ScreenSelector = ({}) => {
const navigation = useNavigation();
return (
<Select
onValueChange={screen => {
navigation.navigate(screen as keyof RootStackParamList);
}}
disablePreventBodyScroll
>
<Select.Trigger width={220} iconAfter={ChevronDown}>
<Select.Value placeholder="Select screen to debug" />
</Select.Trigger>
<Adapt when="sm" platform="touch">
<Sheet native modal dismissOnSnapToBottom animation="medium">
<Sheet.Frame>
<Sheet.ScrollView>
<Adapt.Contents />
</Sheet.ScrollView>
</Sheet.Frame>
<Sheet.Overlay
backgroundColor="$shadowColor"
animation="lazy"
enterStyle={{ opacity: 0 }}
exitStyle={{ opacity: 0 }}
/>
</Sheet>
</Adapt>
<Select.Content zIndex={200000}>
<Select.Viewport minWidth={200}>
<Select.Group>
{React.useMemo(
() =>
items.map((item, i) => {
return (
<Select.Item index={i} key={item} value={item}>
<Select.ItemText>{item}</Select.ItemText>
<Select.ItemIndicator marginLeft="auto">
<Check size={16} />
</Select.ItemIndicator>
</Select.Item>
);
}),
[items],
)}
</Select.Group>
</Select.Viewport>
</Select.Content>
</Select>
);
};
const DevSettingsScreen: React.FC<DevSettingsScreenProps> = ({}) => {
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> */}
<Fieldset gap="$4" mt="$1" horizontal>
<Label color={textBlack} justifyContent="flex-end" htmlFor="skip">
Shortcut
</Label>
<ScreenSelector />
</Fieldset>
</YStack>
);
};
export default DevSettingsScreen;

View File

@@ -4,6 +4,7 @@ import { getCountry, getLocales, getTimeZone } from 'react-native-localize';
import { SvgProps } from 'react-native-svg';
import { useNavigation } from '@react-navigation/native';
import { Bug } from '@tamagui/lucide-icons';
import { Button, XStack, YStack } from 'tamagui';
import { version } from '../../package.json';
@@ -48,6 +49,11 @@ const routes = [
[ShareIcon, 'Share Self app', 'share'],
] as [React.FC<SvgProps>, string, RouteOption][];
if (__DEV__) {
// @ts-expect-error
routes.push([Bug, 'Debug menu', 'DevSettings']);
}
const social = [
[Github, 'https://github.com/selfxyz/self'],
[Web, 'https://www.self.xyz/'],

View File

@@ -11,22 +11,23 @@ import { impactLight } from '../utils/haptic';
const SplashScreen: React.FC = ({}) => {
const navigation = useNavigation();
const { userLoaded, passportData } = useUserStore();
const { userLoaded } = useUserStore();
const redirect = useCallback(() => {
if (passportData) {
navigation.navigate('Home');
} else {
navigation.navigate('Launch');
}
}, [passportData, userLoaded]);
// TODO: Uncomment when we are done testing
// const redirect = useCallback(() => {
// if (passportData) {
// navigation.navigate('Home');
// } else {
// navigation.navigate('Launch');
// }
// }, [passportData, userLoaded]);
const handleAnimationFinish = useCallback(() => {
setTimeout(() => {
impactLight();
redirect();
}, 750);
}, [redirect]);
navigation.navigate('Launch');
}, 1000);
}, [userLoaded]);
return (
<LottieView

View File

@@ -12485,6 +12485,7 @@ __metadata:
react-native-keychain: "npm:^8.2.0"
react-native-localize: "npm:^3.4.1"
react-native-nfc-manager: "npm:^3.15.1"
react-native-orientation-locker: "npm:^1.7.0"
react-native-passport-reader: "npm:^1.0.3"
react-native-randombytes: "npm:^3.6.1"
react-native-safe-area-context: "npm:^5.2.0"
@@ -13147,7 +13148,7 @@ __metadata:
peerDependencies:
react: ">= 17.0.1"
react-native: ">= 0.64.3"
checksum: 10c0/ebb3dfb9a111bf6082866999d31e10f952fcd12bcf2a81210ff9f44a4cb4d25e1b4caac5fdec512b367685e56a37e61de3e6648c2af45b93fcbaf704355dbaa6
checksum: 10c0/0e3f83f59dadc337ab46fa0c59bb2b80eb91d4756f0ac67d7b11e0d97044d58bbaca7ee5c7dcd1e0a389af3f16b0eaca12122d59905013a6f5a2f4423ead49d0
languageName: node
linkType: hard
@@ -13254,6 +13255,20 @@ __metadata:
languageName: node
linkType: hard
"react-native-orientation-locker@npm:^1.7.0":
version: 1.7.0
resolution: "react-native-orientation-locker@npm:1.7.0"
peerDependencies:
react: ">=16.13.1"
react-native: ">=0.63.2"
react-native-windows: ">=0.63.3"
peerDependenciesMeta:
react-native-windows:
optional: true
checksum: 10c0/68ac54460a0722c203cda17d8a26cf8409115e6ed1ecc4324c25b60f94476f6200ae6646fa71495288d553f3a77a20c5548d0d33b434066ef8d7284dbbd094ff
languageName: node
linkType: hard
"react-native-passport-reader@npm:^1.0.3":
version: 1.0.3
resolution: "react-native-passport-reader@npm:1.0.3"