Files
self/app/src/screens/MainScreen.tsx
turnoffthiscomputer 7f7aa2ebe7 update UI
2024-08-23 10:26:47 +01:00

910 lines
37 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from 'react';
import { NativeEventEmitter, NativeModules, Linking, Modal, Platform, Pressable } from 'react-native';
import { YStack, XStack, Text, Button, Tabs, Sheet, Label, Fieldset, Input, Switch, H2, Image, useWindowDimensions, H4, H3, View, Separator } from 'tamagui'
import { HelpCircle, IterationCw, VenetianMask, Cog, CheckCircle2, ChevronLeft, Share, Eraser, ArrowRight, UserPlus, CalendarSearch, X } from '@tamagui/lucide-icons';
import Telegram from '../images/telegram.png'
import Github from '../images/github.png'
import Internet from "../images/internet.png"
import forge from 'node-forge';
import Dialog from "react-native-dialog";
import { ethers } from 'ethers';
// import ressources
import Xlogo from '../images/x.png'
import NFC_IMAGE from '../images/nfc.png'
import { ToastViewport } from '@tamagui/toast';
import { ToastMessage } from '../components/ToastMessage';
// import stores
import useUserStore from '../stores/userStore';
import useNavigationStore from '../stores/navigationStore';
// import utils
import { bgColor, bgGreen, bgWhite, blueColorLight, borderColor, componentBgColor, componentBgColor2, separatorColor, textBlack, textColor1, textColor2 } from '../utils/colors';
import { ModalProofSteps, Steps } from '../utils/utils';
import { scan } from '../utils/nfcScanner';
import { CircuitName, fetchZkey } from '../utils/zkeyDownload';
import { contribute } from '../utils/contribute';
import { sendCSCARequest } from '../utils/cscaRequest';
import { sendRegisterTransaction } from '../utils/transactions';
// import utils from common
import { mockPassportData_sha256_rsa_65537 } from '../../../common/src/constants/mockPassportData';
import { getCSCAInputs } from '../../../common/src/utils/csca';
import { formatSigAlgNameForCircuit } from '../../../common/src/utils/utils';
// import screens
import ProveScreen from './ProveScreen';
import NfcScreen from './NfcScreen';
import CameraScreen from './CameraScreen';
import NextScreen from './NextScreen';
import RegisterScreen from './RegisterScreen';
import SendProofScreen from './SendProofScreen';
import AppScreen from './AppScreen';
// import constants
import { RPC_URL, SignatureAlgorithm } from '../../../common/src/constants/constants';
import { mock_csca_sha256_rsa_4096, mock_dsc_sha256_rsa_4096 } from '../../../common/src/constants/mockCertificates';
const { nativeModule } = NativeModules;
let emitter: NativeEventEmitter | null = null;
if (Platform.OS === 'android') {
emitter = new NativeEventEmitter(nativeModule);
}
import DatePicker from 'react-native-date-picker'
import StartScreen from './StartScreen';
import CustomButton from '../components/CustomButton';
import StepOneStepTwo from '../components/StepOneStepTwo';
import SplashScreen from './SplashScreen';
import ValidProofScreen from './ValidProofScreen';
import WrongProofScreen from './WrongProofScreen';
const MainScreen: React.FC = () => {
const [NFCScanIsOpen, setNFCScanIsOpen] = useState(false);
const [scanningMessage, setScanningMessage] = useState('');
const [displayOtherOptions, setDisplayOtherOptions] = useState(false);
const [SettingsIsOpen, setSettingsIsOpen] = useState(false);
const [DialogContributeIsOpen, setDialogContributeIsOpen] = useState(false);
const [dialogDeleteSecretIsOpen, setDialogDeleteSecretIsOpen] = useState(false);
const [HelpIsOpen, setHelpIsOpen] = useState(false);
const [sheetIsOpen, setSheetIsOpen] = useState(false);
const [sheetAppListOpen, setSheetAppListOpen] = useState(false);
const [sheetRegisterIsOpen, setSheetRegisterIsOpen] = useState(false);
const [modalProofStep, setModalProofStep] = useState(0);
const [dateOfBirthDatePicker, setDateOfBirthDatePicker] = useState<Date | null>(null)
const [dateOfExpiryDatePicker, setDateOfExpiryDatePicker] = useState<Date | null>(null)
const [dateOfBirthDatePickerIsOpen, setDateOfBirthDatePickerIsOpen] = useState(false)
const [dateOfExpiryDatePickerIsOpen, setDateOfExpiryDatePickerIsOpen] = useState(false)
const [isFormComplete, setIsFormComplete] = useState(false);
const {
passportNumber,
dateOfBirth,
dateOfExpiry,
deleteMrzFields,
update,
clearPassportDataFromStorage,
clearSecretFromStorage,
clearProofsFromStorage,
passportData,
registered,
setRegistered,
cscaProof,
localProof,
} = useUserStore()
const {
showWarningModal,
update: updateNavigationStore,
step,
setStep,
selectedTab,
setSelectedTab,
hideData,
toast,
showRegistrationErrorSheet,
registrationErrorMessage,
nfcSheetIsOpen,
setNfcSheetIsOpen,
} = useNavigationStore();
const handleRestart = () => {
updateNavigationStore({
selectedTab: "start",
step: Steps.MRZ_SCAN,
})
deleteMrzFields();
}
const handleHideData = () => {
updateNavigationStore({
hideData: !hideData,
})
}
const handleSkip = () => {
update({
passportData: mockPassportData_sha256_rsa_65537
})
setStep(Steps.REGISTERED);
deleteMrzFields();
// const n_dsc = 121;
// const k_dsc = 17;
// const n_csca = 121;
// const k_csca = 34;
// const max_cert_bytes = 1664;
// const dsc = mock_dsc_sha256_rsa_4096;
// const csca = mock_csca_sha256_rsa_4096;
// const dscCert = forge.pki.certificateFromPem(dsc);
// const cscaCert = forge.pki.certificateFromPem(csca);
// let secret = useUserStore.getState().dscSecret;
// if (secret === null) {
// // Finally, generate CSCA Inputs and request modal server
// // Generate a cryptographically secure random secret of (31 bytes)
// const secretBytes = forge.random.getBytesSync(31);
// secret = BigInt(`0x${forge.util.bytesToHex(secretBytes)}`).toString();
// console.log('Generated secret:', secret.toString());
// useUserStore.getState().setDscSecret(secret);
// }
// const inputs_csca = getCSCAInputs(
// secret,
// dscCert,
// cscaCert,
// n_dsc,
// k_dsc,
// n_csca,
// k_csca,
// max_cert_bytes,
// true
// );
// sendCSCARequest(inputs_csca, setModalProofStep);
toast.show("Using mock passport data!", { type: "info" })
}
const castDate = (date: Date) => {
return (date.toISOString().slice(2, 4) + date.toISOString().slice(5, 7) + date.toISOString().slice(8, 10)).toString();
}
const decrementStep = () => {
if (selectedTab === "scan") {
setSelectedTab("start");
}
else if (selectedTab === "nfc") {
setSelectedTab("scan");
}
else if (selectedTab === "next") {
setSelectedTab("nfc");
}
else if (selectedTab === "register") {
setStep(Steps.NEXT_SCREEN);
}
else if (selectedTab === "app") {
setStep(Steps.REGISTER);
}
else if (selectedTab === "prove") {
setStep(Steps.REGISTERED);
}
else if (selectedTab === "mint") {
setStep(Steps.REGISTERED);
}
};
const handleNFCScan = () => {
if ((Platform.OS === 'ios')) {
console.log('ios');
scan(setModalProofStep);
}
else {
console.log('android :)');
setNFCScanIsOpen(true);
scan(setModalProofStep);
}
}
function handleContribute() {
contribute(passportData);
setDialogContributeIsOpen(false);
}
function handleDeleteSecret() {
clearSecretFromStorage()
setDialogDeleteSecretIsOpen(false);
}
useEffect(() => {
const handleNativeEvent = (event: string) => {
setScanningMessage(event);
};
if (Platform.OS === 'android' && emitter) {
const subscription = emitter.addListener('NativeEvent', handleNativeEvent);
return () => {
subscription.remove();
};
}
}, []);
// useEffect(() => {
// if (cscaProof && (modalProofStep === ModalProofSteps.MODAL_SERVER_SUCCESS)) {
// console.log('CSCA Proof received:', cscaProof);
// if ((cscaProof !== null) && (localProof !== null)) {
// const sendTransaction = async () => {
// const sigAlgFormatted = formatSigAlgNameForCircuit(passportData.signatureAlgorithm, passportData.pubKey.exponent);
// const sigAlgIndex = SignatureAlgorithm[sigAlgFormatted as keyof typeof SignatureAlgorithm]
// console.log("local proof already generated, sending transaction");
// const provider = new ethers.JsonRpcProvider(RPC_URL);
// const serverResponse = await sendRegisterTransaction(localProof, cscaProof, sigAlgIndex)
// const txHash = serverResponse?.data.hash;
// const receipt = await provider.waitForTransaction(txHash);
// console.log('receipt status:', receipt?.status);
// if (receipt?.status === 0) {
// throw new Error("Transaction failed");
// }
// setRegistered(true);
// setSelectedTab("app");
// setStep(Steps.REGISTERED);
// toast.show('✅', {
// message: "Registered",
// customData: {
// type: "success",
// },
// })
// }
// sendTransaction();
// }
// }
// }, [modalProofStep]);
useEffect(() => {
setIsFormComplete(passportNumber?.length >= 3 && dateOfBirth?.length >= 6 && dateOfExpiry?.length >= 6);
}, [passportNumber, dateOfBirth, dateOfExpiry]);
// useEffect(() => {
// if (registered && step < Steps.REGISTERED) {
// setStep(Steps.REGISTERED);
// }
// }, [registered]);
// useEffect(() => {
// let timeoutId: ReturnType<typeof setTimeout>;
// if (step == Steps.START) {
// updateNavigationStore({
// selectedTab: "start",
// })
// }
// if (step == Steps.MRZ_SCAN) {
// updateNavigationStore({
// selectedTab: "scan",
// })
// timeoutId = setTimeout(() => {
// setNFCScanIsOpen(false);
// }, 0);
// }
// else if (step == Steps.MRZ_SCAN_COMPLETED) {
// updateNavigationStore({
// selectedTab: "nfc",
// })
// timeoutId = setTimeout(() => {
// setNFCScanIsOpen(false);
// }, 0);
// }
// else if (step == Steps.NEXT_SCREEN) {
// // Set the timeout and store its ID
// timeoutId = setTimeout(() => {
// setNFCScanIsOpen(false);
// }, 700);
// }
// else if (step == Steps.PROOF_GENERATED) {
// updateNavigationStore({
// selectedTab: "mint",
// })
// }
// if (step == Steps.NEXT_SCREEN) {
// updateNavigationStore({
// selectedTab: "next",
// })
// }
// if (step == Steps.REGISTER) {
// updateNavigationStore({
// selectedTab: "register",
// })
// }
// if (step == Steps.REGISTERED) {
// updateNavigationStore({
// selectedTab: "app",
// })
// }
// return () => {
// if (timeoutId) {
// clearTimeout(timeoutId);
// }
// };
// }, [step]);
const { height } = useWindowDimensions();
return (
<YStack f={1}>
<ToastViewport portalToRoot flexDirection="column-reverse" top={85} right={0} left={0} />
<ToastMessage />
<YStack f={1} mt={Platform.OS === 'ios' ? "$8" : "$0"} mb={Platform.OS === 'ios' ? "$4" : "$2"}>
<YStack >
<StepOneStepTwo variable={selectedTab} step1="scan" step2="nfc" />
{selectedTab !== ("app") && selectedTab !== ("splash") && <XStack onPress={() => setSelectedTab("app")} px="$4" py="$2" mt="$3" alignSelf='flex-end'><X size={28} color={textBlack} /></XStack>}
{selectedTab === "app" &&
<XStack px="$4" py="$2" mt="$0" ai="center">
<Text fontSize="$9" >OpenPassport</Text>
<XStack f={1} />
<XStack onPress={() => setHelpIsOpen(true)}><HelpCircle size={28} color={textBlack} /></XStack>
<XStack p="$2" onPress={() => setSettingsIsOpen(true)}><Cog size={24} color={textBlack} /></XStack>
</XStack>
}
{/* {selectedTab !== "start" && selectedTab !== "scan" && selectedTab !== "nfc" && selectedTab !== "next" && selectedTab !== "register" && (
<YStack>
<XStack jc="space-between" ai="center" px="$3">
<Button p="$2" py="$3" unstyled onPress={decrementStep}><ChevronLeft color={(selectedTab === "start") ? "transparent" : "#a0a0a0"} /></Button>
<Text fontSize="$6" color="#a0a0a0">
{selectedTab === "scan" ? "Scan" : (selectedTab === "app" ? "Apps" : "Prove")}
</Text>
<XStack>
<Button p="$2" py="$3" unstyled onPress={() => setSettingsIsOpen(true)}><Cog color="#a0a0a0" /></Button>
<Button p="$2" py="$3" unstyled onPress={() => setHelpIsOpen(true)}><HelpCircle color="#a0a0a0" /></Button>
</XStack>
</XStack>
<Separator borderColor={separatorColor} />
</YStack>
)} */}
<Sheet open={nfcSheetIsOpen} onOpenChange={setNfcSheetIsOpen} dismissOnSnapToBottom modal dismissOnOverlayPress={false} disableDrag animation="medium" snapPoints={[35]}>
<Sheet.Overlay />
<Sheet.Frame>
<YStack gap="$5" f={1} pt="$3">
<View>
<H2 textAlign='center'>Ready to scan</H2>
<Text textAlign='center'>{scanningMessage}</Text>
</View>
{step >= Steps.NEXT_SCREEN ?
<CheckCircle2
size="$8"
alignSelf='center'
color="#3185FC"
animation="quick"
/> :
<Image
h="$8"
w="$8"
alignSelf='center'
borderRadius={1000}
source={{
uri: NFC_IMAGE
}}
/>
}
<Text textAlign='center'>Hold your device near the NFC tag and stop moving when it vibrates.</Text>
</YStack>
</Sheet.Frame>
</Sheet>
<Sheet open={SettingsIsOpen} onOpenChange={setSettingsIsOpen} dismissOnSnapToBottom modal animation="medium" snapPoints={[88]}>
<Sheet.Overlay />
<Sheet.Frame bg={bgWhite} borderTopLeftRadius="$9" borderTopRightRadius="$9" pt="$2" pb="$3" >
<YStack p="$3" pb="$5" f={1} gap={height > 750 ? "$3" : "$1"} mb="$1.5">
<XStack gap="$2" ml="$2" >
<H2 color={textBlack}>Settings</H2>
<Cog color={textBlack} mt="$1" alignSelf='center' size="$2" />
</XStack>
<Fieldset gap="$4" mt="$1" horizontal>
<Label color={textBlack} width={200} justifyContent="flex-end" htmlFor="restart">
Contribute
</Label>
<Button bg="white" jc="center" borderColor={borderColor} borderWidth={1.2} size="$3.5" ml="$2" onPress={() => setDialogContributeIsOpen(true)}>
<Share 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 horizontal>
<Label color={textBlack} width={225} justifyContent="flex-end" htmlFor="restart" >
Display other options
</Label>
<Switch size="$3.5" checked={displayOtherOptions} onCheckedChange={() => setDisplayOtherOptions(!displayOtherOptions)}>
<Switch.Thumb animation="bouncy" bc={bgColor} />
</Switch>
</Fieldset>
<Dialog.Container visible={DialogContributeIsOpen}>
<Dialog.Title>Contribute</Dialog.Title>
<Dialog.Description>
By pressing yes, you accept sending your passport data.
Passport data are encrypted and will be deleted once the signature algorithm is implemented.
</Dialog.Description>
<Dialog.Button onPress={() => setDialogContributeIsOpen(false)} label="Cancel" />
<Dialog.Button onPress={() => handleContribute()} label="Contribute" />
</Dialog.Container>
<Dialog.Container visible={dialogDeleteSecretIsOpen}>
<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>
{displayOtherOptions && (
<>
<XStack my="$3" alignSelf='center' h={2} w="80%" bg={componentBgColor} borderRadius={100} />
<Fieldset gap="$4" horizontal>
<Label color={textBlack} width={200} justifyContent="flex-end" htmlFor="restart">
Restart to step 1
</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" >
Use mock passport data
</Label>
<Button bg="white" jc="center" borderColor={borderColor} borderWidth={1.2} size="$3.5" ml="$2" onPress={handleSkip}>
<VenetianMask 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 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>
<Fieldset gap="$4" mt="$1" horizontal>
<Label color={textBlack} width={200} justifyContent="flex-end" htmlFor="skip" >
go to register
</Label>
<Button bg="white" jc="center" borderColor={borderColor} borderWidth={1.2} size="$3.5" ml="$2" onPress={() => setSelectedTab('register')}>
<ArrowRight color={textColor2} />
</Button>
</Fieldset>
<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 flex={1} />
<YStack mb="$0">
{/* <Button p="$2.5" borderRadius="$3" bg={componentBgColor} jc="center" borderColor={borderColor} borderWidth={1.2} onPress={() => setSettingsIsOpen(false)} w="80%" alignSelf='center'>
<Text color={textBlack}textAlign='center' fow="bold">Close</Text>
</Button> */}
</YStack>
</YStack>
</Sheet.Frame>
</Sheet>
<Sheet open={HelpIsOpen} onOpenChange={setHelpIsOpen} dismissOnSnapToBottom modal animation="medium" snapPoints={[76]}>
<Sheet.Overlay />
<Sheet.Frame bg={bgWhite} borderTopLeftRadius="$9" borderTopRightRadius="$9" pt="$2" pb="$3">
<YStack p="$4" f={1} gap="$3">
<XStack>
<Text fontSize="$8" mb="$2">Help 💁</Text>
<XStack f={1} />
<XStack onPress={() => setHelpIsOpen(false)} p="$2">
<X color={borderColor} size="$1.5" mr="$2" />
</XStack>
</XStack>
<Separator borderColor={separatorColor} />
<YStack flex={1} jc="space-between">
{/* <YStack >
<H3 color={textBlack}>Security and Privacy</H3>
<Text color={textBlack} ml="$2" mt="$1">OpenPassport uses zero-knowledge cryptography to allow you to prove facts about yourself like humanity, nationality or age without disclosing sensitive information.</Text>
</YStack>
<YStack >
<H3 color={textBlack}>About ZK Proofs</H3>
<Text color={textBlack} ml="$2" mt="$1">Zero-knowledge proofs rely on mathematical magic tricks to show the validity of some computation without revealing of all its inputs. In our case, the proof shows the passport has not been forged, but allows you to hide sensitive data.</Text>
</YStack> */}
<YStack gap="$1">
<H3 color={textBlack}>FAQ</H3>
<YStack ml="$1">
<H4 color={textBlack}>My passport is not supported</H4>
<Text color={textBlack} ml="$2">Please contact us on Telegram, or if you have programming skills, you can easily <Text onPress={() => Linking.openURL('https://t.me/openpassport')} color={blueColorLight} style={{ textDecorationLine: 'underline', fontStyle: 'italic' }}>contribute</Text> to the project by adding your signature algorithm.</Text>
</YStack>
</YStack>
</YStack>
<XStack justifyContent="center" mb="$2" gap="$5" mt="$8">
<Pressable onPress={() => Linking.openURL('https://openpassport.app')}>
<Image
source={{ uri: Internet, width: 24, height: 24 }}
/>
</Pressable>
<Pressable onPress={() => Linking.openURL('https://t.me/openpassport')}>
<Image
source={{ uri: Telegram, width: 24, height: 24 }}
/>
</Pressable>
<Pressable onPress={() => Linking.openURL('https://x.com/openpassportapp')}>
<Image
source={{ uri: Xlogo, width: 24, height: 24 }}
/>
</Pressable>
<Pressable onPress={() => Linking.openURL('https://github.com/zk-passport/proof-of-passport')}>
<Image
tintColor={textBlack}
source={{ uri: Github, width: 24, height: 24 }}
/>
</Pressable>
</XStack>
</YStack>
</Sheet.Frame>
</Sheet>
<Sheet open={sheetIsOpen} onOpenChange={setSheetIsOpen} dismissOnSnapToBottom modal animation="medium" snapPoints={[44]} moveOnKeyboardChange>
<Sheet.Overlay />
<Sheet.Frame bg={bgWhite} borderTopLeftRadius="$9" borderTopRightRadius="$9" pt="$2" pb="$3">
<YStack p="$4" f={1} gap="$3">
<XStack>
<Text fontSize="$8" mb="$">Manual input </Text>
<XStack f={1} />
<XStack onPress={() => setSheetIsOpen(false)} p="$2">
<X color={borderColor} size="$1.5" mr="$2" />
</XStack>
</XStack>
<Separator borderColor={separatorColor} />
<Fieldset gap="$4" horizontal mt="$2">
<Text color={textBlack} width={160} justifyContent="flex-end" fontSize="$5">
Passport Number
</Text>
<Input
bg={bgWhite}
color={textBlack}
h="$3.5"
borderColor={passportNumber?.length >= 3 ? bgGreen : textBlack}
flex={1}
id="passportnumber"
onChangeText={(text) => {
update({ passportNumber: text.toUpperCase() })
}}
value={passportNumber}
keyboardType="default"
/>
</Fieldset>
<Fieldset gap="$4" horizontal>
<Text color={textBlack} width={160} justifyContent="flex-end" fontSize="$5">
Date of birth
</Text>
<Text color={textBlack} f={1}>
{dateOfBirthDatePicker ? dateOfBirthDatePicker.toISOString().slice(0, 10) : ''}
</Text>
<Button bg={bgGreen} onPress={() => setDateOfBirthDatePickerIsOpen(true)}
borderRadius={"$10"}
>
<CalendarSearch />
</Button>
<DatePicker
modal
mode='date'
open={dateOfBirthDatePickerIsOpen}
date={dateOfBirthDatePicker || new Date()}
onConfirm={(date) => {
setDateOfBirthDatePickerIsOpen(false)
setDateOfBirthDatePicker(date)
update({ dateOfBirth: castDate(date) })
}}
onCancel={() => {
setDateOfBirthDatePickerIsOpen(false)
}}
/>
</Fieldset>
<Fieldset gap="$4" horizontal>
<Text color={textBlack} width={160} justifyContent="flex-end" fontSize="$5">
Date of expiry
</Text>
<Text color={textBlack} f={1}>
{dateOfExpiryDatePicker ? dateOfExpiryDatePicker.toISOString().slice(0, 10) : ''}
</Text>
<Button bg={bgGreen} onPress={() => setDateOfExpiryDatePickerIsOpen(true)}
borderRadius="$10"
>
<CalendarSearch />
</Button>
<DatePicker
modal
mode='date'
open={dateOfExpiryDatePickerIsOpen}
date={dateOfExpiryDatePicker || new Date()}
onConfirm={(date) => {
setDateOfExpiryDatePickerIsOpen(false)
setDateOfExpiryDatePicker(date)
update({ dateOfExpiry: castDate(date) })
}}
onCancel={() => {
setDateOfExpiryDatePickerIsOpen(false)
}}
/>
</Fieldset>
<XStack f={1} />
<YStack gap="$2">
<CustomButton
text="Submit"
onPress={() => {
setSelectedTab("nfc");
setSheetIsOpen(false);
}}
bgColor={isFormComplete ? bgGreen : separatorColor}
isDisabled={!isFormComplete}
disabledOnPress={() => toast.show('✍️', {
message: "Please fill in all fields.",
customData: {
type: "info",
},
})}
/>
</YStack>
</YStack>
</Sheet.Frame>
</Sheet>
<Sheet open={sheetAppListOpen} onOpenChange={setSheetAppListOpen} dismissOnSnapToBottom modal animation="medium" snapPoints={[35]}>
<Sheet.Overlay />
<Sheet.Frame bg={bgWhite} borderTopLeftRadius="$9" borderTopRightRadius="$9" pt="$2" mb="$3">
<YStack p="$4" f={1} gap="$3">
<XStack>
<Text fontSize="$8" mb="$2">Applications</Text>
<XStack f={1} />
<XStack onPress={() => setSheetAppListOpen(false)} p="$2">
<X color={borderColor} size="$1.5" mr="$2" />
</XStack>
</XStack>
<Separator borderColor={separatorColor} />
<XStack f={1} />
<YStack gap="$2">
<CustomButton
text="Zupass"
onPress={() => toast.show('😖', {
message: "Work in progress",
customData: {
type: "info",
},
})}
/>
<CustomButton
text="Gitcoin passport"
onPress={() => toast.show('😖', {
message: "Work in progress",
customData: {
type: "info",
},
})}
/>
<CustomButton
text="SBT"
onPress={() => toast.show('😖', {
message: "Work in progress",
customData: {
type: "info",
},
})}
/>
</YStack>
<XStack f={1} />
</YStack>
</Sheet.Frame>
</Sheet>
<Sheet open={sheetRegisterIsOpen} onOpenChange={setSheetRegisterIsOpen} dismissOnSnapToBottom modal animation="medium" snapPoints={[35]}>
<Sheet.Overlay />
<Sheet.Frame bg={bgWhite} borderTopLeftRadius="$9" borderTopRightRadius="$9" pt="$2" >
<YStack p="$4" f={1} gap="$3">
<XStack>
<Text fontSize="$8" mb="$2">👋 Not registered yet?</Text>
<XStack f={1} />
<XStack onPress={() => setSheetRegisterIsOpen(false)} p="$2">
<X color={borderColor} size="$1.5" mr="$2" />
</XStack>
</XStack>
<Separator borderColor={separatorColor} />
<YStack gap="$2">
<Text fontSize="$7" color={textBlack}>Registering to OpenPassport does not leak anything about your personal information.</Text>
<Text fontSize="$6" onPress={() => Linking.openURL('https://zk-passport.github.io/posts/how-to-scan-your-passport-using-nfc/')} color={blueColorLight} style={{ textDecorationLine: 'underline', fontStyle: 'italic' }}>Learn more.</Text>
</YStack>
<XStack f={1} />
<YStack gap="$2">
<CustomButton
text="Register"
Icon={<UserPlus color={textBlack} />}
onPress={() => {
setSheetRegisterIsOpen(false);
setSelectedTab("start");
}}
/>
</YStack>
<XStack f={1} />
</YStack>
</Sheet.Frame>
</Sheet>
<Sheet
open={showRegistrationErrorSheet}
onOpenChange={(open: boolean) => {
updateNavigationStore({
showRegistrationErrorSheet: open
})
}}
dismissOnSnapToBottom modal animation="medium" snapPoints={[80]}
>
<Sheet.Overlay />
<Sheet.Frame bg={bgWhite} borderRadius="$9" pt="$2">
<YStack p="$4" f={1} gap="$3" pb="$6">
<YStack jc="flex-start" >
<Text fontSize="$9" textAlign='left' mb="$2" color={textBlack}>Sorry, an error has occurred</Text>
</YStack>
<Text fontSize="$7">Error details:</Text>
<Text fontSize="$6" mb="$2" textAlign="center" color="#a0a0a0">{registrationErrorMessage} </Text>
<Separator borderColor={separatorColor} />
<Text mt="$4" fontSize="$6" mb="$4" color={textBlack}>Unfortunately, your passport is currently not supported.</Text>
<Text fontSize="$6" mb="$4" color={textBlack}>To help us add support for it, please consider contributing its data!</Text>
<XStack f={1} />
<CustomButton
text="Contribute"
onPress={() => setDialogContributeIsOpen(true)}
Icon={<Share />}
/>
</YStack>
</Sheet.Frame>
</Sheet>
</YStack>
<Tabs f={1} orientation="horizontal" flexDirection="column" defaultValue={"splash"}
value={selectedTab}
onValueChange={(value) => updateNavigationStore({ selectedTab: value })}
>
<Tabs.Content value="splash" f={1}>
<SplashScreen
/>
</Tabs.Content>
<Tabs.Content value="start" f={1}>
<StartScreen
/>
</Tabs.Content>
<Tabs.Content value="scan" f={1}>
<CameraScreen
sheetIsOpen={sheetIsOpen}
setSheetIsOpen={setSheetIsOpen}
/>
</Tabs.Content>
<Tabs.Content value="nfc" f={1}>
<NfcScreen
handleNFCScan={handleNFCScan}
/>
</Tabs.Content>
<Tabs.Content value="next" f={1}>
<NextScreen />
</Tabs.Content>
<Tabs.Content value="register" f={1}>
<RegisterScreen />
</Tabs.Content>
<Tabs.Content value="app" f={1}>
<AppScreen
setSheetAppListOpen={setSheetAppListOpen}
setSheetRegisterIsOpen={setSheetRegisterIsOpen}
/>
</Tabs.Content>
<Tabs.Content value="prove" f={1}>
<ProveScreen
setSheetRegisterIsOpen={setSheetRegisterIsOpen}
/>
</Tabs.Content>
<Tabs.Content value="mint" f={1}>
<SendProofScreen />
</Tabs.Content>
<Tabs.Content value="valid" f={1}>
<ValidProofScreen />
</Tabs.Content>
<Tabs.Content value="wrong" f={1}>
<WrongProofScreen />
</Tabs.Content>
</Tabs>
</YStack>
<Modal visible={showWarningModal.show} animationType="slide" transparent={true}>
<YStack bc="#161616" p={20} ai="center" jc="center" position="absolute" top={0} bottom={0} left={0} right={0}>
<YStack bc="#343434" p={20} borderRadius={10} ai="center" jc="center">
<Text fontWeight="bold" fontSize={18} color="#a0a0a0">👋 Hi</Text>
<Text mt={10} textAlign="center" color="#a0a0a0">
The app needs to download a large file ({(showWarningModal.size / 1024 / 1024).toFixed(2)}MB). Please make sure you're connected to a Wi-Fi network before continuing.
</Text>
<XStack mt={20}>
<Button
onPress={() => {
fetchZkey(showWarningModal.circuit as CircuitName);
updateNavigationStore({
showWarningModal: {
show: false,
circuit: '',
size: 0,
}
});
}}
bc="#4caf50" borderRadius={5} padding={10}
>
<Text color="#ffffff">Continue</Text>
</Button>
</XStack>
</YStack>
</YStack>
</Modal>
</YStack >
);
};
export default MainScreen;