diff --git a/.github/workflows/mobile-sdk-demo-e2e.yml b/.github/workflows/mobile-sdk-demo-e2e.yml index 2d7b6e8ed..58eb6440d 100644 --- a/.github/workflows/mobile-sdk-demo-e2e.yml +++ b/.github/workflows/mobile-sdk-demo-e2e.yml @@ -31,6 +31,7 @@ on: jobs: android-e2e: + name: Android E2E Tests Demo App # Currently build-only for Android. E2E steps are preserved but skipped (if: false). # To re-enable full E2E: change `if: false` to `if: true` on emulator steps. concurrency: @@ -192,6 +193,7 @@ jobs: ios-e2e: timeout-minutes: 60 runs-on: macos-latest-large + name: iOS E2E Tests Demo App concurrency: group: ${{ github.workflow }}-ios-${{ github.ref }} cancel-in-progress: true diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index 0ec54a0a5..60745d8c4 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -2201,7 +2201,7 @@ DEPENDENCIES: - lottie-ios - lottie-react-native (from `../node_modules/lottie-react-native`) - Mixpanel-swift (~> 5.0.0) - - "NFCPassportReader (from `git@github.com:selfxyz/NFCPassportReader.git`, commit `9eff7c4e3a9037fdc1e03301584e0d5dcf14d76b`)" + - NFCPassportReader (from `https://github.com/selfxyz/NFCPassportReader.git`, commit `9eff7c4e3a9037fdc1e03301584e0d5dcf14d76b`) - QKMRZScanner - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) @@ -2340,7 +2340,7 @@ EXTERNAL SOURCES: :path: "../node_modules/lottie-react-native" NFCPassportReader: :commit: 9eff7c4e3a9037fdc1e03301584e0d5dcf14d76b - :git: "git@github.com:selfxyz/NFCPassportReader.git" + :git: https://github.com/selfxyz/NFCPassportReader.git RCT-Folly: :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTDeprecation: @@ -2517,15 +2517,15 @@ EXTERNAL SOURCES: CHECKOUT OPTIONS: NFCPassportReader: :commit: 9eff7c4e3a9037fdc1e03301584e0d5dcf14d76b - :git: "git@github.com:selfxyz/NFCPassportReader.git" + :git: https://github.com/selfxyz/NFCPassportReader.git SwiftQRScanner: :commit: c71ff91297640a944de4bca61434155c3f9b0979 :git: https://github.com/vinodiOS/SwiftQRScanner SPEC CHECKSUMS: AppAuth: 1c1a8afa7e12f2ec3a294d9882dfa5ab7d3cb063 - boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 + boost: 4cb898d0bf20404aab1850c656dcea009429d6c1 + DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 Firebase: 91fefd38712feb9186ea8996af6cbdef41473442 @@ -2540,7 +2540,7 @@ SPEC CHECKSUMS: FirebaseRemoteConfigInterop: 6efda51fb5e2f15b16585197e26eaa09574e8a4d FirebaseSharedSwift: 20530f495084b8d840f78a100d8c5ee613375f6e fmt: 01b82d4ca6470831d1cc0852a1af644be019e8f6 - glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a + glog: 69ef571f3de08433d766d614c73a9838a06bf7eb GoogleAppMeasurement: f3abf08495ef2cba7829f15318c373b8d9226491 GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 diff --git a/app/package.json b/app/package.json index 32acd1c1a..04456a425 100644 --- a/app/package.json +++ b/app/package.json @@ -74,10 +74,14 @@ "web:preview": "vite preview" }, "resolutions": { - "punycode": "npm:punycode.js@2.3.1" + "punycode": "npm:punycode.js@2.3.1", + "react-native-blur-effect": "1.1.3", + "react-native-webview": "13.16.0" }, "overrides": { - "punycode": "npm:punycode.js@2.3.1" + "punycode": "npm:punycode.js@2.3.1", + "react-native-blur-effect": "1.1.3", + "react-native-webview": "13.16.0" }, "dependencies": { "@babel/runtime": "^7.28.3", @@ -137,6 +141,7 @@ "react-native": "0.76.9", "react-native-app-auth": "^8.0.3", "react-native-biometrics": "^3.0.1", + "react-native-blur-effect": "^1.1.3", "react-native-check-version": "^1.3.0", "react-native-cloud-storage": "^2.2.2", "react-native-device-info": "^14.0.4", diff --git a/app/src/providers/selfClientProvider.tsx b/app/src/providers/selfClientProvider.tsx index 1006174a5..34cf31c67 100644 --- a/app/src/providers/selfClientProvider.tsx +++ b/app/src/providers/selfClientProvider.tsx @@ -89,6 +89,23 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => { }, }, documents: selfClientDocumentsAdapter, + navigation: { + goBack: () => { + if (navigationRef.isReady()) { + navigationRef.goBack(); + } + }, + goTo: (routeName, params) => { + if (navigationRef.isReady()) { + if (params !== undefined) { + // @ts-expect-error + navigationRef.navigate(routeName, params); + } else { + navigationRef.navigate(routeName as never); + } + } + }, + }, crypto: { async hash( data: Uint8Array, diff --git a/app/src/screens/app/LoadingScreen.tsx b/app/src/screens/app/LoadingScreen.tsx index 2173e088d..9d9e9deae 100644 --- a/app/src/screens/app/LoadingScreen.tsx +++ b/app/src/screens/app/LoadingScreen.tsx @@ -9,6 +9,7 @@ import type { StaticScreenProps } from '@react-navigation/native'; import { useFocusEffect, useIsFocused } from '@react-navigation/native'; import type { DocumentCategory } from '@selfxyz/common/utils/types'; +import type { ProvingStateType } from '@selfxyz/mobile-sdk-alpha'; import { advercase, dinot, @@ -17,7 +18,6 @@ import { } from '@selfxyz/mobile-sdk-alpha'; import failAnimation from '@selfxyz/mobile-sdk-alpha/animations/loading/fail.json'; import proveLoadingAnimation from '@selfxyz/mobile-sdk-alpha/animations/loading/prove.json'; -import type { ProvingStateType } from '@selfxyz/mobile-sdk-alpha/browser'; import { black, slate400, diff --git a/app/src/screens/documents/selection/CountryPickerScreen.tsx b/app/src/screens/documents/selection/CountryPickerScreen.tsx index 3b1b5ef45..0ab66bf3e 100644 --- a/app/src/screens/documents/selection/CountryPickerScreen.tsx +++ b/app/src/screens/documents/selection/CountryPickerScreen.tsx @@ -2,17 +2,8 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { YStack } from '@selfxyz/mobile-sdk-alpha/components'; -import { slate100 } from '@selfxyz/mobile-sdk-alpha/constants/colors'; import SDKCountryPickerScreen from '@selfxyz/mobile-sdk-alpha/onboarding/country-picker-screen'; -import { DocumentFlowNavBar } from '@/components/navbar/DocumentFlowNavBar'; - export default function CountryPickerScreen() { - return ( - - - - - ); + return ; } diff --git a/circuits/package.json b/circuits/package.json index 32209e56e..6d1d22fdf 100644 --- a/circuits/package.json +++ b/circuits/package.json @@ -52,7 +52,7 @@ "@zk-email/zk-regex-circom": "^1.2.1", "@zk-kit/binary-merkle-root.circom": "2.0.0", "@zk-kit/circuits": "^1.0.0-beta", - "anon-aadhaar-circuits": "https://gitpkg.vercel.app/selfxyz/anon-aadhaar/packages/circuits?main", + "anon-aadhaar-circuits": "https://github.com/selfxyz/anon-aadhaar.git#commit=1b9efa501cff3cf25dc260b060bf611229e316a4&workspace=@anon-aadhaar/circuits", "asn1": "^0.2.6", "asn1.js": "^5.4.1", "asn1js": "^3.0.5", diff --git a/circuits/tests/disclose/vc_and_disclose_aadhaar.test.ts b/circuits/tests/disclose/vc_and_disclose_aadhaar.test.ts index cf1aace15..a3e9010bc 100644 --- a/circuits/tests/disclose/vc_and_disclose_aadhaar.test.ts +++ b/circuits/tests/disclose/vc_and_disclose_aadhaar.test.ts @@ -17,9 +17,28 @@ import nameAndYobAadhaarjson from '../consts/ofac/nameAndYobAadhaarSMT.json' wit import fs from 'fs'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -// const privateKeyPath = path.join(__dirname, '../../../node_modules/anon-aadhaar-circuits/assets/testPrivateKey.pem'); + +// Dynamically resolve the anon-aadhaar-circuits package location +function resolvePackagePath(packageName: string, subpath: string): string { + try { + // Try to resolve the package's package.json + const packageJsonPath = require.resolve(`${packageName}/package.json`, { + paths: [__dirname], + }); + const packageDir = path.dirname(packageJsonPath); + return path.join(packageDir, subpath); + } catch (error) { + // Fallback to traditional node_modules search + const modulePath = path.join(__dirname, '../../node_modules', packageName, subpath); + if (fs.existsSync(modulePath)) { + return modulePath; + } + throw new Error(`Could not resolve ${packageName}/${subpath}`); + } +} + const privateKeyPem = fs.readFileSync( - path.join(__dirname, '../../node_modules/anon-aadhaar-circuits/assets/testPrivateKey.pem'), + resolvePackagePath('anon-aadhaar-circuits', 'assets/testPrivateKey.pem'), 'utf8' ); diff --git a/package.json b/package.json index e6a7715bb..866a27c10 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,9 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-native": "0.76.9", - "react-native-passkey": "3.3.1" + "react-native-blur-effect": "1.1.3", + "react-native-passkey": "3.3.1", + "react-native-webview": "13.16.0" }, "dependencies": { "@babel/runtime": "^7.28.3", diff --git a/packages/mobile-sdk-alpha/package.json b/packages/mobile-sdk-alpha/package.json index d2768df27..b1709b082 100644 --- a/packages/mobile-sdk-alpha/package.json +++ b/packages/mobile-sdk-alpha/package.json @@ -151,6 +151,7 @@ "dependencies": { "@babel/runtime": "^7.28.3", "@selfxyz/common": "workspace:^", + "@selfxyz/euclid": "^0.4.1", "@xstate/react": "^5.0.5", "node-forge": "^1.3.1", "react-native-nfc-manager": "^3.17.1", @@ -191,9 +192,11 @@ "lottie-react-native": "7.2.2", "react": "^18.3.1", "react-native": "0.76.9", + "react-native-blur-effect": "^1.1.3", "react-native-haptic-feedback": "*", "react-native-localize": "*", - "react-native-svg": "*" + "react-native-svg": "*", + "react-native-webview": "^13.16.0" }, "packageManager": "yarn@4.6.0", "publishConfig": { diff --git a/packages/mobile-sdk-alpha/src/client.ts b/packages/mobile-sdk-alpha/src/client.ts index 5845869ad..a03337ad3 100644 --- a/packages/mobile-sdk-alpha/src/client.ts +++ b/packages/mobile-sdk-alpha/src/client.ts @@ -42,7 +42,7 @@ const optionalDefaults: Required> = { }, }; -const REQUIRED_ADAPTERS = ['auth', 'scanner', 'network', 'crypto', 'documents'] as const; +const REQUIRED_ADAPTERS = ['auth', 'scanner', 'network', 'crypto', 'documents', 'navigation'] as const; export const createListenersMap = (): { map: Map void>>; @@ -212,7 +212,12 @@ export function createSelfClient({ getMRZState: () => { return useMRZStore.getState(); }, - + goBack: () => { + adapters.navigation.goBack(); + }, + goTo: (routeName, params) => { + adapters.navigation.goTo(routeName, params); + }, // for reactivity (if needed) useProvingStore, useSelfAppStore, diff --git a/packages/mobile-sdk-alpha/src/flows/onboarding/country-picker-screen.tsx b/packages/mobile-sdk-alpha/src/flows/onboarding/country-picker-screen.tsx index 83f1bef80..32ff30b05 100644 --- a/packages/mobile-sdk-alpha/src/flows/onboarding/country-picker-screen.tsx +++ b/packages/mobile-sdk-alpha/src/flows/onboarding/country-picker-screen.tsx @@ -2,67 +2,26 @@ // SPDX-License-Identifier: BUSL-1.1 // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. -import { memo, useCallback } from 'react'; -import { ActivityIndicator, FlatList, StyleSheet, TouchableOpacity, View } from 'react-native'; +import { useCallback, useState } from 'react'; import { commonNames } from '@selfxyz/common/constants/countries'; +import { CountryPickerScreen as CountryPickerUI } from '@selfxyz/euclid'; -import { BodyText, RoundFlag, XStack, YStack } from '../../components'; -import { black, slate100, slate500 } from '../../constants/colors'; -import { advercase, dinot } from '../../constants/fonts'; +import { RoundFlag } from '../../components'; import { useSelfClient } from '../../context'; import { useCountries } from '../../documents/useCountries'; import { buttonTap } from '../../haptic'; import { SdkEvents } from '../../types/events'; -interface CountryListItem { - key: string; - countryCode: string; -} - -const ITEM_HEIGHT = 65; -const FLAG_SIZE = 32; - -const CountryItem = memo<{ - countryCode: string; - onSelect: (code: string) => void; -}>(({ countryCode, onSelect }) => { - const countryName = commonNames[countryCode as keyof typeof commonNames]; - - if (!countryName) return null; - - return ( - onSelect(countryCode)} style={styles.countryItemContainer}> - - - {countryName} - - - ); -}); - -CountryItem.displayName = 'CountryItem'; - -const Loading = () => ( - - - -); -Loading.displayName = 'Loading'; - const CountryPickerScreen: React.FC = () => { const selfClient = useSelfClient(); + const [searchValue, setSearchValue] = useState(''); const { countryData, countryList, loading, userCountryCode, showSuggestion } = useCountries(); - const onPressCountry = useCallback( + const onCountrySelect = useCallback( (countryCode: string) => { buttonTap(); - // if (__DEV__) { - // console.log('Selected country code:', countryCode); - // console.log('Current countryData:', countryData); - // console.log('Available country codes:', Object.keys(countryData)); - // } const documentTypes = countryData[countryCode]; if (__DEV__) { console.log('documentTypes for', countryCode, ':', documentTypes); @@ -87,105 +46,34 @@ const CountryPickerScreen: React.FC = () => { [countryData, selfClient], ); - const renderItem = useCallback( - ({ item }: { item: CountryListItem }) => , - [onPressCountry], - ); + const renderFlag = useCallback((countryCode: string, size: number) => { + return ; + }, []); - const keyExtractor = useCallback((item: CountryListItem) => item.countryCode, []); + const getCountryName = useCallback((countryCode: string) => { + return commonNames[countryCode as keyof typeof commonNames] || countryCode; + }, []); - const getItemLayout = useCallback( - (_data: ArrayLike | null | undefined, index: number) => ({ - length: ITEM_HEIGHT, - offset: ITEM_HEIGHT * index, - index, - }), - [], - ); + const onSearchChange = useCallback((value: string) => { + setSearchValue(value); + }, []); return ( - - - Select the country that issued your ID - - Self has support for over 300 ID types. You can select the type of ID in the next step - - - {loading ? ( - - ) : ( - - {showSuggestion && ( - - SUGGESTION - - SELECT AN ISSUING COUNTRY - - )} - - - )} - + console.log('Info pressed TODO: Implement')} + onSearchChange={onSearchChange} + /> ); }; CountryPickerScreen.displayName = 'CountryPickerScreen'; -const styles = StyleSheet.create({ - countryItemContainer: { - paddingVertical: 13, - }, - countryItemContent: { - alignItems: 'center', - gap: 16, - }, - countryItemText: { - fontSize: 16, - color: black, - flex: 1, - }, - loadingContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, - titleText: { - fontSize: 29, - fontFamily: advercase, - color: black, - }, - subtitleText: { - fontSize: 16, - color: slate500, - marginTop: 20, - }, - sectionLabel: { - fontSize: 16, - color: black, - fontFamily: dinot, - letterSpacing: 0.8, - marginBottom: 8, - }, - sectionLabelBottom: { - fontSize: 16, - color: black, - fontFamily: dinot, - letterSpacing: 0.8, - marginTop: 20, - }, -}); - export default CountryPickerScreen; diff --git a/packages/mobile-sdk-alpha/src/index.ts b/packages/mobile-sdk-alpha/src/index.ts index 51bf05283..b1517f99c 100644 --- a/packages/mobile-sdk-alpha/src/index.ts +++ b/packages/mobile-sdk-alpha/src/index.ts @@ -17,8 +17,10 @@ export type { MRZValidation, NFCScanResult, NFCScannerAdapter, + NavigationAdapter, NetworkAdapter, Progress, + RouteName, SelfClient, StorageAdapter, TrackEventParams, diff --git a/packages/mobile-sdk-alpha/src/types/public.ts b/packages/mobile-sdk-alpha/src/types/public.ts index 149f63154..c24c40a61 100644 --- a/packages/mobile-sdk-alpha/src/types/public.ts +++ b/packages/mobile-sdk-alpha/src/types/public.ts @@ -199,6 +199,36 @@ export interface Adapters { auth: AuthAdapter; /** Required document persistence layer. Implementations must be idempotent. */ documents: DocumentsAdapter; + /** Required navigation adapter for handling screen transitions. */ + navigation: NavigationAdapter; +} + +/** + * Map these route names to your navigation configuration. + * Includes all screens that the SDK may navigate to across host applications. + */ +export type RouteName = + // Document acquisition flow + | 'DocumentCamera' + | 'DocumentOnboarding' + | 'CountryPicker' + | 'IDPicker' + | 'DocumentNFCScan' + | 'ManageDocuments' + // Account/onboarding flow + | 'Home' + | 'AccountVerifiedSuccess' + | 'AccountRecoveryChoice' + | 'SaveRecoveryPhrase' + // Error/fallback screens + | 'ComingSoon' + | 'DocumentDataNotFound' + // Settings + | 'Settings'; + +export interface NavigationAdapter { + goBack(): void; + goTo(routeName: RouteName, params?: Record): void; } /** @@ -284,6 +314,8 @@ export interface SelfClient { scanNFC(opts: NFCScanOpts & { signal?: AbortSignal }): Promise; /** Parses MRZ text and returns structured fields plus checksum metadata. */ extractMRZInfo(mrz: string): MRZInfo; + goBack(): void; + goTo(routeName: RouteName, params?: Record): void; /** * Convenience wrapper around {@link AnalyticsAdapter.trackEvent}. Calls are diff --git a/packages/mobile-sdk-alpha/tests/client.test.ts b/packages/mobile-sdk-alpha/tests/client.test.ts index bf8fab887..a6e842a0a 100644 --- a/packages/mobile-sdk-alpha/tests/client.test.ts +++ b/packages/mobile-sdk-alpha/tests/client.test.ts @@ -6,7 +6,7 @@ import { describe, expect, it, vi } from 'vitest'; import type { CryptoAdapter, DocumentsAdapter, NetworkAdapter, NFCScannerAdapter } from '../src'; import { createListenersMap, createSelfClient, SdkEvents } from '../src/index'; -import type { AuthAdapter } from '../src/types/public'; +import type { AuthAdapter, NavigationAdapter } from '../src/types/public'; describe('createSelfClient', () => { // Test eager validation during client creation @@ -27,21 +27,21 @@ describe('createSelfClient', () => { it('throws when network adapter missing during creation', () => { // @ts-expect-error -- missing adapters - expect(() => createSelfClient({ config: {}, adapters: { scanner, crypto, documents, auth } })).toThrow( + expect(() => createSelfClient({ config: {}, adapters: { scanner, crypto, documents, auth, navigation } })).toThrow( 'network adapter not provided', ); }); it('throws when crypto adapter missing during creation', () => { // @ts-expect-error -- missing adapters - expect(() => createSelfClient({ config: {}, adapters: { scanner, network, documents, auth } })).toThrow( + expect(() => createSelfClient({ config: {}, adapters: { scanner, network, documents, auth, navigation } })).toThrow( 'crypto adapter not provided', ); }); it('throws when documents adapter missing during creation', () => { // @ts-expect-error -- missing adapters - expect(() => createSelfClient({ config: {}, adapters: { scanner, network, crypto, auth } })).toThrow( + expect(() => createSelfClient({ config: {}, adapters: { scanner, network, crypto, auth, navigation } })).toThrow( 'documents adapter not provided', ); }); @@ -49,7 +49,7 @@ describe('createSelfClient', () => { it('creates client successfully with all required adapters', () => { const client = createSelfClient({ config: {}, - adapters: { scanner, network, crypto, documents, auth }, + adapters: { scanner, network, crypto, documents, auth, navigation }, listeners: new Map(), }); expect(client).toBeTruthy(); @@ -59,7 +59,7 @@ describe('createSelfClient', () => { const scanMock = vi.fn().mockResolvedValue({ passportData: { mock: true } }); const client = createSelfClient({ config: {}, - adapters: { scanner: { scan: scanMock }, network, crypto, documents, auth }, + adapters: { scanner: { scan: scanMock }, network, crypto, documents, auth, navigation }, listeners: new Map(), }); const result = await client.scanNFC({ @@ -85,7 +85,7 @@ describe('createSelfClient', () => { const scanMock = vi.fn().mockRejectedValue(err); const client = createSelfClient({ config: {}, - adapters: { scanner: { scan: scanMock }, network, crypto, documents, auth }, + adapters: { scanner: { scan: scanMock }, network, crypto, documents, auth, navigation }, listeners: new Map(), }); await expect( @@ -106,7 +106,7 @@ describe('createSelfClient', () => { const client = createSelfClient({ config: {}, - adapters: { scanner, network, crypto, documents, auth }, + adapters: { scanner, network, crypto, documents, auth, navigation }, listeners: listeners.map, }); @@ -134,7 +134,7 @@ describe('createSelfClient', () => { it('parses MRZ via client', () => { const client = createSelfClient({ config: {}, - adapters: { scanner, network, crypto, documents, auth }, + adapters: { scanner, network, crypto, documents, auth, navigation }, listeners: new Map(), }); const sample = `P { const client = createSelfClient({ config: {}, adapters: { + navigation, scanner, network, crypto, @@ -171,7 +172,7 @@ describe('createSelfClient', () => { const getPrivateKey = vi.fn(() => Promise.resolve('stubbed-private-key')); const client = createSelfClient({ config: {}, - adapters: { scanner, network, crypto, documents, auth: { getPrivateKey } }, + adapters: { scanner, network, crypto, documents, navigation, auth: { getPrivateKey } }, listeners: new Map(), }); @@ -181,7 +182,7 @@ describe('createSelfClient', () => { const getPrivateKey = vi.fn(() => Promise.resolve('stubbed-private-key')); const client = createSelfClient({ config: {}, - adapters: { scanner, network, crypto, documents, auth: { getPrivateKey } }, + adapters: { scanner, network, crypto, documents, navigation, auth: { getPrivateKey } }, listeners: new Map(), }); await expect(client.hasPrivateKey()).resolves.toBe(true); @@ -222,3 +223,8 @@ const documents: DocumentsAdapter = { saveDocument: async () => {}, deleteDocument: async () => {}, }; + +const navigation: NavigationAdapter = { + goBack: vi.fn(), + goTo: vi.fn(), +}; diff --git a/packages/mobile-sdk-alpha/tests/documents/utils.test.ts b/packages/mobile-sdk-alpha/tests/documents/utils.test.ts index 2aec22457..5bb68e197 100644 --- a/packages/mobile-sdk-alpha/tests/documents/utils.test.ts +++ b/packages/mobile-sdk-alpha/tests/documents/utils.test.ts @@ -35,6 +35,10 @@ const createMockSelfClientWithDocumentsAdapter = (documentsAdapter: DocumentsAda }), }, }, + navigation: { + goBack: () => {}, + goTo: (_routeName: string, _params?: Record) => {}, + }, scanner: { scan: async () => ({ passportData: { diff --git a/packages/mobile-sdk-alpha/tests/utils/testHelpers.ts b/packages/mobile-sdk-alpha/tests/utils/testHelpers.ts index bf9ec616c..cfcf2853b 100644 --- a/packages/mobile-sdk-alpha/tests/utils/testHelpers.ts +++ b/packages/mobile-sdk-alpha/tests/utils/testHelpers.ts @@ -3,6 +3,8 @@ // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. /* eslint-disable sort-exports/sort-exports */ +import type { NavigationAdapter } from 'src/types/public'; + import type { CryptoAdapter, DocumentsAdapter, NetworkAdapter, NFCScannerAdapter } from '../../src'; // Shared test data @@ -60,12 +62,18 @@ const mockAuth = { getPrivateKey: async () => 'stubbed-private-key', }; +const mockNavigation: NavigationAdapter = { + goBack: vi.fn(), + goTo: vi.fn(), +}; + export const mockAdapters = { scanner: mockScanner, network: mockNetwork, crypto: mockCrypto, documents: mockDocuments, auth: mockAuth, + navigation: mockNavigation, }; // Shared test expectations diff --git a/packages/mobile-sdk-demo/package.json b/packages/mobile-sdk-demo/package.json index 5450494ea..adbd866e5 100644 --- a/packages/mobile-sdk-demo/package.json +++ b/packages/mobile-sdk-demo/package.json @@ -42,6 +42,7 @@ "lottie-react-native": "7.2.2", "react": "^18.3.1", "react-native": "0.76.9", + "react-native-blur-effect": "1.1.3", "react-native-get-random-values": "^1.11.0", "react-native-haptic-feedback": "^2.3.3", "react-native-keychain": "^10.0.0", @@ -49,6 +50,7 @@ "react-native-safe-area-context": "^5.6.1", "react-native-svg": "15.12.1", "react-native-vector-icons": "^10.3.0", + "react-native-webview": "13.16.0", "stream-browserify": "^3.0.0", "util": "^0.12.5" }, diff --git a/packages/mobile-sdk-demo/src/providers/SelfClientProvider.tsx b/packages/mobile-sdk-demo/src/providers/SelfClientProvider.tsx index 33f7f94ce..ab9316d6d 100644 --- a/packages/mobile-sdk-demo/src/providers/SelfClientProvider.tsx +++ b/packages/mobile-sdk-demo/src/providers/SelfClientProvider.tsx @@ -11,6 +11,7 @@ import { createListenersMap, SdkEvents, type Adapters, + type RouteName, type TrackEventParams, type WsConn, reactNativeScannerAdapter, @@ -18,8 +19,40 @@ import { import { persistentDocumentsAdapter } from '../utils/documentStore'; import { getOrCreateSecret } from '../utils/secureStorage'; +import type { ScreenName } from '../navigation/NavigationProvider'; import { useNavigation } from '../navigation/NavigationProvider'; +/** + * Maps SDK RouteName values to demo app ScreenName values. + * Routes not in this map are not supported in the demo app. + */ +const ROUTE_TO_SCREEN_MAP: Partial> = { + 'Home': 'Home', + 'CountryPicker': 'CountrySelection', + 'IDPicker': 'IDSelection', + 'DocumentCamera': 'MRZ', + 'DocumentNFCScan': 'NFC', + 'ManageDocuments': 'Documents', + 'AccountVerifiedSuccess': 'Success', + // Routes not implemented in demo app: + // 'DocumentOnboarding': null, + // 'SaveRecoveryPhrase': null, + // 'AccountRecoveryChoice': null, + // 'ComingSoon': null, + // 'DocumentDataNotFound': null, + // 'Settings': null, +} as const; + +/** + * Translates SDK RouteName to demo app ScreenName. + * + * @param routeName - The route name from the SDK + * @returns The corresponding demo app screen name, or null if not supported + */ +function translateRouteToScreen(routeName: RouteName): ScreenName | null { + return ROUTE_TO_SCREEN_MAP[routeName] ?? null; +} + const createFetch = () => { const fetchImpl = globalThis.fetch; if (!fetchImpl) { @@ -129,6 +162,21 @@ export function SelfClientProvider({ children, onNavigate }: SelfClientProviderP }, ws: createWsAdapter(), }, + navigation: { + goBack: () => { + navigation.goBack(); + }, + goTo: (routeName, params) => { + const screenName = translateRouteToScreen(routeName); + if (screenName) { + // SDK passes generic Record, but demo navigation expects specific types + // This is safe because we control the route mapping + navigation.navigate(screenName, params as any); + } else { + console.warn(`[SelfClientProvider] SDK route "${routeName}" is not mapped to a demo screen. Ignoring navigation request.`); + } + }, + }, documents: persistentDocumentsAdapter, crypto: { async hash(data: Uint8Array): Promise { diff --git a/yarn.lock b/yarn.lock index 43f516ff3..608a82ccb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7579,7 +7579,7 @@ __metadata: "@zk-email/zk-regex-circom": "npm:^1.2.1" "@zk-kit/binary-merkle-root.circom": "npm:2.0.0" "@zk-kit/circuits": "npm:^1.0.0-beta" - anon-aadhaar-circuits: "https://gitpkg.vercel.app/selfxyz/anon-aadhaar/packages/circuits?main" + anon-aadhaar-circuits: "https://github.com/selfxyz/anon-aadhaar.git#commit=1b9efa501cff3cf25dc260b060bf611229e316a4&workspace=@anon-aadhaar/circuits" asn1: "npm:^0.2.6" asn1.js: "npm:^5.4.1" asn1js: "npm:^3.0.5" @@ -7750,6 +7750,19 @@ __metadata: languageName: unknown linkType: soft +"@selfxyz/euclid@npm:^0.4.1": + version: 0.4.1 + resolution: "@selfxyz/euclid@npm:0.4.1" + peerDependencies: + react: ">=18.2.0" + react-native: ">=0.72.0" + react-native-blur-effect: ^1.1.3 + react-native-svg: ">=15.14.0" + react-native-webview: ^13.16.0 + checksum: 10c0/f25a30b936d5ab1c154008296c64e0b4f97d91cf16e420b9bc3d2f4d9196ae426d1c2b28af653e36a9a580f78a42953f6bca3e9e9fbb36a15860636b9a0cb5fd + languageName: node + linkType: hard + "@selfxyz/mobile-app@workspace:app": version: 0.0.0-use.local resolution: "@selfxyz/mobile-app@workspace:app" @@ -7862,6 +7875,7 @@ __metadata: react-native: "npm:0.76.9" react-native-app-auth: "npm:^8.0.3" react-native-biometrics: "npm:^3.0.1" + react-native-blur-effect: "npm:^1.1.3" react-native-check-version: "npm:^1.3.0" react-native-cloud-storage: "npm:^2.2.2" react-native-device-info: "npm:^14.0.4" @@ -7909,6 +7923,7 @@ __metadata: dependencies: "@babel/runtime": "npm:^7.28.3" "@selfxyz/common": "workspace:^" + "@selfxyz/euclid": "npm:^0.4.1" "@testing-library/react": "npm:^14.1.2" "@types/react": "npm:^18.3.4" "@types/react-dom": "npm:^18.3.0" @@ -7946,9 +7961,11 @@ __metadata: lottie-react-native: 7.2.2 react: ^18.3.1 react-native: 0.76.9 + react-native-blur-effect: ^1.1.3 react-native-haptic-feedback: "*" react-native-localize: "*" react-native-svg: "*" + react-native-webview: ^13.16.0 languageName: unknown linkType: soft @@ -14505,13 +14522,13 @@ __metadata: languageName: node linkType: hard -"anon-aadhaar-circuits@https://gitpkg.vercel.app/selfxyz/anon-aadhaar/packages/circuits?main": +"anon-aadhaar-circuits@https://github.com/selfxyz/anon-aadhaar.git#commit=1b9efa501cff3cf25dc260b060bf611229e316a4&workspace=@anon-aadhaar/circuits": version: 2.4.3 - resolution: "anon-aadhaar-circuits@https://gitpkg.vercel.app/selfxyz/anon-aadhaar/packages/circuits?main" + resolution: "anon-aadhaar-circuits@https://github.com/selfxyz/anon-aadhaar.git#workspace=%40anon-aadhaar%2Fcircuits&commit=1b9efa501cff3cf25dc260b060bf611229e316a4" dependencies: "@anon-aadhaar/core": "npm:^2.4.3" "@zk-email/circuits": "npm:^6.1.1" - checksum: 10c0/93138d1c251988402482f1719ed37764b962250a51deb67bf5b855b91a6f89df2776ffe6135e8accc7a0d57dd13e7c210fc02fc6562af249ea4305f24d7d55f4 + checksum: 10c0/1e092f002e6a413fd034016320eedfb789158996f707d0c8c2055450baa35660fd90657e34e05c4a23094ed397e9088b6e9feb3463287bf9a0b272cc1fde592f languageName: node linkType: hard @@ -25613,6 +25630,7 @@ __metadata: react: "npm:^18.3.1" react-dom: "npm:^18.3.1" react-native: "npm:0.76.9" + react-native-blur-effect: "npm:1.1.3" react-native-get-random-values: "npm:^1.11.0" react-native-haptic-feedback: "npm:^2.3.3" react-native-keychain: "npm:^10.0.0" @@ -25621,6 +25639,7 @@ __metadata: react-native-svg: "npm:15.12.1" react-native-svg-transformer: "npm:^1.5.1" react-native-vector-icons: "npm:^10.3.0" + react-native-webview: "npm:13.16.0" stream-browserify: "npm:^3.0.0" typescript: "npm:^5.9.2" util: "npm:^0.12.5" @@ -28332,6 +28351,17 @@ __metadata: languageName: node linkType: hard +"react-native-blur-effect@npm:1.1.3": + version: 1.1.3 + resolution: "react-native-blur-effect@npm:1.1.3" + peerDependencies: + react: ^17.0.2 + react-native: ^0.66.4 + react-native-webview: ^13.6.2 + checksum: 10c0/5036214ac36fd430c7cea41bf0f14b2aa18338ae7f3e5df4142775dd4462f26ea3bc53710397bfe01c3a2c4450c219978f86dbc5d1989deefa39ca3c4ac80bb6 + languageName: node + linkType: hard + "react-native-check-version@npm:^1.3.0": version: 1.4.0 resolution: "react-native-check-version@npm:1.4.0" @@ -28716,7 +28746,7 @@ __metadata: languageName: node linkType: hard -"react-native-webview@npm:^13.16.0": +"react-native-webview@npm:13.16.0": version: 13.16.0 resolution: "react-native-webview@npm:13.16.0" dependencies: