mirror of
https://github.com/selfxyz/self.git
synced 2026-02-19 02:24:25 -05:00
[SELF-1891] feat(kyc): Other IDs button (#1660)
* feat(kyc): Other IDs button * trigger sumsub flow directly from event listener * formatting * formatting * add todo * add feature flag --------- Co-authored-by: Justin Hernandez <justin.hernandez@self.xyz>
This commit is contained in:
committed by
GitHub
parent
e0c0c37372
commit
80d9e2d625
@@ -21,19 +21,25 @@ export interface SumsubConfig {
|
||||
const FETCH_TIMEOUT_MS = 30000; // 30 seconds
|
||||
|
||||
export const fetchAccessToken = async (
|
||||
phoneNumber: string,
|
||||
phoneNumber?: string,
|
||||
): Promise<AccessTokenResponse> => {
|
||||
const apiUrl = SUMSUB_TEE_URL;
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
||||
|
||||
try {
|
||||
const requestBody: Record<string, string> = {};
|
||||
|
||||
if (phoneNumber) {
|
||||
requestBody.phone = phoneNumber;
|
||||
}
|
||||
|
||||
const response = await fetch(`${apiUrl}/access-token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ phone: phoneNumber }),
|
||||
body: JSON.stringify(requestBody),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
import { logNFCEvent, logProofEvent } from '@/config/sentry';
|
||||
import { fetchAccessToken, launchSumsub } from '@/integrations/sumsub';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { navigationRef } from '@/navigation';
|
||||
import {
|
||||
@@ -298,6 +299,16 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
navigationRef.navigate('AadhaarUpload', { countryCode });
|
||||
}
|
||||
break;
|
||||
case 'kyc':
|
||||
fetchAccessToken()
|
||||
.then(accessToken => {
|
||||
launchSumsub({ accessToken: accessToken.token });
|
||||
})
|
||||
// TODO: show sumsub error screen
|
||||
.catch(error => {
|
||||
console.error('Error launching Sumsub:', error);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
if (countryCode) {
|
||||
navigationRef.navigate('ComingSoon', { countryCode });
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -3,9 +3,17 @@ import { wasm as wasmTester } from 'circom_tester';
|
||||
import path from 'path';
|
||||
import { packBytesAndPoseidon } from '@selfxyz/common/utils/hash';
|
||||
import { poseidon2 } from 'poseidon-lite';
|
||||
import { generateKycRegisterInput, generateMockKycRegisterInput } from '@selfxyz/common/utils/kyc/generateInputs.js';
|
||||
import {
|
||||
generateKycRegisterInput,
|
||||
generateMockKycRegisterInput,
|
||||
} from '@selfxyz/common/utils/kyc/generateInputs.js';
|
||||
import { KycRegisterInput } from '@selfxyz/common/utils/kyc/types';
|
||||
import { KYC_ID_NUMBER_INDEX, KYC_ID_NUMBER_LENGTH, KYC_ID_TYPE_INDEX, KYC_ID_TYPE_LENGTH } from '@selfxyz/common/utils/kyc/constants';
|
||||
import {
|
||||
KYC_ID_NUMBER_INDEX,
|
||||
KYC_ID_NUMBER_LENGTH,
|
||||
KYC_ID_TYPE_INDEX,
|
||||
KYC_ID_TYPE_LENGTH,
|
||||
} from '@selfxyz/common/utils/kyc/constants';
|
||||
|
||||
describe('REGISTER KYC Circuit Tests', () => {
|
||||
let circuit: any;
|
||||
@@ -42,7 +50,11 @@ describe('REGISTER KYC Circuit Tests', () => {
|
||||
KYC_ID_NUMBER_INDEX,
|
||||
KYC_ID_NUMBER_INDEX + KYC_ID_NUMBER_LENGTH
|
||||
);
|
||||
const nullifierInputs = [...'sumsub'.split('').map((x) => x.charCodeAt(0)), ...idnumber, ...input.data_padded.slice(KYC_ID_TYPE_INDEX, KYC_ID_TYPE_INDEX + KYC_ID_TYPE_LENGTH)];
|
||||
const nullifierInputs = [
|
||||
...'sumsub'.split('').map((x) => x.charCodeAt(0)),
|
||||
...idnumber,
|
||||
...input.data_padded.slice(KYC_ID_TYPE_INDEX, KYC_ID_TYPE_INDEX + KYC_ID_TYPE_LENGTH),
|
||||
];
|
||||
const nullifier = packBytesAndPoseidon(nullifierInputs);
|
||||
const commitment = poseidon2([
|
||||
input.secret,
|
||||
|
||||
@@ -1,28 +1,78 @@
|
||||
//Helper function to destructure the kyc data from the api response
|
||||
import { Point } from "@zk-kit/baby-jubjub";
|
||||
import { KYC_ADDRESS_INDEX, KYC_ADDRESS_LENGTH, KYC_COUNTRY_INDEX, KYC_COUNTRY_LENGTH, KYC_DOB_INDEX, KYC_DOB_LENGTH, KYC_EXPIRY_DATE_INDEX, KYC_EXPIRY_DATE_LENGTH, KYC_FULL_NAME_INDEX, KYC_FULL_NAME_LENGTH, KYC_GENDER_INDEX, KYC_GENDER_LENGTH, KYC_ID_NUMBER_INDEX, KYC_ID_NUMBER_LENGTH, KYC_ID_TYPE_INDEX, KYC_ID_TYPE_LENGTH, KYC_ISSUANCE_DATE_INDEX, KYC_ISSUANCE_DATE_LENGTH, KYC_PHONE_NUMBER_INDEX, KYC_PHONE_NUMBER_LENGTH, KYC_PHOTO_HASH_INDEX, KYC_PHOTO_HASH_LENGTH } from "./constants.js";
|
||||
import { KycData } from "./types.js";
|
||||
import { Point } from '@zk-kit/baby-jubjub';
|
||||
import {
|
||||
KYC_ADDRESS_INDEX,
|
||||
KYC_ADDRESS_LENGTH,
|
||||
KYC_COUNTRY_INDEX,
|
||||
KYC_COUNTRY_LENGTH,
|
||||
KYC_DOB_INDEX,
|
||||
KYC_DOB_LENGTH,
|
||||
KYC_EXPIRY_DATE_INDEX,
|
||||
KYC_EXPIRY_DATE_LENGTH,
|
||||
KYC_FULL_NAME_INDEX,
|
||||
KYC_FULL_NAME_LENGTH,
|
||||
KYC_GENDER_INDEX,
|
||||
KYC_GENDER_LENGTH,
|
||||
KYC_ID_NUMBER_INDEX,
|
||||
KYC_ID_NUMBER_LENGTH,
|
||||
KYC_ID_TYPE_INDEX,
|
||||
KYC_ID_TYPE_LENGTH,
|
||||
KYC_ISSUANCE_DATE_INDEX,
|
||||
KYC_ISSUANCE_DATE_LENGTH,
|
||||
KYC_PHONE_NUMBER_INDEX,
|
||||
KYC_PHONE_NUMBER_LENGTH,
|
||||
KYC_PHOTO_HASH_INDEX,
|
||||
KYC_PHOTO_HASH_LENGTH,
|
||||
} from './constants.js';
|
||||
import { KycData } from './types.js';
|
||||
|
||||
//accepts a base64 signature and returns a signature object
|
||||
export function deserializeSignature(signature: string): { R: Point<bigint>; s: bigint} {
|
||||
export function deserializeSignature(signature: string): { R: Point<bigint>; s: bigint } {
|
||||
const [Rx, Ry, s] = Buffer.from(signature, 'base64').toString('utf-8').split(',').map(BigInt);
|
||||
return { R: [Rx, Ry] as Point<bigint>, s };
|
||||
}
|
||||
|
||||
//accepts a base64 applicant info and returns a kyc data object
|
||||
export function deserializeApplicantInfo(applicantInfoBase64: string): Omit<KycData, 'user_identifier' | 'current_date' | 'majority_age_ASCII' | 'selector_older_than'> {
|
||||
export function deserializeApplicantInfo(
|
||||
applicantInfoBase64: string
|
||||
): Omit<
|
||||
KycData,
|
||||
'user_identifier' | 'current_date' | 'majority_age_ASCII' | 'selector_older_than'
|
||||
> {
|
||||
const applicantInfo = Buffer.from(applicantInfoBase64, 'base64').toString('utf-8');
|
||||
const country = applicantInfo.slice(KYC_COUNTRY_INDEX, KYC_COUNTRY_INDEX + KYC_COUNTRY_LENGTH).replace(/\x00/g, '');
|
||||
const idType = applicantInfo.slice(KYC_ID_TYPE_INDEX, KYC_ID_TYPE_INDEX + KYC_ID_TYPE_LENGTH).replace(/\x00/g, '');
|
||||
const idNumber = applicantInfo.slice(KYC_ID_NUMBER_INDEX, KYC_ID_NUMBER_INDEX + KYC_ID_NUMBER_LENGTH).replace(/\x00/g, '');
|
||||
const issuanceDate = applicantInfo.slice(KYC_ISSUANCE_DATE_INDEX, KYC_ISSUANCE_DATE_INDEX + KYC_ISSUANCE_DATE_LENGTH).replace(/\x00/g, '');
|
||||
const expiryDate = applicantInfo.slice(KYC_EXPIRY_DATE_INDEX, KYC_EXPIRY_DATE_INDEX + KYC_EXPIRY_DATE_LENGTH).replace(/\x00/g, '');
|
||||
const fullName = applicantInfo.slice(KYC_FULL_NAME_INDEX, KYC_FULL_NAME_INDEX + KYC_FULL_NAME_LENGTH).replace(/\x00/g, '');
|
||||
const dob = applicantInfo.slice(KYC_DOB_INDEX, KYC_DOB_INDEX + KYC_DOB_LENGTH).replace(/\x00/g, '');
|
||||
const photoHash = applicantInfo.slice(KYC_PHOTO_HASH_INDEX, KYC_PHOTO_HASH_INDEX + KYC_PHOTO_HASH_LENGTH).replace(/\x00/g, '');
|
||||
const phoneNumber = applicantInfo.slice(KYC_PHONE_NUMBER_INDEX, KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH).replace(/\x00/g, '');
|
||||
const gender = applicantInfo.slice(KYC_GENDER_INDEX, KYC_GENDER_INDEX + KYC_GENDER_LENGTH).replace(/\x00/g, '');
|
||||
const address = applicantInfo.slice(KYC_ADDRESS_INDEX, KYC_ADDRESS_INDEX + KYC_ADDRESS_LENGTH).replace(/\x00/g, '');
|
||||
const country = applicantInfo
|
||||
.slice(KYC_COUNTRY_INDEX, KYC_COUNTRY_INDEX + KYC_COUNTRY_LENGTH)
|
||||
.replace(/\x00/g, '');
|
||||
const idType = applicantInfo
|
||||
.slice(KYC_ID_TYPE_INDEX, KYC_ID_TYPE_INDEX + KYC_ID_TYPE_LENGTH)
|
||||
.replace(/\x00/g, '');
|
||||
const idNumber = applicantInfo
|
||||
.slice(KYC_ID_NUMBER_INDEX, KYC_ID_NUMBER_INDEX + KYC_ID_NUMBER_LENGTH)
|
||||
.replace(/\x00/g, '');
|
||||
const issuanceDate = applicantInfo
|
||||
.slice(KYC_ISSUANCE_DATE_INDEX, KYC_ISSUANCE_DATE_INDEX + KYC_ISSUANCE_DATE_LENGTH)
|
||||
.replace(/\x00/g, '');
|
||||
const expiryDate = applicantInfo
|
||||
.slice(KYC_EXPIRY_DATE_INDEX, KYC_EXPIRY_DATE_INDEX + KYC_EXPIRY_DATE_LENGTH)
|
||||
.replace(/\x00/g, '');
|
||||
const fullName = applicantInfo
|
||||
.slice(KYC_FULL_NAME_INDEX, KYC_FULL_NAME_INDEX + KYC_FULL_NAME_LENGTH)
|
||||
.replace(/\x00/g, '');
|
||||
const dob = applicantInfo
|
||||
.slice(KYC_DOB_INDEX, KYC_DOB_INDEX + KYC_DOB_LENGTH)
|
||||
.replace(/\x00/g, '');
|
||||
const photoHash = applicantInfo
|
||||
.slice(KYC_PHOTO_HASH_INDEX, KYC_PHOTO_HASH_INDEX + KYC_PHOTO_HASH_LENGTH)
|
||||
.replace(/\x00/g, '');
|
||||
const phoneNumber = applicantInfo
|
||||
.slice(KYC_PHONE_NUMBER_INDEX, KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH)
|
||||
.replace(/\x00/g, '');
|
||||
const gender = applicantInfo
|
||||
.slice(KYC_GENDER_INDEX, KYC_GENDER_INDEX + KYC_GENDER_LENGTH)
|
||||
.replace(/\x00/g, '');
|
||||
const address = applicantInfo
|
||||
.slice(KYC_ADDRESS_INDEX, KYC_ADDRESS_INDEX + KYC_ADDRESS_LENGTH)
|
||||
.replace(/\x00/g, '');
|
||||
|
||||
return {
|
||||
country,
|
||||
@@ -35,6 +85,6 @@ export function deserializeApplicantInfo(applicantInfoBase64: string): Omit<KycD
|
||||
photoHash,
|
||||
phoneNumber,
|
||||
gender,
|
||||
address
|
||||
address,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -87,7 +87,12 @@ export const generateMockKycRegisterInput = async (
|
||||
return kycRegisterInput;
|
||||
};
|
||||
|
||||
export const generateKycRegisterInput = async (applicantInfoBase64: string, signatureBase64: string, pubkeyStr: [string, string], secret: string) => {
|
||||
export const generateKycRegisterInput = async (
|
||||
applicantInfoBase64: string,
|
||||
signatureBase64: string,
|
||||
pubkeyStr: [string, string],
|
||||
secret: string
|
||||
) => {
|
||||
const applicantInfo = deserializeApplicantInfo(applicantInfoBase64);
|
||||
const signature = deserializeSignature(signatureBase64);
|
||||
const pubkey = [BigInt(pubkeyStr[0]), BigInt(pubkeyStr[1])] as [bigint, bigint];
|
||||
@@ -105,7 +110,7 @@ export const generateKycRegisterInput = async (applicantInfoBase64: string, sign
|
||||
};
|
||||
|
||||
return kycRegisterInput;
|
||||
}
|
||||
};
|
||||
|
||||
export const generateCircuitInputsOfac = (data: KycData, smt: SMT, proofLevel: number) => {
|
||||
const name = data.fullName;
|
||||
|
||||
@@ -19,7 +19,12 @@ export type KycData = {
|
||||
selector_older_than: string;
|
||||
};
|
||||
|
||||
export const serializeKycData = (kycData: Omit<KycData, 'user_identifier' | 'current_date' | 'majority_age_ASCII' | 'selector_older_than'>) => {
|
||||
export const serializeKycData = (
|
||||
kycData: Omit<
|
||||
KycData,
|
||||
'user_identifier' | 'current_date' | 'majority_age_ASCII' | 'selector_older_than'
|
||||
>
|
||||
) => {
|
||||
//ensure max length of each field
|
||||
let serializedData = '';
|
||||
serializedData += kycData.country.padEnd(constants.KYC_COUNTRY_LENGTH, '\0');
|
||||
|
||||
@@ -782,7 +782,12 @@ export const getNameDobLeafKyc = (name: string, dob: string) => {
|
||||
return generateSmallKey(poseidon2([dobHash, nameHash]));
|
||||
};
|
||||
|
||||
const processNameKyc = (firstName: string, lastName: string, i: number, reverse: boolean): bigint => {
|
||||
const processNameKyc = (
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
i: number,
|
||||
reverse: boolean
|
||||
): bigint => {
|
||||
const namePaddingLength = 64;
|
||||
|
||||
firstName = firstName.replace(/'/g, '');
|
||||
@@ -792,7 +797,7 @@ const processNameKyc = (firstName: string, lastName: string, i: number, reverse:
|
||||
lastName = lastName.replace(/[- ]/g, '<');
|
||||
lastName = lastName.replace(/\./g, '');
|
||||
|
||||
let nameStr = reverse ? (lastName + ' ' + firstName) : (firstName + ' ' + lastName);
|
||||
let nameStr = reverse ? lastName + ' ' + firstName : firstName + ' ' + lastName;
|
||||
const nameArr = nameStr
|
||||
.padEnd(namePaddingLength, '\0')
|
||||
.split('')
|
||||
|
||||
@@ -491,7 +491,10 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
/// @dev Callable only by the owner for testing or administration.
|
||||
/// @param nullifier The nullifier associated with the identity commitment.
|
||||
/// @param commitment The identity commitment to add.
|
||||
function devAddIdentityCommitment(uint256 nullifier, uint256 commitment) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
function devAddIdentityCommitment(
|
||||
uint256 nullifier,
|
||||
uint256 commitment
|
||||
) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
_nullifiers[nullifier] = true;
|
||||
uint256 imt_root = _identityCommitmentIMT._insert(commitment);
|
||||
_rootTimestamps[imt_root] = block.timestamp;
|
||||
@@ -504,7 +507,11 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
/// @param oldLeaf The current identity commitment to update.
|
||||
/// @param newLeaf The new identity commitment.
|
||||
/// @param siblingNodes An array of sibling nodes for Merkle proof generation.
|
||||
function devUpdateCommitment(uint256 oldLeaf, uint256 newLeaf, uint256[] calldata siblingNodes) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
function devUpdateCommitment(
|
||||
uint256 oldLeaf,
|
||||
uint256 newLeaf,
|
||||
uint256[] calldata siblingNodes
|
||||
) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
uint256 imt_root = _identityCommitmentIMT._update(oldLeaf, newLeaf, siblingNodes);
|
||||
_rootTimestamps[imt_root] = block.timestamp;
|
||||
emit DevCommitmentUpdated(oldLeaf, newLeaf, imt_root, block.timestamp);
|
||||
@@ -514,7 +521,10 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
/// @dev Caller must be the owner. Provides sibling nodes for proof of position.
|
||||
/// @param oldLeaf The identity commitment to remove.
|
||||
/// @param siblingNodes An array of sibling nodes for Merkle proof generation.
|
||||
function devRemoveCommitment(uint256 oldLeaf, uint256[] calldata siblingNodes) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
function devRemoveCommitment(
|
||||
uint256 oldLeaf,
|
||||
uint256[] calldata siblingNodes
|
||||
) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
uint256 imt_root = _identityCommitmentIMT._remove(oldLeaf, siblingNodes);
|
||||
_rootTimestamps[imt_root] = block.timestamp;
|
||||
emit DevCommitmentRemoved(oldLeaf, imt_root, block.timestamp);
|
||||
|
||||
16
packages/mobile-sdk-alpha/src/config/features.ts
Normal file
16
packages/mobile-sdk-alpha/src/config/features.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
/**
|
||||
* Static feature flags for the SDK.
|
||||
* These are compile-time constants that control feature availability.
|
||||
* Set to true when ready to launch the feature.
|
||||
*/
|
||||
export const FeatureFlags = {
|
||||
/**
|
||||
* Enable Sumsub/KYC "Other IDs" option in the ID selection screen.
|
||||
* When false, the KYC button will be hidden from users.
|
||||
*/
|
||||
KYC_ENABLED: false,
|
||||
} as const;
|
||||
@@ -7,10 +7,12 @@ import { StyleSheet } from 'react-native';
|
||||
|
||||
import AadhaarLogo from '../../../svgs/icons/aadhaar.svg';
|
||||
import EPassportLogoRounded from '../../../svgs/icons/epassport_rounded.svg';
|
||||
import PassportCameraScanIcon from '../../../svgs/icons/passport_camera_scan.svg';
|
||||
import PlusIcon from '../../../svgs/icons/plus.svg';
|
||||
import SelfLogo from '../../../svgs/logo.svg';
|
||||
import { BodyText, RoundFlag, View, XStack, YStack } from '../../components';
|
||||
import { black, slate100, slate300, slate400, white } from '../../constants/colors';
|
||||
import { FeatureFlags } from '../../config/features';
|
||||
import { black, blue100, blue600, slate100, slate300, slate400, white } from '../../constants/colors';
|
||||
import { advercase, dinot } from '../../constants/fonts';
|
||||
import { useSelfClient } from '../../context';
|
||||
import { buttonTap } from '../../haptic';
|
||||
@@ -24,6 +26,8 @@ const getDocumentName = (docType: string): string => {
|
||||
return 'ID card';
|
||||
case 'a':
|
||||
return 'Aadhaar';
|
||||
case 'kyc':
|
||||
return 'Other IDs';
|
||||
default:
|
||||
return 'Unknown Document';
|
||||
}
|
||||
@@ -37,12 +41,14 @@ const getDocumentNameForEvent = (docType: string): string => {
|
||||
return 'id_card';
|
||||
case 'a':
|
||||
return 'aadhaar';
|
||||
case 'kyc':
|
||||
return 'kyc';
|
||||
default:
|
||||
return 'unknown_document';
|
||||
}
|
||||
};
|
||||
|
||||
const getDocumentDescription = (docType: string): string => {
|
||||
const getDocumentDescription = (docType: string): string | null => {
|
||||
switch (docType) {
|
||||
case 'p':
|
||||
return 'Verified Biometric Passport';
|
||||
@@ -50,6 +56,8 @@ const getDocumentDescription = (docType: string): string => {
|
||||
return 'Verified Biometric ID card';
|
||||
case 'a':
|
||||
return 'Verified mAadhaar QR code';
|
||||
case 'kyc':
|
||||
return "National ID, Driver's License etc.";
|
||||
default:
|
||||
return 'Unknown Document';
|
||||
}
|
||||
@@ -63,11 +71,61 @@ const getDocumentLogo = (docType: string): React.ReactNode => {
|
||||
return <EPassportLogoRounded />;
|
||||
case 'a':
|
||||
return <AadhaarLogo />;
|
||||
case 'kyc':
|
||||
// same color as epassport_rounded.svg
|
||||
return <PassportCameraScanIcon color={'#075985'} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getDocumentSecurityBadge = (docType: string): string | null => {
|
||||
switch (docType) {
|
||||
case 'p':
|
||||
case 'i':
|
||||
case 'a':
|
||||
return 'Best security';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
type DocumentItemProps = {
|
||||
docType: string;
|
||||
onPress: () => void;
|
||||
};
|
||||
|
||||
const DocumentItem: React.FC<DocumentItemProps> = ({ docType, onPress }) => {
|
||||
const securityBadge = getDocumentSecurityBadge(docType);
|
||||
const description = getDocumentDescription(docType);
|
||||
|
||||
return (
|
||||
<XStack
|
||||
style={styles.documentItem}
|
||||
backgroundColor={white}
|
||||
borderWidth={1}
|
||||
borderColor={slate300}
|
||||
elevation={4}
|
||||
borderRadius={'$5'}
|
||||
padding={'$3'}
|
||||
pressStyle={{
|
||||
transform: [{ scale: 0.97 }],
|
||||
backgroundColor: slate100,
|
||||
}}
|
||||
onPress={onPress}
|
||||
>
|
||||
<XStack alignItems="center" gap={'$3'} flex={1}>
|
||||
{securityBadge && <BodyText style={styles.securityBadgeText}>{securityBadge}</BodyText>}
|
||||
<View style={styles.documentLogoContainer}>{getDocumentLogo(docType)}</View>
|
||||
<YStack gap={'$1'}>
|
||||
<BodyText style={styles.documentNameText}>{getDocumentName(docType)}</BodyText>
|
||||
{description && <BodyText style={styles.documentDescriptionText}>{description}</BodyText>}
|
||||
</YStack>
|
||||
</XStack>
|
||||
</XStack>
|
||||
);
|
||||
};
|
||||
|
||||
type IDSelectionScreenProps = {
|
||||
countryCode: string;
|
||||
documentTypes: string[];
|
||||
@@ -112,30 +170,14 @@ const IDSelectionScreen: React.FC<IDSelectionScreenProps> = props => {
|
||||
</YStack>
|
||||
<YStack gap="$3">
|
||||
{documentTypes.map((docType: string) => (
|
||||
<XStack
|
||||
key={docType}
|
||||
backgroundColor={white}
|
||||
borderWidth={1}
|
||||
borderColor={slate300}
|
||||
elevation={4}
|
||||
borderRadius={'$5'}
|
||||
padding={'$3'}
|
||||
pressStyle={{
|
||||
transform: [{ scale: 0.97 }],
|
||||
backgroundColor: slate100,
|
||||
}}
|
||||
onPress={() => onSelectDocumentType(docType)}
|
||||
>
|
||||
<XStack alignItems="center" gap={'$3'} flex={1}>
|
||||
{getDocumentLogo(docType)}
|
||||
<YStack gap={'$1'}>
|
||||
<BodyText style={styles.documentNameText}>{getDocumentName(docType)}</BodyText>
|
||||
<BodyText style={styles.documentDescriptionText}>{getDocumentDescription(docType)}</BodyText>
|
||||
</YStack>
|
||||
</XStack>
|
||||
</XStack>
|
||||
<DocumentItem key={docType} docType={docType} onPress={() => onSelectDocumentType(docType)} />
|
||||
))}
|
||||
<BodyText style={styles.footerText}>Be sure your document is ready to scan</BodyText>
|
||||
{FeatureFlags.KYC_ENABLED && (
|
||||
<View style={styles.kycContainer}>
|
||||
<DocumentItem docType="kyc" onPress={() => onSelectDocumentType('kyc')} />
|
||||
</View>
|
||||
)}
|
||||
</YStack>
|
||||
</YStack>
|
||||
);
|
||||
@@ -149,6 +191,33 @@ const styles = StyleSheet.create({
|
||||
textAlign: 'center',
|
||||
color: black,
|
||||
},
|
||||
documentLogoContainer: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
documentItem: {
|
||||
position: 'relative',
|
||||
borderWidth: 1,
|
||||
},
|
||||
securityBadgeText: {
|
||||
fontSize: 12,
|
||||
fontFamily: dinot,
|
||||
color: blue600,
|
||||
backgroundColor: blue100,
|
||||
borderRadius: 12,
|
||||
borderWidth: 1,
|
||||
borderColor: blue600,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 4,
|
||||
position: 'absolute',
|
||||
top: -20,
|
||||
right: -20,
|
||||
},
|
||||
kycContainer: {
|
||||
marginTop: 36,
|
||||
},
|
||||
documentNameText: {
|
||||
fontSize: 24,
|
||||
fontFamily: dinot,
|
||||
|
||||
Reference in New Issue
Block a user