update UI/UX

This commit is contained in:
turnoffthiscomputer
2024-07-23 18:07:38 +02:00
parent c369d7e3e3
commit 475181162d
21 changed files with 753 additions and 303 deletions

View File

@@ -9,28 +9,36 @@ import useNavigationStore from './src/stores/navigationStore';
import { AMPLITUDE_KEY } from '@env';
import * as amplitude from '@amplitude/analytics-react-native';
import useUserStore from './src/stores/userStore';
import { bgWhite } from './src/utils/colors';
global.Buffer = Buffer;
function App(): JSX.Element {
const toast = useToastController();
const setToast = useNavigationStore((state) => state.setToast);
const initUserStore = useUserStore((state) => state.initUserStore);
const setSelectedTab = useNavigationStore((state) => state.setSelectedTab);
useEffect(() => {
initUserStore();
}, [initUserStore]);
useEffect(() => {
setToast(toast);
}, [toast, setToast]);
useEffect(() => {
setSelectedTab('splash');
}, [setSelectedTab]);
useEffect(() => {
if (AMPLITUDE_KEY) {
amplitude.init(AMPLITUDE_KEY);
}
initUserStore();
//initUserStore();
}, []);
// TODO: when passportData already stored, retrieve and jump to main screen
return (
<YStack f={1} bc="#161616" h="100%" w="100%">
<YStack f={1} bc={bgWhite} h="100%" w="100%">
<YStack h="100%" w="100%">
<MainScreen />
</YStack>

View File

@@ -45,6 +45,7 @@
"react-native-fs": "^2.20.0",
"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-svg": "13.4.0",
"react-native-zip-archive": "^6.1.0",

View File

@@ -0,0 +1,116 @@
import React, { useState } from 'react'
import { AnimatePresence } from '@tamagui/animate-presence'
import { ArrowLeft, ArrowRight, Nfc, ShieldCheck } from '@tamagui/lucide-icons'
import { Button, Image, XStack, YStack, styled, Text } from 'tamagui'
import { bgBlue, bgGreen, borderColor, textBlack, textColor1, textColor2 } from '../utils/colors'
import CustomButton from './CustomButton'
const GalleryItem = styled(YStack, {
zIndex: 1,
x: 0,
opacity: 1,
fullscreen: true,
variants: {
going: {
':number': (going) => ({
enterStyle: {
x: going > 0 ? 1000 : -1000,
opacity: 0,
},
exitStyle: {
zIndex: 0,
x: going < 0 ? 1000 : -1000,
opacity: 0,
},
}),
},
} as const,
})
const wrap = (min: number, max: number, v: number) => {
const rangeSize = max - min
return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min
}
interface CarouselProps {
images: string[];
height?: number;
onSlideChange?: (index: number) => void;
handleNfcScan?: () => void;
}
export function Carousel({ images, height = 300, onSlideChange, handleNfcScan }: CarouselProps) {
const [[page, going], setPage] = useState([0, 0])
const imageIndex = wrap(0, images.length, page)
const paginate = (going: number) => {
const newPage = page + going
setPage([newPage, going])
onSlideChange?.(newPage)
}
const isLastImage = imageIndex === images.length - 1
const slideTexts = [
{ header: "Follow this guide carefully", subtitle: "", acknowledgment: "I'm ready to start" },
{ header: "Remove your phone case", subtitle: "If your phone does not have a case, you can skip this step.", acknowledgment: "I have removed my phone case" },
{ header: "Open your passport to the last page", subtitle: "", acknowledgment: "I have opened my passport to the last page" },
{ header: "Put your phone on the passport", subtitle: "Press your phone against the last page of the passport as in the image.", acknowledgment: "I have placed my phone on the passport" },
{ header: "Start scanning", subtitle: "Press Start NFC Scan and follow the on-screen instructions.", acknowledgment: "Start scanning" },
]
const currentSlide = slideTexts[imageIndex] || { header: "No header", subtitle: "No subtitle for this slide", acknowledgment: "Continue" }
return (
<YStack f={1} >
<XStack
overflow="hidden"
backgroundColor="#000"
position="relative"
height={height}
alignItems="center"
borderRadius="$10"
mt="$5"
>
<AnimatePresence initial={false} custom={{ going }}>
<GalleryItem key={page} animation="medium" going={going}>
<Image source={{ uri: images[imageIndex] }} height={height} resizeMode="contain" />
</GalleryItem>
</AnimatePresence>
{imageIndex > 0 && (
<Button
icon={ArrowLeft}
size="$5"
position="absolute"
left="$4"
circular
elevate
onPress={() => paginate(-1)}
zi={100}
/>
)}
</XStack>
<YStack ml="$2">
<Text fontSize="$8" mt="$4" color={textBlack} textAlign='center'>{currentSlide.header}</Text>
<Text color={textBlack} fontSize="$5" mt="$2" textAlign='center' style={{ opacity: 0.7 }} fontStyle='italic'>{currentSlide.subtitle}</Text>
</YStack>
<XStack justifyContent='center' alignItems='center' gap="$1.5" position="absolute" style={{ bottom: 120, left: 0, right: 0 }}>
<ShieldCheck color={textBlack} size={14} />
<Text color={textBlack} fontSize="$4" >private and secured</Text>
</XStack>
<XStack f={1} />
<CustomButton
onPress={isLastImage ? () => handleNfcScan?.() : () => paginate(+1)}
text={currentSlide.acknowledgment}
Icon={isLastImage ? <Nfc /> : undefined}
blueVariant={!isLastImage}
/>
</YStack>
)
}

View File

@@ -1,23 +1,32 @@
import React from 'react';
import { Button } from 'tamagui';
import { bgGreen, textBlack } from '../utils/colors';
import { Button, Text } from 'tamagui';
import { bgBlue, bgGreen, textBlack } from '../utils/colors';
import useNavigationStore from '../stores/navigationStore';
interface CustomButtonProps {
text: string;
onPress: () => void;
Icon?: React.ReactNode;
bgColor?: string;
h?: string;
isDisabled?: boolean;
disabledOnPress?: () => void;
blueVariant?: boolean;
}
const CustomButton: React.FC<CustomButtonProps> = ({ text, onPress, Icon, bgColor }) => {
const CustomButton: React.FC<CustomButtonProps> = ({ text, onPress, Icon, bgColor, h, isDisabled, disabledOnPress, blueVariant }) => {
const {
toast,
} = useNavigationStore();
return (
<Button bg={bgColor ? bgColor : bgGreen} h="$4.5" borderRadius="$10" mx="$3" onPress={onPress}>
<Button bg={bgColor ? bgColor : blueVariant ? bgBlue : bgGreen} h={blueVariant ? "$8" : "$4.5"} borderRadius="$10" mx="$3" onPress={isDisabled ? disabledOnPress : onPress}>
{Icon && <Button.Icon>{Icon}</Button.Icon>}
<Button.Text fontSize="$5" fontWeight="bold" color={textBlack}>
<Text textAlign='center' fontSize={blueVariant ? "$6" : "$5"} fontWeight="bold" color={textBlack}>
{text}
</Button.Text>
</Text>
</Button>
);
};
export default CustomButton;
export default CustomButton;

View File

@@ -0,0 +1,20 @@
import { XStack } from "tamagui";
import { textBlack } from "../utils/colors";
interface StepOneStepTwoProps {
variable: string;
step1: string;
step2: string;
}
const StepOneStepTwo = ({ variable, step1, step2 }: StepOneStepTwoProps) => {
const isVisible = variable === step1 || variable === step2;
return (
<XStack px="$4" mt="$4" gap="$3" style={{ opacity: isVisible ? 1 : 0 }}>
<XStack h="$0.25" f={1} bg={textBlack} borderRadius={100} style={{ opacity: variable === step1 ? 1 : 0.2 }} />
<XStack h="$0.25" f={1} bg={textBlack} borderRadius={100} style={{ opacity: variable === step2 ? 1 : 0.2 }} />
</XStack>
)
}
export default StepOneStepTwo;

View File

@@ -0,0 +1,55 @@
import React from 'react';
import { ScrollView, YStack } from 'tamagui';
import AppCard from '../components/AppCard';
import { Steps } from '../utils/utils';
import useNavigationStore from '../stores/navigationStore';
import { AppType } from '../utils/appType';
import sbtApp from '../apps/sbt';
import zupassApp from '../apps/zupass';
import gitcoinApp from '../apps/gitcoin';
const AppScreen: React.FC = () => {
const {
selectedApp,
update
} = useNavigationStore();
const handleCardSelect = (app: AppType) => {
update({
selectedTab: "prove",
selectedApp: app,
step: Steps.APP_SELECTED,
})
};
// add new apps here
const cardsData = [
sbtApp,
zupassApp,
gitcoinApp
];
return (
<ScrollView f={1}>
<YStack my="$8" gap="$5" px="$5" jc="center" alignItems='center'>
{
cardsData.map(app => (
<AppCard
key={app.id}
title={app.title}
description={app.description}
id={app.id}
onTouchStart={() => handleCardSelect(app)}
selected={selectedApp && selectedApp.id === app.id ? true : false}
selectable={app.selectable}
icon={app.icon}
tags={app.tags}
/>
))
}
</YStack>
</ScrollView>
);
}
export default AppScreen;

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { ScrollView, YStack } from 'tamagui';
import { ScrollView, Text, YStack } from 'tamagui';
import AppCard from '../components/AppCard';
import { Steps } from '../utils/utils';
import useNavigationStore from '../stores/navigationStore';
@@ -7,13 +7,26 @@ import { AppType } from '../utils/appType';
import sbtApp from '../apps/sbt';
import zupassApp from '../apps/zupass';
import gitcoinApp from '../apps/gitcoin';
import { XStack } from 'tamagui';
import CustomButton from '../components/CustomButton';
import { BadgeCheck, Binary, LockKeyhole, QrCode, ShieldCheck, Smartphone, UserPlus } from '@tamagui/lucide-icons';
import { bgBlue, bgGreen, separatorColor, textBlack } from '../utils/colors';
import { orange } from '@tamagui/colors';
import useUserStore from '../stores/userStore';
const AppScreen: React.FC = () => {
const {
selectedApp,
update
update,
selectedTab,
setSelectedTab
} = useNavigationStore();
const {
registered,
setRegistered
} = useUserStore();
const handleCardSelect = (app: AppType) => {
update({
selectedTab: "prove",
@@ -22,7 +35,6 @@ const AppScreen: React.FC = () => {
})
};
// add new apps here
const cardsData = [
sbtApp,
zupassApp,
@@ -30,8 +42,76 @@ const AppScreen: React.FC = () => {
];
return (
<ScrollView f={1}>
<YStack my="$8" gap="$5" px="$5" jc="center" alignItems='center'>
<YStack f={1} pb="$3" px="$3">
{/* <XStack h="$0.25" bg={separatorColor} mx="$0" /> */}
<ScrollView >
<YStack >
<Text fontSize="$8" mt="$2" >Account</Text>
<XStack ml="$2" gap="$2" ai="center">
<Text fontSize="$5">status:</Text>
{registered ?
<XStack bg={bgGreen} px="$2.5" py="$2" borderRadius="$10">
<Text color={textBlack} fontSize="$4">registered</Text>
</XStack> :
<XStack bg={'#FFB897'} px="$2.5" py="$2" borderRadius="$10">
<Text color={textBlack} fontSize="$4">not registered</Text>
</XStack>}
</XStack>
{/* <XStack ml="$2" gap="$2" mt="$1">
<Text fontSize="$5">userID:</Text>
<Text color={textBlack} fontSize="$5">0x1234567890</Text>
</XStack> */}
</YStack>
<YStack>
<Text mt="$4" fontSize="$8" >How to use Proof of Passport?</Text>
<YStack>
<XStack mt="$3" px="$5" gap="$2" >
<QrCode size={50} color={textBlack} />
<YStack>
<Text fontSize="$5" mb="$1">Scan QR code</Text>
<XStack gap="$2"><Text fontSize="$3">1</Text><Text fontSize="$3" maxWidth={220}>Find the QR code on the page of the app that asks for proof of passport.</Text></XStack>
<XStack mt="$1" gap="$2"><Text fontSize="$3">2</Text><Text fontSize="$3" maxWidth={220}>Scan the QR code.</Text></XStack>
</YStack>
</XStack>
<XStack mt="$4" px="$5" gap="$2" >
<BadgeCheck size={50} color={textBlack} />
<YStack>
<Text fontSize="$5" mb="$1">Generate a Proof</Text>
<XStack gap="$2"><Text fontSize="$3">1</Text><Text fontSize="$3" maxWidth={220}>Generate a proof of the selected information.</Text></XStack>
<XStack mt="$1" gap="$2"><Text fontSize="$3">2</Text><Text fontSize="$3" maxWidth={220}>Share the proof with the application.</Text></XStack>
</YStack>
</XStack>
</YStack>
</YStack>
<YStack mb="$4">
<Text mt="$5" fontSize="$8" >How does it works?</Text>
<YStack>
<XStack mt="$3" px="$5" gap="$2" >
<Binary size={50} color={textBlack} />
<YStack>
<Text fontSize="$5" mb="$1">Strong cryptography</Text>
<XStack gap="$2"><Text fontSize="$3">·</Text><Text fontSize="$3" maxWidth={220}>Proof of Passport uses ZK technologies which allows you to prove a statement without revealing why it's true.</Text></XStack>
<XStack gap="$2"><Text fontSize="$3">·</Text><Text fontSize="$3" maxWidth={220}>You are always anonymous</Text></XStack>
</YStack>
</XStack>
<XStack mt="$3" px="$5" gap="$2" >
<Smartphone size={50} color={textBlack} />
<YStack>
<Text fontSize="$5" mb="$1">Serverless</Text>
<XStack gap="$2"><Text fontSize="$3">·</Text><Text fontSize="$3" maxWidth={220}>Proof of Passport will never receive your data and will never know who you are.</Text></XStack>
<XStack gap="$2"><Text fontSize="$3">·</Text><Text fontSize="$3" maxWidth={220}>Everything is achieved on your device, even the camera and NFC scanning.</Text></XStack>
</YStack>
</XStack>
</YStack>
</YStack>
</ScrollView>
<XStack f={1} minHeight="$1" />
{registered ? <CustomButton text="Scan QR code" onPress={() => setRegistered(false)} Icon={<QrCode size={18} color={textBlack} />} /> :
<CustomButton text="Register" onPress={() => { setSelectedTab("start"); setRegistered(true); }} Icon={<UserPlus size={18} color={textBlack} />} />}
{/* <YStack my="$8" gap="$5" px="$5" jc="center" alignItems='center'>
{
cardsData.map(app => (
<AppCard
@@ -47,8 +127,8 @@ const AppScreen: React.FC = () => {
/>
))
}
</YStack>
</ScrollView>
</YStack> */}
</YStack>
);
}

View File

@@ -1,9 +1,10 @@
import React from 'react';
import { YStack, Button, Image, Text } from 'tamagui';
import { Camera, SquarePen } from '@tamagui/lucide-icons';
import { bgColor, borderColor, textColor1, textColor2 } from '../utils/colors';
import { YStack, Button, Image, Text, ScrollView, XStack, Separator } from 'tamagui';
import { Camera, ShieldCheck, SquarePen, X } from '@tamagui/lucide-icons';
import { bgColor, bgGreen, borderColor, componentBgColor, componentBgColor2, separatorColor, textBlack, textColor1, textColor2 } from '../utils/colors';
import SCANHelp from '../images/scan_help.png'
import { startCameraScan } from '../utils/cameraScanner';
import CustomButton from '../components/CustomButton';
interface CameraScreenProps {
sheetIsOpen: boolean
@@ -11,30 +12,31 @@ interface CameraScreenProps {
}
const CameraScreen: React.FC<CameraScreenProps> = ({ sheetIsOpen, setSheetIsOpen }) => {
return (
<YStack f={1} p="$3">
<YStack f={1} jc="center">
<Image borderRadius="$5"
<YStack f={1} mt="$16">
<Text ml="$1" fontSize={34} color={textBlack}><Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>Scan</Text> or type your passport ID</Text>
<Text ml="$2" mt="$8" fontSize="$8" color={textBlack}>Open your passport on the <Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>main page</Text> to scan it.</Text>
<Text ml="$2" mt="$2" fontSize="$8" color={textBlack} style={{ opacity: 0.7 }}>Your data never leaves your device.</Text>
<XStack f={1} />
<XStack justifyContent='center' alignItems='center' gap="$1.5">
<ShieldCheck color={textBlack} size={14} />
<Text color={textBlack} fontSize="$4">private and secured</Text>
</XStack>
</YStack>
{/* <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 gap="$2.5" mt="$5" >
<CustomButton text="Open Camera" onPress={startCameraScan} Icon={<Camera color={textBlack} size={24} />} />
<CustomButton bgColor='#ffff' text="Manual Input" onPress={() => setSheetIsOpen(true)} Icon={<SquarePen color={textBlack} size={24} />} />
</YStack>
<YStack gap="$2" mb="$6">
<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 >
</YStack>
);
};
export default CameraScreen;
export default CameraScreen;

View File

@@ -4,9 +4,9 @@ import forge from 'node-forge';
import Dialog from "react-native-dialog";
import { ethers } from 'ethers';
// import ressources
import { YStack, XStack, Text, Button, Tabs, Sheet, Label, Fieldset, Input, Switch, H2, Image, useWindowDimensions, H4, H3 } from 'tamagui'
import { HelpCircle, IterationCw, VenetianMask, Cog, CheckCircle2, ChevronLeft, Share, Eraser, CalendarSearch } from '@tamagui/lucide-icons';
import X from '../images/x.png'
import { YStack, XStack, Text, Button, Tabs, Sheet, Label, Fieldset, Input, Switch, H2, Image, useWindowDimensions, H4, H3, Separator } from 'tamagui'
import { HelpCircle, IterationCw, VenetianMask, Cog, CheckCircle2, ChevronLeft, Share, Eraser, CalendarSearch, Cross, X } from '@tamagui/lucide-icons';
import Xlogo from '../images/x.png'
import Telegram from '../images/telegram.png'
import Github from '../images/github.png'
import Internet from "../images/internet.png"
@@ -17,7 +17,7 @@ import { ToastMessage } from '../components/ToastMessage';
import useUserStore from '../stores/userStore';
import useNavigationStore from '../stores/navigationStore';
// import utils
import { bgColor, blueColorLight, borderColor, componentBgColor, componentBgColor2, textColor1, textColor2 } from '../utils/colors';
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';
@@ -41,6 +41,10 @@ import { RPC_URL, SignatureAlgorithm } from '../../../common/src/constants/const
import { mock_csca_sha256_rsa_4096, mock_dsc_sha256_rsa_4096 } from '../../../common/src/constants/mockCertificates';
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';
const MainScreen: React.FC = () => {
const [NFCScanIsOpen, setNFCScanIsOpen] = useState(false);
@@ -51,10 +55,11 @@ const MainScreen: React.FC = () => {
const [HelpIsOpen, setHelpIsOpen] = useState(false);
const [sheetIsOpen, setSheetIsOpen] = useState(false);
const [modalProofStep, setModalProofStep] = useState(0);
const [dateOfBirthDatePicker, setDateOfBirthDatePicker] = useState(new Date())
const [dateOfExpiryDatePicker, setDateOfExpiryDatePicker] = useState(new Date())
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,
@@ -78,6 +83,7 @@ const MainScreen: React.FC = () => {
step,
setStep,
selectedTab,
setSelectedTab,
hideData,
toast,
showRegistrationErrorSheet,
@@ -86,8 +92,7 @@ const MainScreen: React.FC = () => {
const handleRestart = () => {
updateNavigationStore({
selectedTab: "scan",
selectedApp: null,
selectedTab: "start",
step: Steps.MRZ_SCAN,
})
deleteMrzFields();
@@ -145,11 +150,14 @@ const MainScreen: React.FC = () => {
}
const decrementStep = () => {
if (selectedTab === "nfc") {
setStep(Steps.MRZ_SCAN);
if (selectedTab === "scan") {
setSelectedTab("start");
}
else if (selectedTab === "nfc") {
setSelectedTab("scan");
}
else if (selectedTab === "next") {
setStep(Steps.MRZ_SCAN_COMPLETED);
setSelectedTab("nfc");
}
else if (selectedTab === "register") {
setStep(Steps.NEXT_SCREEN);
@@ -218,87 +226,115 @@ const MainScreen: React.FC = () => {
}
}, [modalProofStep]);
useEffect(() => {
if (passportNumber?.length === 9 && (dateOfBirth?.length === 6 && dateOfExpiry?.length === 6)) {
setStep(Steps.MRZ_SCAN_COMPLETED);
}
setIsFormComplete(passportNumber?.length === 9 && dateOfBirth?.length === 6 && dateOfExpiry?.length === 6);
}, [passportNumber, dateOfBirth, dateOfExpiry]);
useEffect(() => {
if (registered && step < Steps.REGISTERED) {
setStep(Steps.REGISTERED);
}
}, [registered]);
// useEffect(() => {
// if (registered && step < Steps.REGISTERED) {
// setStep(Steps.REGISTERED);
// }
// }, [registered]);
useEffect(() => {
let timeoutId: ReturnType<typeof setTimeout>;
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]);
// 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} bc="#161616" mt={Platform.OS === 'ios' ? "$8" : "$0"} >
<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 >
<XStack jc="space-between" ai="center" px="$3">
<Button p="$2" py="$3" unstyled onPress={decrementStep}><ChevronLeft color={(selectedTab === "scan") ? "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>
<StepOneStepTwo variable={selectedTab} step1="scan" step2="nfc" />
{selectedTab !== "app" && <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" >Proof of Passport</Text>
{/* <XStack onPress={() => setHelpIsOpen(true)}><HelpCircle size={28} color={textBlack} /></XStack> */}
<XStack f={1} />
<XStack p="$2" onPress={() => setSettingsIsOpen(true)}><Cog size={24} color={textBlack} /></XStack>
</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={NFCScanIsOpen} onOpenChange={setNFCScanIsOpen} dismissOnSnapToBottom modal dismissOnOverlayPress={false} disableDrag animation="medium" snapPoints={[35]}>
<Sheet.Overlay />
<Sheet.Frame>
@@ -464,7 +500,7 @@ const MainScreen: React.FC = () => {
</Pressable>
<Pressable onPress={() => Linking.openURL('https://x.com/proofofpassport')}>
<Image
source={{ uri: X, width: 24, height: 24 }}
source={{ uri: Xlogo, width: 24, height: 24 }}
/>
</Pressable>
<Pressable onPress={() => Linking.openURL('https://github.com/zk-passport/proof-of-passport')}>
@@ -501,28 +537,32 @@ const MainScreen: React.FC = () => {
</YStack>
</YStack>
{/* <Button mt="$3" bg={componentBgColor} jc="center" borderColor={borderColor} borderWidth={1.2} size="$3.5" ml="$2" alignSelf='center' w="80%" onPress={() => setHelpIsOpen(false)}>
<Text color={textColor1} w="80%" textAlign='center' fow="bold">Close</Text>
</Button> */}
</YStack>
</Sheet.Frame>
</Sheet>
<Sheet open={sheetIsOpen} onOpenChange={setSheetIsOpen} dismissOnSnapToBottom modal animation="medium" snapPoints={[80]}>
<Sheet open={sheetIsOpen} onOpenChange={setSheetIsOpen} dismissOnSnapToBottom modal animation="medium" snapPoints={[60]}>
<Sheet.Overlay />
<Sheet.Frame bg={bgColor} borderRadius="$9" pt="$2">
<Sheet.Frame bg={bgWhite} borderRadius="$9" pt="$2" mb="$3">
<YStack p="$4" f={1} gap="$3">
<Text fontSize="$6" mb="$4" color={textColor1}>Please provide the following information</Text>
<XStack>
<Text fontSize="$8" mb="$2">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>
<Text color={textColor1} width={160} justifyContent="flex-end" fontSize="$4">
<Text color={textBlack} width={160} justifyContent="flex-end" fontSize="$5">
Passport Number
</Text>
<Input
bg={componentBgColor}
color={textColor1}
bg={bgWhite}
color={textBlack}
h="$3.5"
borderColor={passportNumber?.length === 9 ? "green" : "unset"}
borderColor={passportNumber?.length === 9 ? bgGreen : textBlack}
flex={1}
id="passportnumber"
onChangeText={(text) => {
@@ -535,24 +575,22 @@ const MainScreen: React.FC = () => {
<Fieldset gap="$4" horizontal>
<Text color={textColor1} width={160} justifyContent="flex-end" fontSize="$4">
<Text color={textBlack} width={160} justifyContent="flex-end" fontSize="$5">
Date of birth
</Text>
<Text color={textColor1}>
{dateOfBirthDatePicker.toISOString().slice(0, 10)}
<Text color={textBlack} f={1}>
{dateOfBirthDatePicker ? dateOfBirthDatePicker.toISOString().slice(0, 10) : ''}
</Text>
<Button bg="white" onPress={() => setDateOfBirthDatePickerIsOpen(true)}
pressStyle={{
bg: componentBgColor2,
borderColor: componentBgColor2,
}}>
<Button bg={bgGreen} onPress={() => setDateOfBirthDatePickerIsOpen(true)}
borderRadius={"$10"}
>
<CalendarSearch />
</Button>
<DatePicker
modal
mode='date'
open={dateOfBirthDatePickerIsOpen}
date={dateOfBirthDatePicker}
date={dateOfBirthDatePicker || new Date()}
onConfirm={(date) => {
setDateOfBirthDatePickerIsOpen(false)
setDateOfBirthDatePicker(date)
@@ -562,39 +600,24 @@ const MainScreen: React.FC = () => {
setDateOfBirthDatePickerIsOpen(false)
}}
/>
{/* <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">
<Text color={textBlack} width={160} justifyContent="flex-end" fontSize="$5">
Date of expiry
</Text>
<Text color={textColor1}>
{dateOfExpiryDatePicker.toISOString().slice(0, 10)}
<Text color={textBlack} f={1}>
{dateOfExpiryDatePicker ? dateOfExpiryDatePicker.toISOString().slice(0, 10) : ''}
</Text>
<Button bg="white" onPress={() => setDateOfExpiryDatePickerIsOpen(true)}
pressStyle={{
bg: componentBgColor2,
borderColor: componentBgColor2,
}}>
<Button bg={bgGreen} onPress={() => setDateOfExpiryDatePickerIsOpen(true)}
borderRadius="$10"
>
<CalendarSearch />
</Button>
<DatePicker
modal
mode='date'
open={dateOfExpiryDatePickerIsOpen}
date={dateOfExpiryDatePicker}
date={dateOfExpiryDatePicker || new Date()}
onConfirm={(date) => {
setDateOfExpiryDatePickerIsOpen(false)
setDateOfExpiryDatePicker(date)
@@ -604,53 +627,24 @@ const MainScreen: React.FC = () => {
setDateOfExpiryDatePickerIsOpen(false)
}}
/>
{/* <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>
<XStack f={1} />
<YStack mb="$6" gap="$2">
<Button
bg={textColor1}
pressStyle={{
bg: componentBgColor2,
borderColor: componentBgColor2,
}}
<YStack gap="$2">
<CustomButton
text="Submit"
onPress={() => {
setSheetIsOpen(false)
setStep(Steps.MRZ_SCAN_COMPLETED)
setSelectedTab("nfc");
setSheetIsOpen(false);
}}
>
<Text
fontSize="$6"
color={bgColor}
>Submit</Text>
</Button>
<Button
bg="gray"
pressStyle={{
bg: componentBgColor2,
borderColor: componentBgColor2,
}}
onPress={() => {
setSheetIsOpen(false)
}}
>
<Text
fontSize="$6"
color={bgColor}
>Cancel</Text>
</Button>
bgColor={isFormComplete ? bgGreen : separatorColor}
isDisabled={!isFormComplete}
disabledOnPress={() => toast.show('✍️', {
message: "Please fill in all fields.",
customData: {
type: "info",
},
})}
/>
</YStack>
</YStack>
@@ -686,14 +680,20 @@ const MainScreen: React.FC = () => {
</Sheet.Frame>
</Sheet>
<XStack bc="#343434" h={1.2} />
</YStack>
<Tabs f={1} orientation="horizontal" flexDirection="column" defaultValue={"scan"}
value={selectedTab}
onValueChange={(value) => updateNavigationStore({ selectedTab: value })}
>
<ToastViewport flexDirection="column-reverse" top={15} right={0} left={0} />
<ToastMessage />
<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}
@@ -749,7 +749,7 @@ const MainScreen: React.FC = () => {
</YStack>
</YStack>
</Modal>
</>
</YStack >
);
};

View File

@@ -1,20 +1,22 @@
import React from 'react';
import { YStack, XStack, Text, Button, Image, useWindowDimensions, Fieldset } from 'tamagui';
import { Info } from '@tamagui/lucide-icons';
import { ArrowRight, Info } from '@tamagui/lucide-icons';
import { getFirstName, maskString } from '../../utils/utils';
import { attributeToPosition } from '../../../common/src/constants/constants';
import USER from '../images/user.png'
import { borderColor, componentBgColor, textColor1, textColor2 } from '../utils/colors';
import { bgGreen, borderColor, componentBgColor, textBlack, textColor1, textColor2 } from '../utils/colors';
import { Platform } from 'react-native';
import { formatAttribute, Steps } from '../utils/utils';
import useUserStore from '../stores/userStore';
import useNavigationStore from '../stores/navigationStore';
import CustomButton from '../components/CustomButton';
const NextScreen: React.FC = () => {
const {
hideData,
setStep,
setSelectedTab
} = useNavigationStore()
@@ -33,7 +35,7 @@ const NextScreen: React.FC = () => {
return (
<YStack px="$4" f={1} mb={Platform.OS === 'ios' ? "$5" : "$0"}>
<YStack flex={1} mx="$2" gap="$2">
<YStack flex={1} mx="$2" gap="$2" mt="$2">
<YStack alignSelf='center' my="$3">
{hideData
? <Image
@@ -54,17 +56,19 @@ const NextScreen: React.FC = () => {
/>
}
</YStack>
<Text color={textColor1} fontSize="$5" fontWeight="bold">
<Text color={textBlack} fontSize="$8" mt="$8" >
Hi{" "}
{
hideData
? maskString(getFirstName(passportData.mrz))
: getFirstName(passportData.mrz)
}
<Text color={textBlack} fontSize="$8" style={{
textDecorationLine: "underline", textDecorationColor: bgGreen
}}>{
hideData
? maskString(getFirstName(passportData.mrz))
: getFirstName(passportData.mrz)
}</Text>
{" "}👋
</Text>
<YStack gap="$2.5" mt="$2" ml="$2">
<YStack gap="$2" mt="$4" >
{Object.keys(disclosureOptions).map((key) => {
const key_ = key;
const indexes = attributeToPosition[key_ as keyof typeof attributeToPosition];
@@ -74,11 +78,15 @@ const NextScreen: React.FC = () => {
return (
<Fieldset horizontal key={key} gap="$3" alignItems='center'>
<Text color={textColor2} w="$10" justifyContent="flex-end" >
<Text color={textBlack} w="$10" justifyContent="flex-end" fontSize="$5" style={{
opacity: 0.7
}}>
{keyFormatted}:
</Text>
<Text
color={textColor1}
color={textBlack}
fontSize="$5"
>
{hideData ? maskString(mrzAttributeFormatted) : mrzAttributeFormatted}
</Text>
@@ -89,20 +97,14 @@ const NextScreen: React.FC = () => {
</YStack>
<YStack f={1} />
<XStack mt="$6" bg={componentBgColor} borderRadius={100} borderWidth={1} borderColor={borderColor} py="$2.5" px="$3">
<Info alignSelf='center' size={24} color={textColor1} />
<Text ml="$3" pr="$6" fontSize="$3" color={textColor1}>Your information will remain confidential and will not be used or shared without your explicit consent.</Text>
<XStack bg="#ffff" borderRadius={100} py="$2.5" px="$3">
<Info alignSelf='center' size={24} color={textBlack} />
<Text ml="$3" pr="$6" fontSize="$3" color={textBlack}>Your information will remain confidential and will not be used or shared without your explicit consent.</Text>
</XStack>
<Button
mt="$8"
alignSelf='center'
onPress={() => setStep(Steps.REGISTER)}
borderWidth={1.3} borderColor={borderColor} borderRadius="$10" bg="#3185FC"
mb="$6"
w="100%"
>
<Text color="white" fontSize="$5">Next</Text>
</Button>
<YStack f={1} />
<CustomButton onPress={() => setSelectedTab("register")} text="Next" Icon={<ArrowRight color={textBlack} />} />
</YStack >
</YStack >
);

View File

@@ -1,57 +1,100 @@
import React from 'react';
import { YStack, Text, XStack, Button, Image } from 'tamagui';
import { Nfc } from '@tamagui/lucide-icons';
import { blueColorDark, blueColorLight, borderColor, componentBgColor2, greenColorDark, greenColorLight, redColorDark, redColorLight, textColor1, textColor2 } from '../utils/colors';
import NFCHelp from '../images/nfc_help.png'
import React, { useState } from 'react';
import { YStack, Text, XStack, Button, ScrollView } from 'tamagui';
import { Nfc, X } from '@tamagui/lucide-icons';
import { bgGreen, borderColor, textBlack, textColor1 } from '../utils/colors';
import { Carousel } from '../components/Carousel';
import US_PASSPORT from '../images/us-passport.png'
import REMOVE_CASE from '../images/remove_case.png'
import US_PASSPORT_LASTPAGE from '../images/passport_lastpage_graybg.png'
import US_PASSPORT_LASTPAGE_IOS from '../images/passport_lastpage_iphone.png'
import US_PASSPORT_LASTPAGE_ANDROID from '../images/passport_lastpage_android.png'
import PHONE_SCANBUTTON from "../images/phone_scanbutton.png"
import Dialog from "react-native-dialog";
import NfcManager from 'react-native-nfc-manager';
import { Platform, Linking, Dimensions } from 'react-native';
interface NfcScreenProps {
handleNFCScan: () => void;
}
const NfcScreen: React.FC<NfcScreenProps> = ({ handleNFCScan }) => {
const [isLastSlideReached, setIsLastSlideReached] = useState(false);
const [dialogVisible, setDialogVisible] = useState(false);
const [dialogMessage, setDialogMessage] = useState('');
const [isNfcSupported, setIsNfcSupported] = useState(true);
const carouselImages = [US_PASSPORT, REMOVE_CASE, US_PASSPORT_LASTPAGE, Platform.OS === 'ios' ? US_PASSPORT_LASTPAGE_IOS : US_PASSPORT_LASTPAGE_ANDROID, PHONE_SCANBUTTON,];
const windowHeight = Dimensions.get('window').height;
const handleSlideChange = (index: number) => {
if (index === carouselImages.length - 1) {
setIsLastSlideReached(true);
}
};
const openNfcSettings = () => {
if (Platform.OS === 'ios') {
Linking.openURL('App-Prefs:root=General&path=About');
} else {
Linking.sendIntent('android.settings.NFC_SETTINGS');
}
setDialogVisible(false);
};
const checkNfcSupport = 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?');
setDialogVisible(true);
setIsNfcSupported(true);
return false;
}
return true;
} else {
setDialogMessage("Sorry, your device doesn't seem to have an NFC reader.");
setDialogVisible(true);
setIsNfcSupported(false);
return false;
}
};
const handleNfcScan = async () => {
const nfcSupported = await checkNfcSupport();
if (nfcSupported) {
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>
<ScrollView flex={1} contentContainerStyle={{ flexGrow: 1 }}>
<YStack f={1} p="$3" >
{/* <Text fontSize="$8" fow="bold" mt="$1.5" mb="$3" color={textColor1} textAlign='center'>Verify your passport using NFC</Text> */}
<Text fontSize="$9" mt="$0" color={textBlack} mb="$4" ml="$2">Verify your passport using <Text fontSize="$9" color={textBlack} style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>NFC</Text></Text>
<Carousel
images={carouselImages}
height={300}
onSlideChange={handleSlideChange}
handleNfcScan={handleNfcScan}
/>
<Dialog.Container visible={dialogVisible}>
<Dialog.Title>NFC Status</Dialog.Title>
<Dialog.Description>
{dialogMessage}
</Dialog.Description>
{isNfcSupported ? (
<XStack>
<XStack f={1} />
<Dialog.Button label="Open Settings" onPress={openNfcSettings} />
</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>
) : (
<Dialog.Button label="OK" onPress={() => setDialogVisible(false)} />
)}
</Dialog.Container>
</YStack>
<YStack gap="$2" mb="$6">
<Button borderWidth={1.3} borderColor={borderColor} borderRadius="$10" bg="#3185FC" onPress={handleNFCScan}><Nfc color={textColor1} /></Button>
</YStack>
</YStack >
</ScrollView>
);
};
export default NfcScreen;
export default NfcScreen;

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { YStack, Button, Image, Text, styled } from 'tamagui';
import { Camera, SquarePen, UserPlus } from '@tamagui/lucide-icons';
import { bgColor, borderColor, textBlack, textColor1, textColor2 } from '../utils/colors';
import { Steps } from "../utils/utils";
import CustomButton from '../components/CustomButton';
const NoSkipCarousel: React.FC = () => {
const textStyle = styled(Text, {
fontSize: 20,
fontWeight: 'bold',
color: textBlack,
});
return (
<YStack f={1} p="$3" bg="white">
<YStack f={1} jc="center">
<YStack gap="$0.5" mt="$3.5">
<Text fontSize="$9" mt="$1" >Lorem ipsum doflor siat amet</Text>
<Text fontSize="$2" mt="$2" color={textBlack}>Lorem ipsum dolor sit amet</Text>
</YStack>
</YStack>
<CustomButton text="Let's start" onPress={() => {
console.log("Let's start");
}} />
</YStack >
);
};
export default NoSkipCarousel;

View File

@@ -1,10 +1,11 @@
import React, { useState } from 'react';
import { YStack, XStack, Text, Button, Spinner } from 'tamagui';
import { LockKeyhole } from '@tamagui/lucide-icons';
import { borderColor, componentBgColor, componentBgColor2, textColor1, textColor2 } from '../utils/colors';
import { LockKeyhole, UserPlus } from '@tamagui/lucide-icons';
import { bgGreen, borderColor, componentBgColor, componentBgColor2, textBlack } from '../utils/colors';
import { Platform } from 'react-native';
import useUserStore from '../stores/userStore';
import useNavigationStore from '../stores/navigationStore';
import CustomButton from '../components/CustomButton';
const RegisterScreen: React.FC = () => {
@@ -31,21 +32,33 @@ const RegisterScreen: React.FC = () => {
return (
<YStack px="$4" f={1} mb={Platform.OS === 'ios' ? "$5" : "$0"}>
<YStack flex={1} mx="$2" gap="$2">
<Text mt="$12" color={textColor1} fontSize="$10" fontWeight="bold">
<Text mt="$12" color={textBlack} fontSize="$10" fontWeight="bold">
Register
</Text>
<Text mt="$6" fontSize="$6" color={textColor1}>Join Proof of Passport to start sharing your identity securely.</Text>
<Text mt="$1" fontSize="$4" color={textColor2}>Easily verify your nationality, humanity, or age and share only what you want to reveal.</Text>
<YStack f={1} />
<Text mt="$7" fontSize="$7" color={textBlack}>Join Proof of Passport to start sharing your identity<Text fontSize="$7" style={{
textDecorationLine: "underline", textDecorationColor: bgGreen
}}> securely. </Text></Text>
<Text mt="$0" fontSize="$6" color={textBlack} style={{
opacity: 0.7
}}>Easily verify your nationality, humanity, or age and share<Text style={{
textDecorationLine: "underline", textDecorationColor: bgGreen
}}> only </Text>what you want to reveal.</Text>
<XStack f={1} />
<XStack mt="$5" bg={componentBgColor} borderRadius={100} borderWidth={1} borderColor={borderColor} py="$2" px="$3">
<XStack bg={componentBgColor2} borderRadius={100} p="$2" >
<LockKeyhole alignSelf='center' size={24} color={textColor1} />
<XStack mt="$5" bg="white" borderRadius={100} mb="$12" py="$2" px="$3">
<XStack p="$2" >
<LockKeyhole alignSelf='center' size={24} color={textBlack} />
</XStack>
<Text alignSelf='center' ml="$3" pr="$5" fontSize="$3" color={textColor1}>Registration does not leak any personal information</Text>
<Text alignSelf='center' ml="$3" pr="$5" fontSize="$3" color={textBlack}>Registration does not leak any personal information</Text>
</XStack>
<Button
<CustomButton
isDisabled={isZkeyDownloading.register_sha256WithRSAEncryption_65537 || registering}
onPress={handleRegister}
text={isZkeyDownloading.register_sha256WithRSAEncryption_65537 ? "Downloading zkey..." : (registerStep || "Register")}
Icon={isZkeyDownloading.register_sha256WithRSAEncryption_65537 || registering ? <Spinner color={textBlack} /> : <UserPlus color={textBlack} />}
/>
{/* <Button
disabled={isZkeyDownloading.register_sha256WithRSAEncryption_65537}
mt="$8"
alignSelf='center'
@@ -56,11 +69,11 @@ const RegisterScreen: React.FC = () => {
>
<XStack gap="$3">
{(registering || isZkeyDownloading.register_sha256WithRSAEncryption_65537) && <Spinner color="white" size="small" />}
<Text color={textColor1} fontSize="$5" >
<Text color={textBlack} fontSize="$5" >
{isZkeyDownloading.register_sha256WithRSAEncryption_65537 ? "Downloading zkey..." : (registerStep || "Register")}
</Text>
</XStack>
</Button>
</Button> */}
</YStack >
</YStack >
);

View File

@@ -0,0 +1,29 @@
import React, { useEffect } from 'react';
import useUserStore from '../stores/userStore';
import useNavigationStore from '../stores/navigationStore';
import { YStack, Text, Spinner, XStack } from 'tamagui';
import { bgGreen, textBlack } from '../utils/colors';
const SplashScreen = () => {
const { registered } = useUserStore();
const { setSelectedTab } = useNavigationStore();
// once registered is retrieved from zustand, navigate to the appropriate screen
useEffect(() => {
if (registered) {
setSelectedTab('app');
} else {
setSelectedTab('start');
}
}, [registered]);
return (
<YStack ai="center" f={1} gap="$8" mt="$18" mb="$8">
<Text fontSize="$9">Proof of Passport</Text>
<XStack f={1} />
<Spinner color={textBlack} />
</YStack>
);
};
export default SplashScreen;

View File

@@ -0,0 +1,39 @@
import React from 'react';
import { YStack, Button, Image, Text, styled } from 'tamagui';
import { ArrowRight, Camera, SquarePen, UserPlus } from '@tamagui/lucide-icons';
import { bgColor, bgGreen, borderColor, textBlack, textColor1, textColor2 } from '../utils/colors';
import { Steps } from "../utils/utils";
import CustomButton from '../components/CustomButton';
import useNavigationStore from '../stores/navigationStore';
const StartScreen: React.FC = () => {
const {
setStep,
step,
selectedTab,
setSelectedTab
} = useNavigationStore();
return (
<YStack f={1} p="$3">
<YStack f={1} mt="$12">
<YStack gap="$0.5" mb="$14">
<Text fontSize="$9" >Welcome to Proof of Passport 👋</Text>
<Text fontSize="$8" mt="$6" color={textBlack}>Proof of Passport allows you to scan your passport, and to prove your identity in a
<Text fontSize="$8" color={textBlack} style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}> secure </Text>way.
</Text>
<Text fontSize="$8" mt="$4" color={textBlack} style={{ opacity: 0.7 }}>You can for example prove that you are over 18 yo while staying fully
<Text fontSize="$8" color={textBlack} style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}> anonymous.</Text>
</Text>
</YStack>
</YStack>
<CustomButton Icon={<ArrowRight />} text="Let's start" onPress={() => {
setSelectedTab("scan");
}} />
</YStack >
);
};
export default StartScreen;

View File

@@ -11,6 +11,7 @@ interface NavigationState {
hideData: boolean
toast: ReturnType<typeof useToastController>
selectedTab: string
setSelectedTab: (tab: string) => void
selectedApp: AppType | null
showRegistrationErrorSheet: boolean
registrationErrorMessage: string
@@ -43,6 +44,7 @@ const useNavigationStore = create<NavigationState>((set, get) => ({
setToast: (toast) => set({ toast }),
setStep: (step) => set({ step }),
setSelectedTab: (tab) => set({ selectedTab: tab }),
update: (patch) => {
set({

View File

@@ -88,7 +88,8 @@ const useUserStore = create<UserState>((set, get) => ({
set({
passportData: JSON.parse(passportData),
});
useNavigationStore.getState().setStep(Steps.NEXT_SCREEN);
// useNavigationStore.getState().setStep(Steps.NEXT_SCREEN);
return;
}
@@ -97,7 +98,7 @@ const useUserStore = create<UserState>((set, get) => ({
passportData: JSON.parse(passportData),
registered: true,
});
// useNavigationStore.getState().setStep(Steps.REGISTERED);
useNavigationStore.getState().setStep(Steps.REGISTERED);
},
// When reading passport for the first time:

View File

@@ -5,7 +5,7 @@ import useUserStore from '../stores/userStore';
import useNavigationStore from '../stores/navigationStore';
export const startCameraScan = async () => {
const {toast, setStep} = useNavigationStore.getState();
const { toast, setSelectedTab } = useNavigationStore.getState();
if (Platform.OS === 'ios') {
try {
@@ -19,17 +19,15 @@ export const startCameraScan = async () => {
dateOfExpiry: formatDateToYYMMDD(result.expiryDate),
})
setStep(Steps.MRZ_SCAN_COMPLETED);
setSelectedTab("nfc");
toast.show("Scan successful", {
message: 'Nice to meet you!',
message: '',
customData: {
type: "success",
},
})
amplitude.track('Camera scan successful');
} catch (e) {
console.error(e);
amplitude.track('Camera scan unsuccessful');
}
} else {
NativeModules.CameraActivityModule.startCameraActivity()
@@ -43,22 +41,19 @@ export const startCameraScan = async () => {
dateOfExpiry: expiryDate,
})
setStep(Steps.MRZ_SCAN_COMPLETED);
amplitude.track('Camera scan successful');
setSelectedTab("nfc");
toast.show("Scan successful", {
message: 'Nice to meet you!',
message: '',
customData: {
type: "success",
},
})
})
} catch (error: any) {
console.error('Invalid MRZ format:', error.message);
amplitude.track('Camera scan unsuccessful');
}
})
.catch((error: any) => {
console.error('Camera Activity Error:', error);
amplitude.track('Camera scan unsuccessful');
});
}
};

View File

@@ -16,4 +16,5 @@ export const yellowColorLight = "#f5d90a"
export const bgWhite = "#F5F5F5"
export const textBlack = "#333333"
export const bgGreen = "#94FBAB"
export const bgBlue = "#69DFFF"
export const separatorColor = "#E0E0E0"

View File

@@ -195,6 +195,7 @@ const handleResponseIOS = async (
sendCSCARequest(inputs_csca, setModalProofStep);
useNavigationStore.getState().setStep(Steps.NEXT_SCREEN);
useNavigationStore.getState().setSelectedTab("next");
} catch (e: any) {
console.log('error during parsing:', e);
useNavigationStore.getState().setStep(Steps.MRZ_SCAN_COMPLETED);
@@ -303,4 +304,5 @@ const handleResponseAndroid = async (
);
sendCSCARequest(inputs_csca, setModalProofStep);
useNavigationStore.getState().setStep(Steps.NEXT_SCREEN);
useNavigationStore.getState().setSelectedTab("next");
};

View File

@@ -34,6 +34,7 @@ export function formatDateToYYMMDD(inputDate: string) {
}
export const Steps = {
START: 0,
MRZ_SCAN: 1,
MRZ_SCAN_COMPLETED: 2,
NFC_SCANNING: 3,