mirror of
https://github.com/selfxyz/self.git
synced 2026-02-19 02:24:25 -05:00
Merge pull request #1713 from selfxyz/release/staging-2026-02-06
Release to Staging - 2026-02-06
This commit is contained in:
2
.github/workflows/mobile-ci.yml
vendored
2
.github/workflows/mobile-ci.yml
vendored
@@ -5,7 +5,7 @@ env:
|
||||
RUBY_VERSION: 3.2
|
||||
JAVA_VERSION: 17
|
||||
ANDROID_NDK_VERSION: 27.0.12077973
|
||||
XCODE_VERSION: 16.4
|
||||
XCODE_VERSION: 26
|
||||
# Path configuration
|
||||
WORKSPACE: ${{ github.workspace }}
|
||||
APP_PATH: ${{ github.workspace }}/app
|
||||
|
||||
2
.github/workflows/mobile-deploy.yml
vendored
2
.github/workflows/mobile-deploy.yml
vendored
@@ -33,7 +33,7 @@ env:
|
||||
JAVA_VERSION: 17
|
||||
ANDROID_API_LEVEL: 35
|
||||
ANDROID_NDK_VERSION: 27.0.12077973
|
||||
XCODE_VERSION: 16.4
|
||||
XCODE_VERSION: 26
|
||||
|
||||
# Cache versioning - increment these to bust caches when needed
|
||||
GH_CACHE_VERSION: v1 # Global cache version
|
||||
|
||||
2
.github/workflows/mobile-e2e.yml
vendored
2
.github/workflows/mobile-e2e.yml
vendored
@@ -5,7 +5,7 @@ env:
|
||||
JAVA_VERSION: 17
|
||||
ANDROID_API_LEVEL: 33
|
||||
ANDROID_NDK_VERSION: 27.0.12077973
|
||||
XCODE_VERSION: 16.4
|
||||
XCODE_VERSION: 26
|
||||
# Cache versions
|
||||
GH_CACHE_VERSION: v2 # Global cache version - bumped to invalidate caches
|
||||
GH_GEMS_CACHE_VERSION: v1 # Ruby gems cache version
|
||||
|
||||
4
.github/workflows/mobile-sdk-demo-e2e.yml
vendored
4
.github/workflows/mobile-sdk-demo-e2e.yml
vendored
@@ -5,7 +5,7 @@ env:
|
||||
JAVA_VERSION: 17
|
||||
ANDROID_API_LEVEL: 33
|
||||
ANDROID_NDK_VERSION: 27.0.12077973
|
||||
XCODE_VERSION: 16.4
|
||||
XCODE_VERSION: 26
|
||||
# Cache versions
|
||||
GH_CACHE_VERSION: v1
|
||||
GH_GEMS_CACHE_VERSION: v1
|
||||
@@ -457,7 +457,7 @@ jobs:
|
||||
fi
|
||||
|
||||
FORCE_BUNDLING=1 RCT_NO_LAUNCH_PACKAGER=1 \
|
||||
xcodebuild -workspace "$WORKSPACE_PATH" -scheme ${{ env.IOS_PROJECT_SCHEME }} -configuration Debug -destination "id=${{ env.IOS_SIMULATOR_ID }}" -derivedDataPath packages/mobile-sdk-demo/ios/build -jobs "$(sysctl -n hw.ncpu)" -parallelizeTargets -quiet COMPILER_INDEX_STORE_ENABLE=NO ONLY_ACTIVE_ARCH=YES SWIFT_COMPILATION_MODE=wholemodule || { echo "❌ iOS build failed"; exit 1; }
|
||||
xcodebuild -workspace "$WORKSPACE_PATH" -scheme ${{ env.IOS_PROJECT_SCHEME }} -configuration Debug -destination "id=${{ env.IOS_SIMULATOR_ID }}" -derivedDataPath packages/mobile-sdk-demo/ios/build -jobs "$(sysctl -n hw.ncpu)" -parallelizeTargets -quiet COMPILER_INDEX_STORE_ENABLE=NO ONLY_ACTIVE_ARCH=YES SWIFT_COMPILATION_MODE=wholemodule 'SWIFT_ACTIVE_COMPILATION_CONDITIONS=$(inherited) E2E_TESTING' || { echo "❌ iOS build failed"; exit 1; }
|
||||
echo "✅ iOS build succeeded"
|
||||
- name: Build iOS Release Archive (unsigned)
|
||||
run: |
|
||||
|
||||
159
.github/workflows/npm-publish.yml
vendored
159
.github/workflows/npm-publish.yml
vendored
@@ -12,11 +12,26 @@ on:
|
||||
- "sdk/qrcode-angular/package.json"
|
||||
- "contracts/package.json"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
strict_mode:
|
||||
description: "Fail workflow on publish errors (false = continue on error)"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: read
|
||||
|
||||
# Error Handling Strategy:
|
||||
# - STRICT_PUBLISH_MODE controls whether publish failures stop the workflow
|
||||
# - Current (false): continue-on-error=true, workflow always succeeds
|
||||
# - Target (true): continue-on-error=false, fail on real errors (expired tokens, network issues)
|
||||
# - Manual override: Use workflow_dispatch with strict_mode input to test
|
||||
# TODO: Set STRICT_PUBLISH_MODE=true once NPM token is rotated and verified
|
||||
env:
|
||||
STRICT_PUBLISH_MODE: false
|
||||
|
||||
jobs:
|
||||
detect-changes:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -86,8 +101,21 @@ jobs:
|
||||
run: |
|
||||
yarn workspace @selfxyz/core build:deps
|
||||
|
||||
- name: Check NPM Token
|
||||
id: check-token
|
||||
run: |
|
||||
if [ -z "${{ secrets.NPM_TOKEN }}" ]; then
|
||||
echo "⚠️ Warning: NPM_TOKEN is not set. Skipping publish."
|
||||
echo "token_available=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "token_available=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Publish to npm
|
||||
if: steps.check-token.outputs.token_available == 'true'
|
||||
working-directory: sdk/core
|
||||
continue-on-error: ${{ github.event.inputs.strict_mode != 'true' && env.STRICT_PUBLISH_MODE != 'true' }}
|
||||
id: publish
|
||||
run: |
|
||||
yarn config set npmPublishAccess public
|
||||
yarn npm publish --access public
|
||||
@@ -95,6 +123,17 @@ jobs:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Publish result
|
||||
if: always()
|
||||
run: |
|
||||
if [ "${{ steps.check-token.outputs.token_available }}" != "true" ]; then
|
||||
echo "::warning::NPM publish skipped - NPM_TOKEN not configured. Please rotate the token in repository secrets."
|
||||
elif [ "${{ steps.publish.outcome }}" != "success" ]; then
|
||||
echo "::warning::NPM publish failed - This may be due to an expired or invalid NPM_TOKEN. Please check and rotate the token."
|
||||
else
|
||||
echo "✅ Package published successfully"
|
||||
fi
|
||||
|
||||
publish-qrcode:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.qrcode_changed == 'true'
|
||||
@@ -114,8 +153,21 @@ jobs:
|
||||
run: |
|
||||
yarn workspace @selfxyz/qrcode build:deps
|
||||
|
||||
- name: Check NPM Token
|
||||
id: check-token
|
||||
run: |
|
||||
if [ -z "${{ secrets.NPM_TOKEN }}" ]; then
|
||||
echo "⚠️ Warning: NPM_TOKEN is not set. Skipping publish."
|
||||
echo "token_available=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "token_available=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Publish to npm
|
||||
if: steps.check-token.outputs.token_available == 'true'
|
||||
working-directory: sdk/qrcode
|
||||
continue-on-error: ${{ github.event.inputs.strict_mode != 'true' && env.STRICT_PUBLISH_MODE != 'true' }}
|
||||
id: publish
|
||||
run: |
|
||||
yarn config set npmPublishAccess public
|
||||
yarn npm publish --access public
|
||||
@@ -123,6 +175,17 @@ jobs:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Publish result
|
||||
if: always()
|
||||
run: |
|
||||
if [ "${{ steps.check-token.outputs.token_available }}" != "true" ]; then
|
||||
echo "::warning::NPM publish skipped - NPM_TOKEN not configured. Please rotate the token in repository secrets."
|
||||
elif [ "${{ steps.publish.outcome }}" != "success" ]; then
|
||||
echo "::warning::NPM publish failed - This may be due to an expired or invalid NPM_TOKEN. Please check and rotate the token."
|
||||
else
|
||||
echo "✅ Package published successfully"
|
||||
fi
|
||||
|
||||
publish-common:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.common_changed == 'true'
|
||||
@@ -141,14 +204,38 @@ jobs:
|
||||
run: |
|
||||
yarn workspace @selfxyz/common build
|
||||
|
||||
- name: Check NPM Token
|
||||
id: check-token
|
||||
run: |
|
||||
if [ -z "${{ secrets.NPM_TOKEN }}" ]; then
|
||||
echo "⚠️ Warning: NPM_TOKEN is not set. Skipping publish."
|
||||
echo "token_available=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "token_available=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Publish to npm
|
||||
if: steps.check-token.outputs.token_available == 'true'
|
||||
working-directory: common
|
||||
continue-on-error: ${{ github.event.inputs.strict_mode != 'true' && env.STRICT_PUBLISH_MODE != 'true' }}
|
||||
id: publish
|
||||
run: |
|
||||
yarn config set npmPublishAccess public
|
||||
yarn npm publish --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Publish result
|
||||
if: always()
|
||||
run: |
|
||||
if [ "${{ steps.check-token.outputs.token_available }}" != "true" ]; then
|
||||
echo "::warning::NPM publish skipped - NPM_TOKEN not configured. Please rotate the token in repository secrets."
|
||||
elif [ "${{ steps.publish.outcome }}" != "success" ]; then
|
||||
echo "::warning::NPM publish failed - This may be due to an expired or invalid NPM_TOKEN. Please check and rotate the token."
|
||||
else
|
||||
echo "✅ Package published successfully"
|
||||
fi
|
||||
publish-contracts:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.contracts_changed == 'true'
|
||||
@@ -165,14 +252,38 @@ jobs:
|
||||
- name: Build package
|
||||
run: |
|
||||
yarn workspace @selfxyz/contracts build
|
||||
- name: Check NPM Token
|
||||
id: check-token
|
||||
run: |
|
||||
if [ -z "${{ secrets.NPM_TOKEN }}" ]; then
|
||||
echo "⚠️ Warning: NPM_TOKEN is not set. Skipping publish."
|
||||
echo "token_available=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "token_available=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Publish to npm
|
||||
if: steps.check-token.outputs.token_available == 'true'
|
||||
working-directory: contracts
|
||||
continue-on-error: ${{ github.event.inputs.strict_mode != 'true' && env.STRICT_PUBLISH_MODE != 'true' }}
|
||||
id: publish
|
||||
run: |
|
||||
yarn config set npmPublishAccess public
|
||||
yarn npm publish --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Publish result
|
||||
if: always()
|
||||
run: |
|
||||
if [ "${{ steps.check-token.outputs.token_available }}" != "true" ]; then
|
||||
echo "::warning::NPM publish skipped - NPM_TOKEN not configured. Please rotate the token in repository secrets."
|
||||
elif [ "${{ steps.publish.outcome }}" != "success" ]; then
|
||||
echo "::warning::NPM publish failed - This may be due to an expired or invalid NPM_TOKEN. Please check and rotate the token."
|
||||
else
|
||||
echo "✅ Package published successfully"
|
||||
fi
|
||||
publish-qrcode-angular:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.qrcode_angular_changed == 'true'
|
||||
@@ -192,8 +303,21 @@ jobs:
|
||||
run: |
|
||||
yarn workspace @selfxyz/qrcode-angular build:deps
|
||||
|
||||
- name: Check NPM Token
|
||||
id: check-token
|
||||
run: |
|
||||
if [ -z "${{ secrets.NPM_TOKEN }}" ]; then
|
||||
echo "⚠️ Warning: NPM_TOKEN is not set. Skipping publish."
|
||||
echo "token_available=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "token_available=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Publish to npm
|
||||
if: steps.check-token.outputs.token_available == 'true'
|
||||
working-directory: sdk/qrcode-angular
|
||||
continue-on-error: ${{ github.event.inputs.strict_mode != 'true' && env.STRICT_PUBLISH_MODE != 'true' }}
|
||||
id: publish
|
||||
run: |
|
||||
yarn config set npmPublishAccess public
|
||||
yarn npm publish --access public
|
||||
@@ -201,6 +325,17 @@ jobs:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Publish result
|
||||
if: always()
|
||||
run: |
|
||||
if [ "${{ steps.check-token.outputs.token_available }}" != "true" ]; then
|
||||
echo "::warning::NPM publish skipped - NPM_TOKEN not configured. Please rotate the token in repository secrets."
|
||||
elif [ "${{ steps.publish.outcome }}" != "success" ]; then
|
||||
echo "::warning::NPM publish failed - This may be due to an expired or invalid NPM_TOKEN. Please check and rotate the token."
|
||||
else
|
||||
echo "✅ Package published successfully"
|
||||
fi
|
||||
|
||||
publish-msdk:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.msdk_changed == 'true'
|
||||
@@ -221,11 +356,35 @@ jobs:
|
||||
yarn workspace @selfxyz/common build
|
||||
yarn workspace @selfxyz/mobile-sdk-alpha build
|
||||
|
||||
- name: Check NPM Token
|
||||
id: check-token
|
||||
run: |
|
||||
if [ -z "${{ secrets.NPM_TOKEN }}" ]; then
|
||||
echo "⚠️ Warning: NPM_TOKEN is not set. Skipping publish."
|
||||
echo "token_available=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "token_available=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Publish to npm
|
||||
if: steps.check-token.outputs.token_available == 'true'
|
||||
working-directory: packages/mobile-sdk-alpha
|
||||
continue-on-error: ${{ github.event.inputs.strict_mode != 'true' && env.STRICT_PUBLISH_MODE != 'true' }}
|
||||
id: publish
|
||||
run: |
|
||||
yarn config set npmPublishAccess restricted
|
||||
yarn npm publish --access restricted --tag alpha
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Publish result
|
||||
if: always()
|
||||
run: |
|
||||
if [ "${{ steps.check-token.outputs.token_available }}" != "true" ]; then
|
||||
echo "::warning::NPM publish skipped - NPM_TOKEN not configured. Please rotate the token in repository secrets."
|
||||
elif [ "${{ steps.publish.outcome }}" != "success" ]; then
|
||||
echo "::warning::NPM publish failed - This may be due to an expired or invalid NPM_TOKEN. Please check and rotate the token."
|
||||
else
|
||||
echo "✅ Package published successfully"
|
||||
fi
|
||||
|
||||
@@ -23,8 +23,8 @@ GEM
|
||||
artifactory (3.0.17)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.4.0)
|
||||
aws-partitions (1.1209.0)
|
||||
aws-sdk-core (3.241.4)
|
||||
aws-partitions (1.1213.0)
|
||||
aws-sdk-core (3.242.0)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.992.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
@@ -35,7 +35,7 @@ GEM
|
||||
aws-sdk-kms (1.121.0)
|
||||
aws-sdk-core (~> 3, >= 3.241.4)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.212.0)
|
||||
aws-sdk-s3 (1.213.0)
|
||||
aws-sdk-core (~> 3, >= 3.241.4)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
@@ -230,7 +230,7 @@ GEM
|
||||
i18n (1.14.8)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jmespath (1.6.2)
|
||||
json (2.18.0)
|
||||
json (2.18.1)
|
||||
jwt (2.10.2)
|
||||
base64
|
||||
logger (1.7.0)
|
||||
@@ -254,7 +254,7 @@ GEM
|
||||
optparse (0.8.1)
|
||||
os (1.1.4)
|
||||
plist (3.7.2)
|
||||
prism (1.8.0)
|
||||
prism (1.9.0)
|
||||
public_suffix (4.0.7)
|
||||
racc (1.8.1)
|
||||
rake (13.3.1)
|
||||
|
||||
@@ -134,7 +134,7 @@ android {
|
||||
applicationId "com.proofofpassportapp"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 136
|
||||
versionCode 140
|
||||
versionName "2.9.15"
|
||||
manifestPlaceholders = [appAuthRedirectScheme: 'com.proofofpassportapp']
|
||||
externalNativeBuild {
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<!-- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" /> -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
@@ -72,9 +72,9 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- android:foregroundServiceType="dataSync" -->
|
||||
<service
|
||||
android:name="com.google.firebase.messaging.FirebaseMessagingService"
|
||||
android:foregroundServiceType="dataSync"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string></string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<string>We use your location to improve document scanning reliability, as performance can vary by region and document type.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>We need access to your photo library to allow you to choose passport photos or save generated QR codes.</string>
|
||||
<key>UIAppFonts</key>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>production</string>
|
||||
<key>aps-environment</key>
|
||||
<string>production</string>
|
||||
<key>com.apple.developer.associated-appclip-app-identifiers</key>
|
||||
<array>
|
||||
<string>5B29R5LYHQ.com.warroom.proofofpassport.Clip</string>
|
||||
@@ -37,7 +37,5 @@
|
||||
</array>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -39,7 +39,5 @@
|
||||
</array>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -4,11 +4,15 @@ source "https://cdn.cocoapods.org/"
|
||||
unless ENV["E2E_TESTING"] == "1"
|
||||
source "https://github.com/SumSubstance/Specs.git"
|
||||
|
||||
# Enable Fisherman (Device Intelligence) module
|
||||
# Enable Fisherman (Device Intelligence) module for fraud detection
|
||||
# Privacy: Device ID collection declared in app/ios/PrivacyInfo.xcprivacy
|
||||
ENV["IDENSIC_WITH_FISHERMAN"] = "true"
|
||||
|
||||
# Enable VideoIdent module
|
||||
ENV["IDENSIC_WITH_VIDEOIDENT"] = "true"
|
||||
# VideoIdent module disabled for current release
|
||||
# This feature provides liveness checks via live video calls with human agents
|
||||
# Disabled to avoid microphone permission requirements on both platforms
|
||||
# TODO: Re-enable for future release when liveness checks are needed
|
||||
# ENV["IDENSIC_WITH_VIDEOIDENT"] = "true"
|
||||
end
|
||||
|
||||
use_frameworks!
|
||||
|
||||
@@ -158,11 +158,6 @@ PODS:
|
||||
- IdensicMobileSDK/Fisherman (1.40.2):
|
||||
- FingerprintPro (~> 2.11)
|
||||
- IdensicMobileSDK/Core
|
||||
- IdensicMobileSDK/VideoIdent (1.40.2):
|
||||
- IdensicMobileSDK/VideoIdent-latest
|
||||
- IdensicMobileSDK/VideoIdent-latest (1.40.2):
|
||||
- IdensicMobileSDK/Core
|
||||
- TwilioVideo (>= 5.8.2)
|
||||
- lottie-ios (4.5.0)
|
||||
- lottie-react-native (7.2.2):
|
||||
- DoubleConversion
|
||||
@@ -1554,7 +1549,6 @@ PODS:
|
||||
- react-native-mobilesdk-module (1.40.2):
|
||||
- IdensicMobileSDK (= 1.40.2)
|
||||
- IdensicMobileSDK/Fisherman (= 1.40.2)
|
||||
- IdensicMobileSDK/VideoIdent (= 1.40.2)
|
||||
- React-Core
|
||||
- react-native-netinfo (11.4.1):
|
||||
- React-Core
|
||||
@@ -2204,7 +2198,6 @@ PODS:
|
||||
- React-Core
|
||||
- SwiftQRScanner (1.1.6)
|
||||
- SwiftyTesseract (3.1.3)
|
||||
- TwilioVideo (5.11.1)
|
||||
- Yoga (0.0.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
@@ -2346,7 +2339,6 @@ SPEC REPOS:
|
||||
- Sentry
|
||||
- SocketRocket
|
||||
- SwiftyTesseract
|
||||
- TwilioVideo
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
boost:
|
||||
@@ -2624,7 +2616,7 @@ SPEC CHECKSUMS:
|
||||
react-native-cloud-storage: 8d89f2bc574cf11068dfd90933905974087fb9e9
|
||||
react-native-compat: b80530ebcd3d574be5dd99cb27b984a17c119abc
|
||||
react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba
|
||||
react-native-mobilesdk-module: 4770cb45fdd19dc4eed04615f0fcdab013b3dfe2
|
||||
react-native-mobilesdk-module: 08c16fea2be97669f8e4c38153106e5fe698126a
|
||||
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187
|
||||
react-native-nfc-manager: c8891e460b4943b695d63f7f4effc6345bbefc83
|
||||
react-native-passkey: 8853c3c635164864da68a6dbbcec7148506c3bcf
|
||||
@@ -2678,9 +2670,8 @@ SPEC CHECKSUMS:
|
||||
sovran-react-native: a3ad3f8ff90c2002b2aa9790001a78b0b0a38594
|
||||
SwiftQRScanner: e85a25f9b843e9231dab89a96e441472fe54a724
|
||||
SwiftyTesseract: 1f3d96668ae92dc2208d9842c8a59bea9fad2cbb
|
||||
TwilioVideo: 9f51085d4e4fb3aff8e168b8215b31cb0f486a2f
|
||||
Yoga: 1259c7a8cbaccf7b4c3ddf8ee36ca11be9dee407
|
||||
|
||||
PODFILE CHECKSUM: 8cfd84595c3e826f512f5c545d232a27f1850ff3
|
||||
PODFILE CHECKSUM: ced4db0072978f965783277bc810af9a7bebe695
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@@ -30,7 +30,20 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeDeviceID</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<true/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeFraudPreventionAndSecurity</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1620;
|
||||
LastUpgradeCheck = 2620;
|
||||
TargetAttributes = {
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
LastSwiftMigration = 1430;
|
||||
@@ -433,8 +433,9 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 189;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 5B29R5LYHQ;
|
||||
ENABLE_APP_SANDBOX = NO;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/DoubleConversion\"",
|
||||
@@ -574,7 +575,8 @@
|
||||
CODE_SIGN_ENTITLEMENTS = OpenPassport/OpenPassport.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 189;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 5B29R5LYHQ;
|
||||
ENABLE_APP_SANDBOX = NO;
|
||||
ENABLE_RESOURCE_ACCESS_CAMERA = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/DoubleConversion\"",
|
||||
@@ -736,8 +738,10 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CXX = "";
|
||||
DEVELOPMENT_TEAM = 5B29R5LYHQ;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
@@ -799,6 +803,7 @@
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
USE_HERMES = true;
|
||||
};
|
||||
@@ -837,8 +842,10 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
CXX = "";
|
||||
DEVELOPMENT_TEAM = 5B29R5LYHQ;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
@@ -892,6 +899,7 @@
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
USE_HERMES = true;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1620"
|
||||
LastUpgradeVersion = "2620"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
1
app/src/assets/icons/epassport_logo.svg
Normal file
1
app/src/assets/icons/epassport_logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="117" height="72" viewBox="0 0 210 297"><path d="M-111.5625 24.75V136.125H23.539306A82.5 82.5 0 0 1 105 66 82.5 82.5 0 0 1 186.5 136.125H321.5625V24.75ZM105 90.75A57.75 57.75 0 0 0 47.25 148.5 57.75 57.75 0 0 0 105 206.25 57.75 57.75 0 0 0 162.75 148.5 57.75 57.75 0 0 0 105 90.75Zm-216.5625 70.125V272.25h433.125V160.875H186.46068A82.5 82.5 0 0 1 105 231 82.5 82.5 0 0 1 23.5 160.875Z" stroke-width="81.90428162"/></svg>
|
||||
|
After Width: | Height: | Size: 467 B |
3
app/src/assets/icons/shield_error.svg
Normal file
3
app/src/assets/icons/shield_error.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="100" height="120" viewBox="0 0 100.078 119.796" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M50.0391 119.796C49.4831 119.796 48.8278 119.677 48.0732 119.438C47.3187 119.24 46.5641 118.942 45.8096 118.545C37.3506 114.018 30.1823 109.927 24.3047 106.273C18.4668 102.66 13.7607 99.1051 10.1865 95.6104C6.65202 92.1156 4.07064 88.3626 2.44238 84.3516C0.814128 80.3008 0 75.5947 0 70.2334V26.3896C0 22.6169 0.714844 19.8171 2.14453 17.9902C3.61393 16.1237 5.95703 14.5352 9.17383 13.2246C10.4049 12.748 12.1523 12.0928 14.416 11.2588C16.7194 10.3851 19.2611 9.45182 22.041 8.45898C24.821 7.42643 27.5811 6.43359 30.3213 5.48047C33.1012 4.52734 35.6032 3.6735 37.8271 2.91895C40.0908 2.12467 41.8382 1.52897 43.0693 1.13184C44.1813 0.814128 45.333 0.55599 46.5244 0.357422C47.7555 0.119141 48.9271 0 50.0391 0C51.151 0 52.3226 0.119141 53.5537 0.357422C54.8245 0.55599 55.9961 0.814128 57.0684 1.13184C58.2995 1.52897 60.027 2.12467 62.251 2.91895C64.5146 3.6735 67.0166 4.5472 69.7568 5.54004C72.5368 6.49316 75.2969 7.46615 78.0371 8.45898C80.8171 9.45182 83.3389 10.3652 85.6025 11.1992C87.9059 12.0332 89.6732 12.7083 90.9043 13.2246C94.1608 14.5749 96.5039 16.1634 97.9336 17.9902C99.3633 19.8171 100.078 22.6169 100.078 26.3896V70.2334C100.078 75.5947 99.2839 80.3206 97.6953 84.4111C96.1465 88.5016 93.6048 92.334 90.0703 95.9082C86.5358 99.4427 81.8298 102.997 75.9521 106.571C70.1143 110.185 62.9062 114.176 54.3281 118.545C53.5339 118.942 52.7594 119.24 52.0049 119.438C51.2503 119.677 50.5951 119.796 50.0391 119.796ZM30.8574 82.3857C32.6842 82.3857 34.2132 81.79 35.4443 80.5986L50.1582 65.8252L64.9316 80.5986C66.0833 81.79 67.5527 82.3857 69.3398 82.3857C71.0872 82.3857 72.5566 81.79 73.748 80.5986C74.9395 79.4072 75.5352 77.9378 75.5352 76.1904C75.5352 74.4827 74.9196 73.0531 73.6885 71.9014L58.8555 57.0684L73.748 42.2354C74.9395 41.0042 75.5352 39.5745 75.5352 37.9463C75.5352 36.1989 74.9395 34.7493 73.748 33.5977C72.5964 32.4062 71.1667 31.8105 69.459 31.8105C67.7116 31.8105 66.2422 32.4062 65.0508 33.5977L50.1582 48.4307L35.3252 33.6572C34.0941 32.4658 32.6048 31.8701 30.8574 31.8701C29.1497 31.8701 27.7002 32.4658 26.5088 33.6572C25.3571 34.8089 24.7812 36.2585 24.7812 38.0059C24.7812 39.6341 25.377 41.0439 26.5684 42.2354L41.4609 57.0684L26.5684 71.9609C25.377 73.1523 24.7812 74.5622 24.7812 76.1904C24.7812 77.9378 25.3571 79.4072 26.5088 80.5986C27.7002 81.79 29.1497 82.3857 30.8574 82.3857Z" fill="#FACC15"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -92,13 +92,22 @@ export const useEarnPointsFlow = ({
|
||||
}, [hasReferrer, navigation, navigateToPointsProof]);
|
||||
|
||||
const showPointsInfoScreen = useCallback(() => {
|
||||
navigation.navigate('PointsInfo', {
|
||||
showNextButton: true,
|
||||
onNextButtonPress: () => {
|
||||
const callbackId = registerModalCallbacks({
|
||||
onButtonPress: () => {
|
||||
showPointsDisclosureModal();
|
||||
},
|
||||
onModalDismiss: () => {
|
||||
if (hasReferrer) {
|
||||
useUserStore.getState().clearDeepLinkReferrer();
|
||||
}
|
||||
},
|
||||
});
|
||||
}, [navigation, showPointsDisclosureModal]);
|
||||
|
||||
navigation.navigate('PointsInfo', {
|
||||
showNextButton: true,
|
||||
callbackId,
|
||||
});
|
||||
}, [hasReferrer, navigation, showPointsDisclosureModal]);
|
||||
|
||||
const handleReferralFlow = useCallback(async () => {
|
||||
if (!referrer) {
|
||||
|
||||
@@ -12,11 +12,7 @@ import { fetchAccessToken, launchSumsub } from '@/integrations/sumsub';
|
||||
import type { SumsubResult } from '@/integrations/sumsub/types';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
|
||||
export type FallbackErrorSource =
|
||||
| 'mrz_scan_failed'
|
||||
| 'nfc_scan_failed'
|
||||
| 'sumsub_initialization'
|
||||
| 'sumsub_verification';
|
||||
export type FallbackErrorSource = 'mrz_scan_failed' | 'nfc_scan_failed';
|
||||
|
||||
export interface UseSumsubLauncherOptions {
|
||||
/**
|
||||
@@ -90,10 +86,12 @@ export const useSumsubLauncher = (options: UseSumsubLauncherOptions) => {
|
||||
if (onError) {
|
||||
await onError(safeError, result);
|
||||
} else {
|
||||
navigation.navigate('RegistrationFallback', {
|
||||
errorSource,
|
||||
countryCode,
|
||||
});
|
||||
// Navigate to the appropriate fallback screen based on error source
|
||||
if (errorSource === 'mrz_scan_failed') {
|
||||
navigation.navigate('RegistrationFallbackMRZ', { countryCode });
|
||||
} else {
|
||||
navigation.navigate('RegistrationFallbackNFC', { countryCode });
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -110,10 +108,12 @@ export const useSumsubLauncher = (options: UseSumsubLauncherOptions) => {
|
||||
if (onError) {
|
||||
await onError(safeError);
|
||||
} else {
|
||||
navigation.navigate('RegistrationFallback', {
|
||||
errorSource,
|
||||
countryCode,
|
||||
});
|
||||
// Navigate to the appropriate fallback screen based on error source
|
||||
if (errorSource === 'mrz_scan_failed') {
|
||||
navigation.navigate('RegistrationFallbackMRZ', { countryCode });
|
||||
} else {
|
||||
navigation.navigate('RegistrationFallbackNFC', { countryCode });
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -5,15 +5,30 @@
|
||||
import { SUMSUB_TEE_URL } from '@env';
|
||||
import SNSMobileSDK from '@sumsub/react-native-mobilesdk-module';
|
||||
|
||||
import { alpha2ToAlpha3 } from '@selfxyz/common';
|
||||
|
||||
import type {
|
||||
AccessTokenResponse,
|
||||
SumsubResult,
|
||||
} from '@/integrations/sumsub/types';
|
||||
|
||||
// Maps Self document type codes to Sumsub document types
|
||||
type SelfDocumentType = 'p' | 'i';
|
||||
type SumsubDocumentType = 'PASSPORT' | 'ID_CARD';
|
||||
|
||||
const DOCUMENT_TYPE_MAP: Record<SelfDocumentType, SumsubDocumentType> = {
|
||||
p: 'PASSPORT',
|
||||
i: 'ID_CARD',
|
||||
};
|
||||
|
||||
export interface SumsubConfig {
|
||||
accessToken: string;
|
||||
locale?: string;
|
||||
debug?: boolean;
|
||||
/** Self document type code ('p' for passport, 'i' for ID card) */
|
||||
documentType?: SelfDocumentType;
|
||||
/** ISO 3166-1 alpha-2 country code (e.g., 'US', 'GB') */
|
||||
countryCode?: string;
|
||||
onStatusChanged?: (prevStatus: string, newStatus: string) => void;
|
||||
onEvent?: (eventType: string, payload: unknown) => void;
|
||||
}
|
||||
@@ -78,7 +93,7 @@ export const fetchAccessToken = async (
|
||||
export const launchSumsub = async (
|
||||
config: SumsubConfig,
|
||||
): Promise<SumsubResult> => {
|
||||
const sdk = SNSMobileSDK.init(config.accessToken, async () => {
|
||||
let sdk = SNSMobileSDK.init(config.accessToken, async () => {
|
||||
// Token refresh not implemented for test flow
|
||||
throw new Error(
|
||||
'Sumsub token expired - refresh not implemented for test flow',
|
||||
@@ -101,8 +116,39 @@ export const launchSumsub = async (
|
||||
})
|
||||
.withDebug(config.debug ?? __DEV__)
|
||||
.withLocale(config.locale ?? 'en')
|
||||
.withAnalyticsEnabled(true) // Device Intelligence requires this
|
||||
.build();
|
||||
// Platform configuration:
|
||||
// - Device Intelligence (Fisherman): Enabled on both iOS and Android
|
||||
// * iOS: Configured via IDENSIC_WITH_FISHERMAN in Podfile
|
||||
// * Android: Configured via idensic-mobile-sdk-fisherman in patch file
|
||||
// * Privacy: iOS declares device ID collection in PrivacyInfo.xcprivacy
|
||||
// * Privacy: Android should declare device fingerprinting in Google Play Data Safety
|
||||
// - VideoIdent (live video calls): Disabled on both platforms for current release
|
||||
// * iOS: Disabled in Podfile (avoids microphone permission requirements)
|
||||
// * Android: Disabled in patch file (avoids FOREGROUND_SERVICE_MICROPHONE permission)
|
||||
// * Note: VideoIdent will be re-enabled on both platforms in future release for liveness checks
|
||||
.withAnalyticsEnabled(true); // Required for Device Intelligence to function
|
||||
|
||||
return sdk.launch();
|
||||
// Pre-select document type and country if provided
|
||||
// This skips the document selection step in Sumsub
|
||||
if (config.documentType && config.countryCode) {
|
||||
const sumsubDocType = DOCUMENT_TYPE_MAP[config.documentType];
|
||||
// Handle both 2-letter (US) and 3-letter (USA) country codes
|
||||
// alpha2ToAlpha3 returns undefined for 3-letter codes, so use the original if conversion fails
|
||||
const alpha3Country =
|
||||
alpha2ToAlpha3(config.countryCode) ?? config.countryCode;
|
||||
|
||||
if (sumsubDocType && alpha3Country) {
|
||||
console.log(
|
||||
`[Sumsub] Pre-selecting document: ${sumsubDocType} from ${alpha3Country}`,
|
||||
);
|
||||
sdk = sdk.withPreferredDocumentDefinitions({
|
||||
IDENTITY: {
|
||||
idDocType: sumsubDocType,
|
||||
country: alpha3Country,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return sdk.build().launch();
|
||||
};
|
||||
|
||||
@@ -19,11 +19,15 @@ import DocumentCameraTroubleScreen from '@/screens/documents/scanning/DocumentCa
|
||||
import DocumentNFCMethodSelectionScreen from '@/screens/documents/scanning/DocumentNFCMethodSelectionScreen';
|
||||
import DocumentNFCScanScreen from '@/screens/documents/scanning/DocumentNFCScanScreen';
|
||||
import DocumentNFCTroubleScreen from '@/screens/documents/scanning/DocumentNFCTroubleScreen';
|
||||
import RegistrationFallbackScreen from '@/screens/documents/scanning/RegistrationFallbackScreen';
|
||||
import RegistrationFallbackMRZScreen from '@/screens/documents/scanning/RegistrationFallbackMRZScreen';
|
||||
import RegistrationFallbackNFCScreen from '@/screens/documents/scanning/RegistrationFallbackNFCScreen';
|
||||
import ConfirmBelongingScreen from '@/screens/documents/selection/ConfirmBelongingScreen';
|
||||
import CountryPickerScreen from '@/screens/documents/selection/CountryPickerScreen';
|
||||
import DocumentOnboardingScreen from '@/screens/documents/selection/DocumentOnboardingScreen';
|
||||
import IDPickerScreen from '@/screens/documents/selection/IDPickerScreen';
|
||||
import LogoConfirmationScreen from '@/screens/documents/selection/LogoConfirmationScreen';
|
||||
import KycConnectionErrorScreen from '@/screens/kyc/KycConnectionErrorScreen';
|
||||
import KycFailureScreen from '@/screens/kyc/KycFailureScreen';
|
||||
|
||||
const documentsScreens = {
|
||||
DocumentCamera: {
|
||||
@@ -94,6 +98,16 @@ const documentsScreens = {
|
||||
documentTypes: [],
|
||||
},
|
||||
},
|
||||
LogoConfirmation: {
|
||||
screen: LogoConfirmationScreen,
|
||||
options: {
|
||||
headerShown: false,
|
||||
} as NativeStackNavigationOptions,
|
||||
initialParams: {
|
||||
documentType: '',
|
||||
countryCode: '',
|
||||
},
|
||||
},
|
||||
ConfirmBelonging: {
|
||||
screen: ConfirmBelongingScreen,
|
||||
options: {
|
||||
@@ -148,22 +162,49 @@ const documentsScreens = {
|
||||
AadhaarUploadError: {
|
||||
screen: AadhaarUploadErrorScreen,
|
||||
options: {
|
||||
title: 'AADHAAR REGISTRATION',
|
||||
header: AadhaarNavBar,
|
||||
headerBackVisible: false,
|
||||
headerShown: false,
|
||||
} as NativeStackNavigationOptions,
|
||||
initialParams: {
|
||||
errorType: 'general',
|
||||
},
|
||||
},
|
||||
RegistrationFallback: {
|
||||
screen: RegistrationFallbackScreen,
|
||||
RegistrationFallbackMRZ: {
|
||||
screen: RegistrationFallbackMRZScreen,
|
||||
options: {
|
||||
title: 'REGISTRATION',
|
||||
headerShown: false,
|
||||
} as NativeStackNavigationOptions,
|
||||
initialParams: {
|
||||
errorSource: 'sumsub_initialization',
|
||||
countryCode: '',
|
||||
},
|
||||
},
|
||||
RegistrationFallbackNFC: {
|
||||
screen: RegistrationFallbackNFCScreen,
|
||||
options: {
|
||||
title: 'REGISTRATION',
|
||||
headerShown: false,
|
||||
} as NativeStackNavigationOptions,
|
||||
initialParams: {
|
||||
countryCode: '',
|
||||
},
|
||||
},
|
||||
KycFailure: {
|
||||
screen: KycFailureScreen,
|
||||
options: {
|
||||
headerShown: false,
|
||||
animation: 'fade',
|
||||
} as NativeStackNavigationOptions,
|
||||
initialParams: {
|
||||
countryCode: '',
|
||||
canRetry: true,
|
||||
},
|
||||
},
|
||||
KycConnectionError: {
|
||||
screen: KycConnectionErrorScreen,
|
||||
options: {
|
||||
headerShown: false,
|
||||
} as NativeStackNavigationOptions,
|
||||
initialParams: {
|
||||
countryCode: '',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
|
||||
import type { DocumentCategory } from '@selfxyz/common/utils/types';
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
|
||||
import { DefaultNavBar } from '@/components/navbar';
|
||||
@@ -28,11 +27,9 @@ import homeScreens from '@/navigation/home';
|
||||
import onboardingScreens from '@/navigation/onboarding';
|
||||
import sharedScreens from '@/navigation/shared';
|
||||
import starfallScreens from '@/navigation/starfall';
|
||||
import type { ExplicitRouteParams, OmittedRouteKeys } from '@/navigation/types';
|
||||
import verificationScreens from '@/navigation/verification';
|
||||
import type { ModalNavigationParams } from '@/screens/app/ModalScreen';
|
||||
import type { WebViewScreenParams } from '@/screens/shared/WebViewScreen';
|
||||
import { trackScreenView } from '@/services/analytics';
|
||||
import type { ProofHistory } from '@/stores/proofTypes';
|
||||
|
||||
export const navigationScreens = {
|
||||
...appScreens,
|
||||
@@ -58,167 +55,13 @@ const AppNavigation = createNativeStackNavigator({
|
||||
|
||||
type BaseRootStackParamList = StaticParamList<typeof AppNavigation>;
|
||||
|
||||
// Explicitly declare route params that are not inferred from initialParams
|
||||
// Explicitly declare route params that are not inferred from initialParams.
|
||||
// Route param types are defined in @/navigation/types for better organization.
|
||||
export type RootStackParamList = Omit<
|
||||
BaseRootStackParamList,
|
||||
| 'AadhaarUpload'
|
||||
| 'AadhaarUploadError'
|
||||
| 'AadhaarUploadSuccess'
|
||||
| 'AccountRecovery'
|
||||
| 'AccountVerifiedSuccess'
|
||||
| 'CloudBackupSettings'
|
||||
| 'ComingSoon'
|
||||
| 'ConfirmBelonging'
|
||||
| 'CreateMock'
|
||||
| 'Disclaimer'
|
||||
| 'DocumentNFCScan'
|
||||
| 'DocumentOnboarding'
|
||||
| 'DocumentSelectorForProving'
|
||||
| 'ProvingScreenRouter'
|
||||
| 'Gratification'
|
||||
| 'Home'
|
||||
| 'IDPicker'
|
||||
| 'IdDetails'
|
||||
| 'KycSuccess'
|
||||
| 'KYCVerified'
|
||||
| 'RegistrationFallback'
|
||||
| 'Loading'
|
||||
| 'Modal'
|
||||
| 'MockDataDeepLink'
|
||||
| 'Points'
|
||||
| 'PointsInfo'
|
||||
| 'ProofHistoryDetail'
|
||||
| 'Prove'
|
||||
| 'SaveRecoveryPhrase'
|
||||
| 'WebView'
|
||||
> & {
|
||||
// Shared screens
|
||||
ComingSoon: {
|
||||
countryCode?: string;
|
||||
documentCategory?: string;
|
||||
};
|
||||
WebView: WebViewScreenParams;
|
||||
|
||||
// Document screens
|
||||
IDPicker: {
|
||||
countryCode: string;
|
||||
documentTypes: string[];
|
||||
};
|
||||
ConfirmBelonging:
|
||||
| {
|
||||
documentCategory?: DocumentCategory;
|
||||
signatureAlgorithm?: string;
|
||||
curveOrExponent?: string;
|
||||
}
|
||||
| undefined;
|
||||
DocumentNFCScan:
|
||||
| {
|
||||
passportNumber?: string;
|
||||
dateOfBirth?: string;
|
||||
dateOfExpiry?: string;
|
||||
}
|
||||
| undefined;
|
||||
DocumentCameraTrouble: undefined;
|
||||
DocumentOnboarding: undefined;
|
||||
|
||||
// Aadhaar screens
|
||||
AadhaarUpload: {
|
||||
countryCode: string;
|
||||
};
|
||||
AadhaarUploadSuccess: undefined;
|
||||
AadhaarUploadError: {
|
||||
errorType: string;
|
||||
};
|
||||
|
||||
// Registration Fallback screens
|
||||
RegistrationFallback: {
|
||||
errorSource:
|
||||
| 'mrz_scan_failed'
|
||||
| 'nfc_scan_failed'
|
||||
| 'sumsub_initialization'
|
||||
| 'sumsub_verification';
|
||||
countryCode: string;
|
||||
};
|
||||
|
||||
// Account/Recovery screens
|
||||
AccountRecovery:
|
||||
| {
|
||||
nextScreen?: string;
|
||||
}
|
||||
| undefined;
|
||||
SaveRecoveryPhrase:
|
||||
| {
|
||||
nextScreen?: string;
|
||||
}
|
||||
| undefined;
|
||||
CloudBackupSettings:
|
||||
| {
|
||||
nextScreen?: 'SaveRecoveryPhrase';
|
||||
returnToScreen?: 'Points';
|
||||
}
|
||||
| undefined;
|
||||
ProofSettings: undefined;
|
||||
AccountVerifiedSuccess: undefined;
|
||||
|
||||
// Proof/Verification screens
|
||||
ProofHistoryDetail: {
|
||||
data: ProofHistory;
|
||||
};
|
||||
Prove:
|
||||
| {
|
||||
scrollOffset?: number;
|
||||
}
|
||||
| undefined;
|
||||
ProvingScreenRouter: undefined;
|
||||
DocumentSelectorForProving:
|
||||
| {
|
||||
documentType?: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
// App screens
|
||||
Loading: {
|
||||
documentCategory?: DocumentCategory;
|
||||
signatureAlgorithm?: string;
|
||||
curveOrExponent?: string;
|
||||
};
|
||||
Modal: ModalNavigationParams;
|
||||
Gratification: {
|
||||
points?: number;
|
||||
};
|
||||
StarfallPushCode: undefined;
|
||||
|
||||
// Home screens
|
||||
Home: {
|
||||
testReferralFlow?: boolean;
|
||||
};
|
||||
Points: undefined;
|
||||
PointsInfo:
|
||||
| {
|
||||
showNextButton?: boolean;
|
||||
onNextButtonPress?: () => void;
|
||||
}
|
||||
| undefined;
|
||||
IdDetails: undefined;
|
||||
|
||||
// Onboarding screens
|
||||
Disclaimer: undefined;
|
||||
KycSuccess:
|
||||
| {
|
||||
userId?: string;
|
||||
}
|
||||
| undefined;
|
||||
KYCVerified:
|
||||
| {
|
||||
status?: string;
|
||||
userId?: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
// Dev screens
|
||||
CreateMock: undefined;
|
||||
MockDataDeepLink: undefined;
|
||||
};
|
||||
OmittedRouteKeys
|
||||
> &
|
||||
ExplicitRouteParams;
|
||||
|
||||
export type RootStackScreenProps<T extends keyof RootStackParamList> =
|
||||
NativeStackScreenProps<RootStackParamList, T>;
|
||||
|
||||
@@ -2,18 +2,213 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import type { DocumentCategory } from '@selfxyz/common/types';
|
||||
import type { DocumentCategory } from '@selfxyz/common/utils/types';
|
||||
|
||||
import type { ModalNavigationParams } from '@/screens/app/ModalScreen';
|
||||
import type { WebViewScreenParams } from '@/screens/shared/WebViewScreen';
|
||||
import type { ProofHistory } from '@/stores/proofTypes';
|
||||
|
||||
// =============================================================================
|
||||
// Aadhaar Screens
|
||||
// =============================================================================
|
||||
|
||||
export type AadhaarRoutesParamList = {
|
||||
AadhaarUpload: {
|
||||
countryCode: string;
|
||||
};
|
||||
AadhaarUploadSuccess: undefined;
|
||||
AadhaarUploadError: {
|
||||
errorType: string;
|
||||
};
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// Account/Recovery Screens
|
||||
// =============================================================================
|
||||
|
||||
export type AccountRoutesParamList = {
|
||||
AccountRecovery:
|
||||
| {
|
||||
nextScreen?: string;
|
||||
}
|
||||
| undefined;
|
||||
SaveRecoveryPhrase:
|
||||
| {
|
||||
nextScreen?: string;
|
||||
}
|
||||
| undefined;
|
||||
CloudBackupSettings:
|
||||
| {
|
||||
nextScreen?: 'SaveRecoveryPhrase';
|
||||
returnToScreen?: 'Points';
|
||||
}
|
||||
| undefined;
|
||||
ProofSettings: undefined;
|
||||
AccountVerifiedSuccess: undefined;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// App Screens
|
||||
// =============================================================================
|
||||
|
||||
export type AppRoutesParamList = {
|
||||
Loading: {
|
||||
documentCategory?: DocumentCategory;
|
||||
signatureAlgorithm?: string;
|
||||
curveOrExponent?: string;
|
||||
};
|
||||
Modal: ModalNavigationParams;
|
||||
Gratification: {
|
||||
points?: number;
|
||||
};
|
||||
StarfallPushCode: undefined;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// Dev Screens
|
||||
// =============================================================================
|
||||
|
||||
export type DevRoutesParamList = {
|
||||
CreateMock: undefined;
|
||||
MockDataDeepLink: undefined;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// Document Screens
|
||||
// =============================================================================
|
||||
|
||||
export type DocumentRoutesParamList = {
|
||||
IDPicker: {
|
||||
countryCode: string;
|
||||
documentTypes: string[];
|
||||
};
|
||||
LogoConfirmation: {
|
||||
documentType: string;
|
||||
countryCode: string;
|
||||
};
|
||||
ConfirmBelonging:
|
||||
| {
|
||||
documentCategory?: DocumentCategory;
|
||||
signatureAlgorithm?: string;
|
||||
curveOrExponent?: string;
|
||||
}
|
||||
| undefined;
|
||||
DocumentNFCScan:
|
||||
| {
|
||||
passportNumber?: string;
|
||||
dateOfBirth?: string;
|
||||
dateOfExpiry?: string;
|
||||
}
|
||||
| undefined;
|
||||
DocumentCameraTrouble: undefined;
|
||||
DocumentOnboarding: undefined;
|
||||
IdDetails: undefined;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// Combined Types
|
||||
// =============================================================================
|
||||
/**
|
||||
* All route param types that need to be explicitly defined (not inferred from initialParams).
|
||||
* This is used to compose RootStackParamList in index.tsx.
|
||||
*/
|
||||
export type ExplicitRouteParams = AadhaarRoutesParamList &
|
||||
AccountRoutesParamList &
|
||||
AppRoutesParamList &
|
||||
DevRoutesParamList &
|
||||
DocumentRoutesParamList &
|
||||
HomeRoutesParamList &
|
||||
OnboardingRoutesParamList &
|
||||
RegistrationRoutesParamList &
|
||||
SharedRoutesParamList &
|
||||
VerificationRoutesParamList;
|
||||
|
||||
// =============================================================================
|
||||
// Home Screens
|
||||
// =============================================================================
|
||||
export type HomeRoutesParamList = {
|
||||
Home: {
|
||||
testReferralFlow?: boolean;
|
||||
};
|
||||
Points: undefined;
|
||||
PointsInfo:
|
||||
| {
|
||||
showNextButton?: boolean;
|
||||
callbackId?: number;
|
||||
}
|
||||
| undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Keys that need to be omitted from BaseRootStackParamList before merging with ExplicitRouteParams.
|
||||
* These are routes whose params are explicitly defined rather than inferred.
|
||||
*/
|
||||
export type OmittedRouteKeys = keyof ExplicitRouteParams;
|
||||
|
||||
// =============================================================================
|
||||
// Onboarding Screens
|
||||
// =============================================================================
|
||||
export type OnboardingRoutesParamList = {
|
||||
Disclaimer: undefined;
|
||||
KycSuccess:
|
||||
| {
|
||||
userId?: string;
|
||||
}
|
||||
| undefined;
|
||||
KYCVerified:
|
||||
| {
|
||||
status?: string;
|
||||
userId?: string;
|
||||
}
|
||||
| undefined;
|
||||
KycFailure: {
|
||||
countryCode?: string;
|
||||
canRetry?: boolean;
|
||||
};
|
||||
KycConnectionError: {
|
||||
countryCode?: string;
|
||||
};
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// Registration Fallback Screens
|
||||
// =============================================================================
|
||||
export type RegistrationRoutesParamList = {
|
||||
RegistrationFallbackMRZ: {
|
||||
countryCode: string;
|
||||
};
|
||||
RegistrationFallbackNFC: {
|
||||
countryCode: string;
|
||||
};
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// Shared Screens
|
||||
// =============================================================================
|
||||
export type SharedRoutesParamList = {
|
||||
ComingSoon: {
|
||||
countryCode?: string;
|
||||
documentCategory?: DocumentCategory;
|
||||
};
|
||||
WebView: {
|
||||
url: string;
|
||||
title?: string;
|
||||
shareTitle?: string;
|
||||
shareMessage?: string;
|
||||
shareUrl?: string;
|
||||
documentCategory?: string;
|
||||
};
|
||||
WebView: WebViewScreenParams;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// Verification/Proof Screens
|
||||
// =============================================================================
|
||||
export type VerificationRoutesParamList = {
|
||||
ProofHistoryDetail: {
|
||||
data: ProofHistory;
|
||||
};
|
||||
Prove:
|
||||
| {
|
||||
scrollOffset?: number;
|
||||
}
|
||||
| undefined;
|
||||
ProvingScreenRouter: undefined;
|
||||
DocumentSelectorForProving:
|
||||
| {
|
||||
documentType?: string;
|
||||
}
|
||||
| undefined;
|
||||
};
|
||||
|
||||
@@ -30,17 +30,26 @@ const executeNotificationNavigation = (
|
||||
const status = remoteMessage.data?.status;
|
||||
|
||||
// Handle KYC result notifications
|
||||
if (notificationType === 'kyc_result' && status === 'approved') {
|
||||
navigationRef.navigate('KYCVerified', {
|
||||
status: String(status),
|
||||
userId: remoteMessage.data?.user_id
|
||||
? String(remoteMessage.data.user_id)
|
||||
: undefined,
|
||||
});
|
||||
return true;
|
||||
if (notificationType === 'kyc_result') {
|
||||
if (status === 'approved') {
|
||||
navigationRef.navigate('KYCVerified', {
|
||||
status: String(status),
|
||||
userId: remoteMessage.data?.user_id
|
||||
? String(remoteMessage.data.user_id)
|
||||
: undefined,
|
||||
});
|
||||
return true;
|
||||
} else if (status === 'rejected') {
|
||||
navigationRef.navigate('KycFailure', {
|
||||
canRetry: false,
|
||||
});
|
||||
return true;
|
||||
} else if (status === 'retry') {
|
||||
// Take user directly to verification flow to retry
|
||||
navigationRef.navigate('CountryPicker');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Add handling for other notification types here as needed
|
||||
// For retry/rejected statuses, could navigate to appropriate screens in future
|
||||
|
||||
return true; // Navigation handled (or not applicable)
|
||||
};
|
||||
|
||||
@@ -284,8 +284,7 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
});
|
||||
|
||||
addListener(SdkEvents.DOCUMENT_MRZ_READ_FAILURE, () => {
|
||||
navigateIfReady('RegistrationFallback', {
|
||||
errorSource: 'mrz_scan_failed',
|
||||
navigateIfReady('RegistrationFallbackMRZ', {
|
||||
countryCode: currentCountryCode,
|
||||
});
|
||||
});
|
||||
@@ -318,10 +317,12 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
if (navigationRef.isReady()) {
|
||||
switch (documentType) {
|
||||
case 'p':
|
||||
navigationRef.navigate('DocumentOnboarding');
|
||||
break;
|
||||
case 'i':
|
||||
navigationRef.navigate('DocumentOnboarding');
|
||||
// Navigate to logo confirmation screen for biometric IDs
|
||||
navigationRef.navigate('LogoConfirmation', {
|
||||
documentType,
|
||||
countryCode,
|
||||
});
|
||||
break;
|
||||
case 'a':
|
||||
if (countryCode) {
|
||||
@@ -348,8 +349,21 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
accessToken: accessToken.token,
|
||||
});
|
||||
|
||||
// User cancelled - return silently
|
||||
if (!result.success && result.status === 'Interrupted') {
|
||||
console.log('[Sumsub] Result:', JSON.stringify(result));
|
||||
|
||||
// User cancelled/dismissed without completing verification
|
||||
// Status values: 'Initial' (never started), 'Incomplete' (started but not finished),
|
||||
// 'Interrupted' (explicitly cancelled)
|
||||
const cancelledStatuses = [
|
||||
'Initial',
|
||||
'Incomplete',
|
||||
'Interrupted',
|
||||
];
|
||||
if (cancelledStatuses.includes(result.status)) {
|
||||
console.log(
|
||||
'[Sumsub] User cancelled or closed without completing, status:',
|
||||
result.status,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -370,15 +384,20 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
}
|
||||
// Guard navigation call after async operations
|
||||
if (navigationRef.isReady()) {
|
||||
navigationRef.navigate('RegistrationFallback', {
|
||||
errorSource: 'sumsub_verification',
|
||||
navigationRef.navigate('KycFailure', {
|
||||
countryCode,
|
||||
canRetry: true,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Success case: navigate to KYC success screen
|
||||
// User completed verification (status: 'Pending', 'Approved', etc.)
|
||||
// Navigate to KYC success screen
|
||||
console.log(
|
||||
'[Sumsub] Verification submitted, status:',
|
||||
result.status,
|
||||
);
|
||||
if (navigationRef.isReady()) {
|
||||
navigationRef.navigate('KycSuccess', {
|
||||
userId: accessToken.userId,
|
||||
@@ -391,8 +410,7 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => {
|
||||
console.error('Error in KYC flow:', safeInitError);
|
||||
// Guard navigation call after async operations
|
||||
if (navigationRef.isReady()) {
|
||||
navigationRef.navigate('RegistrationFallback', {
|
||||
errorSource: 'sumsub_initialization',
|
||||
navigationRef.navigate('KycConnectionError', {
|
||||
countryCode,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,25 +2,35 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React from 'react';
|
||||
import { XStack, YStack } from 'tamagui';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Button, XStack, YStack } from 'tamagui';
|
||||
import type { RouteProp } from '@react-navigation/native';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { Image } from '@tamagui/lucide-icons';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { BodyText, PrimaryButton } from '@selfxyz/mobile-sdk-alpha/components';
|
||||
import { BodyText } from '@selfxyz/mobile-sdk-alpha/components';
|
||||
import { AadhaarEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
|
||||
import {
|
||||
black,
|
||||
cyan300,
|
||||
slate100,
|
||||
slate200,
|
||||
slate300,
|
||||
slate500,
|
||||
white,
|
||||
} from '@selfxyz/mobile-sdk-alpha/constants/colors';
|
||||
import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts';
|
||||
import { useSafeBottomPadding } from '@selfxyz/mobile-sdk-alpha/hooks';
|
||||
import { getErrorMessages } from '@selfxyz/mobile-sdk-alpha/onboarding/import-aadhaar';
|
||||
|
||||
import WarningIcon from '@/assets/images/warning.svg';
|
||||
import { NavBar } from '@/components/navbar/BaseNavBar';
|
||||
import { useSumsubLauncher } from '@/hooks/useSumsubLauncher';
|
||||
import { buttonTap } from '@/integrations/haptics';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { useSettingStore } from '@/stores/settingStore';
|
||||
import { extraYPadding } from '@/utils/styleUtils';
|
||||
|
||||
type AadhaarUploadErrorRouteParams = {
|
||||
@@ -32,80 +42,218 @@ type AadhaarUploadErrorRoute = RouteProp<
|
||||
string
|
||||
>;
|
||||
|
||||
const getErrorMessages = (
|
||||
errorType: 'general' | 'expired',
|
||||
): { title: string; description: string } => {
|
||||
switch (errorType) {
|
||||
case 'expired':
|
||||
return {
|
||||
title: 'Your Aadhaar document has expired',
|
||||
description: 'Please upload a valid Aadhaar document',
|
||||
};
|
||||
case 'general':
|
||||
default:
|
||||
return {
|
||||
title: 'There was a problem reading the code',
|
||||
description: 'Make sure the QR code is valid and try again',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const AadhaarUploadErrorScreen: React.FC = () => {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = useSafeBottomPadding(extraYPadding + 35);
|
||||
const navigation = useNavigation();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const route = useRoute<AadhaarUploadErrorRoute>();
|
||||
const { trackEvent } = useSelfClient();
|
||||
const errorType = route.params?.errorType || 'general';
|
||||
const kycEnabled = useSettingStore(state => state.kycEnabled);
|
||||
|
||||
const errorType = route.params?.errorType || 'general';
|
||||
const { title, description } = getErrorMessages(errorType);
|
||||
|
||||
const { launchSumsubVerification, isLoading: isRetrying } = useSumsubLauncher(
|
||||
{
|
||||
countryCode: 'IND',
|
||||
errorSource: 'mrz_scan_failed', // Use a compatible error source
|
||||
onCancel: () => {
|
||||
navigation.goBack();
|
||||
},
|
||||
onError: () => {
|
||||
// Stay on this screen - user can try again
|
||||
},
|
||||
onSuccess: () => {
|
||||
// Success - provider handles its own success UI
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
buttonTap();
|
||||
navigation.goBack();
|
||||
}, [navigation]);
|
||||
|
||||
const handleTryAgain = useCallback(() => {
|
||||
trackEvent(AadhaarEvents.RETRY_BUTTON_PRESSED, { errorType });
|
||||
navigation.goBack();
|
||||
}, [errorType, navigation, trackEvent]);
|
||||
|
||||
const handleTryAlternative = useCallback(async () => {
|
||||
trackEvent(AadhaarEvents.HELP_BUTTON_PRESSED, { errorType });
|
||||
await launchSumsubVerification();
|
||||
}, [errorType, launchSumsubVerification, trackEvent]);
|
||||
|
||||
return (
|
||||
<YStack flex={1} backgroundColor={slate100}>
|
||||
<YStack flex={1} paddingHorizontal={20} paddingTop={20}>
|
||||
<YStack
|
||||
flex={1}
|
||||
justifyContent="center"
|
||||
{/* Header */}
|
||||
<YStack backgroundColor={slate100}>
|
||||
<NavBar.Container
|
||||
backgroundColor={slate100}
|
||||
barStyle="dark"
|
||||
paddingHorizontal="$4"
|
||||
paddingTop={insets.top + extraYPadding}
|
||||
paddingBottom={10}
|
||||
alignItems="center"
|
||||
paddingVertical={20}
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<WarningIcon width={120} height={120} />
|
||||
<NavBar.LeftAction
|
||||
component="close"
|
||||
color={black}
|
||||
onPress={handleClose}
|
||||
/>
|
||||
<NavBar.Title style={{ fontFamily: dinot, fontSize: 17 }}>
|
||||
AADHAAR REGISTRATION
|
||||
</NavBar.Title>
|
||||
{/* Invisible spacer to balance header */}
|
||||
<YStack width={30} height={30} />
|
||||
</NavBar.Container>
|
||||
|
||||
{/* Progress Bar - Step 2 for Aadhaar upload */}
|
||||
<YStack paddingHorizontal={40} paddingBottom={14} paddingTop={4}>
|
||||
<XStack gap={3} height={6}>
|
||||
{[1, 2, 3, 4].map(step => (
|
||||
<YStack
|
||||
key={step}
|
||||
flex={1}
|
||||
backgroundColor={step === 2 ? cyan300 : slate300}
|
||||
borderRadius={10}
|
||||
/>
|
||||
))}
|
||||
</XStack>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<YStack
|
||||
flex={1}
|
||||
backgroundColor={slate100}
|
||||
borderBottomWidth={1}
|
||||
borderBottomColor={slate200}
|
||||
>
|
||||
{/* Warning Icon */}
|
||||
<YStack flex={1} paddingHorizontal={20} paddingBottom={20}>
|
||||
<YStack flex={1} justifyContent="center" alignItems="center">
|
||||
<WarningIcon width={150} height={150} />
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Error Message and Retry Button */}
|
||||
<YStack
|
||||
paddingHorizontal={20}
|
||||
paddingTop={20}
|
||||
paddingBottom={20}
|
||||
gap={20}
|
||||
borderTopWidth={1}
|
||||
borderTopColor={slate200}
|
||||
>
|
||||
<YStack alignItems="center" gap={4}>
|
||||
<BodyText
|
||||
style={{ fontSize: 18, textAlign: 'center', color: black }}
|
||||
>
|
||||
{title}
|
||||
</BodyText>
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 16,
|
||||
textAlign: 'center',
|
||||
color: slate500,
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
</BodyText>
|
||||
</YStack>
|
||||
|
||||
{/* Retry Button - Primary style with icon */}
|
||||
<Button
|
||||
backgroundColor={black}
|
||||
borderRadius={100}
|
||||
height={52}
|
||||
pressStyle={{ opacity: 0.8 }}
|
||||
onPress={handleTryAgain}
|
||||
disabled={isRetrying}
|
||||
>
|
||||
<XStack alignItems="center" gap={8}>
|
||||
<Image size={20} color={white} />
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
fontFamily: dinot,
|
||||
color: white,
|
||||
}}
|
||||
>
|
||||
Try upload again
|
||||
</BodyText>
|
||||
</XStack>
|
||||
</Button>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Bottom Section */}
|
||||
<YStack
|
||||
paddingHorizontal={20}
|
||||
paddingTop={20}
|
||||
alignItems="center"
|
||||
paddingVertical={25}
|
||||
borderBlockWidth={1}
|
||||
borderBlockColor={slate200}
|
||||
>
|
||||
<BodyText style={{ fontSize: 19, textAlign: 'center', color: black }}>
|
||||
{title}
|
||||
</BodyText>
|
||||
<BodyText
|
||||
style={{
|
||||
marginTop: 6,
|
||||
fontSize: 17,
|
||||
textAlign: 'center',
|
||||
color: slate500,
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
</BodyText>
|
||||
</YStack>
|
||||
|
||||
<YStack
|
||||
paddingHorizontal={25}
|
||||
backgroundColor={white}
|
||||
paddingBottom={paddingBottom}
|
||||
paddingTop={25}
|
||||
gap={10}
|
||||
>
|
||||
<XStack gap="$3" alignItems="stretch">
|
||||
<YStack flex={1}>
|
||||
<PrimaryButton
|
||||
onPress={() => {
|
||||
trackEvent(AadhaarEvents.RETRY_BUTTON_PRESSED, { errorType });
|
||||
// Navigate back to upload screen to try again
|
||||
navigation.goBack();
|
||||
{kycEnabled && (
|
||||
<>
|
||||
{/* Secondary Button - White fill, black text, rounded */}
|
||||
<Button
|
||||
backgroundColor={white}
|
||||
borderWidth={1}
|
||||
borderColor={slate200}
|
||||
borderRadius={100}
|
||||
height={52}
|
||||
pressStyle={{ opacity: 0.8 }}
|
||||
onPress={handleTryAlternative}
|
||||
disabled={isRetrying}
|
||||
>
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
fontFamily: dinot,
|
||||
color: black,
|
||||
}}
|
||||
>
|
||||
{isRetrying ? 'Loading...' : 'Try a different method'}
|
||||
</BodyText>
|
||||
</Button>
|
||||
|
||||
{/* Footer Text - Not italic */}
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 16,
|
||||
textAlign: 'center',
|
||||
color: slate500,
|
||||
}}
|
||||
>
|
||||
Try Again
|
||||
</PrimaryButton>
|
||||
</YStack>
|
||||
{/* <YStack flex={1}>
|
||||
<SecondaryButton
|
||||
onPress={() => {
|
||||
trackEvent(AadhaarEvents.HELP_BUTTON_PRESSED, { errorType });
|
||||
// TODO: Implement help functionality
|
||||
}}
|
||||
>
|
||||
Need Help?
|
||||
</SecondaryButton>
|
||||
</YStack> */}
|
||||
</XStack>
|
||||
Registering with alternative methods may take longer to verify
|
||||
your document.
|
||||
</BodyText>
|
||||
</>
|
||||
)}
|
||||
</YStack>
|
||||
</YStack>
|
||||
);
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { View, XStack, YStack } from 'tamagui';
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
import { useIsFocused, useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import {
|
||||
DelayedLottieView,
|
||||
@@ -33,21 +34,45 @@ import {
|
||||
import passportScanAnimation from '@/assets/animations/passport_scan.json';
|
||||
import Scan from '@/assets/icons/passport_camera_scan.svg';
|
||||
import { PassportCamera } from '@/components/native/PassportCamera';
|
||||
import { useErrorInjection } from '@/hooks/useErrorInjection';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { getDocumentScanPrompt } from '@/utils/documentAttributes';
|
||||
|
||||
const DocumentCameraScreen: React.FC = () => {
|
||||
const isFocused = useIsFocused();
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const selfClient = useSelfClient();
|
||||
const selectedDocumentType = selfClient.useMRZStore(
|
||||
state => state.documentType,
|
||||
);
|
||||
const countryCode = selfClient.useMRZStore(state => state.countryCode);
|
||||
const { shouldInjectError } = useErrorInjection();
|
||||
|
||||
// Add a ref to track when the camera screen is mounted
|
||||
const scanStartTimeRef = useRef(Date.now());
|
||||
const { onPassportRead } = useReadMRZ(scanStartTimeRef);
|
||||
|
||||
// Dev-only: Auto-trigger MRZ error after short delay if error injection is enabled
|
||||
useEffect(() => {
|
||||
if (
|
||||
shouldInjectError('mrz_invalid_format') ||
|
||||
shouldInjectError('mrz_unknown_error')
|
||||
) {
|
||||
const timer = setTimeout(() => {
|
||||
console.log(
|
||||
'[DEV] Injecting MRZ error - navigating to fallback screen',
|
||||
);
|
||||
navigation.navigate('RegistrationFallbackMRZ', {
|
||||
countryCode: countryCode || '',
|
||||
});
|
||||
}, 1500); // 1.5 second delay to show camera briefly
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [shouldInjectError, navigation, countryCode]);
|
||||
|
||||
const scanPrompt = getDocumentScanPrompt(selectedDocumentType);
|
||||
|
||||
const navigateToHome = useHapticNavigation('Home', {
|
||||
|
||||
@@ -58,7 +58,7 @@ const DocumentCameraTroubleScreen: React.FC = () => {
|
||||
const kycEnabled = useSettingStore(state => state.kycEnabled);
|
||||
const { launchSumsubVerification, isLoading } = useSumsubLauncher({
|
||||
countryCode,
|
||||
errorSource: 'sumsub_initialization',
|
||||
errorSource: 'mrz_scan_failed',
|
||||
});
|
||||
|
||||
// error screen, flush analytics
|
||||
|
||||
@@ -191,8 +191,7 @@ const DocumentNFCScanScreen: React.FC = () => {
|
||||
},
|
||||
{ message: sanitizeErrorMessage(message) },
|
||||
);
|
||||
navigation.navigate('RegistrationFallback', {
|
||||
errorSource: 'nfc_scan_failed',
|
||||
navigation.navigate('RegistrationFallbackNFC', {
|
||||
countryCode,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
||||
import { YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { Caption, SecondaryButton } from '@selfxyz/mobile-sdk-alpha/components';
|
||||
@@ -16,6 +17,7 @@ import Tips from '@/components/Tips';
|
||||
import { useFeedbackAutoHide } from '@/hooks/useFeedbackAutoHide';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import { useSumsubLauncher } from '@/hooks/useSumsubLauncher';
|
||||
import { selectionChange } from '@/integrations/haptics';
|
||||
import SimpleScrolledTitleLayout from '@/layouts/SimpleScrolledTitleLayout';
|
||||
import { flushAllAnalytics } from '@/services/analytics';
|
||||
import { openSupportForm, SUPPORT_FORM_BUTTON_TEXT } from '@/services/support';
|
||||
@@ -49,7 +51,11 @@ const tips: TipProps[] = [
|
||||
];
|
||||
|
||||
const DocumentNFCTroubleScreen: React.FC = () => {
|
||||
const go = useHapticNavigation('DocumentNFCScan', { action: 'cancel' });
|
||||
const navigation = useNavigation();
|
||||
const handleDismiss = useCallback(() => {
|
||||
selectionChange();
|
||||
navigation.goBack();
|
||||
}, [navigation]);
|
||||
const goToNFCMethodSelection = useHapticNavigation(
|
||||
'DocumentNFCMethodSelection',
|
||||
);
|
||||
@@ -59,7 +65,7 @@ const DocumentNFCTroubleScreen: React.FC = () => {
|
||||
const kycEnabled = useSettingStore(state => state.kycEnabled);
|
||||
const { launchSumsubVerification, isLoading } = useSumsubLauncher({
|
||||
countryCode,
|
||||
errorSource: 'sumsub_initialization',
|
||||
errorSource: 'nfc_scan_failed',
|
||||
});
|
||||
useFeedbackAutoHide();
|
||||
|
||||
@@ -78,7 +84,7 @@ const DocumentNFCTroubleScreen: React.FC = () => {
|
||||
return (
|
||||
<SimpleScrolledTitleLayout
|
||||
title="Having trouble verifying your ID?"
|
||||
onDismiss={go}
|
||||
onDismiss={handleDismiss}
|
||||
secondaryButtonText="Open NFC Options"
|
||||
onSecondaryButtonPress={goToNFCMethodSelection}
|
||||
footer={
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
// 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 React, { useCallback } from 'react';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Button, XStack, YStack } from 'tamagui';
|
||||
import type { RouteProp } from '@react-navigation/native';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { BodyText } from '@selfxyz/mobile-sdk-alpha/components';
|
||||
import {
|
||||
black,
|
||||
cyan300,
|
||||
slate100,
|
||||
slate200,
|
||||
slate300,
|
||||
slate500,
|
||||
white,
|
||||
} from '@selfxyz/mobile-sdk-alpha/constants/colors';
|
||||
import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts';
|
||||
import { useSafeBottomPadding } from '@selfxyz/mobile-sdk-alpha/hooks';
|
||||
|
||||
import WarningIcon from '@/assets/images/warning.svg';
|
||||
import { NavBar } from '@/components/navbar/BaseNavBar';
|
||||
import { useSumsubLauncher } from '@/hooks/useSumsubLauncher';
|
||||
import { buttonTap } from '@/integrations/haptics';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { useSettingStore } from '@/stores/settingStore';
|
||||
import { extraYPadding } from '@/utils/styleUtils';
|
||||
|
||||
type RegistrationFallbackMRZRouteParams = {
|
||||
countryCode: string;
|
||||
};
|
||||
|
||||
type RegistrationFallbackMRZRoute = RouteProp<
|
||||
Record<string, RegistrationFallbackMRZRouteParams>,
|
||||
string
|
||||
>;
|
||||
|
||||
const getHeaderTitle = (documentType: string): string => {
|
||||
switch (documentType) {
|
||||
case 'p':
|
||||
return 'PASSPORT REGISTRATION';
|
||||
case 'i':
|
||||
return 'ID CARD REGISTRATION';
|
||||
default:
|
||||
return 'DOCUMENT REGISTRATION';
|
||||
}
|
||||
};
|
||||
|
||||
const RegistrationFallbackMRZScreen: React.FC = () => {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = useSafeBottomPadding(extraYPadding + 35);
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const route = useRoute<RegistrationFallbackMRZRoute>();
|
||||
const selfClient = useSelfClient();
|
||||
const { trackEvent, useMRZStore } = selfClient;
|
||||
const storeCountryCode = useMRZStore(state => state.countryCode);
|
||||
const documentType = useMRZStore(state => state.documentType);
|
||||
const kycEnabled = useSettingStore(state => state.kycEnabled);
|
||||
|
||||
// Use country code from route params, or fall back to MRZ store
|
||||
const countryCode = route.params?.countryCode || storeCountryCode || '';
|
||||
|
||||
const headerTitle = getHeaderTitle(documentType);
|
||||
|
||||
const { launchSumsubVerification, isLoading: isRetrying } = useSumsubLauncher(
|
||||
{
|
||||
countryCode,
|
||||
errorSource: 'mrz_scan_failed',
|
||||
onCancel: () => {
|
||||
navigation.goBack();
|
||||
},
|
||||
onError: (_error, _result) => {
|
||||
// Stay on this screen - user can try again
|
||||
// Error is already logged in the hook
|
||||
},
|
||||
onSuccess: () => {
|
||||
// Success - provider handles its own success UI
|
||||
// The screen will be navigated away by the provider's flow
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
buttonTap();
|
||||
navigation.goBack();
|
||||
}, [navigation]);
|
||||
|
||||
const handleTryAlternative = useCallback(async () => {
|
||||
trackEvent('REGISTRATION_FALLBACK_TRY_ALTERNATIVE', {
|
||||
errorSource: 'mrz_scan_failed',
|
||||
});
|
||||
await launchSumsubVerification();
|
||||
}, [launchSumsubVerification, trackEvent]);
|
||||
|
||||
const handleRetryOriginal = useCallback(() => {
|
||||
trackEvent('REGISTRATION_FALLBACK_RETRY_ORIGINAL', {
|
||||
errorSource: 'mrz_scan_failed',
|
||||
});
|
||||
navigation.navigate('DocumentCamera');
|
||||
}, [navigation, trackEvent]);
|
||||
|
||||
return (
|
||||
<YStack flex={1} backgroundColor={slate100}>
|
||||
{/* Header */}
|
||||
<YStack backgroundColor={slate100}>
|
||||
<NavBar.Container
|
||||
backgroundColor={slate100}
|
||||
barStyle="dark"
|
||||
paddingHorizontal="$4"
|
||||
paddingTop={insets.top + extraYPadding}
|
||||
paddingBottom={10}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<NavBar.LeftAction
|
||||
component="close"
|
||||
color={black}
|
||||
onPress={handleClose}
|
||||
/>
|
||||
<NavBar.Title style={{ fontFamily: dinot, fontSize: 17 }}>
|
||||
{headerTitle}
|
||||
</NavBar.Title>
|
||||
{/* Invisible spacer to balance header */}
|
||||
<YStack width={30} height={30} />
|
||||
</NavBar.Container>
|
||||
|
||||
{/* Progress Bar - Step 2 for MRZ */}
|
||||
<YStack paddingHorizontal={40} paddingBottom={14} paddingTop={4}>
|
||||
<XStack gap={3} height={6}>
|
||||
{[1, 2, 3, 4].map(step => (
|
||||
<YStack
|
||||
key={step}
|
||||
flex={1}
|
||||
backgroundColor={step === 2 ? cyan300 : slate300}
|
||||
borderRadius={10}
|
||||
/>
|
||||
))}
|
||||
</XStack>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<YStack
|
||||
flex={1}
|
||||
backgroundColor={slate100}
|
||||
borderBottomWidth={1}
|
||||
borderBottomColor={slate200}
|
||||
>
|
||||
{/* Warning Icon */}
|
||||
<YStack flex={1} paddingHorizontal={20} paddingBottom={20}>
|
||||
<YStack flex={1} justifyContent="center" alignItems="center">
|
||||
<WarningIcon width={150} height={150} />
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Error Message and Retry Button */}
|
||||
<YStack
|
||||
paddingHorizontal={20}
|
||||
paddingTop={20}
|
||||
paddingBottom={20}
|
||||
gap={20}
|
||||
borderTopWidth={1}
|
||||
borderTopColor={slate200}
|
||||
>
|
||||
<YStack alignItems="center" gap={4}>
|
||||
<BodyText
|
||||
style={{ fontSize: 18, textAlign: 'center', color: black }}
|
||||
>
|
||||
We couldn't read your document's MRZ
|
||||
</BodyText>
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 16,
|
||||
textAlign: 'center',
|
||||
color: slate500,
|
||||
}}
|
||||
>
|
||||
Make sure the machine-readable zone at the bottom is clearly
|
||||
visible and try again
|
||||
</BodyText>
|
||||
</YStack>
|
||||
|
||||
{/* Retry Button - Primary style with very rounded corners */}
|
||||
<Button
|
||||
backgroundColor={black}
|
||||
borderRadius={100}
|
||||
height={52}
|
||||
pressStyle={{ opacity: 0.8 }}
|
||||
onPress={handleRetryOriginal}
|
||||
disabled={isRetrying}
|
||||
>
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
fontFamily: dinot,
|
||||
color: white,
|
||||
}}
|
||||
>
|
||||
Try scanning again
|
||||
</BodyText>
|
||||
</Button>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Bottom Section */}
|
||||
<YStack
|
||||
paddingHorizontal={20}
|
||||
paddingTop={20}
|
||||
paddingBottom={paddingBottom}
|
||||
gap={10}
|
||||
>
|
||||
{kycEnabled && (
|
||||
<>
|
||||
{/* Secondary Button - White fill, black text, rounded */}
|
||||
<Button
|
||||
backgroundColor={white}
|
||||
borderWidth={1}
|
||||
borderColor={slate200}
|
||||
borderRadius={100}
|
||||
height={52}
|
||||
pressStyle={{ opacity: 0.8 }}
|
||||
onPress={handleTryAlternative}
|
||||
disabled={isRetrying}
|
||||
>
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
fontFamily: dinot,
|
||||
color: black,
|
||||
}}
|
||||
>
|
||||
{isRetrying ? 'Loading...' : 'Try a different method'}
|
||||
</BodyText>
|
||||
</Button>
|
||||
|
||||
{/* Footer Text - Not italic */}
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 16,
|
||||
textAlign: 'center',
|
||||
color: slate500,
|
||||
}}
|
||||
>
|
||||
Registering with alternative methods may take longer to verify
|
||||
your document.
|
||||
</BodyText>
|
||||
</>
|
||||
)}
|
||||
</YStack>
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegistrationFallbackMRZScreen;
|
||||
@@ -0,0 +1,288 @@
|
||||
// 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 React, { useCallback } from 'react';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Button, XStack, YStack } from 'tamagui';
|
||||
import type { RouteProp } from '@react-navigation/native';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import { BodyText } from '@selfxyz/mobile-sdk-alpha/components';
|
||||
import {
|
||||
black,
|
||||
blue600,
|
||||
cyan300,
|
||||
slate100,
|
||||
slate200,
|
||||
slate300,
|
||||
slate500,
|
||||
white,
|
||||
} from '@selfxyz/mobile-sdk-alpha/constants/colors';
|
||||
import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts';
|
||||
import { useSafeBottomPadding } from '@selfxyz/mobile-sdk-alpha/hooks';
|
||||
|
||||
import WarningIcon from '@/assets/images/warning.svg';
|
||||
import { NavBar } from '@/components/navbar/BaseNavBar';
|
||||
import { useSumsubLauncher } from '@/hooks/useSumsubLauncher';
|
||||
import { buttonTap } from '@/integrations/haptics';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { useSettingStore } from '@/stores/settingStore';
|
||||
import { extraYPadding } from '@/utils/styleUtils';
|
||||
|
||||
type RegistrationFallbackNFCRouteParams = {
|
||||
countryCode: string;
|
||||
};
|
||||
|
||||
type RegistrationFallbackNFCRoute = RouteProp<
|
||||
Record<string, RegistrationFallbackNFCRouteParams>,
|
||||
string
|
||||
>;
|
||||
|
||||
const getHeaderTitle = (documentType: string): string => {
|
||||
switch (documentType) {
|
||||
case 'p':
|
||||
return 'PASSPORT REGISTRATION';
|
||||
case 'i':
|
||||
return 'ID CARD REGISTRATION';
|
||||
default:
|
||||
return 'DOCUMENT REGISTRATION';
|
||||
}
|
||||
};
|
||||
|
||||
const RegistrationFallbackNFCScreen: React.FC = () => {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = useSafeBottomPadding(extraYPadding + 35);
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const route = useRoute<RegistrationFallbackNFCRoute>();
|
||||
const selfClient = useSelfClient();
|
||||
const { trackEvent, useMRZStore } = selfClient;
|
||||
const storeCountryCode = useMRZStore(state => state.countryCode);
|
||||
const documentType = useMRZStore(state => state.documentType);
|
||||
const kycEnabled = useSettingStore(state => state.kycEnabled);
|
||||
|
||||
// Use country code from route params, or fall back to MRZ store
|
||||
const countryCode = route.params?.countryCode || storeCountryCode || '';
|
||||
|
||||
const headerTitle = getHeaderTitle(documentType);
|
||||
|
||||
const { launchSumsubVerification, isLoading: isRetrying } = useSumsubLauncher(
|
||||
{
|
||||
countryCode,
|
||||
errorSource: 'nfc_scan_failed',
|
||||
onCancel: () => {
|
||||
navigation.goBack();
|
||||
},
|
||||
onError: (_error, _result) => {
|
||||
// Stay on this screen - user can try again
|
||||
// Error is already logged in the hook
|
||||
},
|
||||
onSuccess: () => {
|
||||
// Success - provider handles its own success UI
|
||||
// The screen will be navigated away by the provider's flow
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
buttonTap();
|
||||
navigation.goBack();
|
||||
}, [navigation]);
|
||||
|
||||
const handleHelp = useCallback(() => {
|
||||
buttonTap();
|
||||
navigation.navigate('DocumentNFCTrouble');
|
||||
}, [navigation]);
|
||||
|
||||
const handleTryAlternative = useCallback(async () => {
|
||||
trackEvent('REGISTRATION_FALLBACK_TRY_ALTERNATIVE', {
|
||||
errorSource: 'nfc_scan_failed',
|
||||
});
|
||||
await launchSumsubVerification();
|
||||
}, [launchSumsubVerification, trackEvent]);
|
||||
|
||||
const handleRetryOriginal = useCallback(() => {
|
||||
trackEvent('REGISTRATION_FALLBACK_RETRY_ORIGINAL', {
|
||||
errorSource: 'nfc_scan_failed',
|
||||
});
|
||||
navigation.navigate('DocumentNFCScan', {});
|
||||
}, [navigation, trackEvent]);
|
||||
|
||||
return (
|
||||
<YStack flex={1} backgroundColor={slate100}>
|
||||
{/* Header */}
|
||||
<YStack backgroundColor={slate100}>
|
||||
<NavBar.Container
|
||||
backgroundColor={slate100}
|
||||
barStyle="dark"
|
||||
paddingHorizontal="$4"
|
||||
paddingTop={insets.top + extraYPadding}
|
||||
paddingBottom={10}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<NavBar.LeftAction
|
||||
component="close"
|
||||
color={black}
|
||||
onPress={handleClose}
|
||||
/>
|
||||
<NavBar.Title style={{ fontFamily: dinot, fontSize: 17 }}>
|
||||
{headerTitle}
|
||||
</NavBar.Title>
|
||||
<Button unstyled onPress={handleHelp} aria-label="Help" hitSlop={8}>
|
||||
<YStack
|
||||
width={26}
|
||||
height={26}
|
||||
borderRadius={13}
|
||||
backgroundColor={blue600}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<BodyText
|
||||
style={{
|
||||
color: white,
|
||||
fontSize: 16,
|
||||
fontWeight: '900',
|
||||
lineHeight: 18,
|
||||
textAlign: 'center',
|
||||
includeFontPadding: false,
|
||||
}}
|
||||
>
|
||||
?
|
||||
</BodyText>
|
||||
</YStack>
|
||||
</Button>
|
||||
</NavBar.Container>
|
||||
|
||||
{/* Progress Bar - Step 3 for NFC */}
|
||||
<YStack paddingHorizontal={40} paddingBottom={14} paddingTop={4}>
|
||||
<XStack gap={3} height={6}>
|
||||
{[1, 2, 3, 4].map(step => (
|
||||
<YStack
|
||||
key={step}
|
||||
flex={1}
|
||||
backgroundColor={step === 3 ? cyan300 : slate300}
|
||||
borderRadius={10}
|
||||
/>
|
||||
))}
|
||||
</XStack>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<YStack
|
||||
flex={1}
|
||||
backgroundColor={slate100}
|
||||
borderBottomWidth={1}
|
||||
borderBottomColor={slate200}
|
||||
>
|
||||
{/* Warning Icon */}
|
||||
<YStack flex={1} paddingHorizontal={20} paddingBottom={20}>
|
||||
<YStack flex={1} justifyContent="center" alignItems="center">
|
||||
<WarningIcon width={150} height={150} />
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Error Message and Retry Button */}
|
||||
<YStack
|
||||
paddingHorizontal={20}
|
||||
paddingTop={20}
|
||||
paddingBottom={20}
|
||||
gap={20}
|
||||
borderTopWidth={1}
|
||||
borderTopColor={slate200}
|
||||
>
|
||||
<YStack alignItems="center" gap={4}>
|
||||
<BodyText
|
||||
style={{ fontSize: 18, textAlign: 'center', color: black }}
|
||||
>
|
||||
There was a problem reading the chip
|
||||
</BodyText>
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 16,
|
||||
textAlign: 'center',
|
||||
color: slate500,
|
||||
}}
|
||||
>
|
||||
Make sure NFC is enabled and try again
|
||||
</BodyText>
|
||||
</YStack>
|
||||
|
||||
{/* Retry Button - Primary style with very rounded corners */}
|
||||
<Button
|
||||
backgroundColor={black}
|
||||
borderRadius={100}
|
||||
height={52}
|
||||
pressStyle={{ opacity: 0.8 }}
|
||||
onPress={handleRetryOriginal}
|
||||
disabled={isRetrying}
|
||||
>
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
fontFamily: dinot,
|
||||
color: white,
|
||||
}}
|
||||
>
|
||||
Try reading again
|
||||
</BodyText>
|
||||
</Button>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Bottom Section */}
|
||||
<YStack
|
||||
paddingHorizontal={20}
|
||||
paddingTop={20}
|
||||
paddingBottom={paddingBottom}
|
||||
gap={10}
|
||||
>
|
||||
{kycEnabled && (
|
||||
<>
|
||||
{/* Secondary Button - White fill, black text, rounded */}
|
||||
<Button
|
||||
backgroundColor={white}
|
||||
borderWidth={1}
|
||||
borderColor={slate200}
|
||||
borderRadius={100}
|
||||
height={52}
|
||||
pressStyle={{ opacity: 0.8 }}
|
||||
onPress={handleTryAlternative}
|
||||
disabled={isRetrying}
|
||||
>
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
fontFamily: dinot,
|
||||
color: black,
|
||||
}}
|
||||
>
|
||||
{isRetrying ? 'Loading...' : 'Try a different method'}
|
||||
</BodyText>
|
||||
</Button>
|
||||
|
||||
{/* Footer Text - Not italic */}
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 16,
|
||||
textAlign: 'center',
|
||||
color: slate500,
|
||||
}}
|
||||
>
|
||||
Registering with alternative methods may take longer to verify
|
||||
your document.
|
||||
</BodyText>
|
||||
</>
|
||||
)}
|
||||
</YStack>
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegistrationFallbackNFCScreen;
|
||||
@@ -1,326 +0,0 @@
|
||||
// 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 React, { useCallback } from 'react';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Button, XStack, YStack } from 'tamagui';
|
||||
import type { RouteProp } from '@react-navigation/native';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { HelpCircle, X } from '@tamagui/lucide-icons';
|
||||
|
||||
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import {
|
||||
BodyText,
|
||||
PrimaryButton,
|
||||
SecondaryButton,
|
||||
} from '@selfxyz/mobile-sdk-alpha/components';
|
||||
import {
|
||||
black,
|
||||
cyan300,
|
||||
slate100,
|
||||
slate200,
|
||||
slate300,
|
||||
slate500,
|
||||
white,
|
||||
} from '@selfxyz/mobile-sdk-alpha/constants/colors';
|
||||
import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts';
|
||||
import { useSafeBottomPadding } from '@selfxyz/mobile-sdk-alpha/hooks';
|
||||
|
||||
import WarningIcon from '@/assets/images/warning.svg';
|
||||
import { useSumsubLauncher } from '@/hooks/useSumsubLauncher';
|
||||
import { buttonTap } from '@/integrations/haptics';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { extraYPadding } from '@/utils/styleUtils';
|
||||
|
||||
type FallbackErrorSource =
|
||||
| 'mrz_scan_failed'
|
||||
| 'nfc_scan_failed'
|
||||
| 'sumsub_initialization'
|
||||
| 'sumsub_verification';
|
||||
|
||||
type RegistrationFallbackRouteParams = {
|
||||
errorSource: FallbackErrorSource;
|
||||
countryCode: string;
|
||||
};
|
||||
|
||||
type RegistrationFallbackRoute = RouteProp<
|
||||
Record<string, RegistrationFallbackRouteParams>,
|
||||
string
|
||||
>;
|
||||
|
||||
const getHeaderTitle = (errorSource: FallbackErrorSource): string => {
|
||||
switch (errorSource) {
|
||||
case 'mrz_scan_failed':
|
||||
return 'MRZ SCAN';
|
||||
case 'nfc_scan_failed':
|
||||
return 'NFC SCAN';
|
||||
default:
|
||||
return 'REGISTRATION';
|
||||
}
|
||||
};
|
||||
|
||||
const getCurrentStep = (errorSource: FallbackErrorSource): number => {
|
||||
switch (errorSource) {
|
||||
case 'mrz_scan_failed':
|
||||
return 1; // Step 1: MRZ scanning
|
||||
case 'nfc_scan_failed':
|
||||
return 2; // Step 2: NFC reading
|
||||
case 'sumsub_initialization':
|
||||
case 'sumsub_verification':
|
||||
return 3; // Step 3: Proving/verification
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
const getRetryButtonText = (errorSource: FallbackErrorSource): string => {
|
||||
switch (errorSource) {
|
||||
case 'mrz_scan_failed':
|
||||
return 'Try scanning again';
|
||||
case 'nfc_scan_failed':
|
||||
return 'Try reading again';
|
||||
default:
|
||||
return 'Try again';
|
||||
}
|
||||
};
|
||||
|
||||
const getErrorMessages = (
|
||||
errorSource: FallbackErrorSource,
|
||||
): { title: string; description: string; canRetryOriginal: boolean } => {
|
||||
switch (errorSource) {
|
||||
case 'mrz_scan_failed':
|
||||
return {
|
||||
title: 'There was a problem scanning your document',
|
||||
description: 'Make sure the document is clearly visible and try again',
|
||||
canRetryOriginal: true,
|
||||
};
|
||||
case 'nfc_scan_failed':
|
||||
return {
|
||||
title: 'There was a problem reading the chip',
|
||||
description: 'Make sure NFC is enabled and try again',
|
||||
canRetryOriginal: true,
|
||||
};
|
||||
case 'sumsub_initialization':
|
||||
return {
|
||||
title: 'Connection Error',
|
||||
description:
|
||||
'Unable to connect to verification service. Please check your internet connection and try again.',
|
||||
canRetryOriginal: false,
|
||||
};
|
||||
case 'sumsub_verification':
|
||||
return {
|
||||
title: 'Verification Error',
|
||||
description:
|
||||
'Something went wrong during the verification process. Please try again.',
|
||||
canRetryOriginal: false,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const RegistrationFallbackScreen: React.FC = () => {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = useSafeBottomPadding(extraYPadding + 35);
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const route = useRoute<RegistrationFallbackRoute>();
|
||||
const selfClient = useSelfClient();
|
||||
const { trackEvent, useMRZStore } = selfClient;
|
||||
const storeCountryCode = useMRZStore(state => state.countryCode);
|
||||
|
||||
const errorSource = route.params?.errorSource || 'sumsub_initialization';
|
||||
// Use country code from route params, or fall back to MRZ store
|
||||
const countryCode = route.params?.countryCode || storeCountryCode || '';
|
||||
|
||||
const headerTitle = getHeaderTitle(errorSource);
|
||||
const retryButtonText = getRetryButtonText(errorSource);
|
||||
const currentStep = getCurrentStep(errorSource);
|
||||
const { title, description, canRetryOriginal } =
|
||||
getErrorMessages(errorSource);
|
||||
|
||||
const { launchSumsubVerification, isLoading: isRetrying } = useSumsubLauncher(
|
||||
{
|
||||
countryCode,
|
||||
errorSource,
|
||||
onCancel: () => {
|
||||
navigation.goBack();
|
||||
},
|
||||
onError: (_error, _result) => {
|
||||
// Stay on this screen - user can try again
|
||||
// Error is already logged in the hook
|
||||
},
|
||||
onSuccess: () => {
|
||||
// Success - provider handles its own success UI
|
||||
// The screen will be navigated away by the provider's flow
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
buttonTap();
|
||||
navigation.goBack();
|
||||
}, [navigation]);
|
||||
|
||||
const handleTryAlternative = useCallback(async () => {
|
||||
trackEvent('REGISTRATION_FALLBACK_TRY_ALTERNATIVE', { errorSource });
|
||||
await launchSumsubVerification();
|
||||
}, [errorSource, launchSumsubVerification, trackEvent]);
|
||||
|
||||
const handleRetryOriginal = useCallback(() => {
|
||||
trackEvent('REGISTRATION_FALLBACK_RETRY_ORIGINAL', { errorSource });
|
||||
|
||||
// Navigate back to the appropriate screen based on error source
|
||||
if (errorSource === 'mrz_scan_failed') {
|
||||
navigation.navigate('DocumentCamera');
|
||||
} else if (errorSource === 'nfc_scan_failed') {
|
||||
navigation.navigate('DocumentNFCScan', {});
|
||||
} else if (errorSource === 'sumsub_initialization') {
|
||||
// Go back to ID Picker
|
||||
navigation.goBack();
|
||||
}
|
||||
// TODO: Handle 'sumsub_verification' case - currently falls through without action
|
||||
// which could leave users stuck when tapping "Try again" after Sumsub verification failure.
|
||||
// Consider: calling launchSumsubVerification() or navigating to appropriate retry screen.
|
||||
// Need to determine the correct retry behavior for failed Sumsub verifications.
|
||||
}, [errorSource, navigation, trackEvent]);
|
||||
|
||||
return (
|
||||
<YStack flex={1} backgroundColor={slate100}>
|
||||
{/* Header */}
|
||||
<YStack backgroundColor={slate100}>
|
||||
<XStack
|
||||
backgroundColor={slate100}
|
||||
padding={20}
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
paddingTop={Math.max(insets.top, 15) + extraYPadding}
|
||||
paddingBottom={10}
|
||||
>
|
||||
<Button
|
||||
unstyled
|
||||
onPress={handleClose}
|
||||
padding={8}
|
||||
borderRadius={20}
|
||||
hitSlop={10}
|
||||
>
|
||||
<X size={24} color={black} />
|
||||
</Button>
|
||||
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: black,
|
||||
fontWeight: '600',
|
||||
fontFamily: dinot,
|
||||
}}
|
||||
>
|
||||
{headerTitle}
|
||||
</BodyText>
|
||||
|
||||
<Button
|
||||
unstyled
|
||||
padding={8}
|
||||
borderRadius={20}
|
||||
hitSlop={10}
|
||||
width={32}
|
||||
height={32}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
disabled
|
||||
>
|
||||
<HelpCircle size={20} color={black} opacity={0} />
|
||||
</Button>
|
||||
</XStack>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<YStack paddingHorizontal={40} paddingBottom={10}>
|
||||
<XStack gap={3} height={6}>
|
||||
{[1, 2, 3, 4].map(step => (
|
||||
<YStack
|
||||
key={step}
|
||||
flex={1}
|
||||
backgroundColor={step === currentStep ? cyan300 : slate300}
|
||||
borderRadius={10}
|
||||
/>
|
||||
))}
|
||||
</XStack>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Warning Icon */}
|
||||
<YStack flex={1} paddingHorizontal={20} paddingTop={20}>
|
||||
<YStack
|
||||
flex={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
paddingVertical={20}
|
||||
>
|
||||
<WarningIcon width={120} height={120} />
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Error Message */}
|
||||
<YStack
|
||||
paddingHorizontal={20}
|
||||
paddingTop={20}
|
||||
alignItems="center"
|
||||
paddingVertical={25}
|
||||
>
|
||||
<BodyText style={{ fontSize: 19, textAlign: 'center', color: black }}>
|
||||
{title}
|
||||
</BodyText>
|
||||
<BodyText
|
||||
style={{
|
||||
marginTop: 6,
|
||||
fontSize: 17,
|
||||
textAlign: 'center',
|
||||
color: slate500,
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
</BodyText>
|
||||
</YStack>
|
||||
|
||||
{/* Top Button - Retry */}
|
||||
{canRetryOriginal && (
|
||||
<YStack paddingHorizontal={25} paddingBottom={20}>
|
||||
<PrimaryButton onPress={handleRetryOriginal} disabled={isRetrying}>
|
||||
{retryButtonText}
|
||||
</PrimaryButton>
|
||||
</YStack>
|
||||
)}
|
||||
|
||||
{/* Bottom Section with Grey Line Separator */}
|
||||
<YStack
|
||||
paddingHorizontal={25}
|
||||
backgroundColor={white}
|
||||
paddingBottom={paddingBottom}
|
||||
paddingTop={25}
|
||||
gap="$3"
|
||||
borderTopWidth={1}
|
||||
borderTopColor={slate200}
|
||||
>
|
||||
<SecondaryButton onPress={handleTryAlternative} disabled={isRetrying}>
|
||||
{isRetrying ? 'Loading...' : 'Try a different method'}
|
||||
</SecondaryButton>
|
||||
|
||||
{/* Footer Text */}
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 15,
|
||||
textAlign: 'center',
|
||||
color: slate500,
|
||||
fontStyle: 'italic',
|
||||
marginTop: 8,
|
||||
}}
|
||||
>
|
||||
Registering with alternative methods may take longer to verify your
|
||||
document.
|
||||
</BodyText>
|
||||
</YStack>
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegistrationFallbackScreen;
|
||||
156
app/src/screens/documents/selection/LogoConfirmationScreen.tsx
Normal file
156
app/src/screens/documents/selection/LogoConfirmationScreen.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
// 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 React, { useCallback } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import type { RouteProp } from '@react-navigation/native';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import {
|
||||
BodyText,
|
||||
ButtonsContainer,
|
||||
PrimaryButton,
|
||||
SecondaryButton,
|
||||
} from '@selfxyz/mobile-sdk-alpha/components';
|
||||
import {
|
||||
black,
|
||||
slate400,
|
||||
white,
|
||||
} from '@selfxyz/mobile-sdk-alpha/constants/colors';
|
||||
import { advercase, dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts';
|
||||
|
||||
import EPassportLogo from '@/assets/icons/epassport_logo.svg';
|
||||
import { DocumentFlowNavBar } from '@/components/navbar/DocumentFlowNavBar';
|
||||
import useHapticNavigation from '@/hooks/useHapticNavigation';
|
||||
import { buttonTap } from '@/integrations/haptics';
|
||||
import {
|
||||
fetchAccessToken,
|
||||
launchSumsub,
|
||||
} from '@/integrations/sumsub/sumsubService';
|
||||
import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { useFeedback } from '@/providers/feedbackProvider';
|
||||
import { useSettingStore } from '@/stores/settingStore';
|
||||
|
||||
type LogoConfirmationScreenRouteProp = RouteProp<
|
||||
RootStackParamList,
|
||||
'LogoConfirmation'
|
||||
>;
|
||||
|
||||
const LogoConfirmationScreen: React.FC = () => {
|
||||
const route = useRoute<LogoConfirmationScreenRouteProp>();
|
||||
const { documentType, countryCode } = route.params;
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const { showModal } = useFeedback();
|
||||
const navigateToOnboarding = useHapticNavigation('DocumentOnboarding');
|
||||
const kycEnabled = useSettingStore(state => state.kycEnabled);
|
||||
|
||||
const handleConfirm = useCallback(() => {
|
||||
buttonTap();
|
||||
navigateToOnboarding();
|
||||
}, [navigateToOnboarding]);
|
||||
|
||||
const handleNotFound = useCallback(() => {
|
||||
buttonTap();
|
||||
showModal({
|
||||
titleText: 'Document Not Supported',
|
||||
bodyText:
|
||||
"To complete registration of a document without a biometric chip, you'll be redirected to our third party verification partner.",
|
||||
buttonText: 'Proceed with an external verifier',
|
||||
onButtonPress: async () => {
|
||||
try {
|
||||
const accessToken = await fetchAccessToken();
|
||||
const result = await launchSumsub({
|
||||
accessToken: accessToken.token,
|
||||
// Pre-select document type and country based on user's earlier selection
|
||||
documentType: documentType as 'p' | 'i',
|
||||
countryCode,
|
||||
});
|
||||
|
||||
// User cancelled/dismissed without completing verification
|
||||
const cancelledStatuses = ['Initial', 'Incomplete', 'Interrupted'];
|
||||
if (cancelledStatuses.includes(result.status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// User completed verification - navigate to KycSuccessScreen
|
||||
navigation.navigate('KycSuccess', { userId: accessToken.userId });
|
||||
} catch (error) {
|
||||
console.error('Error launching Sumsub:', error);
|
||||
showModal({
|
||||
titleText: 'Error',
|
||||
bodyText: 'Unable to start verification. Please try again.',
|
||||
buttonText: 'OK',
|
||||
onButtonPress: () => {},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}, [documentType, countryCode, navigation, showModal]);
|
||||
|
||||
return (
|
||||
<ExpandableBottomLayout.Layout backgroundColor={white}>
|
||||
<DocumentFlowNavBar title="GETTING STARTED" />
|
||||
<ExpandableBottomLayout.TopSection backgroundColor={white}>
|
||||
<View style={styles.contentContainer}>
|
||||
<BodyText style={styles.titleText}>
|
||||
Does your document have this symbol?
|
||||
</BodyText>
|
||||
|
||||
<View style={styles.logoContainer}>
|
||||
<EPassportLogo width={160} height={98} />
|
||||
</View>
|
||||
|
||||
<BodyText style={styles.descriptionText}>
|
||||
This symbol indicates your document has a biometric chip, which is
|
||||
required for registration.
|
||||
</BodyText>
|
||||
</View>
|
||||
</ExpandableBottomLayout.TopSection>
|
||||
|
||||
<ExpandableBottomLayout.BottomSection backgroundColor={white}>
|
||||
<ButtonsContainer>
|
||||
<PrimaryButton onPress={handleConfirm}>Yes</PrimaryButton>
|
||||
{kycEnabled && (
|
||||
<SecondaryButton onPress={handleNotFound}>No</SecondaryButton>
|
||||
)}
|
||||
</ButtonsContainer>
|
||||
</ExpandableBottomLayout.BottomSection>
|
||||
</ExpandableBottomLayout.Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogoConfirmationScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
contentContainer: {
|
||||
alignItems: 'center',
|
||||
gap: 24,
|
||||
maxWidth: 340,
|
||||
},
|
||||
titleText: {
|
||||
fontSize: 20,
|
||||
fontFamily: advercase,
|
||||
textAlign: 'center',
|
||||
color: black,
|
||||
},
|
||||
logoContainer: {
|
||||
backgroundColor: white,
|
||||
borderRadius: 16,
|
||||
padding: 24,
|
||||
shadowColor: black,
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 8,
|
||||
elevation: 4,
|
||||
},
|
||||
descriptionText: {
|
||||
fontSize: 16,
|
||||
fontFamily: dinot,
|
||||
textAlign: 'center',
|
||||
color: slate400,
|
||||
},
|
||||
});
|
||||
@@ -22,11 +22,12 @@ import CloudBackupIcon from '@/assets/icons/cloud_backup.svg';
|
||||
import PushNotificationsIcon from '@/assets/icons/push_notifications.svg';
|
||||
import StarIcon from '@/assets/icons/star.svg';
|
||||
import Referral from '@/assets/images/referral.png';
|
||||
import { getModalCallbacks } from '@/utils/modalCallbackRegistry';
|
||||
|
||||
type PointsInfoScreenProps = StaticScreenProps<
|
||||
| {
|
||||
showNextButton?: boolean;
|
||||
onNextButtonPress?: () => void;
|
||||
callbackId?: number;
|
||||
}
|
||||
| undefined
|
||||
>;
|
||||
@@ -90,8 +91,9 @@ const EARN_POINTS_ITEMS = [
|
||||
const PointsInfoScreen: React.FC<PointsInfoScreenProps> = ({
|
||||
route: { params },
|
||||
}) => {
|
||||
const { showNextButton, onNextButtonPress } = params || {};
|
||||
const { showNextButton, callbackId } = params || {};
|
||||
const { left, right, bottom } = useSafeAreaInsets();
|
||||
const callbacks = callbackId ? getModalCallbacks(callbackId) : undefined;
|
||||
|
||||
return (
|
||||
<YStack flex={1} gap={40} paddingBottom={bottom} backgroundColor={white}>
|
||||
@@ -138,7 +140,7 @@ const PointsInfoScreen: React.FC<PointsInfoScreenProps> = ({
|
||||
</ScrollView>
|
||||
{showNextButton && (
|
||||
<View paddingTop={20} paddingLeft={20 + left} paddingRight={20 + right}>
|
||||
<PrimaryButton onPress={onNextButtonPress}>Next</PrimaryButton>
|
||||
<PrimaryButton onPress={callbacks?.onButtonPress}>Next</PrimaryButton>
|
||||
</View>
|
||||
)}
|
||||
</YStack>
|
||||
|
||||
175
app/src/screens/kyc/KycConnectionErrorScreen.tsx
Normal file
175
app/src/screens/kyc/KycConnectionErrorScreen.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
// 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 React, { useCallback } from 'react';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Button, XStack, YStack } from 'tamagui';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { X } from '@tamagui/lucide-icons';
|
||||
|
||||
import {
|
||||
BodyText,
|
||||
PrimaryButton,
|
||||
SecondaryButton,
|
||||
} from '@selfxyz/mobile-sdk-alpha/components';
|
||||
import {
|
||||
black,
|
||||
slate100,
|
||||
slate200,
|
||||
slate500,
|
||||
white,
|
||||
} from '@selfxyz/mobile-sdk-alpha/constants/colors';
|
||||
import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts';
|
||||
import { useSafeBottomPadding } from '@selfxyz/mobile-sdk-alpha/hooks';
|
||||
|
||||
import WarningIcon from '@/assets/images/warning.svg';
|
||||
import { buttonTap } from '@/integrations/haptics';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
import { extraYPadding } from '@/utils/styleUtils';
|
||||
|
||||
const KycConnectionErrorScreen: React.FC = () => {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = useSafeBottomPadding(extraYPadding + 35);
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
buttonTap();
|
||||
navigation.goBack();
|
||||
}, [navigation]);
|
||||
|
||||
const handleRetry = useCallback(() => {
|
||||
buttonTap();
|
||||
navigation.goBack();
|
||||
}, [navigation]);
|
||||
|
||||
const handleTryDifferentMethod = useCallback(() => {
|
||||
buttonTap();
|
||||
navigation.navigate('CountryPicker');
|
||||
}, [navigation]);
|
||||
|
||||
return (
|
||||
<YStack flex={1} backgroundColor={slate100}>
|
||||
{/* Header */}
|
||||
<YStack backgroundColor={slate100}>
|
||||
<XStack
|
||||
backgroundColor={slate100}
|
||||
padding={20}
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
paddingTop={Math.max(insets.top, 15) + extraYPadding}
|
||||
paddingBottom={10}
|
||||
>
|
||||
<Button
|
||||
unstyled
|
||||
onPress={handleClose}
|
||||
padding={8}
|
||||
borderRadius={20}
|
||||
hitSlop={10}
|
||||
>
|
||||
<X size={24} color={black} />
|
||||
</Button>
|
||||
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: black,
|
||||
fontWeight: '600',
|
||||
fontFamily: dinot,
|
||||
}}
|
||||
>
|
||||
CONNECTION ERROR
|
||||
</BodyText>
|
||||
|
||||
<Button
|
||||
unstyled
|
||||
padding={8}
|
||||
borderRadius={20}
|
||||
hitSlop={10}
|
||||
width={32}
|
||||
height={32}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
disabled
|
||||
opacity={0}
|
||||
>
|
||||
<X size={20} color={black} />
|
||||
</Button>
|
||||
</XStack>
|
||||
</YStack>
|
||||
|
||||
{/* Warning Icon */}
|
||||
<YStack flex={1} paddingHorizontal={20} paddingTop={20}>
|
||||
<YStack
|
||||
flex={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
paddingVertical={20}
|
||||
>
|
||||
<WarningIcon width={120} height={120} />
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
||||
{/* Error Message */}
|
||||
<YStack
|
||||
paddingHorizontal={20}
|
||||
paddingTop={20}
|
||||
alignItems="center"
|
||||
paddingVertical={25}
|
||||
>
|
||||
<BodyText style={{ fontSize: 19, textAlign: 'center', color: black }}>
|
||||
Connection Error
|
||||
</BodyText>
|
||||
<BodyText
|
||||
style={{
|
||||
marginTop: 6,
|
||||
fontSize: 17,
|
||||
textAlign: 'center',
|
||||
color: slate500,
|
||||
}}
|
||||
>
|
||||
Unable to connect to verification service. Please check your internet
|
||||
connection and try again.
|
||||
</BodyText>
|
||||
</YStack>
|
||||
|
||||
{/* Retry Button */}
|
||||
<YStack paddingHorizontal={25} paddingBottom={20}>
|
||||
<PrimaryButton onPress={handleRetry}>Try again</PrimaryButton>
|
||||
</YStack>
|
||||
|
||||
{/* Bottom Section with Grey Line Separator */}
|
||||
<YStack
|
||||
paddingHorizontal={25}
|
||||
backgroundColor={white}
|
||||
paddingBottom={paddingBottom}
|
||||
paddingTop={25}
|
||||
gap="$3"
|
||||
borderTopWidth={1}
|
||||
borderTopColor={slate200}
|
||||
>
|
||||
<SecondaryButton onPress={handleTryDifferentMethod}>
|
||||
Try a different method
|
||||
</SecondaryButton>
|
||||
|
||||
{/* Footer Text */}
|
||||
<BodyText
|
||||
style={{
|
||||
fontSize: 15,
|
||||
textAlign: 'center',
|
||||
color: slate500,
|
||||
fontStyle: 'italic',
|
||||
marginTop: 8,
|
||||
}}
|
||||
>
|
||||
Registering with alternative methods may take longer to verify your
|
||||
document.
|
||||
</BodyText>
|
||||
</YStack>
|
||||
</YStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default KycConnectionErrorScreen;
|
||||
126
app/src/screens/kyc/KycFailureScreen.tsx
Normal file
126
app/src/screens/kyc/KycFailureScreen.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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 React, { useCallback } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { YStack } from 'tamagui';
|
||||
import type { RouteProp } from '@react-navigation/native';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
import {
|
||||
AbstractButton,
|
||||
Description,
|
||||
Title,
|
||||
} from '@selfxyz/mobile-sdk-alpha/components';
|
||||
import {
|
||||
black,
|
||||
slate600,
|
||||
white,
|
||||
} from '@selfxyz/mobile-sdk-alpha/constants/colors';
|
||||
|
||||
import ShieldErrorIcon from '@/assets/icons/shield_error.svg';
|
||||
import { buttonTap } from '@/integrations/haptics';
|
||||
import type { RootStackParamList } from '@/navigation';
|
||||
|
||||
type KycFailureRouteParams = {
|
||||
countryCode?: string;
|
||||
canRetry?: boolean;
|
||||
};
|
||||
|
||||
type KycFailureRoute = RouteProp<Record<string, KycFailureRouteParams>, string>;
|
||||
|
||||
const KycFailureScreen: React.FC = () => {
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const route = useRoute<KycFailureRoute>();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
const canRetry = route.params?.canRetry ?? true;
|
||||
|
||||
const handleDismiss = useCallback(() => {
|
||||
buttonTap();
|
||||
navigation.navigate('Home', {});
|
||||
}, [navigation]);
|
||||
|
||||
const handleTryAgain = useCallback(() => {
|
||||
buttonTap();
|
||||
navigation.navigate('CountryPicker');
|
||||
}, [navigation]);
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { paddingBottom: insets.bottom }]}>
|
||||
<YStack
|
||||
flex={1}
|
||||
justifyContent="flex-end"
|
||||
alignItems="center"
|
||||
paddingBottom={60}
|
||||
>
|
||||
<ShieldErrorIcon width={150} height={150} />
|
||||
</YStack>
|
||||
<YStack
|
||||
paddingHorizontal={32}
|
||||
alignItems="center"
|
||||
gap={16}
|
||||
marginBottom={64}
|
||||
>
|
||||
<Title style={styles.title}>
|
||||
Unfortunately we couldn't verify your ID
|
||||
</Title>
|
||||
<Description style={styles.description}>
|
||||
This may be because the files you uploaded were unreadable for some
|
||||
other issue.
|
||||
</Description>
|
||||
</YStack>
|
||||
<YStack gap={12} paddingHorizontal={24} paddingBottom={32}>
|
||||
<AbstractButton
|
||||
bgColor="transparent"
|
||||
color={white}
|
||||
borderColor={slate600}
|
||||
borderWidth={1}
|
||||
style={styles.button}
|
||||
onPress={handleDismiss}
|
||||
>
|
||||
Dismiss
|
||||
</AbstractButton>
|
||||
{canRetry && (
|
||||
<AbstractButton
|
||||
bgColor={white}
|
||||
color={black}
|
||||
style={styles.button}
|
||||
onPress={handleTryAgain}
|
||||
>
|
||||
Try again
|
||||
</AbstractButton>
|
||||
)}
|
||||
</YStack>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: black,
|
||||
},
|
||||
title: {
|
||||
color: white,
|
||||
textAlign: 'center',
|
||||
fontSize: 36,
|
||||
lineHeight: 44,
|
||||
letterSpacing: 1,
|
||||
},
|
||||
description: {
|
||||
color: white,
|
||||
textAlign: 'center',
|
||||
fontSize: 20,
|
||||
lineHeight: 30,
|
||||
},
|
||||
button: {
|
||||
borderRadius: 100,
|
||||
},
|
||||
});
|
||||
|
||||
export default KycFailureScreen;
|
||||
@@ -207,12 +207,15 @@ describe('useEarnPointsFlow', () => {
|
||||
|
||||
expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', {
|
||||
showNextButton: true,
|
||||
onNextButtonPress: expect.any(Function),
|
||||
callbackId: expect.any(Number),
|
||||
});
|
||||
|
||||
// We pass onNextButtonPress() that displays the points disclosure modal
|
||||
// We pass callbackId to retrieve and invoke the callback that displays the points disclosure modal
|
||||
const callbackId = mockNavigate.mock.calls[0][1].callbackId;
|
||||
const callbacks = getModalCallbacks(callbackId);
|
||||
|
||||
await act(async () => {
|
||||
await mockNavigate.mock.calls[0][1].onNextButtonPress();
|
||||
await callbacks!.onButtonPress();
|
||||
});
|
||||
|
||||
expect(mockNavigate).toHaveBeenCalledWith('Modal', {
|
||||
@@ -243,11 +246,14 @@ describe('useEarnPointsFlow', () => {
|
||||
|
||||
expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', {
|
||||
showNextButton: true,
|
||||
onNextButtonPress: expect.any(Function),
|
||||
callbackId: expect.any(Number),
|
||||
});
|
||||
|
||||
const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId;
|
||||
const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId);
|
||||
|
||||
await act(async () => {
|
||||
await mockNavigate.mock.calls[0][1].onNextButtonPress();
|
||||
await pointsInfoCallbacks!.onButtonPress();
|
||||
});
|
||||
|
||||
const callbackId = mockNavigate.mock.calls[1][1].callbackId;
|
||||
@@ -290,11 +296,14 @@ describe('useEarnPointsFlow', () => {
|
||||
|
||||
expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', {
|
||||
showNextButton: true,
|
||||
onNextButtonPress: expect.any(Function),
|
||||
callbackId: expect.any(Number),
|
||||
});
|
||||
|
||||
const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId;
|
||||
const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId);
|
||||
|
||||
await act(async () => {
|
||||
await mockNavigate.mock.calls[0][1].onNextButtonPress();
|
||||
await pointsInfoCallbacks!.onButtonPress();
|
||||
});
|
||||
|
||||
const callbackId = mockNavigate.mock.calls[1][1].callbackId;
|
||||
@@ -662,11 +671,14 @@ describe('useEarnPointsFlow', () => {
|
||||
|
||||
expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', {
|
||||
showNextButton: true,
|
||||
onNextButtonPress: expect.any(Function),
|
||||
callbackId: expect.any(Number),
|
||||
});
|
||||
|
||||
const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId;
|
||||
const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId);
|
||||
|
||||
await act(async () => {
|
||||
await mockNavigate.mock.calls[0][1].onNextButtonPress();
|
||||
await pointsInfoCallbacks!.onButtonPress();
|
||||
});
|
||||
|
||||
// The function catches errors and returns false, so it should show points disclosure modal
|
||||
@@ -697,11 +709,14 @@ describe('useEarnPointsFlow', () => {
|
||||
|
||||
expect(mockNavigate).toHaveBeenCalledWith('PointsInfo', {
|
||||
showNextButton: true,
|
||||
onNextButtonPress: expect.any(Function),
|
||||
callbackId: expect.any(Number),
|
||||
});
|
||||
|
||||
const pointsInfoCallbackId = mockNavigate.mock.calls[0][1].callbackId;
|
||||
const pointsInfoCallbacks = getModalCallbacks(pointsInfoCallbackId);
|
||||
|
||||
await act(async () => {
|
||||
await mockNavigate.mock.calls[0][1].onNextButtonPress();
|
||||
await pointsInfoCallbacks!.onButtonPress();
|
||||
});
|
||||
|
||||
const callbackId = mockNavigate.mock.calls[1][1].callbackId;
|
||||
|
||||
@@ -86,8 +86,11 @@ describe('navigation', () => {
|
||||
'IDPicker',
|
||||
'IdDetails',
|
||||
'KYCVerified',
|
||||
'KycConnectionError',
|
||||
'KycFailure',
|
||||
'KycSuccess',
|
||||
'Loading',
|
||||
'LogoConfirmation',
|
||||
'ManageDocuments',
|
||||
'MockDataDeepLink',
|
||||
'Modal',
|
||||
@@ -103,7 +106,8 @@ describe('navigation', () => {
|
||||
'QRCodeViewFinder',
|
||||
'RecoverWithPhrase',
|
||||
'Referral',
|
||||
'RegistrationFallback',
|
||||
'RegistrationFallbackMRZ',
|
||||
'RegistrationFallbackNFC',
|
||||
'SaveRecoveryPhrase',
|
||||
'Settings',
|
||||
'ShowRecoveryPhrase',
|
||||
|
||||
@@ -116,7 +116,7 @@ describe('NotificationTrackingProvider', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should not navigate when status is retry', async () => {
|
||||
it('should navigate to CountryPicker when status is retry', async () => {
|
||||
let notificationHandler:
|
||||
| ((message: FirebaseMessagingTypes.RemoteMessage) => void)
|
||||
| null = null;
|
||||
@@ -151,11 +151,10 @@ describe('NotificationTrackingProvider', () => {
|
||||
expect(analytics.trackEvent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Should not navigate for retry status
|
||||
expect(mockNavigationRef.navigate).not.toHaveBeenCalled();
|
||||
expect(mockNavigationRef.navigate).toHaveBeenCalledWith('CountryPicker');
|
||||
});
|
||||
|
||||
it('should not navigate when status is rejected', async () => {
|
||||
it('should navigate to KycFailure when status is rejected', async () => {
|
||||
let notificationHandler:
|
||||
| ((message: FirebaseMessagingTypes.RemoteMessage) => void)
|
||||
| null = null;
|
||||
@@ -190,8 +189,9 @@ describe('NotificationTrackingProvider', () => {
|
||||
expect(analytics.trackEvent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Should not navigate for rejected status
|
||||
expect(mockNavigationRef.navigate).not.toHaveBeenCalled();
|
||||
expect(mockNavigationRef.navigate).toHaveBeenCalledWith('KycFailure', {
|
||||
canRetry: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle missing notification data gracefully', async () => {
|
||||
@@ -331,7 +331,7 @@ describe('NotificationTrackingProvider', () => {
|
||||
expect(mockNavigationRef.navigate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not navigate when status is retry on cold start', async () => {
|
||||
it('should navigate to CountryPicker when status is retry on cold start', async () => {
|
||||
mockOnNotificationOpenedApp.mockReturnValue(jest.fn());
|
||||
|
||||
const remoteMessage = {
|
||||
@@ -358,8 +358,7 @@ describe('NotificationTrackingProvider', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// Should not navigate for retry status
|
||||
expect(mockNavigationRef.navigate).not.toHaveBeenCalled();
|
||||
expect(mockNavigationRef.navigate).toHaveBeenCalledWith('CountryPicker');
|
||||
});
|
||||
|
||||
it('should queue navigation when navigationRef is not ready on cold start', async () => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"ios": {
|
||||
"build": 210,
|
||||
"lastDeployed": "2026-02-04T08:27:35.956Z"
|
||||
"build": 211,
|
||||
"lastDeployed": "2026-02-04T22:15:04.337Z"
|
||||
},
|
||||
"android": {
|
||||
"build": 139,
|
||||
"lastDeployed": "2026-01-22T23:03:43.685Z"
|
||||
"build": 140,
|
||||
"lastDeployed": "2026-02-05T00:58:22Z"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
contract Groth16Verifier {
|
||||
contract Verifier_vc_and_disclose_kyc {
|
||||
// Scalar field size
|
||||
uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
// Base field size
|
||||
|
||||
@@ -104,5 +104,6 @@
|
||||
"DeployKycRegistryModule#IdentityRegistryKycImplV1": "0x94f6DE38E10140B9E3963a770B5B769b38459a3B",
|
||||
"DeployKycRegistryModule#IdentityRegistry": "0x90e907E4AaB6e9bcFB94997Af4A097e8CAadBdf3",
|
||||
"UpdateAllRegistries#PCR0Manager": "0xf2810D5E9938816D42F0Ae69D33F013a23C0aED2",
|
||||
"UpdateAllRegistries#a3": "0x90e907E4AaB6e9bcFB94997Af4A097e8CAadBdf3"
|
||||
"UpdateAllRegistries#a3": "0x90e907E4AaB6e9bcFB94997Af4A097e8CAadBdf3",
|
||||
"DeployAllVerifiers#Verifier_vc_and_disclose_kyc": "0xAAFA189a079D04462e8ab596d9c103e081A1c810"
|
||||
}
|
||||
|
||||
800
error-selectors.json
Normal file
800
error-selectors.json
Normal file
@@ -0,0 +1,800 @@
|
||||
[
|
||||
{
|
||||
"name": "REGISTERED_COMMITMENT",
|
||||
"signature": "REGISTERED_COMMITMENT()",
|
||||
"selector": "0x034acfcc",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryAadhaarImplV1.sol",
|
||||
"line": 136
|
||||
},
|
||||
{
|
||||
"name": "REGISTERED_COMMITMENT",
|
||||
"signature": "REGISTERED_COMMITMENT()",
|
||||
"selector": "0x034acfcc",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryIdCardImplV1.sol",
|
||||
"line": 144
|
||||
},
|
||||
{
|
||||
"name": "REGISTERED_COMMITMENT",
|
||||
"signature": "REGISTERED_COMMITMENT()",
|
||||
"selector": "0x034acfcc",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryImplV1.sol",
|
||||
"line": 151
|
||||
},
|
||||
{
|
||||
"name": "REGISTERED_COMMITMENT",
|
||||
"signature": "REGISTERED_COMMITMENT()",
|
||||
"selector": "0x034acfcc",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryKycImplV1.sol",
|
||||
"line": 179
|
||||
},
|
||||
{
|
||||
"name": "InvalidProof",
|
||||
"signature": "InvalidProof()",
|
||||
"selector": "0x09bde339",
|
||||
"file": "contracts/contracts/example/Airdrop.sol",
|
||||
"line": 57
|
||||
},
|
||||
{
|
||||
"name": "InvalidProof",
|
||||
"signature": "InvalidProof()",
|
||||
"selector": "0x09bde339",
|
||||
"file": "contracts/contracts/tests/TestAirdrop.sol",
|
||||
"line": 35
|
||||
},
|
||||
{
|
||||
"name": "InvalidPubSignalsLength",
|
||||
"signature": "InvalidPubSignalsLength(uint256,uint256)",
|
||||
"selector": "0x0b42b970",
|
||||
"file": "contracts/contracts/libraries/RegisterProofVerifierLib.sol",
|
||||
"line": 43
|
||||
},
|
||||
{
|
||||
"name": "NoVerifierSet",
|
||||
"signature": "NoVerifierSet()",
|
||||
"selector": "0x0ee78d58",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 151
|
||||
},
|
||||
{
|
||||
"name": "NoVerifierSet",
|
||||
"signature": "NoVerifierSet()",
|
||||
"selector": "0x0ee78d58",
|
||||
"file": "contracts/contracts/libraries/DscProofVerifierLib.sol",
|
||||
"line": 17
|
||||
},
|
||||
{
|
||||
"name": "NoVerifierSet",
|
||||
"signature": "NoVerifierSet()",
|
||||
"selector": "0x0ee78d58",
|
||||
"file": "contracts/contracts/libraries/RegisterProofVerifierLib.sol",
|
||||
"line": 22
|
||||
},
|
||||
{
|
||||
"name": "INVALID_TIMESTAMP",
|
||||
"signature": "INVALID_TIMESTAMP()",
|
||||
"selector": "0x118818d1",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryKycImplV1.sol",
|
||||
"line": 189
|
||||
},
|
||||
{
|
||||
"name": "InvalidAttestationId",
|
||||
"signature": "InvalidAttestationId()",
|
||||
"selector": "0x12ec75fe",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 183
|
||||
},
|
||||
{
|
||||
"name": "InvalidAttestationId",
|
||||
"signature": "InvalidAttestationId()",
|
||||
"selector": "0x12ec75fe",
|
||||
"file": "contracts/contracts/libraries/CustomVerifier.sol",
|
||||
"line": 10
|
||||
},
|
||||
{
|
||||
"name": "InvalidAttestationId",
|
||||
"signature": "InvalidAttestationId()",
|
||||
"selector": "0x12ec75fe",
|
||||
"file": "contracts/contracts/libraries/DscProofVerifierLib.sol",
|
||||
"line": 26
|
||||
},
|
||||
{
|
||||
"name": "InvalidAttestationId",
|
||||
"signature": "InvalidAttestationId()",
|
||||
"selector": "0x12ec75fe",
|
||||
"file": "contracts/contracts/libraries/OfacCheckLib.sol",
|
||||
"line": 22
|
||||
},
|
||||
{
|
||||
"name": "InvalidAttestationId",
|
||||
"signature": "InvalidAttestationId()",
|
||||
"selector": "0x12ec75fe",
|
||||
"file": "contracts/contracts/libraries/ProofVerifierLib.sol",
|
||||
"line": 21
|
||||
},
|
||||
{
|
||||
"name": "InvalidAttestationId",
|
||||
"signature": "InvalidAttestationId()",
|
||||
"selector": "0x12ec75fe",
|
||||
"file": "contracts/contracts/libraries/RegisterProofVerifierLib.sol",
|
||||
"line": 31
|
||||
},
|
||||
{
|
||||
"name": "InvalidAttestationId",
|
||||
"signature": "InvalidAttestationId()",
|
||||
"selector": "0x12ec75fe",
|
||||
"file": "contracts/contracts/libraries/RootCheckLib.sol",
|
||||
"line": 22
|
||||
},
|
||||
{
|
||||
"name": "RegistrationNotOpen",
|
||||
"signature": "RegistrationNotOpen()",
|
||||
"selector": "0x153745d3",
|
||||
"file": "contracts/contracts/example/Airdrop.sol",
|
||||
"line": 66
|
||||
},
|
||||
{
|
||||
"name": "RegistrationNotOpen",
|
||||
"signature": "RegistrationNotOpen()",
|
||||
"selector": "0x153745d3",
|
||||
"file": "contracts/contracts/tests/TestAirdrop.sol",
|
||||
"line": 38
|
||||
},
|
||||
{
|
||||
"name": "InvalidDscProof",
|
||||
"signature": "InvalidDscProof()",
|
||||
"selector": "0x1644e049",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 163
|
||||
},
|
||||
{
|
||||
"name": "InvalidDscProof",
|
||||
"signature": "InvalidDscProof()",
|
||||
"selector": "0x1644e049",
|
||||
"file": "contracts/contracts/libraries/DscProofVerifierLib.sol",
|
||||
"line": 20
|
||||
},
|
||||
{
|
||||
"name": "InvalidYearRange",
|
||||
"signature": "InvalidYearRange()",
|
||||
"selector": "0x16f40c94",
|
||||
"file": "contracts/contracts/libraries/Formatter.sol",
|
||||
"line": 12
|
||||
},
|
||||
{
|
||||
"name": "InvalidDateDigit",
|
||||
"signature": "InvalidDateDigit()",
|
||||
"selector": "0x17af8154",
|
||||
"file": "contracts/contracts/libraries/Formatter.sol",
|
||||
"line": 16
|
||||
},
|
||||
{
|
||||
"name": "INVALID_OFAC_ROOT",
|
||||
"signature": "INVALID_OFAC_ROOT()",
|
||||
"selector": "0x1ce3d3ca",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 176
|
||||
},
|
||||
{
|
||||
"name": "HUB_ADDRESS_ZERO",
|
||||
"signature": "HUB_ADDRESS_ZERO()",
|
||||
"selector": "0x22697ffa",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryAadhaarImplV1.sol",
|
||||
"line": 138
|
||||
},
|
||||
{
|
||||
"name": "HUB_ADDRESS_ZERO",
|
||||
"signature": "HUB_ADDRESS_ZERO()",
|
||||
"selector": "0x22697ffa",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryKycImplV1.sol",
|
||||
"line": 181
|
||||
},
|
||||
{
|
||||
"name": "RegisteredNullifier",
|
||||
"signature": "RegisteredNullifier()",
|
||||
"selector": "0x22cbc6a2",
|
||||
"file": "contracts/contracts/example/Airdrop.sol",
|
||||
"line": 81
|
||||
},
|
||||
{
|
||||
"name": "RegisteredNullifier",
|
||||
"signature": "RegisteredNullifier()",
|
||||
"selector": "0x22cbc6a2",
|
||||
"file": "contracts/contracts/tests/TestAirdrop.sol",
|
||||
"line": 43
|
||||
},
|
||||
{
|
||||
"name": "InvalidMonthRange",
|
||||
"signature": "InvalidMonthRange()",
|
||||
"selector": "0x25e62788",
|
||||
"file": "contracts/contracts/libraries/Formatter.sol",
|
||||
"line": 13
|
||||
},
|
||||
{
|
||||
"name": "ONLY_TEE_CAN_ACCESS",
|
||||
"signature": "ONLY_TEE_CAN_ACCESS()",
|
||||
"selector": "0x2822d0cb",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryKycImplV1.sol",
|
||||
"line": 177
|
||||
},
|
||||
{
|
||||
"name": "UserIdentifierAlreadyRegistered",
|
||||
"signature": "UserIdentifierAlreadyRegistered()",
|
||||
"selector": "0x29393238",
|
||||
"file": "contracts/contracts/example/Airdrop.sol",
|
||||
"line": 78
|
||||
},
|
||||
{
|
||||
"name": "UserIdentifierAlreadyRegistered",
|
||||
"signature": "UserIdentifierAlreadyRegistered()",
|
||||
"selector": "0x29393238",
|
||||
"file": "contracts/contracts/tests/TestAirdrop.sol",
|
||||
"line": 42
|
||||
},
|
||||
{
|
||||
"name": "InvalidFieldElement",
|
||||
"signature": "InvalidFieldElement()",
|
||||
"selector": "0x3ae4ed6b",
|
||||
"file": "contracts/contracts/libraries/Formatter.sol",
|
||||
"line": 15
|
||||
},
|
||||
{
|
||||
"name": "InvalidPubkey",
|
||||
"signature": "InvalidPubkey()",
|
||||
"selector": "0x422cc3b7",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 211
|
||||
},
|
||||
{
|
||||
"name": "InvalidPubkey",
|
||||
"signature": "InvalidPubkey()",
|
||||
"selector": "0x422cc3b7",
|
||||
"file": "contracts/contracts/libraries/RegisterProofVerifierLib.sol",
|
||||
"line": 34
|
||||
},
|
||||
{
|
||||
"name": "InvalidOlderThan",
|
||||
"signature": "InvalidOlderThan()",
|
||||
"selector": "0x49aecbc2",
|
||||
"file": "contracts/contracts/libraries/CustomVerifier.sol",
|
||||
"line": 13
|
||||
},
|
||||
{
|
||||
"name": "InvalidDscCommitmentRoot",
|
||||
"signature": "InvalidDscCommitmentRoot()",
|
||||
"selector": "0x4cb305bb",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 175
|
||||
},
|
||||
{
|
||||
"name": "InvalidDscCommitmentRoot",
|
||||
"signature": "InvalidDscCommitmentRoot()",
|
||||
"selector": "0x4cb305bb",
|
||||
"file": "contracts/contracts/libraries/RegisterProofVerifierLib.sol",
|
||||
"line": 28
|
||||
},
|
||||
{
|
||||
"name": "HUB_NOT_SET",
|
||||
"signature": "HUB_NOT_SET()",
|
||||
"selector": "0x4ffa9998",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryAadhaarImplV1.sol",
|
||||
"line": 132
|
||||
},
|
||||
{
|
||||
"name": "HUB_NOT_SET",
|
||||
"signature": "HUB_NOT_SET()",
|
||||
"selector": "0x4ffa9998",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryIdCardImplV1.sol",
|
||||
"line": 140
|
||||
},
|
||||
{
|
||||
"name": "HUB_NOT_SET",
|
||||
"signature": "HUB_NOT_SET()",
|
||||
"selector": "0x4ffa9998",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryImplV1.sol",
|
||||
"line": 147
|
||||
},
|
||||
{
|
||||
"name": "HUB_NOT_SET",
|
||||
"signature": "HUB_NOT_SET()",
|
||||
"selector": "0x4ffa9998",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryKycImplV1.sol",
|
||||
"line": 171
|
||||
},
|
||||
{
|
||||
"name": "INVALID_COMMITMENT_ROOT",
|
||||
"signature": "INVALID_COMMITMENT_ROOT()",
|
||||
"selector": "0x52906601",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 172
|
||||
},
|
||||
{
|
||||
"name": "UnauthorizedCaller",
|
||||
"signature": "UnauthorizedCaller()",
|
||||
"selector": "0x5c427cd9",
|
||||
"file": "contracts/contracts/abstract/SelfVerificationRoot.sol",
|
||||
"line": 49
|
||||
},
|
||||
{
|
||||
"name": "UnauthorizedCaller",
|
||||
"signature": "UnauthorizedCaller()",
|
||||
"selector": "0x5c427cd9",
|
||||
"file": "contracts/contracts/abstract/SelfVerificationRootUpgradeable.sol",
|
||||
"line": 69
|
||||
},
|
||||
{
|
||||
"name": "UserIdentifierAlreadyMinted",
|
||||
"signature": "UserIdentifierAlreadyMinted()",
|
||||
"selector": "0x5dd09265",
|
||||
"file": "contracts/contracts/example/SelfIdentityERC721.sol",
|
||||
"line": 51
|
||||
},
|
||||
{
|
||||
"name": "UserIdentifierAlreadyMinted",
|
||||
"signature": "UserIdentifierAlreadyMinted()",
|
||||
"selector": "0x5dd09265",
|
||||
"file": "contracts/contracts/example/SelfPassportERC721.sol",
|
||||
"line": 48
|
||||
},
|
||||
{
|
||||
"name": "InvalidOfacCheck",
|
||||
"signature": "InvalidOfacCheck()",
|
||||
"selector": "0x5fb542f4",
|
||||
"file": "contracts/contracts/libraries/CustomVerifier.sol",
|
||||
"line": 11
|
||||
},
|
||||
{
|
||||
"name": "CrossChainIsNotSupportedYet",
|
||||
"signature": "CrossChainIsNotSupportedYet()",
|
||||
"selector": "0x61296fbb",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 191
|
||||
},
|
||||
{
|
||||
"name": "AlreadyClaimed",
|
||||
"signature": "AlreadyClaimed()",
|
||||
"selector": "0x646cf558",
|
||||
"file": "contracts/contracts/example/Airdrop.sol",
|
||||
"line": 60
|
||||
},
|
||||
{
|
||||
"name": "AlreadyClaimed",
|
||||
"signature": "AlreadyClaimed()",
|
||||
"selector": "0x646cf558",
|
||||
"file": "contracts/contracts/example/HappyBirthday.sol",
|
||||
"line": 67
|
||||
},
|
||||
{
|
||||
"name": "AlreadyClaimed",
|
||||
"signature": "AlreadyClaimed()",
|
||||
"selector": "0x646cf558",
|
||||
"file": "contracts/contracts/tests/TestAirdrop.sol",
|
||||
"line": 36
|
||||
},
|
||||
{
|
||||
"name": "InputTooShort",
|
||||
"signature": "InputTooShort()",
|
||||
"selector": "0x65ec0cf1",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 195
|
||||
},
|
||||
{
|
||||
"name": "InvalidRegisterProof",
|
||||
"signature": "InvalidRegisterProof()",
|
||||
"selector": "0x67b61dc7",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 159
|
||||
},
|
||||
{
|
||||
"name": "InvalidRegisterProof",
|
||||
"signature": "InvalidRegisterProof()",
|
||||
"selector": "0x67b61dc7",
|
||||
"file": "contracts/contracts/libraries/RegisterProofVerifierLib.sol",
|
||||
"line": 25
|
||||
},
|
||||
{
|
||||
"name": "RegistrationNotClosed",
|
||||
"signature": "RegistrationNotClosed()",
|
||||
"selector": "0x697e379b",
|
||||
"file": "contracts/contracts/example/Airdrop.sol",
|
||||
"line": 69
|
||||
},
|
||||
{
|
||||
"name": "RegistrationNotClosed",
|
||||
"signature": "RegistrationNotClosed()",
|
||||
"selector": "0x697e379b",
|
||||
"file": "contracts/contracts/tests/TestAirdrop.sol",
|
||||
"line": 39
|
||||
},
|
||||
{
|
||||
"name": "INVALID_DSC_PROOF",
|
||||
"signature": "INVALID_DSC_PROOF()",
|
||||
"selector": "0x6a86dd76",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 164
|
||||
},
|
||||
{
|
||||
"name": "ClaimNotOpen",
|
||||
"signature": "ClaimNotOpen()",
|
||||
"selector": "0x6b687806",
|
||||
"file": "contracts/contracts/example/Airdrop.sol",
|
||||
"line": 72
|
||||
},
|
||||
{
|
||||
"name": "ClaimNotOpen",
|
||||
"signature": "ClaimNotOpen()",
|
||||
"selector": "0x6b687806",
|
||||
"file": "contracts/contracts/tests/TestAirdrop.sol",
|
||||
"line": 40
|
||||
},
|
||||
{
|
||||
"name": "InvalidUidaiTimestamp",
|
||||
"signature": "InvalidUidaiTimestamp(uint256,uint256)",
|
||||
"selector": "0x6f26ab8d",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 215
|
||||
},
|
||||
{
|
||||
"name": "InvalidUidaiTimestamp",
|
||||
"signature": "InvalidUidaiTimestamp(uint256,uint256)",
|
||||
"selector": "0x6f26ab8d",
|
||||
"file": "contracts/contracts/libraries/RegisterProofVerifierLib.sol",
|
||||
"line": 37
|
||||
},
|
||||
{
|
||||
"name": "INVALID_PROOF",
|
||||
"signature": "INVALID_PROOF()",
|
||||
"selector": "0x712eb087",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryKycImplV1.sol",
|
||||
"line": 183
|
||||
},
|
||||
{
|
||||
"name": "INVALID_OFAC",
|
||||
"signature": "INVALID_OFAC()",
|
||||
"selector": "0x71b125ed",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 156
|
||||
},
|
||||
{
|
||||
"name": "INVALID_IMAGE",
|
||||
"signature": "INVALID_IMAGE()",
|
||||
"selector": "0x7f91b413",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryKycImplV1.sol",
|
||||
"line": 187
|
||||
},
|
||||
{
|
||||
"name": "InvalidForbiddenCountries",
|
||||
"signature": "InvalidForbiddenCountries()",
|
||||
"selector": "0x82cba848",
|
||||
"file": "contracts/contracts/libraries/CustomVerifier.sol",
|
||||
"line": 12
|
||||
},
|
||||
{
|
||||
"name": "InsufficientCharcodeLen",
|
||||
"signature": "InsufficientCharcodeLen()",
|
||||
"selector": "0x86d41225",
|
||||
"file": "contracts/contracts/libraries/CircuitAttributeHandler.sol",
|
||||
"line": 15
|
||||
},
|
||||
{
|
||||
"name": "InsufficientCharcodeLen",
|
||||
"signature": "InsufficientCharcodeLen()",
|
||||
"selector": "0x86d41225",
|
||||
"file": "contracts/contracts/libraries/CircuitAttributeHandlerV2.sol",
|
||||
"line": 17
|
||||
},
|
||||
{
|
||||
"name": "InsufficientCharcodeLen",
|
||||
"signature": "InsufficientCharcodeLen()",
|
||||
"selector": "0x86d41225",
|
||||
"file": "contracts/contracts/libraries/IdCardAttributeHandler.sol",
|
||||
"line": 16
|
||||
},
|
||||
{
|
||||
"name": "InvalidDayRange",
|
||||
"signature": "InvalidDayRange()",
|
||||
"selector": "0x8930acef",
|
||||
"file": "contracts/contracts/libraries/Formatter.sol",
|
||||
"line": 14
|
||||
},
|
||||
{
|
||||
"name": "LENGTH_MISMATCH",
|
||||
"signature": "LENGTH_MISMATCH()",
|
||||
"selector": "0x899ef10d",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 136
|
||||
},
|
||||
{
|
||||
"name": "NO_VERIFIER_SET",
|
||||
"signature": "NO_VERIFIER_SET()",
|
||||
"selector": "0x8e727f46",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 140
|
||||
},
|
||||
{
|
||||
"name": "InvalidCscaRoot",
|
||||
"signature": "InvalidCscaRoot()",
|
||||
"selector": "0x8f1b44c7",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 179
|
||||
},
|
||||
{
|
||||
"name": "InvalidCscaRoot",
|
||||
"signature": "InvalidCscaRoot()",
|
||||
"selector": "0x8f1b44c7",
|
||||
"file": "contracts/contracts/libraries/DscProofVerifierLib.sol",
|
||||
"line": 23
|
||||
},
|
||||
{
|
||||
"name": "INVALID_REGISTER_PROOF",
|
||||
"signature": "INVALID_REGISTER_PROOF()",
|
||||
"selector": "0x9003ac4d",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 160
|
||||
},
|
||||
{
|
||||
"name": "UserContextDataTooShort",
|
||||
"signature": "UserContextDataTooShort()",
|
||||
"selector": "0x94ec3503",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 199
|
||||
},
|
||||
{
|
||||
"name": "NotWithinBirthdayWindow",
|
||||
"signature": "NotWithinBirthdayWindow()",
|
||||
"selector": "0x9b7983d7",
|
||||
"file": "contracts/contracts/example/HappyBirthday.sol",
|
||||
"line": 66
|
||||
},
|
||||
{
|
||||
"name": "INVALID_CSCA_ROOT",
|
||||
"signature": "INVALID_CSCA_ROOT()",
|
||||
"selector": "0xa294ad3c",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 180
|
||||
},
|
||||
{
|
||||
"name": "InvalidDataFormat",
|
||||
"signature": "InvalidDataFormat()",
|
||||
"selector": "0xa512e2ff",
|
||||
"file": "contracts/contracts/abstract/SelfVerificationRoot.sol",
|
||||
"line": 45
|
||||
},
|
||||
{
|
||||
"name": "InvalidDataFormat",
|
||||
"signature": "InvalidDataFormat()",
|
||||
"selector": "0xa512e2ff",
|
||||
"file": "contracts/contracts/abstract/SelfVerificationRootUpgradeable.sol",
|
||||
"line": 65
|
||||
},
|
||||
{
|
||||
"name": "ConfigNotSet",
|
||||
"signature": "ConfigNotSet()",
|
||||
"selector": "0xace124bc",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 207
|
||||
},
|
||||
{
|
||||
"name": "InvalidDateLength",
|
||||
"signature": "InvalidDateLength()",
|
||||
"selector": "0xb3375953",
|
||||
"file": "contracts/contracts/libraries/Formatter.sol",
|
||||
"line": 11
|
||||
},
|
||||
{
|
||||
"name": "ONLY_HUB_CAN_ACCESS",
|
||||
"signature": "ONLY_HUB_CAN_ACCESS()",
|
||||
"selector": "0xba0318cb",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryAadhaarImplV1.sol",
|
||||
"line": 134
|
||||
},
|
||||
{
|
||||
"name": "ONLY_HUB_CAN_ACCESS",
|
||||
"signature": "ONLY_HUB_CAN_ACCESS()",
|
||||
"selector": "0xba0318cb",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryIdCardImplV1.sol",
|
||||
"line": 142
|
||||
},
|
||||
{
|
||||
"name": "ONLY_HUB_CAN_ACCESS",
|
||||
"signature": "ONLY_HUB_CAN_ACCESS()",
|
||||
"selector": "0xba0318cb",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryImplV1.sol",
|
||||
"line": 149
|
||||
},
|
||||
{
|
||||
"name": "ONLY_HUB_CAN_ACCESS",
|
||||
"signature": "ONLY_HUB_CAN_ACCESS()",
|
||||
"selector": "0xba0318cb",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryKycImplV1.sol",
|
||||
"line": 173
|
||||
},
|
||||
{
|
||||
"name": "INVALID_FORBIDDEN_COUNTRIES",
|
||||
"signature": "INVALID_FORBIDDEN_COUNTRIES()",
|
||||
"selector": "0xbf21b11c",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 152
|
||||
},
|
||||
{
|
||||
"name": "NotRegistered",
|
||||
"signature": "NotRegistered(address)",
|
||||
"selector": "0xbfc6c337",
|
||||
"file": "contracts/contracts/example/Airdrop.sol",
|
||||
"line": 63
|
||||
},
|
||||
{
|
||||
"name": "NotRegistered",
|
||||
"signature": "NotRegistered(address)",
|
||||
"selector": "0xbfc6c337",
|
||||
"file": "contracts/contracts/tests/TestAirdrop.sol",
|
||||
"line": 37
|
||||
},
|
||||
{
|
||||
"name": "InvalidOfacRoots",
|
||||
"signature": "InvalidOfacRoots()",
|
||||
"selector": "0xc67a44d2",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 223
|
||||
},
|
||||
{
|
||||
"name": "InvalidOfacRoots",
|
||||
"signature": "InvalidOfacRoots()",
|
||||
"selector": "0xc67a44d2",
|
||||
"file": "contracts/contracts/libraries/OfacCheckLib.sol",
|
||||
"line": 19
|
||||
},
|
||||
{
|
||||
"name": "CurrentDateNotInValidRange",
|
||||
"signature": "CurrentDateNotInValidRange()",
|
||||
"selector": "0xcf46551c",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 155
|
||||
},
|
||||
{
|
||||
"name": "INVALID_VC_AND_DISCLOSE_PROOF",
|
||||
"signature": "INVALID_VC_AND_DISCLOSE_PROOF()",
|
||||
"selector": "0xd4d37a7a",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 168
|
||||
},
|
||||
{
|
||||
"name": "AttestationIdMismatch",
|
||||
"signature": "AttestationIdMismatch()",
|
||||
"selector": "0xd7ca437d",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 219
|
||||
},
|
||||
{
|
||||
"name": "InvalidVcAndDiscloseProof",
|
||||
"signature": "InvalidVcAndDiscloseProof()",
|
||||
"selector": "0xda7bd3a6",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 167
|
||||
},
|
||||
{
|
||||
"name": "InvalidVcAndDiscloseProof",
|
||||
"signature": "InvalidVcAndDiscloseProof()",
|
||||
"selector": "0xda7bd3a6",
|
||||
"file": "contracts/contracts/libraries/ProofVerifierLib.sol",
|
||||
"line": 18
|
||||
},
|
||||
{
|
||||
"name": "RegistryNotSet",
|
||||
"signature": "RegistryNotSet()",
|
||||
"selector": "0xe048e710",
|
||||
"file": "contracts/contracts/libraries/RootCheckLib.sol",
|
||||
"line": 25
|
||||
},
|
||||
{
|
||||
"name": "INVALID_REVEALED_DATA_TYPE",
|
||||
"signature": "INVALID_REVEALED_DATA_TYPE()",
|
||||
"selector": "0xe0f15544",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 184
|
||||
},
|
||||
{
|
||||
"name": "ScopeMismatch",
|
||||
"signature": "ScopeMismatch()",
|
||||
"selector": "0xe7bee380",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 187
|
||||
},
|
||||
{
|
||||
"name": "InvalidUserIdentifierInProof",
|
||||
"signature": "InvalidUserIdentifierInProof()",
|
||||
"selector": "0xebbcc178",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 203
|
||||
},
|
||||
{
|
||||
"name": "InvalidPubkeyCommitment",
|
||||
"signature": "InvalidPubkeyCommitment()",
|
||||
"selector": "0xebc2fedc",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 227
|
||||
},
|
||||
{
|
||||
"name": "InvalidPubkeyCommitment",
|
||||
"signature": "InvalidPubkeyCommitment()",
|
||||
"selector": "0xebc2fedc",
|
||||
"file": "contracts/contracts/libraries/RegisterProofVerifierLib.sol",
|
||||
"line": 40
|
||||
},
|
||||
{
|
||||
"name": "CURRENT_DATE_NOT_IN_VALID_RANGE",
|
||||
"signature": "CURRENT_DATE_NOT_IN_VALID_RANGE()",
|
||||
"selector": "0xed8cf9ff",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 144
|
||||
},
|
||||
{
|
||||
"name": "INVALID_ROOT_CA",
|
||||
"signature": "INVALID_ROOT_CA()",
|
||||
"selector": "0xee57533e",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryKycImplV1.sol",
|
||||
"line": 185
|
||||
},
|
||||
{
|
||||
"name": "InvalidUserIdentifier",
|
||||
"signature": "InvalidUserIdentifier()",
|
||||
"selector": "0xf0c426db",
|
||||
"file": "contracts/contracts/example/Airdrop.sol",
|
||||
"line": 75
|
||||
},
|
||||
{
|
||||
"name": "InvalidUserIdentifier",
|
||||
"signature": "InvalidUserIdentifier()",
|
||||
"selector": "0xf0c426db",
|
||||
"file": "contracts/contracts/example/SelfIdentityERC721.sol",
|
||||
"line": 52
|
||||
},
|
||||
{
|
||||
"name": "InvalidUserIdentifier",
|
||||
"signature": "InvalidUserIdentifier()",
|
||||
"selector": "0xf0c426db",
|
||||
"file": "contracts/contracts/example/SelfPassportERC721.sol",
|
||||
"line": 49
|
||||
},
|
||||
{
|
||||
"name": "InvalidUserIdentifier",
|
||||
"signature": "InvalidUserIdentifier()",
|
||||
"selector": "0xf0c426db",
|
||||
"file": "contracts/contracts/tests/TestAirdrop.sol",
|
||||
"line": 41
|
||||
},
|
||||
{
|
||||
"name": "INVALID_OLDER_THAN",
|
||||
"signature": "INVALID_OLDER_THAN()",
|
||||
"selector": "0xf0e539b9",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV1.sol",
|
||||
"line": 148
|
||||
},
|
||||
{
|
||||
"name": "InvalidIdentityCommitmentRoot",
|
||||
"signature": "InvalidIdentityCommitmentRoot()",
|
||||
"selector": "0xf53393a7",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 171
|
||||
},
|
||||
{
|
||||
"name": "InvalidIdentityCommitmentRoot",
|
||||
"signature": "InvalidIdentityCommitmentRoot()",
|
||||
"selector": "0xf53393a7",
|
||||
"file": "contracts/contracts/libraries/RootCheckLib.sol",
|
||||
"line": 19
|
||||
},
|
||||
{
|
||||
"name": "TEE_NOT_SET",
|
||||
"signature": "TEE_NOT_SET()",
|
||||
"selector": "0xfc833fc6",
|
||||
"file": "contracts/contracts/registry/IdentityRegistryKycImplV1.sol",
|
||||
"line": 175
|
||||
},
|
||||
{
|
||||
"name": "LengthMismatch",
|
||||
"signature": "LengthMismatch()",
|
||||
"selector": "0xff633a38",
|
||||
"file": "contracts/contracts/IdentityVerificationHubImplV2.sol",
|
||||
"line": 147
|
||||
}
|
||||
]
|
||||
@@ -186,7 +186,7 @@
|
||||
"react-native-blur-effect": "^1.1.3",
|
||||
"react-native-haptic-feedback": "^2.3.3",
|
||||
"react-native-localize": "^3.6.1",
|
||||
"react-native-svg": "15.15.1",
|
||||
"react-native-svg": "15.12.1",
|
||||
"react-native-web": "^0.21.2",
|
||||
"react-native-webview": "13.16.0",
|
||||
"tsup": "^8.0.1",
|
||||
|
||||
@@ -131,6 +131,7 @@ const styles = StyleSheet.create({
|
||||
borderRadius: 5,
|
||||
},
|
||||
text: {
|
||||
flex: 1,
|
||||
fontFamily: dinot,
|
||||
textAlign: 'center',
|
||||
fontSize: 18,
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// 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 React from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { BodyText, PrimaryButton, SecondaryButton, View, YStack } from '../../components';
|
||||
import ButtonsContainer from '../../components/ButtonsContainer';
|
||||
import { black, slate400, white } from '../../constants/colors';
|
||||
import { advercase, dinot } from '../../constants/fonts';
|
||||
import { useSelfClient } from '../../context';
|
||||
import { buttonTap } from '../../haptic';
|
||||
import { ExpandableBottomLayout } from '../../layouts/ExpandableBottomLayout';
|
||||
import { SdkEvents } from '../../types/events';
|
||||
|
||||
type LogoConfirmationScreenProps = {
|
||||
documentType: string;
|
||||
countryCode: string;
|
||||
logo: ReactNode;
|
||||
safeAreaBottom?: number;
|
||||
onConfirm?: () => void;
|
||||
onNotFound?: () => void;
|
||||
};
|
||||
|
||||
const LogoConfirmationScreen: React.FC<LogoConfirmationScreenProps> = ({
|
||||
documentType,
|
||||
countryCode,
|
||||
logo,
|
||||
safeAreaBottom,
|
||||
onConfirm,
|
||||
onNotFound,
|
||||
}) => {
|
||||
const selfClient = useSelfClient();
|
||||
|
||||
const onYesPress = () => {
|
||||
buttonTap();
|
||||
if (onConfirm) {
|
||||
onConfirm();
|
||||
} else {
|
||||
selfClient.emit(SdkEvents.LOGO_CONFIRMED, { documentType, countryCode });
|
||||
}
|
||||
};
|
||||
|
||||
const onNoPress = () => {
|
||||
buttonTap();
|
||||
if (onNotFound) {
|
||||
onNotFound();
|
||||
} else {
|
||||
selfClient.emit(SdkEvents.LOGO_NOT_FOUND, { documentType, countryCode });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ExpandableBottomLayout.Layout backgroundColor={white}>
|
||||
<ExpandableBottomLayout.TopSection backgroundColor={white} safeAreaTop={0}>
|
||||
<YStack alignItems="center" gap="$6" maxWidth={340}>
|
||||
<BodyText style={styles.titleText}>Does your document have this symbol?</BodyText>
|
||||
|
||||
<View style={styles.logoContainer}>{logo}</View>
|
||||
|
||||
<BodyText style={styles.descriptionText}>
|
||||
This symbol indicates your document has a biometric chip, which is required for registration.
|
||||
</BodyText>
|
||||
</YStack>
|
||||
</ExpandableBottomLayout.TopSection>
|
||||
|
||||
<ExpandableBottomLayout.BottomSection backgroundColor={white} safeAreaBottom={safeAreaBottom}>
|
||||
<ButtonsContainer>
|
||||
<PrimaryButton onPress={onYesPress}>Yes</PrimaryButton>
|
||||
<SecondaryButton onPress={onNoPress}>No</SecondaryButton>
|
||||
</ButtonsContainer>
|
||||
</ExpandableBottomLayout.BottomSection>
|
||||
</ExpandableBottomLayout.Layout>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
titleText: {
|
||||
fontSize: 20,
|
||||
fontFamily: advercase,
|
||||
textAlign: 'center',
|
||||
color: black,
|
||||
},
|
||||
logoContainer: {
|
||||
backgroundColor: white,
|
||||
borderRadius: 16,
|
||||
padding: 24,
|
||||
shadowColor: black,
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 8,
|
||||
elevation: 4,
|
||||
},
|
||||
descriptionText: {
|
||||
fontSize: 16,
|
||||
fontFamily: dinot,
|
||||
textAlign: 'center',
|
||||
color: slate400,
|
||||
},
|
||||
});
|
||||
|
||||
export default LogoConfirmationScreen;
|
||||
@@ -70,6 +70,8 @@ export {
|
||||
sdkError,
|
||||
} from './errors';
|
||||
|
||||
export { default as LogoConfirmationScreen } from './flows/onboarding/logo-confirmation-screen';
|
||||
|
||||
export { NFCScannerScreen } from './components/screens/NFCScannerScreen';
|
||||
|
||||
export { type ProvingStateType } from './proving/provingMachine';
|
||||
|
||||
@@ -165,6 +165,20 @@ export enum SdkEvents {
|
||||
*
|
||||
*/
|
||||
DOCUMENT_OWNERSHIP_CONFIRMED = 'DOCUMENT_OWNERSHIP_CONFIRMED',
|
||||
|
||||
/**
|
||||
* Emitted when the user confirms they see the e-passport chip logo on their document.
|
||||
*
|
||||
* **Required:** Navigate to the document scanning flow (DocumentOnboarding).
|
||||
*/
|
||||
LOGO_CONFIRMED = 'LOGO_CONFIRMED',
|
||||
|
||||
/**
|
||||
* Emitted when the user indicates they do not see the e-passport chip logo on their document.
|
||||
*
|
||||
* **Required:** Show an error message indicating the document is not supported as it is not a biometric ID.
|
||||
*/
|
||||
LOGO_NOT_FOUND = 'LOGO_NOT_FOUND',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,6 +237,14 @@ export interface SDKEventMap {
|
||||
signatureAlgorithm?: string;
|
||||
curveOrExponent?: string;
|
||||
};
|
||||
[SdkEvents.LOGO_CONFIRMED]: {
|
||||
documentType: string;
|
||||
countryCode: string;
|
||||
};
|
||||
[SdkEvents.LOGO_NOT_FOUND]: {
|
||||
documentType: string;
|
||||
countryCode: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,10 +34,9 @@ target "SelfDemoApp" do
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
:hermes_enabled => true,
|
||||
# An absolute path to your application root.
|
||||
:app_path => "#{Pod::Config.instance.installation_root}/..",
|
||||
:fabric_enabled => false,
|
||||
:hermes_enabled => true,
|
||||
)
|
||||
|
||||
# Use the custom NFCPassportReader fork
|
||||
|
||||
@@ -8,17 +8,32 @@ PODS:
|
||||
- hermes-engine (0.76.9):
|
||||
- hermes-engine/Pre-built (= 0.76.9)
|
||||
- hermes-engine/Pre-built (0.76.9)
|
||||
- Mixpanel-swift (5.0.0):
|
||||
- Mixpanel-swift/Complete (= 5.0.0)
|
||||
- Mixpanel-swift/Complete (5.0.0)
|
||||
- lottie-ios (4.5.0)
|
||||
- lottie-react-native (7.2.2):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
- lottie-ios (= 4.5.0)
|
||||
- RCT-Folly (= 2024.10.14.00)
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- mobile-sdk-alpha (0.1.0):
|
||||
- NFCPassportReader
|
||||
- QKMRZParser
|
||||
- React-Core
|
||||
- NFCPassportReader (2.1.1):
|
||||
- Mixpanel-swift (~> 5.0.0)
|
||||
- OpenSSL-Universal (= 1.1.1900)
|
||||
- OpenSSL-Universal (1.1.1900)
|
||||
- QKMRZParser (2.0.0)
|
||||
- RCT-Folly (2024.10.14.00):
|
||||
- boost
|
||||
@@ -1300,7 +1315,7 @@ PODS:
|
||||
- Yoga
|
||||
- react-native-get-random-values (1.11.0):
|
||||
- React-Core
|
||||
- react-native-safe-area-context (5.6.1):
|
||||
- react-native-safe-area-context (5.6.2):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@@ -1313,8 +1328,8 @@ PODS:
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- react-native-safe-area-context/common (= 5.6.1)
|
||||
- react-native-safe-area-context/fabric (= 5.6.1)
|
||||
- react-native-safe-area-context/common (= 5.6.2)
|
||||
- react-native-safe-area-context/fabric (= 5.6.2)
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-rendererdebug
|
||||
@@ -1323,7 +1338,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- react-native-safe-area-context/common (5.6.1):
|
||||
- react-native-safe-area-context/common (5.6.2):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@@ -1344,7 +1359,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- react-native-safe-area-context/fabric (5.6.1):
|
||||
- react-native-safe-area-context/fabric (5.6.2):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@@ -1366,6 +1381,27 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- react-native-webview (13.16.0):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
- RCT-Folly (= 2024.10.14.00)
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- React-nativeconfig (0.76.9)
|
||||
- React-NativeModulesApple (0.76.9):
|
||||
- glog
|
||||
@@ -1680,6 +1716,27 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- RNLocalize (3.6.1):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
- RCT-Folly (= 2024.10.14.00)
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- RNReactNativeHapticFeedback (2.3.3):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
@@ -1701,7 +1758,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- RNSVG (15.12.1):
|
||||
- RNSVG (15.15.1):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@@ -1721,9 +1778,9 @@ PODS:
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- RNSVG/common (= 15.12.1)
|
||||
- RNSVG/common (= 15.15.1)
|
||||
- Yoga
|
||||
- RNSVG/common (15.12.1):
|
||||
- RNSVG/common (15.15.1):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@@ -1776,8 +1833,8 @@ DEPENDENCIES:
|
||||
- fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`)
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
|
||||
- lottie-react-native (from `../node_modules/lottie-react-native`)
|
||||
- "mobile-sdk-alpha (from `../node_modules/@selfxyz/mobile-sdk-alpha`)"
|
||||
- "NFCPassportReader (from `git@github.com:selfxyz/NFCPassportReader.git`, commit `04ede227cbfd377e2b4bc9b38f9a89eebdcab52f`)"
|
||||
- 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`)
|
||||
- RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)
|
||||
@@ -1811,6 +1868,7 @@ DEPENDENCIES:
|
||||
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
|
||||
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
|
||||
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
|
||||
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
|
||||
@@ -1840,6 +1898,7 @@ DEPENDENCIES:
|
||||
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
|
||||
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
|
||||
- RNKeychain (from `../node_modules/react-native-keychain`)
|
||||
- RNLocalize (from `../node_modules/react-native-localize`)
|
||||
- RNReactNativeHapticFeedback (from `../node_modules/react-native-haptic-feedback`)
|
||||
- RNSVG (from `../node_modules/react-native-svg`)
|
||||
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
|
||||
@@ -1847,8 +1906,7 @@ DEPENDENCIES:
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- Mixpanel-swift
|
||||
- OpenSSL-Universal
|
||||
- lottie-ios
|
||||
- QKMRZParser
|
||||
- SocketRocket
|
||||
|
||||
@@ -1868,11 +1926,10 @@ EXTERNAL SOURCES:
|
||||
hermes-engine:
|
||||
:podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
|
||||
:tag: hermes-2024-11-12-RNv0.76.2-5b4aa20c719830dcf5684832b89a6edb95ac3d64
|
||||
lottie-react-native:
|
||||
:path: "../node_modules/lottie-react-native"
|
||||
mobile-sdk-alpha:
|
||||
:path: "../node_modules/@selfxyz/mobile-sdk-alpha"
|
||||
NFCPassportReader:
|
||||
:commit: 04ede227cbfd377e2b4bc9b38f9a89eebdcab52f
|
||||
:git: "git@github.com:selfxyz/NFCPassportReader.git"
|
||||
RCT-Folly:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
|
||||
RCTDeprecation:
|
||||
@@ -1935,6 +1992,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-get-random-values"
|
||||
react-native-safe-area-context:
|
||||
:path: "../node_modules/react-native-safe-area-context"
|
||||
react-native-webview:
|
||||
:path: "../node_modules/react-native-webview"
|
||||
React-nativeconfig:
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
React-NativeModulesApple:
|
||||
@@ -1993,6 +2052,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/@react-native-async-storage/async-storage"
|
||||
RNKeychain:
|
||||
:path: "../node_modules/react-native-keychain"
|
||||
RNLocalize:
|
||||
:path: "../node_modules/react-native-localize"
|
||||
RNReactNativeHapticFeedback:
|
||||
:path: "../node_modules/react-native-haptic-feedback"
|
||||
RNSVG:
|
||||
@@ -2002,11 +2063,6 @@ EXTERNAL SOURCES:
|
||||
Yoga:
|
||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
NFCPassportReader:
|
||||
:commit: 04ede227cbfd377e2b4bc9b38f9a89eebdcab52f
|
||||
:git: "git@github.com:selfxyz/NFCPassportReader.git"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost: 1dca942403ed9342f98334bf4c3621f011aa7946
|
||||
DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385
|
||||
@@ -2015,10 +2071,9 @@ SPEC CHECKSUMS:
|
||||
fmt: 01b82d4ca6470831d1cc0852a1af644be019e8f6
|
||||
glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a
|
||||
hermes-engine: 9e868dc7be781364296d6ee2f56d0c1a9ef0bb11
|
||||
Mixpanel-swift: e9bef28a9648faff384d5ba6f48ecc2787eb24c0
|
||||
mobile-sdk-alpha: 126edf71b65b5a9e294725e4353c2705fa0fd20d
|
||||
NFCPassportReader: 48873f856f91215dbfa1eaaec20eae639672862e
|
||||
OpenSSL-Universal: 84efb8a29841f2764ac5403e0c4119a28b713346
|
||||
lottie-ios: a881093fab623c467d3bce374367755c272bdd59
|
||||
lottie-react-native: cf02eef1b7d48972f9422cf24f2f7b9a49235a32
|
||||
mobile-sdk-alpha: 4daf7bbd7c8d7f4a71947234414c2c7f29777b1f
|
||||
QKMRZParser: 6b419b6f07d6bff6b50429b97de10846dc902c29
|
||||
RCT-Folly: ea9d9256ba7f9322ef911169a9f696e5857b9e17
|
||||
RCTDeprecation: ebe712bb05077934b16c6bf25228bdec34b64f83
|
||||
@@ -2050,7 +2105,8 @@ SPEC CHECKSUMS:
|
||||
React-Mapbuffer: 33546a3ebefbccb8770c33a1f8a5554fa96a54de
|
||||
React-microtasksnativemodule: d80ff86c8902872d397d9622f1a97aadcc12cead
|
||||
react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba
|
||||
react-native-safe-area-context: 76bd6904253fc0f68fbc3d7f594b6a394d0ac34c
|
||||
react-native-safe-area-context: a588bcb12a67155e7e16e7b322d4cc43794ed701
|
||||
react-native-webview: 416f3e4913deea20b2b7fdc5c44cb9ebf93509dc
|
||||
React-nativeconfig: 8efdb1ef1e9158c77098a93085438f7e7b463678
|
||||
React-NativeModulesApple: cebca2e5320a3d66e123cade23bd90a167ffce5e
|
||||
React-perflogger: 72e653eb3aba9122f9e57cf012d22d2486f33358
|
||||
@@ -2080,12 +2136,13 @@ SPEC CHECKSUMS:
|
||||
ReactCommon: 300d8d9c5cb1a6cd79a67cf5d8f91e4d477195f9
|
||||
RNCAsyncStorage: 87a74d13ba0128f853817e45e21c4051e1f2cd45
|
||||
RNKeychain: 850638785745df5f70c37251130617a66ec82102
|
||||
RNLocalize: bb04044d17e11029cbd3d6df7629f703434498de
|
||||
RNReactNativeHapticFeedback: a4429a7c923cdd31d44c74417b8330d8e93737bd
|
||||
RNSVG: 8dd938fb169dd81009b74c2334780d7d2a04a373
|
||||
RNSVG: bf688cc1423de273f23abc4c79acac9ad2d61d95
|
||||
RNVectorIcons: c95fdae217b0ed388f2b4d7ed7a4edc457c1df47
|
||||
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
|
||||
Yoga: feb4910aba9742cfedc059e2b2902e22ffe9954a
|
||||
|
||||
PODFILE CHECKSUM: fdd4646d0ec2813c3447acfca4303578e4346210
|
||||
PODFILE CHECKSUM: c5da90e01a1f2fbadddbc1fe2c36caacd4e29251
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"clean": "rm -rf ios/build android/app/build android/build && cd android && ./gradlew clean && cd ..",
|
||||
"format": "yarn nice",
|
||||
"ia": "yarn install-app",
|
||||
"postinstall": "npx patch-package --patch-dir ../../patches || true",
|
||||
"install-app": "yarn install && yarn prebuild && cd ios && pod install && cd ..",
|
||||
"preios": "yarn prebuild",
|
||||
"ios": "react-native run-ios",
|
||||
@@ -48,7 +49,7 @@
|
||||
"react-native-keychain": "^10.0.0",
|
||||
"react-native-localize": "^3.6.1",
|
||||
"react-native-safe-area-context": "^5.6.2",
|
||||
"react-native-svg": "15.15.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",
|
||||
|
||||
@@ -5,12 +5,18 @@ index 0000000..0000001 100644
|
||||
@@ -77,11 +77,11 @@ dependencies {
|
||||
implementation "com.sumsub.sns:idensic-mobile-sdk:1.40.2"
|
||||
|
||||
// Enable Device Intelligence (Fisherman) for fraud detection
|
||||
// Privacy: Declare device fingerprinting/identifiers in Google Play Data Safety form
|
||||
// remove comment to enable Device Intelligence
|
||||
- // implementation "com.sumsub.sns:idensic-mobile-sdk-fisherman:1.40.2"
|
||||
+ implementation "com.sumsub.sns:idensic-mobile-sdk-fisherman:1.40.2"
|
||||
// VideoIdent disabled on both iOS and Android for current release
|
||||
// Reason: Avoids microphone permission requirements (FOREGROUND_SERVICE_MICROPHONE on Android)
|
||||
// Feature: Provides liveness checks via live video calls with human agents
|
||||
// TODO: Re-enable on both platforms for future release when liveness checks are needed
|
||||
// remove comment if you need VideoIdent support
|
||||
- // implementation "com.sumsub.sns:idensic-mobile-sdk-videoident:1.40.2"
|
||||
+ implementation "com.sumsub.sns:idensic-mobile-sdk-videoident:1.40.2"
|
||||
+ // implementation "com.sumsub.sns:idensic-mobile-sdk-videoident:1.40.2"
|
||||
// remove comment if you need EID support
|
||||
// implementation "com.sumsub.sns:idensic-mobile-sdk-eid:1.40.2"
|
||||
// remove comment if you need NFC support
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@selfxyz/core",
|
||||
"version": "1.1.0-beta.7",
|
||||
"version": "1.2.0-beta.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/selfxyz/self"
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
AadhaarVerifier__factory,
|
||||
IdentityVerificationHubImpl,
|
||||
IdentityVerificationHubImpl__factory,
|
||||
KycVerifier,
|
||||
KycVerifier__factory,
|
||||
Registry__factory,
|
||||
Verifier,
|
||||
Verifier__factory,
|
||||
@@ -222,7 +224,26 @@ export class SelfBackendVerifier {
|
||||
let circuitTimestampYy: number[];
|
||||
let circuitTimestampMm: number[];
|
||||
let circuitTimestampDd: number[];
|
||||
if (attestationId === 3) {
|
||||
if (attestationId === 4) {
|
||||
circuitTimestampYy = publicSignals
|
||||
.slice(
|
||||
discloseIndices[attestationId].currentDateIndex,
|
||||
discloseIndices[attestationId].currentDateIndex + 4
|
||||
)
|
||||
.map(Number);
|
||||
circuitTimestampMm = publicSignals
|
||||
.slice(
|
||||
discloseIndices[attestationId].currentDateIndex + 4,
|
||||
discloseIndices[attestationId].currentDateIndex + 6
|
||||
)
|
||||
.map(Number);
|
||||
circuitTimestampDd = publicSignals
|
||||
.slice(
|
||||
discloseIndices[attestationId].currentDateIndex + 6,
|
||||
discloseIndices[attestationId].currentDateIndex + 8
|
||||
)
|
||||
.map(Number);
|
||||
} else if (attestationId === 3) {
|
||||
circuitTimestampYy = String(publicSignals[discloseIndices[attestationId].currentDateIndex])
|
||||
.split('')
|
||||
.map(Number);
|
||||
@@ -285,7 +306,7 @@ export class SelfBackendVerifier {
|
||||
throw new ConfigMismatchError(issues);
|
||||
}
|
||||
|
||||
let verifierContract: Verifier | AadhaarVerifier;
|
||||
let verifierContract: Verifier | AadhaarVerifier | KycVerifier;
|
||||
try {
|
||||
const verifierAddress = await this.identityVerificationHubContract.discloseVerifier(
|
||||
'0x' + attestationId.toString(16).padStart(64, '0')
|
||||
@@ -293,7 +314,9 @@ export class SelfBackendVerifier {
|
||||
if (verifierAddress === '0x0000000000000000000000000000000000000000') {
|
||||
throw new VerifierContractError('Verifier contract not found');
|
||||
}
|
||||
if (attestationId === 3) {
|
||||
if (attestationId === 4) {
|
||||
verifierContract = KycVerifier__factory.connect(verifierAddress, this.provider);
|
||||
} else if (attestationId === 3) {
|
||||
verifierContract = AadhaarVerifier__factory.connect(verifierAddress, this.provider);
|
||||
} else {
|
||||
verifierContract = Verifier__factory.connect(verifierAddress, this.provider);
|
||||
|
||||
36
sdk/core/src/abi/KycVerifier.json
Normal file
36
sdk/core/src/abi/KycVerifier.json
Normal file
@@ -0,0 +1,36 @@
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256[2]",
|
||||
"name": "a",
|
||||
"type": "uint256[2]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[2][2]",
|
||||
"name": "b",
|
||||
"type": "uint256[2][2]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[2]",
|
||||
"name": "c",
|
||||
"type": "uint256[2]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[29]",
|
||||
"name": "pubSignals",
|
||||
"type": "uint256[29]"
|
||||
}
|
||||
],
|
||||
"name": "verifyProof",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
@@ -50,9 +50,9 @@ export const discloseIndices = {
|
||||
// Selfrica ID Card - see CircuitConstantsV2.sol for layout documentation
|
||||
4: {
|
||||
revealedDataPackedIndex: 0,
|
||||
forbiddenCountriesListPackedIndex: 9,
|
||||
nullifierIndex: 13,
|
||||
attestationIdIndex: 29,
|
||||
forbiddenCountriesListPackedIndex: 10,
|
||||
nullifierIndex: 14,
|
||||
attestationIdIndex: 15,
|
||||
merkleRootIndex: 17,
|
||||
currentDateIndex: 21,
|
||||
namedobSmtRootIndex: 18,
|
||||
@@ -139,25 +139,24 @@ export const revealedDataIndices: Record<
|
||||
ofacEnd: 117,
|
||||
},
|
||||
4: {
|
||||
//put everything as 99
|
||||
issuingStateStart: 99,
|
||||
issuingStateEnd: 99,
|
||||
nameStart: 99,
|
||||
nameEnd: 99,
|
||||
idNumberStart: 99,
|
||||
idNumberEnd: 99,
|
||||
nationalityStart: 99,
|
||||
nationalityEnd: 99,
|
||||
dateOfBirthStart: 99,
|
||||
dateOfBirthEnd: 99,
|
||||
genderStart: 99,
|
||||
genderEnd: 99,
|
||||
expiryDateStart: 99,
|
||||
expiryDateEnd: 99,
|
||||
olderThanStart: 99,
|
||||
olderThanEnd: 99,
|
||||
ofacStart: 99,
|
||||
ofacEnd: 99,
|
||||
nameStart: 78,
|
||||
nameEnd: 141,
|
||||
idNumberStart: 30,
|
||||
idNumberEnd: 61,
|
||||
nationalityStart: 0,
|
||||
nationalityEnd: 2,
|
||||
dateOfBirthStart: 142,
|
||||
dateOfBirthEnd: 149,
|
||||
genderStart: 194,
|
||||
genderEnd: 194,
|
||||
expiryDateStart: 70,
|
||||
expiryDateEnd: 77,
|
||||
olderThanStart: 297,
|
||||
olderThanEnd: 297,
|
||||
ofacStart: 295,
|
||||
ofacEnd: 296,
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -23,12 +23,19 @@ export const formatRevealedDataPacked = (
|
||||
discloseIndices[attestationId].forbiddenCountriesListPackedIndex,
|
||||
discloseIndices[attestationId].forbiddenCountriesListPackedIndex + 4
|
||||
);
|
||||
const issuingState = revealedDataPackedString
|
||||
.subarray(
|
||||
revealedDataIndices[attestationId].issuingStateStart,
|
||||
revealedDataIndices[attestationId].issuingStateEnd + 1
|
||||
)
|
||||
.toString('utf-8');
|
||||
let issuingState = '';
|
||||
|
||||
if (attestationId === 4) {
|
||||
issuingState = 'UNAVAILABLE';
|
||||
} else {
|
||||
issuingState = revealedDataPackedString
|
||||
.subarray(
|
||||
revealedDataIndices[attestationId].issuingStateStart,
|
||||
revealedDataIndices[attestationId].issuingStateEnd + 1
|
||||
)
|
||||
.toString('utf-8');
|
||||
}
|
||||
|
||||
const name = revealedDataPackedString
|
||||
.subarray(
|
||||
revealedDataIndices[attestationId].nameStart,
|
||||
@@ -93,7 +100,7 @@ export const formatRevealedDataPacked = (
|
||||
.toString('utf-8');
|
||||
}
|
||||
let olderThan: string;
|
||||
if (attestationId === 3) {
|
||||
if (attestationId === 3 || attestationId === 4) {
|
||||
olderThan = revealedDataPackedString
|
||||
.subarray(
|
||||
revealedDataIndices[attestationId].olderThanStart,
|
||||
|
||||
@@ -20,7 +20,7 @@ export function getRevealedDataPublicSignalsLength(attestationId: AttestationId)
|
||||
case 3:
|
||||
return Math.ceil(119 / 31);
|
||||
case 4:
|
||||
return Math.ceil(30 / 31);
|
||||
return Math.ceil(298 / 31);
|
||||
default:
|
||||
throw new ProofError(`Invalid attestation ID: ${attestationId}`);
|
||||
}
|
||||
@@ -30,7 +30,7 @@ export const bytesCount: Record<AttestationId, number[]> = {
|
||||
1: [31, 31, 31],
|
||||
2: [31, 31, 31, 1],
|
||||
3: [31, 31, 31, 26],
|
||||
4: [31, 31, 31, 26],
|
||||
4: [31, 31, 31, 31, 31, 31, 31, 31, 31, 19],
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
18
yarn.lock
18
yarn.lock
@@ -9149,7 +9149,7 @@ __metadata:
|
||||
react-native-haptic-feedback: "npm:^2.3.3"
|
||||
react-native-localize: "npm:^3.6.1"
|
||||
react-native-nfc-manager: "npm:^3.17.2"
|
||||
react-native-svg: "npm:15.15.1"
|
||||
react-native-svg: "npm:15.12.1"
|
||||
react-native-svg-circle-country-flags: "npm:^0.2.2"
|
||||
react-native-web: "npm:^0.21.2"
|
||||
react-native-webview: "npm:13.16.0"
|
||||
@@ -27347,7 +27347,7 @@ __metadata:
|
||||
react-native-keychain: "npm:^10.0.0"
|
||||
react-native-localize: "npm:^3.6.1"
|
||||
react-native-safe-area-context: "npm:^5.6.2"
|
||||
react-native-svg: "npm:15.15.1"
|
||||
react-native-svg: "npm:15.12.1"
|
||||
react-native-svg-transformer: "npm:^1.5.2"
|
||||
react-native-vector-icons: "npm:^10.3.0"
|
||||
react-native-webview: "npm:13.16.0"
|
||||
@@ -30329,20 +30329,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-native-svg@npm:15.15.1":
|
||||
version: 15.15.1
|
||||
resolution: "react-native-svg@npm:15.15.1"
|
||||
dependencies:
|
||||
css-select: "npm:^5.1.0"
|
||||
css-tree: "npm:^1.1.3"
|
||||
warn-once: "npm:0.1.1"
|
||||
peerDependencies:
|
||||
react: "*"
|
||||
react-native: "*"
|
||||
checksum: 10c0/9e047e8afdd5121296a3402c5c37363b9b37fbddc41f7b0b49b923ac4d2898954c8b57a6784a625321236987e494ee54f594e22f9de815f807e03a3433d7fefd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-native-url-polyfill@npm:2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "react-native-url-polyfill@npm:2.0.0"
|
||||
|
||||
Reference in New Issue
Block a user