diff --git a/app/Gemfile.lock b/app/Gemfile.lock index 85a4f3c2c..af6eb14f9 100644 --- a/app/Gemfile.lock +++ b/app/Gemfile.lock @@ -133,7 +133,7 @@ GEM faraday_middleware (1.2.1) faraday (~> 1.0) fastimage (2.4.0) - fastlane (2.232.0) + fastlane (2.232.1) CFPropertyList (>= 2.3, < 4.0.0) abbrev (~> 0.1.2) addressable (>= 2.8, < 3.0.0) diff --git a/app/src/assets/images/card_background_id1.png b/app/src/assets/images/card_background_id1.png new file mode 100644 index 000000000..5b17fa731 Binary files /dev/null and b/app/src/assets/images/card_background_id1.png differ diff --git a/app/src/assets/images/card_background_id2.png b/app/src/assets/images/card_background_id2.png new file mode 100644 index 000000000..19bb3ce75 Binary files /dev/null and b/app/src/assets/images/card_background_id2.png differ diff --git a/app/src/assets/images/card_background_id3.png b/app/src/assets/images/card_background_id3.png new file mode 100644 index 000000000..7b48bce3e Binary files /dev/null and b/app/src/assets/images/card_background_id3.png differ diff --git a/app/src/assets/images/card_background_id4.png b/app/src/assets/images/card_background_id4.png new file mode 100644 index 000000000..7781f399e Binary files /dev/null and b/app/src/assets/images/card_background_id4.png differ diff --git a/app/src/assets/images/card_background_id5.png b/app/src/assets/images/card_background_id5.png new file mode 100644 index 000000000..42771d941 Binary files /dev/null and b/app/src/assets/images/card_background_id5.png differ diff --git a/app/src/assets/images/card_background_id6.png b/app/src/assets/images/card_background_id6.png new file mode 100644 index 000000000..2735393ec Binary files /dev/null and b/app/src/assets/images/card_background_id6.png differ diff --git a/app/src/assets/images/dev_card_logo.svg b/app/src/assets/images/dev_card_logo.svg new file mode 100644 index 000000000..1742ab720 --- /dev/null +++ b/app/src/assets/images/dev_card_logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/app/src/assets/images/dev_card_wave.svg b/app/src/assets/images/dev_card_wave.svg new file mode 100644 index 000000000..3722fb1ad --- /dev/null +++ b/app/src/assets/images/dev_card_wave.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/assets/images/self_logo_inactive.svg b/app/src/assets/images/self_logo_inactive.svg new file mode 100644 index 000000000..65cccf8ce --- /dev/null +++ b/app/src/assets/images/self_logo_inactive.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/app/src/assets/images/self_logo_pending.svg b/app/src/assets/images/self_logo_pending.svg new file mode 100644 index 000000000..f28076f18 --- /dev/null +++ b/app/src/assets/images/self_logo_pending.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/app/src/assets/images/self_logo_unverified.svg b/app/src/assets/images/self_logo_unverified.svg new file mode 100644 index 000000000..67807f06a --- /dev/null +++ b/app/src/assets/images/self_logo_unverified.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/app/src/assets/images/wave_overlay.png b/app/src/assets/images/wave_overlay.png new file mode 100644 index 000000000..436c6fce1 Binary files /dev/null and b/app/src/assets/images/wave_overlay.png differ diff --git a/app/src/assets/images/wave_pattern_body.png b/app/src/assets/images/wave_pattern_body.png new file mode 100644 index 000000000..0d4c90de7 Binary files /dev/null and b/app/src/assets/images/wave_pattern_body.png differ diff --git a/app/src/assets/images/wave_pattern_pending.png b/app/src/assets/images/wave_pattern_pending.png new file mode 100644 index 000000000..4c86c41be Binary files /dev/null and b/app/src/assets/images/wave_pattern_pending.png differ diff --git a/app/src/assets/images/wave_pattern_transparent.png b/app/src/assets/images/wave_pattern_transparent.png new file mode 100644 index 000000000..7a9547a7c Binary files /dev/null and b/app/src/assets/images/wave_pattern_transparent.png differ diff --git a/app/src/components/homescreen/EmptyIdCard.tsx b/app/src/components/homescreen/EmptyIdCard.tsx new file mode 100644 index 000000000..8d46e487e --- /dev/null +++ b/app/src/components/homescreen/EmptyIdCard.tsx @@ -0,0 +1,179 @@ +// 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. + +import type { FC } from 'react'; +import React from 'react'; +import { Dimensions, Image, StyleSheet } from 'react-native'; +import { Text, XStack, YStack } from 'tamagui'; + +import { + black, + gray400, + slate200, + slate300, + white, +} from '@selfxyz/mobile-sdk-alpha/constants/colors'; +import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts'; + +import SelfLogoUnverified from '@/assets/images/self_logo_unverified.svg'; +import WavePatternBody from '@/assets/images/wave_pattern_body.png'; + +interface EmptyIdCardProps { + onRegisterPress: () => void; +} + +/** + * Empty state card shown when user has no registered documents. + * Matches Figma design exactly: + * - White header with gray Self logo and "NO IDENTITY FOUND" text + * - Solid gray divider line + * - White body with gray wave pattern (from original unverified_human.png) + * - Pill-shaped white button with gray border + */ +const EmptyIdCard: FC = ({ onRegisterPress }) => { + const { width: screenWidth } = Dimensions.get('window'); + + // Card dimensions (matching IdCardLayout) + const cardWidth = screenWidth * 0.95 - 16; + const cardHeight = cardWidth * 0.635; + const borderRadius = 12; + + // Figma exact dimensions (scaled from 353px reference width) + const scale = cardWidth / 353; + const headerHeight = 67 * scale; + const figmaPadding = 14 * scale; + const logoSize = 32 * scale; + const headerGap = 12 * scale; + + // Font sizes from Figma + const fontSize = { + header: 20 * scale, // 20px in Figma + subtitle: 7 * scale, // 7px in Figma + button: 16 * scale, // 16px in Figma + }; + + return ( + + + {/* Header Section - White background with bottom border */} + + {/* Content row */} + + {/* Logo + Text */} + + {/* Self logo (gray) - exact Figma asset */} + + + + {/* Text container */} + + + NO IDENTITY FOUND + + + NO IDENTITY FOUND + + + + + + + {/* Body Section - White background with wave pattern */} + + {/* Wave pattern background - exact same as unverified_human.png */} + + + {/* Register button - pill-shaped with gray border */} + + + + Register a new ID + + + + + + + ); +}; + +const styles = StyleSheet.create({ + body: { + flex: 1, + position: 'relative', + overflow: 'hidden', + backgroundColor: 'white', + }, + wavePattern: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + width: '100%', + height: '100%', + }, +}); + +export default EmptyIdCard; diff --git a/app/src/components/homescreen/ExpiredIdCard.tsx b/app/src/components/homescreen/ExpiredIdCard.tsx new file mode 100644 index 000000000..2f010fd7d --- /dev/null +++ b/app/src/components/homescreen/ExpiredIdCard.tsx @@ -0,0 +1,163 @@ +// 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. + +import type { FC } from 'react'; +import React from 'react'; +import { Dimensions, Image, StyleSheet } from 'react-native'; +import { Text, XStack, YStack } from 'tamagui'; + +import { + black, + gray400, + red600, + white, +} from '@selfxyz/mobile-sdk-alpha/constants/colors'; +import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts'; + +import SelfLogoInactive from '@/assets/images/self_logo_inactive.svg'; +import WavePatternBody from '@/assets/images/wave_pattern_body.png'; + +/** + * Expired state card shown when user's identity document has expired. + * Matches Figma design exactly: + * - White header with red Self logo and "EXPIRED ID" text + * - Red divider line + * - White body with gray wave pattern + * - Black "EXPIRED ID" badge in bottom right + */ +const ExpiredIdCard: FC = () => { + const { width: screenWidth } = Dimensions.get('window'); + + // Card dimensions (matching IdCardLayout) + const cardWidth = screenWidth * 0.95 - 16; + const cardHeight = cardWidth * 0.635; + const borderRadius = 12; + + // Figma exact dimensions (scaled from 353px reference width) + const scale = cardWidth / 353; + const headerHeight = 67 * scale; + const figmaPadding = 14 * scale; + const logoSize = 32 * scale; + const headerGap = 12 * scale; + + // Font sizes from Figma + const fontSize = { + header: 20 * scale, // 20px in Figma + subtitle: 7 * scale, // 7px in Figma + }; + + return ( + + + {/* Header Section - White background with red divider */} + + {/* Content row */} + + {/* Logo + Text */} + + {/* Red Self logo (reuses inactive logo) */} + + + + {/* Text container */} + + + EXPIRED ID + + + TIME TO REGISTER A VALID COPY + + + + + + + {/* Body Section - White background with wave pattern */} + + {/* Wave pattern background */} + + + {/* Expired badge - bottom right (black background) */} + + + EXPIRED ID + + + + + + ); +}; + +const styles = StyleSheet.create({ + wavePattern: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + width: '100%', + height: '100%', + }, +}); + +export default ExpiredIdCard; diff --git a/app/src/components/homescreen/IdCard.tsx b/app/src/components/homescreen/IdCard.tsx index 1a9cfbffc..db76832a5 100644 --- a/app/src/components/homescreen/IdCard.tsx +++ b/app/src/components/homescreen/IdCard.tsx @@ -4,520 +4,632 @@ import type { FC } from 'react'; import React from 'react'; -import { Dimensions } from 'react-native'; -import { Separator, Text, XStack, YStack } from 'tamagui'; +import { Dimensions, Image, StyleSheet } from 'react-native'; +import LinearGradient from 'react-native-linear-gradient'; +import { Text, XStack, YStack } from 'tamagui'; import type { AadhaarData } from '@selfxyz/common'; import type { PassportData } from '@selfxyz/common/types/passport'; -import { isAadhaarDocument, isMRZDocument } from '@selfxyz/common/utils/types'; +import type { KycData } from '@selfxyz/common/utils/types'; import { - black, - slate100, - slate300, - slate400, - slate500, - white, -} from '@selfxyz/mobile-sdk-alpha/constants/colors'; + isAadhaarDocument, + isKycDocument, + isMRZDocument, +} from '@selfxyz/common/utils/types'; +import { RoundFlag } from '@selfxyz/mobile-sdk-alpha/components'; +import { white } from '@selfxyz/mobile-sdk-alpha/constants/colors'; import { dinot, plexMono } from '@selfxyz/mobile-sdk-alpha/constants/fonts'; -import AadhaarIcon from '@selfxyz/mobile-sdk-alpha/svgs/icons/aadhaar.svg'; -import EPassport from '@selfxyz/mobile-sdk-alpha/svgs/icons/epassport.svg'; -import LogoGray from '@/assets/images/logo_gray.svg'; -import { SvgXml } from '@/components/homescreen/SvgXmlWrapper'; -import { - formatDateFromYYMMDD, - getDocumentAttributes, - getNameAndSurname, -} from '@/utils/documentAttributes'; +import CardBackgroundId1 from '@/assets/images/card_background_id1.png'; +import CardBackgroundId2 from '@/assets/images/card_background_id2.png'; +import CardBackgroundId3 from '@/assets/images/card_background_id3.png'; +import CardBackgroundId4 from '@/assets/images/card_background_id4.png'; +import CardBackgroundId5 from '@/assets/images/card_background_id5.png'; +import CardBackgroundId6 from '@/assets/images/card_background_id6.png'; +import DevCardLogo from '@/assets/images/dev_card_logo.svg'; +import DevCardWave from '@/assets/images/dev_card_wave.svg'; +import SelfLogoPending from '@/assets/images/self_logo_pending.svg'; +import WaveOverlay from '@/assets/images/wave_overlay.png'; +import { getSecurityLevel } from '@/components/homescreen/cardSecurityBadge'; +import KycIdCard from '@/components/homescreen/KycIdCard'; +import { getBackgroundIndex } from '@/utils/cardBackgroundSelector'; +import { getDocumentAttributes } from '@/utils/documentAttributes'; -// Import the logo SVG as a string -const logoSvg = ` - - - -`; +const CARD_BACKGROUNDS = [ + CardBackgroundId1, + CardBackgroundId2, + CardBackgroundId3, + CardBackgroundId4, + CardBackgroundId5, + CardBackgroundId6, +]; + +// Design tokens from Figma +const DEV_LOGO_BG = '#52525B'; // zinc/600 - grey circle background for dev logo +const DEV_BODY_COLOR = '#1E1B4B'; // indigo/950 - dev card body background + +// Country code to demonym mapping - comprehensive list for all supported countries +const COUNTRY_DEMONYMS: Record = { + // Major countries + USA: 'AMERICAN', + GBR: 'BRITISH', + JPN: 'JAPANESE', + DEU: 'GERMAN', + 'D<<': 'GERMAN', // German passports use D<< + FRA: 'FRENCH', + CAN: 'CANADIAN', + IND: 'INDIAN', + AUS: 'AUSTRALIAN', + NGA: 'NIGERIAN', + FIN: 'FINNISH', + ITA: 'ITALIAN', + ESP: 'SPANISH', + BRA: 'BRAZILIAN', + MEX: 'MEXICAN', + CHN: 'CHINESE', + KOR: 'SOUTH KOREAN', + PRK: 'NORTH KOREAN', + NLD: 'DUTCH', + SWE: 'SWEDISH', + NOR: 'NORWEGIAN', + DNK: 'DANISH', + CHE: 'SWISS', + AUT: 'AUSTRIAN', + BEL: 'BELGIAN', + PRT: 'PORTUGUESE', + GRC: 'GREEK', + POL: 'POLISH', + IRL: 'IRISH', + NZL: 'NEW ZEALANDER', + ZAF: 'SOUTH AFRICAN', + SGP: 'SINGAPOREAN', + MYS: 'MALAYSIAN', + THA: 'THAI', + PHL: 'FILIPINO', + IDN: 'INDONESIAN', + VNM: 'VIETNAMESE', + ARE: 'EMIRATI', + SAU: 'SAUDI', + ISR: 'ISRAELI', + EGY: 'EGYPTIAN', + TUR: 'TURKISH', + RUS: 'RUSSIAN', + UKR: 'UKRAINIAN', + ARG: 'ARGENTINIAN', + COL: 'COLOMBIAN', + CHL: 'CHILEAN', + PER: 'PERUVIAN', + // Europe + ALB: 'ALBANIAN', + AND: 'ANDORRAN', + ARM: 'ARMENIAN', + AZE: 'AZERBAIJANI', + BLR: 'BELARUSIAN', + BIH: 'BOSNIAN', + BGR: 'BULGARIAN', + HRV: 'CROATIAN', + CYP: 'CYPRIOT', + CZE: 'CZECH', + EST: 'ESTONIAN', + GEO: 'GEORGIAN', + HUN: 'HUNGARIAN', + ISL: 'ICELANDIC', + LVA: 'LATVIAN', + LIE: 'LIECHTENSTEINER', + LTU: 'LITHUANIAN', + LUX: 'LUXEMBOURGISH', + MLT: 'MALTESE', + MDA: 'MOLDOVAN', + MCO: 'MONACAN', + MNE: 'MONTENEGRIN', + MKD: 'MACEDONIAN', + ROU: 'ROMANIAN', + SMR: 'SAMMARINESE', + SRB: 'SERBIAN', + SVK: 'SLOVAK', + SVN: 'SLOVENIAN', + VAT: 'VATICAN', + // Americas + ATG: 'ANTIGUAN', + BHS: 'BAHAMIAN', + BRB: 'BARBADIAN', + BLZ: 'BELIZEAN', + BOL: 'BOLIVIAN', + CRI: 'COSTA RICAN', + CUB: 'CUBAN', + DMA: 'DOMINICAN', + DOM: 'DOMINICAN', + ECU: 'ECUADORIAN', + SLV: 'SALVADORAN', + GRD: 'GRENADIAN', + GTM: 'GUATEMALAN', + GUY: 'GUYANESE', + HTI: 'HAITIAN', + HND: 'HONDURAN', + JAM: 'JAMAICAN', + NIC: 'NICARAGUAN', + PAN: 'PANAMANIAN', + PRY: 'PARAGUAYAN', + KNA: 'KITTITIAN', + LCA: 'SAINT LUCIAN', + VCT: 'VINCENTIAN', + SUR: 'SURINAMESE', + TTO: 'TRINIDADIAN', + URY: 'URUGUAYAN', + VEN: 'VENEZUELAN', + // Africa + DZA: 'ALGERIAN', + AGO: 'ANGOLAN', + BEN: 'BENINESE', + BWA: 'BOTSWANAN', + BFA: 'BURKINABE', + BDI: 'BURUNDIAN', + CPV: 'CAPE VERDEAN', + CMR: 'CAMEROONIAN', + CAF: 'CENTRAL AFRICAN', + TCD: 'CHADIAN', + COM: 'COMORIAN', + COG: 'CONGOLESE', + COD: 'CONGOLESE', + CIV: 'IVORIAN', + DJI: 'DJIBOUTIAN', + GNQ: 'EQUATOGUINEAN', + ERI: 'ERITREAN', + SWZ: 'SWAZI', + ETH: 'ETHIOPIAN', + GAB: 'GABONESE', + GMB: 'GAMBIAN', + GHA: 'GHANAIAN', + GIN: 'GUINEAN', + GNB: 'BISSAU-GUINEAN', + KEN: 'KENYAN', + LSO: 'BASOTHO', + LBR: 'LIBERIAN', + LBY: 'LIBYAN', + MDG: 'MALAGASY', + MWI: 'MALAWIAN', + MLI: 'MALIAN', + MRT: 'MAURITANIAN', + MUS: 'MAURITIAN', + MAR: 'MOROCCAN', + MOZ: 'MOZAMBICAN', + NAM: 'NAMIBIAN', + NER: 'NIGERIEN', + RWA: 'RWANDAN', + STP: 'SAO TOMEAN', + SEN: 'SENEGALESE', + SYC: 'SEYCHELLOIS', + SLE: 'SIERRA LEONEAN', + SOM: 'SOMALI', + SSD: 'SOUTH SUDANESE', + SDN: 'SUDANESE', + TZA: 'TANZANIAN', + TGO: 'TOGOLESE', + TUN: 'TUNISIAN', + UGA: 'UGANDAN', + ZMB: 'ZAMBIAN', + ZWE: 'ZIMBABWEAN', + // Asia & Middle East + AFG: 'AFGHAN', + BHR: 'BAHRAINI', + BGD: 'BANGLADESHI', + BTN: 'BHUTANESE', + BRN: 'BRUNEIAN', + KHM: 'CAMBODIAN', + TWN: 'TAIWANESE', + HKG: 'HONG KONGER', + IRQ: 'IRAQI', + IRN: 'IRANIAN', + JOR: 'JORDANIAN', + KAZ: 'KAZAKHSTANI', + KWT: 'KUWAITI', + KGZ: 'KYRGYZSTANI', + LAO: 'LAOTIAN', + LBN: 'LEBANESE', + MAC: 'MACANESE', + MDV: 'MALDIVIAN', + MNG: 'MONGOLIAN', + MMR: 'MYANMAR', + NPL: 'NEPALI', + OMN: 'OMANI', + PAK: 'PAKISTANI', + PSE: 'PALESTINIAN', + QAT: 'QATARI', + LKA: 'SRI LANKAN', + SYR: 'SYRIAN', + TJK: 'TAJIKISTANI', + TKM: 'TURKMEN', + UZB: 'UZBEKISTANI', + YEM: 'YEMENI', + // Oceania + FJI: 'FIJIAN', + KIR: 'I-KIRIBATI', + MHL: 'MARSHALLESE', + FSM: 'MICRONESIAN', + NRU: 'NAURUAN', + PLW: 'PALAUAN', + PNG: 'PAPUA NEW GUINEAN', + WSM: 'SAMOAN', + SLB: 'SOLOMON ISLANDER', + TON: 'TONGAN', + TUV: 'TUVALUAN', + VUT: 'NI-VANUATU', + TLS: 'TIMORESE', +}; + +/** + * Get country demonym from 3-letter country code. + * Falls back to the code itself if no mapping exists. + * Note: D<< (German passports) should be normalized to DEU before calling this. + */ +const getCountryDemonym = (code: string): string => { + if (!code) return ''; + const upperCode = code.toUpperCase().replace(/ = ({ idDocument, selected, hidden, }) => { - // Early return if document is null if (!idDocument) { return null; } - // Function to mask MRZ characters except '<' and spaces - const maskMrzValue = (text: string): string => { - return text.replace(/./g, 'X'); - }; + // KYC documents use a distinct dark card design + if (isKycDocument(idDocument)) { + return ( +