add neo bank UX 2

This commit is contained in:
turnoffthiscomputer
2024-05-16 16:16:08 +02:00
parent ad8d759aa3
commit cc13f3ce24
4 changed files with 234 additions and 39 deletions

View 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;

View File

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

View 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;

View File

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