mirror of
https://github.com/selfxyz/self.git
synced 2026-04-05 03:00:53 -04:00
add neo bank UX 2
This commit is contained in:
54
app/src/screens/CameraScreen.tsx
Normal file
54
app/src/screens/CameraScreen.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import React, { useState } from 'react';
|
||||
import { YStack, XStack, Button, Image, ScrollView, Sheet, Fieldset, Text, Input } from 'tamagui';
|
||||
import { Camera, ExternalLink, Nfc, X, SquarePen } from '@tamagui/lucide-icons';
|
||||
import { bgColor, blueColorDark, blueColorLight, borderColor, componentBgColor, componentBgColor2, greenColorDark, greenColorLight, redColorDark, redColorLight, textColor1, textColor2 } from '../utils/colors';
|
||||
import SCANHelp from '../images/scan_help.png'
|
||||
import { startCameraScan } from '../utils/cameraScanner';
|
||||
import { Platform } from 'react-native';
|
||||
import useUserStore from '../stores/userStore';
|
||||
|
||||
interface CameraScreenProps {
|
||||
sheetIsOpen: boolean
|
||||
setSheetIsOpen: (value: boolean) => void
|
||||
}
|
||||
|
||||
const CameraScreen: React.FC<CameraScreenProps> = ({ sheetIsOpen, setSheetIsOpen }) => {
|
||||
|
||||
|
||||
const {
|
||||
passportNumber,
|
||||
dateOfBirth,
|
||||
dateOfExpiry,
|
||||
deleteMrzFields,
|
||||
update,
|
||||
clearPassportDataFromStorage,
|
||||
clearSecretFromStorage,
|
||||
} = useUserStore()
|
||||
|
||||
return (
|
||||
<YStack f={1} p="$3">
|
||||
<YStack f={1} jc="center">
|
||||
<Image borderRadius="$5"
|
||||
w="full"
|
||||
h="$13"
|
||||
source={{ uri: SCANHelp }}
|
||||
/>
|
||||
<YStack gap="$0.5" mt="$3.5">
|
||||
<Text mt="$1" color={textColor1}>Use your camera to scan the main page of your passport.</Text>
|
||||
<Text fontSize="$2" color={textColor2} mt="$2">You can also enter those data manually.</Text>
|
||||
<Text fontSize="$2" style={{ fontStyle: 'italic' }} color={textColor2}>The app does not take a picture of your passport, it only reads some fields.</Text>
|
||||
</YStack>
|
||||
|
||||
</YStack>
|
||||
|
||||
<YStack gap="$2">
|
||||
<Button borderWidth={1.3} borderColor={borderColor} borderRadius="$10" bg="#3185FC" onPress={startCameraScan}><Camera color={textColor1} /></Button>
|
||||
<Button bg={textColor2} borderColor={borderColor} borderRadius="$10" onPress={() => setSheetIsOpen(true)}><SquarePen /></Button>
|
||||
</YStack>
|
||||
|
||||
|
||||
</YStack >
|
||||
);
|
||||
};
|
||||
|
||||
export default CameraScreen;
|
||||
@@ -19,12 +19,15 @@ import { CircuitName, fetchZkey } from '../utils/zkeyDownload';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { scan } from '../utils/nfcScanner';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
import NfcScreen from './NfcScreen';
|
||||
import CameraScreen from './CameraScreen';
|
||||
|
||||
const MainScreen: React.FC = () => {
|
||||
const [NFCScanIsOpen, setNFCScanIsOpen] = useState(false);
|
||||
const [SettingsIsOpen, setSettingsIsOpen] = useState(false);
|
||||
const [HelpIsOpen, setHelpIsOpen] = useState(false);
|
||||
const [brokenCamera, setBrokenCamera] = useState(false);
|
||||
const [sheetIsOpen, setSheetIsOpen] = useState(false);
|
||||
|
||||
const {
|
||||
passportNumber,
|
||||
@@ -85,6 +88,9 @@ const MainScreen: React.FC = () => {
|
||||
useEffect(() => {
|
||||
let timeoutId: ReturnType<typeof setTimeout>;
|
||||
if (step == Steps.MRZ_SCAN_COMPLETED) {
|
||||
updateNavigationStore({
|
||||
selectedTab: "nfc",
|
||||
})
|
||||
timeoutId = setTimeout(() => {
|
||||
setNFCScanIsOpen(false);
|
||||
}, 0);
|
||||
@@ -183,7 +189,7 @@ const MainScreen: React.FC = () => {
|
||||
flex={1}
|
||||
id="passportnumber"
|
||||
onChangeText={(text) => {
|
||||
update({passportNumber: text.toUpperCase()})
|
||||
update({ passportNumber: text.toUpperCase() })
|
||||
}}
|
||||
value={passportNumber}
|
||||
keyboardType="default"
|
||||
@@ -201,7 +207,7 @@ const MainScreen: React.FC = () => {
|
||||
flex={1}
|
||||
id="dateofbirth"
|
||||
onChangeText={(text) => {
|
||||
update({dateOfBirth: text})
|
||||
update({ dateOfBirth: text })
|
||||
}}
|
||||
value={dateOfBirth}
|
||||
keyboardType={Platform.OS === "ios" ? "default" : "number-pad"}
|
||||
@@ -219,7 +225,7 @@ const MainScreen: React.FC = () => {
|
||||
flex={1}
|
||||
id="dateofexpiry"
|
||||
onChangeText={(text) => {
|
||||
update({dateOfExpiry: text})
|
||||
update({ dateOfExpiry: text })
|
||||
}}
|
||||
value={dateOfExpiry}
|
||||
keyboardType={Platform.OS === "ios" ? "default" : "number-pad"}
|
||||
@@ -348,6 +354,69 @@ const MainScreen: React.FC = () => {
|
||||
</YStack>
|
||||
</Sheet.Frame>
|
||||
</Sheet>
|
||||
|
||||
<Sheet open={sheetIsOpen} onOpenChange={setSheetIsOpen} modal dismissOnOverlayPress={true} animation="medium" snapPoints={[80]}>
|
||||
<Sheet.Overlay />
|
||||
<Sheet.Frame>
|
||||
<YStack p="$4" f={1} bg={bgColor} pl="$3" gap="$3" borderColor="white" >
|
||||
<Text fontSize="$6" mb="$4" color={textColor1}>Enter your the information manually</Text>
|
||||
<Fieldset gap="$4" horizontal>
|
||||
<Text color={textColor1} width={160} justifyContent="flex-end" fontSize="$4">
|
||||
Passport Number
|
||||
</Text>
|
||||
<Input
|
||||
bg={componentBgColor}
|
||||
color={textColor1}
|
||||
h="$3.5"
|
||||
borderColor={passportNumber?.length === 9 ? "green" : "unset"}
|
||||
flex={1}
|
||||
id="passportnumber"
|
||||
onChangeText={(text) => {
|
||||
update({ passportNumber: text.toUpperCase() })
|
||||
}}
|
||||
value={passportNumber}
|
||||
keyboardType="default"
|
||||
/>
|
||||
</Fieldset>
|
||||
<Fieldset gap="$4" horizontal>
|
||||
<Text color={textColor1} width={160} justifyContent="flex-end" fontSize="$4">
|
||||
Date of birth (yymmdd)
|
||||
</Text>
|
||||
<Input
|
||||
bg={componentBgColor}
|
||||
color={textColor1}
|
||||
h="$3.5"
|
||||
borderColor={dateOfBirth?.length === 6 ? "green" : "unset"}
|
||||
flex={1}
|
||||
id="dateofbirth"
|
||||
onChangeText={(text) => {
|
||||
update({ dateOfBirth: text })
|
||||
}}
|
||||
value={dateOfBirth}
|
||||
keyboardType={Platform.OS === "ios" ? "default" : "number-pad"}
|
||||
/>
|
||||
</Fieldset>
|
||||
<Fieldset gap="$4" horizontal>
|
||||
<Text color={textColor1} width={160} justifyContent="flex-end" fontSize="$4">
|
||||
Date of expiry (yymmdd)
|
||||
</Text>
|
||||
<Input
|
||||
bg={componentBgColor}
|
||||
color={textColor1}
|
||||
h="$3.5"
|
||||
borderColor={dateOfExpiry?.length === 6 ? "green" : "unset"}
|
||||
flex={1}
|
||||
id="dateofexpiry"
|
||||
onChangeText={(text) => {
|
||||
update({ dateOfExpiry: text })
|
||||
}}
|
||||
value={dateOfExpiry}
|
||||
keyboardType={Platform.OS === "ios" ? "default" : "number-pad"}
|
||||
/>
|
||||
</Fieldset>
|
||||
</YStack>
|
||||
</Sheet.Frame>
|
||||
</Sheet>
|
||||
<XStack bc="#343434" h={1.2} />
|
||||
</YStack>
|
||||
<Tabs f={1} orientation="horizontal" flexDirection="column" defaultValue="scan"
|
||||
@@ -357,9 +426,14 @@ const MainScreen: React.FC = () => {
|
||||
<ToastViewport flexDirection="column-reverse" top={15} right={0} left={0} />
|
||||
<ToastMessage />
|
||||
<Tabs.Content value="scan" f={1}>
|
||||
<ScanScreen
|
||||
<CameraScreen
|
||||
sheetIsOpen={sheetIsOpen}
|
||||
setSheetIsOpen={setSheetIsOpen}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="nfc" f={1}>
|
||||
<NfcScreen
|
||||
handleNFCScan={handleNFCScan}
|
||||
step={step}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
|
||||
67
app/src/screens/NfcScreen.tsx
Normal file
67
app/src/screens/NfcScreen.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import { YStack, Text, XStack, Button, Image, ScrollView } from 'tamagui';
|
||||
import { Steps } from '../utils/utils';
|
||||
import { Camera, ExternalLink, Nfc, X, SquarePen } from '@tamagui/lucide-icons';
|
||||
import { blueColorDark, blueColorLight, borderColor, componentBgColor2, greenColorDark, greenColorLight, redColorDark, redColorLight, textColor1, textColor2 } from '../utils/colors';
|
||||
import { useToastController } from '@tamagui/toast'
|
||||
import NFCHelp from '../images/nfc_help.png'
|
||||
import SCANHelp from '../images/scan_help.png'
|
||||
import { Linking } from 'react-native';
|
||||
import { startCameraScan } from '../utils/cameraScanner';
|
||||
|
||||
interface NfcScreenProps {
|
||||
handleNFCScan: () => void;
|
||||
}
|
||||
|
||||
|
||||
const NfcScreen: React.FC<NfcScreenProps> = ({ handleNFCScan }) => {
|
||||
return (
|
||||
<YStack f={1} p="$3">
|
||||
|
||||
<Image borderRadius="$5" alignSelf='center'
|
||||
w="$12"
|
||||
h="$14"
|
||||
source={{ uri: NFCHelp }}
|
||||
/>
|
||||
|
||||
<YStack f={1} gap="$2">
|
||||
|
||||
|
||||
<YStack mt="$2">
|
||||
<Text fontSize="$7" fow="bold" mt="$1" color={textColor1}>Scan the NFC chip in your passport.</Text>
|
||||
<Text fontSize="$6" color={textColor1} mt="$2">How do I find and scan the NFC chip?</Text>
|
||||
<YStack ml="$3" gap="$2" mt="$1" >
|
||||
<XStack gap="$1">
|
||||
<Text fontSize="$4" color={textColor2}>1.</Text>
|
||||
<Text fontSize="$4" color={textColor2}>Close your passport and hold the middle of the back cover of your passport to the top of the phoneThe passport should touch the phone.</Text>
|
||||
</XStack>
|
||||
<XStack gap="$1">
|
||||
<Text fontSize="$4" color={textColor2}>2.</Text>
|
||||
<Text fontSize="$4" color={textColor2}>If that does not work, try using the front cover of your passport.</Text>
|
||||
</XStack>
|
||||
<XStack gap="$1">
|
||||
<Text fontSize="$4" color={textColor2}>3.</Text>
|
||||
<Text fontSize="$4" color={textColor2}>Move your phone slowly up and down until scanning starts.</Text>
|
||||
</XStack>
|
||||
<XStack gap="$1">
|
||||
<Text fontSize="$4" color={textColor2}>4.</Text>
|
||||
<Text fontSize="$4" color={textColor2}>Hold your device still when scanning starts.</Text>
|
||||
</XStack>
|
||||
</YStack>
|
||||
|
||||
</YStack>
|
||||
|
||||
</YStack>
|
||||
|
||||
<YStack gap="$2">
|
||||
<Button borderWidth={1.3} borderColor={borderColor} borderRadius="$10" bg="#3185FC" onPress={handleNFCScan}><Nfc color={textColor1} /></Button>
|
||||
</YStack>
|
||||
|
||||
|
||||
|
||||
|
||||
</YStack >
|
||||
);
|
||||
};
|
||||
|
||||
export default NfcScreen;
|
||||
@@ -39,10 +39,10 @@ const useUserStore = create<UserState>((set, get) => ({
|
||||
secret: "",
|
||||
|
||||
// When user opens the app, checks presence of passportData
|
||||
// - If passportData is not present, starts the onboarding flow
|
||||
// - If passportData is present, then secret must be here too (they are always set together). Request the tree.
|
||||
// - If the commitment is present in the tree, proceed to main screen
|
||||
// - If the commitment is not present in the tree, proceed to main screen AND try registering it in the background
|
||||
// - If passportData is not present, starts the onboarding flow
|
||||
// - If passportData is present, then secret must be here too (they are always set together). Request the tree.
|
||||
// - If the commitment is present in the tree, proceed to main screen
|
||||
// - If the commitment is not present in the tree, proceed to main screen AND try registering it in the background
|
||||
initUserStore: async () => {
|
||||
const passportDataCreds = await Keychain.getGenericPassword({ service: "passportData" });
|
||||
if (!passportDataCreds) {
|
||||
@@ -72,8 +72,8 @@ const useUserStore = create<UserState>((set, get) => ({
|
||||
},
|
||||
|
||||
// When reading passport for the first time:
|
||||
// - Check presence of secret. If there is none, create one and store it
|
||||
// - Store the passportData and try registering the commitment in the background
|
||||
// - Check presence of secret. If there is none, create one and store it
|
||||
// - Store the passportData and try registering the commitment in the background
|
||||
registerPassportData: async (passportData) => {
|
||||
const secretCreds = await Keychain.getGenericPassword({ service: "secret" });
|
||||
|
||||
@@ -115,38 +115,38 @@ const useUserStore = create<UserState>((set, get) => ({
|
||||
} = useNavigationStore.getState();
|
||||
|
||||
try {
|
||||
// const inputs = generateCircuitInputsRegister(
|
||||
// passportData,
|
||||
// secret,
|
||||
// { developmentMode: false }
|
||||
// );
|
||||
// const inputs = generateCircuitInputsRegister(
|
||||
// passportData,
|
||||
// secret,
|
||||
// { developmentMode: false }
|
||||
// );
|
||||
|
||||
// amplitude.track(`Sig alg supported: ${passportData.signatureAlgorithm}`);
|
||||
|
||||
// Object.keys(inputs).forEach((key) => {
|
||||
// if (Array.isArray(inputs[key as keyof typeof inputs])) {
|
||||
// console.log(key, inputs[key as keyof typeof inputs].slice(0, 10), '...');
|
||||
// } else {
|
||||
// console.log(key, inputs[key as keyof typeof inputs]);
|
||||
// }
|
||||
// });
|
||||
|
||||
// const start = Date.now();
|
||||
// amplitude.track(`Sig alg supported: ${passportData.signatureAlgorithm}`);
|
||||
|
||||
// const proof = await generateProof(
|
||||
// `Register_${passportData.signatureAlgorithm}`, // TODO format it
|
||||
// inputs,
|
||||
// );
|
||||
// Object.keys(inputs).forEach((key) => {
|
||||
// if (Array.isArray(inputs[key as keyof typeof inputs])) {
|
||||
// console.log(key, inputs[key as keyof typeof inputs].slice(0, 10), '...');
|
||||
// } else {
|
||||
// console.log(key, inputs[key as keyof typeof inputs]);
|
||||
// }
|
||||
// });
|
||||
|
||||
// const end = Date.now();
|
||||
// console.log('Total proof time from frontend:', end - start);
|
||||
// amplitude.track('Proof generation successful, took ' + ((end - start) / 1000) + ' seconds');
|
||||
// const start = Date.now();
|
||||
|
||||
// // TODO send the proof to the relayer
|
||||
// const proof = await generateProof(
|
||||
// `Register_${passportData.signatureAlgorithm}`, // TODO format it
|
||||
// inputs,
|
||||
// );
|
||||
|
||||
// set({
|
||||
// registered: true,
|
||||
// });
|
||||
// const end = Date.now();
|
||||
// console.log('Total proof time from frontend:', end - start);
|
||||
// amplitude.track('Proof generation successful, took ' + ((end - start) / 1000) + ' seconds');
|
||||
|
||||
// // TODO send the proof to the relayer
|
||||
|
||||
// set({
|
||||
// registered: true,
|
||||
// });
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
toast?.show('Error', {
|
||||
@@ -178,7 +178,7 @@ const useUserStore = create<UserState>((set, get) => ({
|
||||
passportNumber: "",
|
||||
dateOfBirth: "",
|
||||
dateOfExpiry: "",
|
||||
}, true),
|
||||
}),
|
||||
}))
|
||||
|
||||
export default useUserStore
|
||||
Reference in New Issue
Block a user