diff --git a/.talismanrc b/.talismanrc
index 8c12920152..6be78e3520 100644
--- a/.talismanrc
+++ b/.talismanrc
@@ -2,7 +2,7 @@ fileignoreconfig:
- filename: package.json
checksum: a5a22604f2b9d917adaf3561fdcaeccd4e69b92e1240070480d7498e1eda5162
- filename: package-lock.json
- checksum: 910a3751a562d84a05a343bb4cc0f1639efb9ce7ae8dffbc662981fca3aa0d25
+ checksum: 30d7382d644494812081e7902d619a38ca97ef86fb06dcac494b1d601e414228
- filename: components/PasscodeVerify.tsx
checksum: 14654c0f038979fcd0d260170a45894a072f81e0767ca9a0e66935d33b5cc703
- filename: i18n.ts
@@ -24,7 +24,11 @@ fileignoreconfig:
- filename: screens/Home/IntroSlidersScreen.tsx
checksum: 9ff212530850e9f921f91d406c01f67ea5dd2a44e9cdb5e63d977749c7a7c5bd
- filename: machines/store.ts
- checksum: fda6511ab88566820cf6b7324178b4646f495fae5168af73a0fb1c3bb52e5b8d
+ checksum: fda6511ab88566820cf6b7324178b4646f495fae5168af73a0fb1c3bb52e5b8d
+- filename: machines/issuersMachine.ts
+ checksum: 37805c0e32765ed07ab54df230d6045679a0f5415eacdac3385fc27e9b298504
+- filename: machines/issuersMachine.typegen.ts
+ checksum: 35e8bfe7945b963edceb9b33532cbaf2ee8cb93356dda526e48f2a1e4efec865
- filename: screens/Home/MyVcs/IdInputModal.tsx
checksum: 6f21b2caf20ee1821b69e178a69e755e0b1094275e4a9f9cb4ae5ab515ce5d23
- filename: screens/WelcomeScreenController.ts
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 4b914efb4a..a34a12ec46 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -1,4 +1,4 @@
-plugins {
+plugins {
id 'com.gladed.androidgitversion' version '0.4.14'
}
@@ -105,9 +105,6 @@ android {
}
- namespace 'io.mosip.residentapp'
-
-
namespace 'io.mosip.residentapp'
defaultConfig {
@@ -134,7 +131,7 @@ android {
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
-
+
signingConfigs {
release {
def keystore = file('release.keystore')
@@ -236,7 +233,7 @@ dependencies {
implementation 'com.facebook.fresco:animated-webp:2.0.0'
}
}
-
+ compileOnly project(':react-native-android-location-services-dialog-box')
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
@@ -256,4 +253,4 @@ dependencies {
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
-apply from: "./eas-build.gradle"
\ No newline at end of file
+apply from: "./eas-build.gradle"
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 4e96975dca..e025fa3e81 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -9,11 +9,8 @@
-
-
-
diff --git a/android/settings.gradle b/android/settings.gradle
index cf75b94c96..38e206ee8b 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -3,6 +3,7 @@ rootProject.name = 'Inji'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../node_modules/react-native-gradle-plugin')
-
+include ':react-native-android-location-services-dialog-box'
+project(':react-native-android-location-services-dialog-box').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-android-location-services-dialog-box/android')
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle")
-useExpoModules()
\ No newline at end of file
+useExpoModules()
diff --git a/components/PasscodeVerify.tsx b/components/PasscodeVerify.tsx
index 0f58edc8c5..3bdac095b3 100644
--- a/components/PasscodeVerify.tsx
+++ b/components/PasscodeVerify.tsx
@@ -3,6 +3,11 @@ import {useTranslation} from 'react-i18next';
import {PinInput} from './PinInput';
import {hashData} from '../shared/commonUtil';
import {argon2iConfig} from '../shared/constants';
+import {
+ getErrorEventData,
+ sendErrorEvent,
+} from '../shared/telemetry/TelemetryUtils';
+import {TelemetryConstants} from '../shared/telemetry/TelemetryConstants';
export const MAX_PIN = 6;
@@ -32,6 +37,13 @@ export const PasscodeVerify: React.FC = props => {
}
}
} catch (error) {
+ sendErrorEvent(
+ getErrorEventData(
+ TelemetryConstants.FlowType.appLogin,
+ TelemetryConstants.ErrorId.mismatch,
+ error,
+ ),
+ );
console.log('error:', error);
}
}
diff --git a/components/ui/Loader.tsx b/components/ui/Loader.tsx
index c23297ccec..e13d347b6b 100644
--- a/components/ui/Loader.tsx
+++ b/components/ui/Loader.tsx
@@ -64,7 +64,33 @@ export const Loader: React.FC = props => {
/>
+ {(props.isHintVisible || props.isBleErrorVisible) && (
+
+
+ {props.hint}
+
+ {props.onStayInProgress && (
+
+ )}
+ {props.onRetry && (
+
+ )}
+
+ )}
= props => {
};
export interface LoaderProps {
- isVisible: boolean;
title: string;
subTitle?: string;
label?: string;
hint?: string;
+ onStayInProgress?: () => void;
+ isHintVisible?: boolean;
+ isBleErrorVisible?: boolean;
onCancel?: () => void;
+ onRetry?: () => void;
requester?: boolean;
progress?: boolean | number;
onBackdropPress?: () => void;
diff --git a/injitest/src/main/java/io/mosip/test/mob/inji/pages/SettingsPage.java b/injitest/src/main/java/io/mosip/test/mob/inji/pages/SettingsPage.java
index 8416ff510f..a4199227e8 100644
--- a/injitest/src/main/java/io/mosip/test/mob/inji/pages/SettingsPage.java
+++ b/injitest/src/main/java/io/mosip/test/mob/inji/pages/SettingsPage.java
@@ -5,6 +5,12 @@
import io.appium.java_client.pagefactory.iOSXCUITFindBy;
import org.openqa.selenium.WebElement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
public class SettingsPage extends BasePage {
@AndroidFindBy(accessibility = "settingsScreen")
@@ -27,6 +33,21 @@ public class SettingsPage extends BasePage {
@iOSXCUITFindBy(iOSClassChain = "**/XCUIElementTypeStaticText[`label == \"Wika\"`]")
private WebElement wikaButton;
+ @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"listItemTitle\")")
+ private List languages;
+
+ @AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"aboutInji\")")
+ private WebElement aboutInji;
+
+ @AndroidFindBy(xpath = "//*[contains(@text,'Tuvali-version: v')]")
+ private WebElement tuvaliVersion;
+
+ @AndroidFindBy(accessibility = "injiTourGuide")
+ private WebElement injiTourGuide;
+
+ @AndroidFindBy(accessibility = "receivedCards")
+ private WebElement receivedCards;
+
public SettingsPage(AppiumDriver driver) {
super(driver);
}
@@ -53,4 +74,31 @@ public boolean verifyFilipinoLanguage() {
return this.isElementDisplayed(wikaButton, "Filipino language");
}
+ public boolean verifyLanguagesInLanguageFilter() {
+ List expectedLanguages = Arrays.asList("English", "Filipino", "عربى", "हिंदी", "ಕನ್ನಡ", "தமிழ்");
+
+ List actualLanguages = languages.stream()
+ .map(WebElement::getText)
+ .collect(Collectors.toList());
+
+ return new HashSet<>(expectedLanguages).equals(new HashSet<>(actualLanguages));
+ }
+
+ public SettingsPage clickOnAboutInji(){
+ clickOnElement(aboutInji);
+ return this;
+ }
+
+ public boolean isTuvaliVersionPresent() {
+ return this.isElementDisplayed(tuvaliVersion, "Tuvali-version");
+ }
+
+ public void clickOnInjiTourGuide(){
+ clickOnElement(injiTourGuide);
+ }
+
+ public boolean isReceivedCardsPresent() {
+ return this.isElementDisplayed(receivedCards, "Received Cards");
+ }
+
}
diff --git a/injitest/src/main/java/io/mosip/test/mob/inji/testcases/ChangeLanguageTest.java b/injitest/src/main/java/io/mosip/test/mob/inji/testcases/ChangeLanguageTest.java
index ebfea55a7b..6a0bae64ac 100644
--- a/injitest/src/main/java/io/mosip/test/mob/inji/testcases/ChangeLanguageTest.java
+++ b/injitest/src/main/java/io/mosip/test/mob/inji/testcases/ChangeLanguageTest.java
@@ -35,4 +35,58 @@ public void changeLanguage() {
assertTrue(settingsPage.verifyFilipinoLanguage(), "Verify if language is changed to filipino");
}
+
+ @Test
+ public void languageShouldBeInNativeLanguages() {
+ ChooseLanguagePage chooseLanguagePage = new ChooseLanguagePage(driver);
+
+ assertTrue(chooseLanguagePage.isChooseLanguagePageLoaded(), "Verify if choose language page is displayed");
+ WelcomePage welcomePage = chooseLanguagePage.clickOnSavePreference();
+
+ assertTrue(welcomePage.isWelcomePageLoaded(), "Verify if welcome page is loaded");
+ AppUnlockMethodPage appUnlockMethodPage = welcomePage.clickOnSkipButton();
+
+ assertTrue(appUnlockMethodPage.isAppUnlockMethodPageLoaded(), "Verify if app unlocked page is displayed");
+ SetPasscode setPasscode = appUnlockMethodPage.clickOnUsePasscode();
+
+ assertTrue(setPasscode.isSetPassCodePageLoaded(), "Verify if set passcode page is displayed");
+ ConfirmPasscode confirmPasscode = setPasscode.enterPasscode(TestDataReader.readData("passcode"), target);
+
+ assertTrue(confirmPasscode.isConfirmPassCodePageLoaded(), "Verify if confirm passcode page is displayed");
+ HomePage homePage = confirmPasscode.confirmPasscode(TestDataReader.readData("passcode"), target);
+
+ assertTrue(homePage.isHomePageLoaded(), "Verify if home page is displayed");
+ SettingsPage settingsPage = homePage.clickOnSettingIcon();
+
+ assertTrue(settingsPage.isSettingPageLoaded(), "Verify if setting page is displayed");
+ settingsPage.clickOnLanguage();
+
+ assertTrue(settingsPage.verifyLanguagesInLanguageFilter(), "Verify if all languages are shown in language filter");
+ }
+
+ @Test
+ public void verifyTuvaliVersion() {
+ ChooseLanguagePage chooseLanguagePage = new ChooseLanguagePage(driver);
+
+ assertTrue(chooseLanguagePage.isChooseLanguagePageLoaded(), "Verify if choose language page is displayed");
+ WelcomePage welcomePage = chooseLanguagePage.clickOnSavePreference();
+
+ assertTrue(welcomePage.isWelcomePageLoaded(), "Verify if welcome page is loaded");
+ AppUnlockMethodPage appUnlockMethodPage = welcomePage.clickOnSkipButton();
+
+ assertTrue(appUnlockMethodPage.isAppUnlockMethodPageLoaded(), "Verify if app unlocked page is displayed");
+ SetPasscode setPasscode = appUnlockMethodPage.clickOnUsePasscode();
+
+ assertTrue(setPasscode.isSetPassCodePageLoaded(), "Verify if set passcode page is displayed");
+ ConfirmPasscode confirmPasscode = setPasscode.enterPasscode(TestDataReader.readData("passcode"), target);
+
+ assertTrue(confirmPasscode.isConfirmPassCodePageLoaded(), "Verify if confirm passcode page is displayed");
+ HomePage homePage = confirmPasscode.confirmPasscode(TestDataReader.readData("passcode"), target);
+
+ assertTrue(homePage.isHomePageLoaded(), "Verify if home page is displayed");
+ SettingsPage settingsPage = homePage.clickOnSettingIcon();
+
+ assertTrue(settingsPage.isSettingPageLoaded(), "Verify if setting page is displayed");
+ assertTrue(settingsPage.clickOnAboutInji().isTuvaliVersionPresent());
+ }
}
diff --git a/injitest/src/main/java/io/mosip/test/mob/inji/testcases/NoNetworkAndroidTest.java b/injitest/src/main/java/io/mosip/test/mob/inji/testcases/NoNetworkAndroidTest.java
index 0945880a8c..1aece85978 100644
--- a/injitest/src/main/java/io/mosip/test/mob/inji/testcases/NoNetworkAndroidTest.java
+++ b/injitest/src/main/java/io/mosip/test/mob/inji/testcases/NoNetworkAndroidTest.java
@@ -123,4 +123,34 @@ public void activateVcWithoutInternet() throws InterruptedException {
CommonMethods.disableAirplaneMode();
}
+
+ @Test
+ public void verifyListOfLanguagesInOfflineMode() {
+ CommonMethods.enableAirplaneMode();
+ ChooseLanguagePage chooseLanguagePage = new ChooseLanguagePage(driver);
+
+ assertTrue(chooseLanguagePage.isChooseLanguagePageLoaded(), "Verify if choose language page is displayed");
+ WelcomePage welcomePage = chooseLanguagePage.clickOnSavePreference();
+
+ assertTrue(welcomePage.isWelcomePageLoaded(), "Verify if welcome page is loaded");
+ AppUnlockMethodPage appUnlockMethodPage = welcomePage.clickOnSkipButton();
+
+ assertTrue(appUnlockMethodPage.isAppUnlockMethodPageLoaded(), "Verify if app unlocked page is displayed");
+ SetPasscode setPasscode = appUnlockMethodPage.clickOnUsePasscode();
+
+ assertTrue(setPasscode.isSetPassCodePageLoaded(), "Verify if set passcode page is displayed");
+ ConfirmPasscode confirmPasscode = setPasscode.enterPasscode(TestDataReader.readData("passcode"), target);
+
+ assertTrue(confirmPasscode.isConfirmPassCodePageLoaded(), "Verify if confirm passcode page is displayed");
+ HomePage homePage = confirmPasscode.confirmPasscode(TestDataReader.readData("passcode"), target);
+
+ assertTrue(homePage.isHomePageLoaded(), "Verify if home page is displayed");
+ SettingsPage settingsPage = homePage.clickOnSettingIcon();
+
+ assertTrue(settingsPage.isSettingPageLoaded(), "Verify if setting page is displayed");
+ settingsPage.clickOnLanguage();
+
+ assertTrue(settingsPage.verifyLanguagesInLanguageFilter(), "Verify if all languages are shown in language filter");
+ CommonMethods.disableAirplaneMode();
+ }
}
diff --git a/injitest/src/main/java/io/mosip/test/mob/inji/testcases/ShareVcTest.java b/injitest/src/main/java/io/mosip/test/mob/inji/testcases/ShareVcTest.java
index 5e1638ae0b..c900f41ed2 100644
--- a/injitest/src/main/java/io/mosip/test/mob/inji/testcases/ShareVcTest.java
+++ b/injitest/src/main/java/io/mosip/test/mob/inji/testcases/ShareVcTest.java
@@ -31,4 +31,31 @@ public void noCardsAvailableToShare() {
ScanPage scanPage = homePage.clickOnScanButton();
assertTrue(scanPage.isNoShareableCardsMessageDisplayed(), "Verify if no shareable cards are available message is displayed");
}
+
+ @Test
+ public void verifyReceivedCardTabPresent() {
+ ChooseLanguagePage chooseLanguagePage = new ChooseLanguagePage(driver);
+
+ assertTrue(chooseLanguagePage.isChooseLanguagePageLoaded(), "Verify if choose language page is displayed");
+ WelcomePage welcomePage = chooseLanguagePage.clickOnSavePreference();
+
+ assertTrue(welcomePage.isWelcomePageLoaded(), "Verify if welcome page is loaded");
+ AppUnlockMethodPage appUnlockMethodPage = welcomePage.clickOnSkipButton();
+
+ assertTrue(appUnlockMethodPage.isAppUnlockMethodPageLoaded(), "Verify if app unlocked page is displayed");
+ SetPasscode setPasscode = appUnlockMethodPage.clickOnUsePasscode();
+
+ assertTrue(setPasscode.isSetPassCodePageLoaded(), "Verify if set passcode page is displayed");
+ ConfirmPasscode confirmPasscode = setPasscode.enterPasscode(TestDataReader.readData("passcode"), target);
+
+ assertTrue(confirmPasscode.isConfirmPassCodePageLoaded(), "Verify if confirm passcode page is displayed");
+ HomePage homePage = confirmPasscode.confirmPasscode(TestDataReader.readData("passcode"), target);
+
+ assertTrue(homePage.isHomePageLoaded(), "Verify if home page is displayed");
+ SettingsPage settingsPage = homePage.clickOnSettingIcon();
+
+ assertTrue(settingsPage.isSettingPageLoaded(), "Verify if setting page is displayed");
+ assertTrue(settingsPage.isReceivedCardsPresent(), "Verify if received cards tab is displayed");
+
+ }
}
diff --git a/injitest/src/main/java/io/mosip/test/mob/inji/testcases/VerifyWelcomePagesTest.java b/injitest/src/main/java/io/mosip/test/mob/inji/testcases/VerifyWelcomePagesTest.java
index 5f4b765b4f..3e5cc876ee 100644
--- a/injitest/src/main/java/io/mosip/test/mob/inji/testcases/VerifyWelcomePagesTest.java
+++ b/injitest/src/main/java/io/mosip/test/mob/inji/testcases/VerifyWelcomePagesTest.java
@@ -1,5 +1,6 @@
package io.mosip.test.mob.inji.testcases;
+import io.mosip.test.mob.inji.utils.TestDataReader;
import org.testng.annotations.Test;
import io.mosip.test.mob.inji.pages.*;
import static org.testng.Assert.assertEquals;
@@ -29,6 +30,49 @@ public void verifyWelcomePagesContent() {
assertEquals(secureSharingPage.getSecureSharingDescription(), "Share your cards securely in a hassle free way and avail various services.");
secureSharingPage.clickOnNextButton();
+ HassleFreeAuthenticationPage hassleFreeAuthenticationPage = new HassleFreeAuthenticationPage(driver);
+ assertTrue(hassleFreeAuthenticationPage.isHassleFreeAuthenticationPageLoaded(), "Verify if hassle free authentication page is loaded");
+ assertEquals(hassleFreeAuthenticationPage.getHassleFreeAuthenticationDescription(), "Authenticate yourself with ease using the stored digital credential.");
+ }
+ @Test
+ public void verifyWelcomePagesFromInjiTourGuide() {
+ ChooseLanguagePage chooseLanguagePage = new ChooseLanguagePage(driver);
+
+ assertTrue(chooseLanguagePage.isChooseLanguagePageLoaded(), "Verify if choose language page is displayed");
+ WelcomePage welcomePage = chooseLanguagePage.clickOnSavePreference();
+
+ assertTrue(welcomePage.isWelcomePageLoaded(), "Verify if welcome page is loaded");
+ AppUnlockMethodPage appUnlockMethodPage = welcomePage.clickOnSkipButton();
+
+ assertTrue(appUnlockMethodPage.isAppUnlockMethodPageLoaded(), "Verify if app unlocked page is displayed");
+ SetPasscode setPasscode = appUnlockMethodPage.clickOnUsePasscode();
+
+ assertTrue(setPasscode.isSetPassCodePageLoaded(), "Verify if set passcode page is displayed");
+ ConfirmPasscode confirmPasscode = setPasscode.enterPasscode(TestDataReader.readData("passcode"), target);
+
+ assertTrue(confirmPasscode.isConfirmPassCodePageLoaded(), "Verify if confirm passcode page is displayed");
+ HomePage homePage = confirmPasscode.confirmPasscode(TestDataReader.readData("passcode"), target);
+
+ assertTrue(homePage.isHomePageLoaded(), "Verify if home page is displayed");
+ SettingsPage settingsPage = homePage.clickOnSettingIcon();
+
+ assertTrue(settingsPage.isSettingPageLoaded(), "Verify if setting page is displayed");
+ settingsPage.clickOnInjiTourGuide();
+
+ assertTrue(welcomePage.isWelcomePageLoaded(), "Verify if welcome page is loaded");
+ assertEquals(welcomePage.getWelcomeDescription(), "Keep your digital credential with you at all times. Inji helps you manage and use them effectively. To get started, add cards to your profile.");
+ welcomePage.clickOnNextButton();
+
+ TrustedDigitalWalletPage trustedDigitalWalletPage = new TrustedDigitalWalletPage(driver);
+ assertTrue(trustedDigitalWalletPage.isTrustedDigitalWalletPageLoaded(), "Verify if trusted digital wallet page is loaded");
+ assertEquals(trustedDigitalWalletPage.getTrustedDigitalWalletDescription(), "Store and carry all your important cards in a single trusted wallet.");
+ trustedDigitalWalletPage.clickOnNextButton();
+
+ SecureSharingPage secureSharingPage = new SecureSharingPage(driver);
+ assertTrue(secureSharingPage.isSecureSharingPageLoaded(), "Verify if secure sharing page is loaded");
+ assertEquals(secureSharingPage.getSecureSharingDescription(), "Share your cards securely in a hassle free way and avail various services.");
+ secureSharingPage.clickOnNextButton();
+
HassleFreeAuthenticationPage hassleFreeAuthenticationPage = new HassleFreeAuthenticationPage(driver);
assertTrue(hassleFreeAuthenticationPage.isHassleFreeAuthenticationPageLoaded(), "Verify if hassle free authentication page is loaded");
assertEquals(hassleFreeAuthenticationPage.getHassleFreeAuthenticationDescription(), "Authenticate yourself with ease using the stored digital credential.");
diff --git a/injitest/testng.xml b/injitest/testng.xml
index e3c02b2405..99b436a9af 100644
--- a/injitest/testng.xml
+++ b/injitest/testng.xml
@@ -12,51 +12,61 @@
+
+
+
+
-
+
-
+
-
+
-
-
+
+
+
+
-
+
+
+
+
+
@@ -65,6 +75,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/locales/ara.json b/locales/ara.json
index ff428a2a78..8d68fa8031 100644
--- a/locales/ara.json
+++ b/locales/ara.json
@@ -176,6 +176,10 @@
"title": "لا يوجد اتصال بالإنترنت",
"message": "الرجاء التحقق من اتصالك وإعادة المحاولة"
},
+ "biometricsCancelled": {
+ "title": "هل تريد إلغاء التنزيل؟",
+ "message": "مطلوب تأكيد البيومترية لمواصلة تنزيل البطاقة."
+ },
"generic": {
"title": "هناك خطأ ما!",
"message":"نواجه بعض المشاكل مع طلبك. حاول مرة اخرى."
@@ -474,7 +478,7 @@
"sharingVc": "مشاركة بطاقة ",
"errors": {
"locationDisabled": {
- "message": "يجب تمكين خدمات الموقع لمشاركة البطاقة",
+ "message": "للمتابعة، اسمح لجهازك بتشغيل الموقع",
"button": "تمكين خدمات الموقع"
},
"locationDenied": {
@@ -504,12 +508,12 @@
"sent": "تم إرسال بطاقة ...",
"sentHint": "في انتظار أن يحفظ المستلم بطاقة أو يتجاهلها",
"sharing": {
- "title": "جاري المشاركة",
+ "title": "جاري المشاركة...",
"timeoutHint": "تأخرت المشاركة، ربما بسبب مشكلة في الاتصال.",
- "hint": "يرجى الانتظار حتى نشارك البطاقة المختارة."
+ "hint": "يرجى الانتظار حتى نشارك البطاقة المختارة..."
},
"accepted": {
- "title": "تمت مشاركة المعرف بنجاح",
+ "title": "تمت مشاركة المعرف بنجاح!",
"message": "تمت مشاركة هويتك بنجاح مع",
"gotohome": "اذهب الى المنزل"
},
@@ -589,6 +593,7 @@
"cancel": "إلغاء",
"accept": "يقبل",
"save": "حفظ",
+ "ok": "نعم",
"dismiss": "رفض",
"editLabel": "يحرر {{label}}",
"tryAgain": "حاول مجددا",
diff --git a/locales/en.json b/locales/en.json
index afa5f55005..c4c636e77b 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -177,6 +177,10 @@
"title": "No internet connection",
"message": "Please check your connection and retry"
},
+ "biometricsCancelled": {
+ "title": "Do you want to cancel download?",
+ "message": "Biometric confirmation is required to continue downloading the card."
+ },
"generic": {
"title": "Something went wrong!",
"message": "We are having some trouble with your request. Please try again."
@@ -307,7 +311,7 @@
"title": "No internet connection",
"message": "Please check your connection and retry"
},
- "downloadLimitExpires":{
+ "downloadLimitExpires": {
"title": "Download Error",
"message": "There was an issue downloading following cards. Please try again"
}
@@ -475,7 +479,7 @@
"scanningGuide": "Hold the phone steady and scan the QR code",
"errors": {
"locationDisabled": {
- "message": "Location services must be enabled to share card",
+ "message": "To continue, let your device turn on location",
"button": "Enable location services"
},
"locationDenied": {
@@ -505,12 +509,12 @@
"sent": "Card has been sent...",
"sentHint": "Waiting for receiver to save or discard your Card",
"sharing": {
- "title": "Sharing in progress",
- "hint": "Please wait while we share the chosen card.",
+ "title": "Sharing in progress...",
+ "hint": "Please wait while we share the chosen card...",
"timeoutHint": "Sharing is delayed, possibly due to a connection issue."
},
"accepted": {
- "title": "ID shared successfully",
+ "title": "ID shared successfully!",
"message": "Your ID has been successfully shared with",
"gotohome": "Go to Home"
},
@@ -613,4 +617,4 @@
"description": "Please use fingerprint to unlock the app"
}
}
-}
+}
\ No newline at end of file
diff --git a/locales/fil.json b/locales/fil.json
index f2c351bf67..f788d326f4 100644
--- a/locales/fil.json
+++ b/locales/fil.json
@@ -175,6 +175,10 @@
"title": "Pakisuri ang iyong koneksyon at subukang muli",
"message": "Mangyaring kumonekta sa internet at subukang muli."
},
+ "biometricsCancelled": {
+ "title": "Gusto mo bang kanselahin ang pag-download?",
+ "message": "Kinakailangan ang biometric confirmation para magpatuloy sa pag-download ng card."
+ },
"generic": {
"title": "may nangyaring mali!",
"message": "Nagkakaproblema kami sa iyong kahilingan. Pakisubukang muli."
@@ -477,7 +481,7 @@
"scanningGuide": "Panatilihin ang telepono at i-scan ang QR code",
"errors": {
"locationDisabled": {
- "message": "Dapat na nakabukas ang Location services ng iyong mobile para maaaring makapag-scan",
+ "message": "Para magpatuloy, hayaang i-on ng iyong device ang lokasyon",
"button": "Buksan ang location services"
},
"locationDenied": {
@@ -507,12 +511,12 @@
"sent": "Naipadala na ang card...",
"sentHint": "Naghihintay para sa receiver na i-save o itapon ang iyong card",
"sharing": {
- "title": "Isinasagawa ang pagbabahagi",
+ "title": "Isinasagawa ang pagbabahagi...",
"timeoutHint": "Naantala ang pagbabahagi, posibleng dahil sa isang isyu sa koneksyon.",
- "hint": "Mangyaring maghintay habang ibinabahagi namin ang napiling card."
+ "hint": "Mangyaring maghintay habang ibinabahagi namin ang napiling card..."
},
"accepted": {
- "title": "Matagumpay na naibahagi ang ID",
+ "title": "Matagumpay na naibahagi ang ID!",
"message": "Ang iyong ID ay matagumpay na naibahagi sa",
"gotohome": "Pumunta sa bahay"
},
@@ -592,6 +596,7 @@
"cancel": "Kanselahin",
"accept": "Tanggapin",
"save": "I-save",
+ "ok": "Ok",
"dismiss": "Dismissed",
"editLabel": "Palitan ang {{label}}",
"tryAgain": "Subukan muli",
diff --git a/locales/hin.json b/locales/hin.json
index 4679524673..33632c0b6c 100644
--- a/locales/hin.json
+++ b/locales/hin.json
@@ -173,6 +173,10 @@
"title": "कोई इंटरनेट कनेक्शन नहीं",
"message": "कृपया अपना कनेक्शन जांचें और पुनः प्रयास करें"
},
+ "biometricsCancelled": {
+ "title": "क्या आप डाउनलोड रद्द करना चाहते हैं?",
+ "message": "कार्ड डाउनलोड करना जारी रखने के लिए बायोमेट्रिक पुष्टिकरण आवश्यक है।"
+ },
"generic": {
"title": "कुछ गलत हो गया!",
"message": "आपके अनुरोध से हमें कुछ परेशानी हो रही है. कृपया पुन: प्रयास करें।"
@@ -474,7 +478,7 @@
"scanningGuide": "फ़ोन को स्थिर रखें और QR कोड को स्कैन करें",
"errors": {
"locationDisabled": {
- "message": "स्थान सेवाओं को स्कैनिंग कार्यक्षमता के लिए सक्षम किया जाना चाहिए",
+ "message": "जारी रखने के लिए, अपने डिवाइस को स्थान चालू करने दें",
"button": "स्थान सेवाएं सक्षम करें"
},
"locationDenied": {
@@ -504,12 +508,12 @@
"sent": "कार्ड भेज दिया गया है...",
"sentHint": "आपके {{vcLabel }} को सहेजने या छोड़ने के लिए रिसीवर की प्रतीक्षा की जा रही है",
"sharing": {
- "title": "साझाकरण प्रगति पर है",
+ "title": "साझाकरण प्रगति पर है...",
"timeoutHint": "संभवतः किसी कनेक्शन समस्या के कारण साझाकरण में देरी हो रही है।",
- "hint": "कृपया तब तक प्रतीक्षा करें जब तक हम चयनित कार्ड साझा नहीं करते।"
+ "hint": "कृपया तब तक प्रतीक्षा करें जब तक हम चयनित कार्ड साझा नहीं करते।..."
},
"accepted": {
- "title": "आईडी सफलतापूर्वक साझा की गई",
+ "title": "आईडी सफलतापूर्वक साझा की गई!",
"message": "आपकी आईडी सफलतापूर्वक साझा कर दी गई है",
"gotohome": "घर जाओ"
},
@@ -588,6 +592,7 @@
"common": {
"cancel": "रद्द करें",
"save": "सहेजें",
+ "ok": "ठीक है",
"dismiss": "नकार देना",
"editLabel": "संपादित करें {{label}}",
"tryAgain": "पुनः प्रयास करें",
diff --git a/locales/kan.json b/locales/kan.json
index e19793ecea..06d4a06fc8 100644
--- a/locales/kan.json
+++ b/locales/kan.json
@@ -172,6 +172,10 @@
"title": "ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವಿಲ್ಲ",
"message": "ದಯವಿಟ್ಟು ನಿಮ್ಮ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ ಮತ್ತು ಮರುಪ್ರಯತ್ನಿಸಿ"
},
+ "biometricsCancelled": {
+ "title": "ನೀವು ಡೌನ್ಲೋಡ್ ರದ್ದುಗೊಳಿಸಲು ಬಯಸುವಿರಾ?",
+ "message": "ಕಾರ್ಡ್ ಡೌನ್ಲೋಡ್ ಮಾಡುವುದನ್ನು ಮುಂದುವರಿಸಲು ಬಯೋಮೆಟ್ರಿಕ್ ದೃಢೀಕರಣದ ಅಗತ್ಯವಿದೆ."
+ },
"generic": {
"title": "ಏನೋ ತಪ್ಪಾಗಿದೆ!",
"message": "ನಿಮ್ಮ ವಿನಂತಿಯೊಂದಿಗೆ ನಮಗೆ ಸ್ವಲ್ಪ ತೊಂದರೆ ಇದೆ. ದಯವಿಟ್ಟು ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."
@@ -472,7 +476,7 @@
"scanningGuide": "ಫೋನ್ ಅನ್ನು ಸ್ಥಿರವಾಗಿ ಹಿಡಿದುಕೊಳ್ಳಿ ಮತ್ತು QR ಕೋಡ್ ಅನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಿ",
"errors": {
"locationDisabled": {
- "message": "ಸ್ಕ್ಯಾನಿಂಗ್ ಕಾರ್ಯಕ್ಕಾಗಿ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಬೇಕು",
+ "message": "ಮುಂದುವರಿಸಲು, ನಿಮ್ಮ ಸಾಧನವು ಸ್ಥಳವನ್ನು ಆನ್ ಮಾಡಲು ಅನುಮತಿಸಿ",
"button": "ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"
},
"locationDenied": {
@@ -502,12 +506,12 @@
"sent": "ಕಾರ್ಡ್ ಅನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ...",
"sentHint": "ನಿಮ್ಮ ಕಾರ್ಡ್ ಅನ್ನು ಉಳಿಸಲು ಅಥವಾ ತ್ಯಜಿಸಲು ರಿಸೀವರ್ಗಾಗಿ ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ",
"sharing": {
- "title": "ಹಂಚಿಕೆ ಪ್ರಗತಿಯಲ್ಲಿದೆ",
+ "title": "ಹಂಚಿಕೆ ಪ್ರಗತಿಯಲ್ಲಿದೆ...",
"timeoutHint": "ಬಹುಶಃ ಸಂಪರ್ಕದ ಸಮಸ್ಯೆಯಿಂದಾಗಿ ಹಂಚಿಕೆ ವಿಳಂಬವಾಗಿದೆ.",
- "hint": "ನಾವು ಆಯ್ಕೆಮಾಡಿದ ಕಾರ್ಡ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುವವರೆಗೆ ದಯವಿಟ್ಟು ನಿರೀಕ್ಷಿಸಿ."
+ "hint": "ನಾವು ಆಯ್ಕೆಮಾಡಿದ ಕಾರ್ಡ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುವವರೆಗೆ ದಯವಿಟ್ಟು ನಿರೀಕ್ಷಿಸಿ..."
},
"accepted": {
- "title": "ಐಡಿಯನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಹಂಚಿಕೊಳ್ಳಲಾಗಿದೆ",
+ "title": "ಐಡಿಯನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಹಂಚಿಕೊಳ್ಳಲಾಗಿದೆ!",
"message": "ನಿಮ್ಮ ಐಡಿಯನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಹಂಚಿಕೊಳ್ಳಲಾಗಿದೆ",
"gotohome": "ಮನೆಗೆ ಹೋಗು"
},
@@ -587,6 +591,7 @@
"cancel": "ರದ್ದು",
"accept": "ಒಪ್ಪಿಕೊಳ್ಳಿ",
"save": "ಉಳಿಸು",
+ "ok": "ಸರಿ",
"dismiss": "ವಜಾಗೊಳಿಸಿ",
"editLabel": "ಸಂಪಾದಿಸು {{label}}",
"tryAgain": "ಮತ್ತೆ ಪ್ರಯತ್ನಿಸು",
diff --git a/locales/spa.json b/locales/spa.json
index da59717fa5..a2b87190bc 100644
--- a/locales/spa.json
+++ b/locales/spa.json
@@ -291,6 +291,10 @@
"title": "Sin conexión a Internet",
"message": "Por favor verifique su conexión y vuelva a intentarlo"
},
+ "biometricsCancelled": {
+ "title": "Quieres cancelar la descarga?",
+ "message": "Se requiere confirmación biométrica para continuar descargando la tarjeta."
+ },
"generic": {
"title": "¡Algo salió mal!",
"message": "Estamos teniendo algunos problemas con su solicitud. Inténtalo de nuevo."
@@ -408,7 +412,7 @@
"enableBluetoothButtonText": "Permitir permisos de Bluetooth",
"errors": {
"locationDisabled": {
- "message": "Los servicios de ubicación deben estar habilitados para compartir la tarjeta",
+ "message": "Para continuar, deja que tu dispositivo active la ubicación.",
"button": "Habilitar servicios de ubicación"
},
"locationDenied": {
@@ -497,6 +501,7 @@
"common": {
"cancel": "Cancelar",
"save": "Guardar",
+ "ok": "Ok",
"dismiss": "Cerrar",
"editLabel": "Editar {{label}}",
"tryAgain": "Intentar de nuevo",
diff --git a/locales/tam.json b/locales/tam.json
index 06333ea85b..86ac212850 100644
--- a/locales/tam.json
+++ b/locales/tam.json
@@ -172,6 +172,10 @@
"title": "இணைய இணைப்பு இல்லை",
"message": "உங்கள் இணைப்பைச் சரிபார்த்து மீண்டும் முயற்சிக்கவும்"
},
+ "biometricsCancelled": {
+ "title": "பதிவிறக்கத்தை ரத்து செய்ய வேண்டுமா?",
+ "message": "கார்டை தொடர்ந்து பதிவிறக்க பயோமெட்ரிக் உறுதிப்படுத்தல் தேவை."
+ },
"generic": {
"title": "ஏதோ தவறு நடந்துவிட்டது!",
"message": "உங்கள் கோரிக்கையில் எங்களுக்கு சில சிக்கல்கள் உள்ளன. தயவு செய்து மீண்டும் முயற்சிக்கவும்."
@@ -472,7 +476,7 @@
"scanningGuide": "ஃபோனை நிலையாகப் பிடித்து, QR குறியீட்டை ஸ்கேன் செய்யவும்s",
"errors": {
"locationDisabled": {
- "message": "ஸ்கேனிங் செயல்பாட்டிற்கு இருப்பிடச் சேவைகள் இயக்கப்பட வேண்டும்",
+ "message": "தொடர, இருப்பிடத்தை இயக்க உங்கள் சாதனத்தை அனுமதிக்கவும்",
"button": "இருப்பிட சேவைகளை இயக்கு"
},
"locationDenied": {
@@ -502,12 +506,12 @@
"sent": "அட்டை அனுப்பப்பட்டது...",
"sentHint": "ரிசீவர் உங்கள் அட்டை ஐ சேமிக்க அல்லது நிராகரிக்க காத்திருக்கிறது",
"sharing": {
- "title": "பகிர்தல் செயல்பாட்டில் உள்ளது",
+ "title": "பகிர்தல் செயல்பாட்டில் உள்ளது...",
"timeoutHint": "இணைப்புச் சிக்கல் காரணமாகப் பகிர்தல் தாமதமாகலாம்.",
- "hint": "தேர்ந்தெடுத்த கார்டைப் பகிரும் வரை காத்திருக்கவும்."
+ "hint": "தேர்ந்தெடுத்த கார்டைப் பகிரும் வரை காத்திருக்கவும்..."
},
"accepted": {
- "title": "ஐடி வெற்றிகரமாகப் பகிரப்பட்டது",
+ "title": "ஐடி வெற்றிகரமாகப் பகிரப்பட்டது!",
"message": "உங்கள் ஐடி வெற்றிகரமாகப் பகிரப்பட்டது",
"gotohome": "வீட்டிற்கு போ"
},
@@ -587,6 +591,7 @@
"cancel": "ரத்துசெய்",
"accept": "ஏற்றுக்கொள்",
"save": "சேமி",
+ "ok": "சரி",
"dismiss": "நிராகரி",
"editLabel": "திருத்து {{label}}",
"tryAgain": "மீண்டும் முயற்சி செய்",
diff --git a/machines/bleShare/scan/scanMachine.ts b/machines/bleShare/scan/scanMachine.ts
index 43cb24c4cf..74cca2b15c 100644
--- a/machines/bleShare/scan/scanMachine.ts
+++ b/machines/bleShare/scan/scanMachine.ts
@@ -22,13 +22,14 @@ import {subscribe} from '../../../shared/openIdBLE/walletEventHandler';
import {
check,
checkMultiple,
- PermissionStatus,
PERMISSIONS,
+ PermissionStatus,
requestMultiple,
RESULTS,
} from 'react-native-permissions';
import {
checkLocationPermissionStatus,
+ checkLocationService,
requestLocationPermission,
} from '../../../shared/location';
import {CameraCapturedPicture} from 'expo-camera';
@@ -40,14 +41,14 @@ import {BLEError} from '../types';
import Storage from '../../../shared/storage';
import {VCMetadata} from '../../../shared/VCMetadata';
import {
- getStartEventData,
getEndEventData,
- sendStartEvent,
- sendEndEvent,
- sendImpressionEvent,
+ getErrorEventData,
getImpressionEventData,
+ getStartEventData,
+ sendEndEvent,
sendErrorEvent,
- getErrorEventData,
+ sendImpressionEvent,
+ sendStartEvent,
} from '../../../shared/telemetry/TelemetryUtils';
import {TelemetryConstants} from '../../../shared/telemetry/TelemetryConstants';
@@ -307,7 +308,7 @@ export const scanMachine =
always: [
{
cond: 'uptoAndroid11',
- target: '#scan.checkingLocationService',
+ target: '#scan.checkingLocationState',
},
{
target: '#scan.clearingConnection',
@@ -337,7 +338,7 @@ export const scanMachine =
always: [
{
cond: 'uptoAndroid11',
- target: '#scan.checkingLocationService',
+ target: '#scan.checkingLocationState',
},
{
target: '#scan.clearingConnection',
@@ -678,9 +679,22 @@ export const scanMachine =
},
},
},
- checkingLocationService: {
- initial: 'checkingPermissionStatus',
+ checkingLocationState: {
+ initial: 'checkLocationService',
states: {
+ checkLocationService: {
+ invoke: {
+ src: 'checkLocationStatus',
+ },
+ on: {
+ LOCATION_ENABLED: {
+ target: 'checkingPermissionStatus',
+ },
+ LOCATION_DISABLED: {
+ target: 'disabled',
+ },
+ },
+ },
checkingPermissionStatus: {
invoke: {
src: 'checkLocationPermission',
@@ -717,6 +731,13 @@ export const scanMachine =
},
},
},
+ disabled: {
+ on: {
+ LOCATION_REQUEST: {
+ target: 'checkLocationService',
+ },
+ },
+ },
},
},
},
@@ -1096,6 +1117,12 @@ export const scanMachine =
() => callback(model.events.LOCATION_DISABLED()),
);
},
+ checkLocationStatus: () => callback => {
+ return checkLocationService(
+ () => callback(model.events.LOCATION_ENABLED()),
+ () => callback(model.events.LOCATION_DISABLED()),
+ );
+ },
startConnection: context => callback => {
wallet.startConnection(context.openId4VpUri);
diff --git a/machines/bleShare/scan/scanMachine.typegen.ts b/machines/bleShare/scan/scanMachine.typegen.ts
index 08447ddf16..e69de29bb2 100644
--- a/machines/bleShare/scan/scanMachine.typegen.ts
+++ b/machines/bleShare/scan/scanMachine.typegen.ts
@@ -1,236 +0,0 @@
-// This file was automatically generated. Edits will be overwritten
-
-export interface Typegen0 {
- '@@xstate/typegen': true;
- internalEvents: {
- '': {type: ''};
- 'done.invoke.QrLogin': {
- type: 'done.invoke.QrLogin';
- data: unknown;
- __tip: 'See the XState TS docs to learn how to strongly type this.';
- };
- 'done.invoke.scan.checkStorage:invocation[0]': {
- type: 'done.invoke.scan.checkStorage:invocation[0]';
- data: unknown;
- __tip: 'See the XState TS docs to learn how to strongly type this.';
- };
- 'done.invoke.scan.reviewing.creatingVp:invocation[0]': {
- type: 'done.invoke.scan.reviewing.creatingVp:invocation[0]';
- data: unknown;
- __tip: 'See the XState TS docs to learn how to strongly type this.';
- };
- 'xstate.after(CONNECTION_TIMEOUT)#scan.connecting.inProgress': {
- type: 'xstate.after(CONNECTION_TIMEOUT)#scan.connecting.inProgress';
- };
- 'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection': {
- type: 'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection';
- };
- 'xstate.after(SHARING_TIMEOUT)#scan.reviewing.sendingVc.inProgress': {
- type: 'xstate.after(SHARING_TIMEOUT)#scan.reviewing.sendingVc.inProgress';
- };
- 'xstate.init': {type: 'xstate.init'};
- 'xstate.stop': {type: 'xstate.stop'};
- };
- invokeSrcNameMap: {
- checkBluetoothPermission: 'done.invoke.scan.checkBluetoothPermission.checking:invocation[0]';
- checkBluetoothState:
- | 'done.invoke.scan.checkBluetoothState.checking:invocation[0]'
- | 'done.invoke.scan.recheckBluetoothState.checking:invocation[0]';
- checkLocationPermission: 'done.invoke.scan.checkingLocationService.checkingPermissionStatus:invocation[0]';
- checkNearByDevicesPermission: 'done.invoke.scan.checkNearbyDevicesPermission.checking:invocation[0]';
- checkStorageAvailability: 'done.invoke.scan.checkStorage:invocation[0]';
- createVp: 'done.invoke.scan.reviewing.creatingVp:invocation[0]';
- disconnect:
- | 'done.invoke.scan.clearingConnection:invocation[0]'
- | 'done.invoke.scan.disconnectDevice:invocation[0]';
- monitorConnection: 'done.invoke.scan:invocation[0]';
- requestBluetooth: 'done.invoke.scan.checkBluetoothState.requesting:invocation[0]';
- requestNearByDevicesPermission: 'done.invoke.scan.checkNearbyDevicesPermission.requesting:invocation[0]';
- requestToEnableLocationPermission: 'done.invoke.scan.checkingLocationService.requestToEnableLocation:invocation[0]';
- sendVc: 'done.invoke.scan.reviewing.sendingVc:invocation[0]';
- startConnection: 'done.invoke.scan.connecting:invocation[0]';
- };
- missingImplementations: {
- actions: never;
- delays: never;
- guards: never;
- services: never;
- };
- eventsCausingActions: {
- clearCreatedVp:
- | ''
- | 'BLE_ERROR'
- | 'DISCONNECT'
- | 'DISMISS'
- | 'RESET'
- | 'SCREEN_BLUR'
- | 'SCREEN_FOCUS'
- | 'xstate.stop';
- clearReason:
- | ''
- | 'BLE_ERROR'
- | 'DISCONNECT'
- | 'DISMISS'
- | 'RESET'
- | 'SCREEN_BLUR'
- | 'SCREEN_FOCUS'
- | 'xstate.stop';
- clearUri:
- | 'DISCONNECT'
- | 'DISMISS'
- | 'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection';
- logFailedVerification: 'FACE_INVALID';
- logShared: 'VC_ACCEPTED';
- openAppPermission: 'GOTO_SETTINGS' | 'LOCATION_REQUEST';
- openBluetoothSettings: 'GOTO_SETTINGS';
- registerLoggers:
- | 'DISCONNECT'
- | 'DISMISS'
- | 'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection';
- removeLoggers:
- | 'DISCONNECT'
- | 'DISMISS'
- | 'SCREEN_BLUR'
- | 'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection'
- | 'xstate.init';
- resetShouldVerifyPresence: 'CANCEL' | 'CONNECTED' | 'DISMISS' | 'RETRY';
- sendScanData: 'SCAN';
- setBleError: 'BLE_ERROR';
- setChildRef:
- | 'DISCONNECT'
- | 'DISMISS'
- | 'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection';
- setCreatedVp: 'done.invoke.scan.reviewing.creatingVp:invocation[0]';
- setLinkCode: 'SCAN';
- setPromptHint: 'CANCEL' | 'RETRY';
- setReadyForBluetoothStateCheck: 'BLUETOOTH_PERMISSION_ENABLED';
- setReason: 'UPDATE_REASON';
- setReceiverInfo: 'CONNECTED';
- setSelectedVc: 'SELECT_VC';
- setSenderInfo: 'CONNECTED';
- setShareLogTypeUnverified: 'ACCEPT_REQUEST';
- setShareLogTypeVerified: 'FACE_VALID';
- setStayInProgress:
- | 'STAY_IN_PROGRESS'
- | 'xstate.after(CONNECTION_TIMEOUT)#scan.connecting.inProgress'
- | 'xstate.after(SHARING_TIMEOUT)#scan.reviewing.sendingVc.inProgress';
- setUri: 'SCAN';
- storeLoginItem: 'done.invoke.QrLogin';
- storingActivityLog: 'STORE_RESPONSE';
- toggleShouldVerifyPresence: 'TOGGLE_USER_CONSENT';
- };
- eventsCausingDelays: {
- CONNECTION_TIMEOUT: 'SCAN';
- DESTROY_TIMEOUT: '' | 'DISMISS' | 'LOCATION_ENABLED';
- SHARING_TIMEOUT:
- | 'ACCEPT_REQUEST'
- | 'FACE_VALID'
- | 'done.invoke.scan.reviewing.creatingVp:invocation[0]';
- };
- eventsCausingGuards: {
- isIOS: 'BLUETOOTH_STATE_DISABLED' | 'START_PERMISSION_CHECK';
- isMinimumStorageRequiredForAuditEntryReached: 'done.invoke.scan.checkStorage:invocation[0]';
- isOpenIdQr: 'SCAN';
- isQrLogin: 'SCAN';
- uptoAndroid11: '' | 'START_PERMISSION_CHECK';
- };
- eventsCausingServices: {
- QrLogin: 'SCAN';
- checkBluetoothPermission:
- | ''
- | 'BLUETOOTH_STATE_DISABLED'
- | 'NEARBY_ENABLED'
- | 'START_PERMISSION_CHECK';
- checkBluetoothState: '' | 'APP_ACTIVE';
- checkLocationPermission: '' | 'APP_ACTIVE';
- checkNearByDevicesPermission: 'APP_ACTIVE' | 'START_PERMISSION_CHECK';
- checkStorageAvailability: 'RESET' | 'SCREEN_FOCUS';
- createVp: never;
- disconnect: '' | 'DISMISS' | 'LOCATION_ENABLED' | 'SCREEN_BLUR';
- monitorConnection: 'DISMISS' | 'SCREEN_BLUR' | 'xstate.init';
- requestBluetooth: 'BLUETOOTH_STATE_DISABLED';
- requestNearByDevicesPermission: 'NEARBY_DISABLED';
- requestToEnableLocationPermission: 'LOCATION_DISABLED';
- sendVc:
- | 'ACCEPT_REQUEST'
- | 'FACE_VALID'
- | 'done.invoke.scan.reviewing.creatingVp:invocation[0]';
- startConnection: 'SCAN';
- };
- matchesStates:
- | 'bluetoothDenied'
- | 'bluetoothPermissionDenied'
- | 'checkBluetoothPermission'
- | 'checkBluetoothPermission.checking'
- | 'checkBluetoothPermission.enabled'
- | 'checkBluetoothState'
- | 'checkBluetoothState.checking'
- | 'checkBluetoothState.enabled'
- | 'checkBluetoothState.requesting'
- | 'checkNearbyDevicesPermission'
- | 'checkNearbyDevicesPermission.checking'
- | 'checkNearbyDevicesPermission.enabled'
- | 'checkNearbyDevicesPermission.requesting'
- | 'checkStorage'
- | 'checkingLocationService'
- | 'checkingLocationService.checkingPermissionStatus'
- | 'checkingLocationService.denied'
- | 'checkingLocationService.requestToEnableLocation'
- | 'clearingConnection'
- | 'connecting'
- | 'connecting.inProgress'
- | 'connecting.timeout'
- | 'disconnectDevice'
- | 'disconnected'
- | 'findingConnection'
- | 'handlingBleError'
- | 'inactive'
- | 'invalid'
- | 'nearByDevicesPermissionDenied'
- | 'recheckBluetoothState'
- | 'recheckBluetoothState.checking'
- | 'recheckBluetoothState.enabled'
- | 'restrictSharingVc'
- | 'reviewing'
- | 'reviewing.accepted'
- | 'reviewing.cancelling'
- | 'reviewing.creatingVp'
- | 'reviewing.invalidIdentity'
- | 'reviewing.navigatingToHome'
- | 'reviewing.rejected'
- | 'reviewing.selectingVc'
- | 'reviewing.sendingVc'
- | 'reviewing.sendingVc.inProgress'
- | 'reviewing.sendingVc.sent'
- | 'reviewing.sendingVc.timeout'
- | 'reviewing.verifyingIdentity'
- | 'showQrLogin'
- | 'showQrLogin.idle'
- | 'showQrLogin.navigatingToHistory'
- | 'showQrLogin.storing'
- | 'startPermissionCheck'
- | {
- checkBluetoothPermission?: 'checking' | 'enabled';
- checkBluetoothState?: 'checking' | 'enabled' | 'requesting';
- checkNearbyDevicesPermission?: 'checking' | 'enabled' | 'requesting';
- checkingLocationService?:
- | 'checkingPermissionStatus'
- | 'denied'
- | 'requestToEnableLocation';
- connecting?: 'inProgress' | 'timeout';
- recheckBluetoothState?: 'checking' | 'enabled';
- reviewing?:
- | 'accepted'
- | 'cancelling'
- | 'creatingVp'
- | 'invalidIdentity'
- | 'navigatingToHome'
- | 'rejected'
- | 'selectingVc'
- | 'sendingVc'
- | 'verifyingIdentity'
- | {sendingVc?: 'inProgress' | 'sent' | 'timeout'};
- showQrLogin?: 'idle' | 'navigatingToHistory' | 'storing';
- };
- tags: never;
-}
diff --git a/machines/bleShare/scan/selectors.ts b/machines/bleShare/scan/selectors.ts
index 6232412b50..57522395d1 100644
--- a/machines/bleShare/scan/selectors.ts
+++ b/machines/bleShare/scan/selectors.ts
@@ -60,11 +60,11 @@ export function selectIsInvalid(state: State) {
}
export function selectIsLocationDenied(state: State) {
- return state.matches('checkingLocationService.denied');
+ return state.matches('checkingLocationState.denied');
}
export function selectIsLocationDisabled(state: State) {
- return state.matches('checkingLocationService.disabled');
+ return state.matches('checkingLocationState.disabled');
}
export function selectIsShowQrLogin(state: State) {
diff --git a/machines/issuersMachine.ts b/machines/issuersMachine.ts
index 48bb71c282..d08572664a 100644
--- a/machines/issuersMachine.ts
+++ b/machines/issuersMachine.ts
@@ -20,7 +20,6 @@ import {log} from 'xstate/lib/actions';
import {verifyCredential} from '../shared/vcjs/verifyCredential';
import {
getBody,
- getIdentifier,
vcDownloadTimeout,
OIDCErrors,
ErrorMessage,
@@ -29,7 +28,6 @@ import {
getVCMetadata,
Issuers_Key_Ref,
} from '../shared/openId4VCI/Utils';
-import {VCMetadata} from '../shared/VCMetadata';
import {
getEndEventData,
getImpressionEventData,
@@ -44,6 +42,7 @@ import {
} from '../types/VC/EsignetMosipVC/vc';
import {CACHED_API} from '../shared/api';
import {request} from '../shared/request';
+import {BiometricCancellationError} from '../shared/error/BiometricCancellationError';
const model = createModel(
{
@@ -62,14 +61,16 @@ const model = createModel(
},
{
events: {
- DISMISS: () => ({}),
SELECTED_ISSUER: (id: string) => ({id}),
DOWNLOAD_ID: () => ({}),
+ BIOMETRIC_CANCELLED: (requester?: string) => ({requester}),
COMPLETED: () => ({}),
TRY_AGAIN: () => ({}),
RESET_ERROR: () => ({}),
CHECK_KEY_PAIR: () => ({}),
CANCEL: () => ({}),
+ STORE_RESPONSE: (response?: unknown) => ({response}),
+ STORE_ERROR: (error: Error, requester?: string) => ({error, requester}),
},
},
);
@@ -115,7 +116,7 @@ export const IssuersMachine = model.createMachine(
on: {
TRY_AGAIN: [
{
- description: 'not fetched issuers config yet',
+ description: 'not fetched issuers yet',
cond: 'shouldFetchIssuersAgain',
actions: ['setLoadingReasonAsDisplayIssuers', 'resetError'],
target: 'displayIssuers',
@@ -195,9 +196,7 @@ export const IssuersMachine = model.createMachine(
'setTokenResponse',
'setLoadingReasonAsSettingUp',
'getKeyPairFromStore',
- 'loadKeyPair',
],
- target: 'checkKeyPair',
},
onError: [
{
@@ -224,10 +223,44 @@ export const IssuersMachine = model.createMachine(
},
],
},
+ initial: 'idle',
+ states: {
+ idle: {
+ on: {
+ STORE_RESPONSE: {
+ actions: 'loadKeyPair',
+ target: '#issuersMachine.checkKeyPair',
+ },
+ BIOMETRIC_CANCELLED: {
+ target: 'userCancelledBiometric',
+ },
+ STORE_ERROR: {
+ target: '#issuersMachine.checkKeyPair',
+ },
+ },
+ },
+ userCancelledBiometric: {
+ on: {
+ TRY_AGAIN: [
+ {
+ actions: ['getKeyPairFromStore'],
+ target: 'idle',
+ },
+ ],
+ RESET_ERROR: {
+ actions: 'resetLoadingReason',
+ target: '#issuersMachine.selectingIssuer',
+ },
+ },
+ },
+ },
},
checkKeyPair: {
description: 'checks whether key pair is generated',
- entry: ['setLoadingReasonAsSettingUp', send('CHECK_KEY_PAIR')],
+ entry: [
+ 'setLoadingReasonAsDownloadingCredentials',
+ send('CHECK_KEY_PAIR'),
+ ],
on: {
CHECK_KEY_PAIR: [
{
@@ -250,18 +283,18 @@ export const IssuersMachine = model.createMachine(
actions: [
'setPublicKey',
'setLoadingReasonAsDownloadingCredentials',
- 'setPrivateKey',
'storeKeyPair',
],
+ cond: 'isCustomSecureKeystore',
target: 'downloadCredentials',
},
{
actions: [
'setPublicKey',
'setLoadingReasonAsDownloadingCredentials',
+ 'setPrivateKey',
'storeKeyPair',
],
- cond: 'isCustomSecureKeystore',
target: 'downloadCredentials',
},
],
@@ -276,6 +309,10 @@ export const IssuersMachine = model.createMachine(
target: 'verifyingCredential',
},
onError: [
+ {
+ cond: 'hasUserCancelledBiometric',
+ target: '.userCancelledBiometric',
+ },
{
actions: ['setError', 'resetLoadingReason'],
target: 'error',
@@ -287,6 +324,24 @@ export const IssuersMachine = model.createMachine(
target: 'selectingIssuer',
},
},
+ initial: 'idle',
+ states: {
+ idle: {},
+ userCancelledBiometric: {
+ on: {
+ TRY_AGAIN: [
+ {
+ actions: ['setLoadingReasonAsDownloadingCredentials'],
+ target: '#issuersMachine.downloadCredentials',
+ },
+ ],
+ RESET_ERROR: {
+ actions: 'resetLoadingReason',
+ target: '#issuersMachine.selectingIssuer',
+ },
+ },
+ },
+ },
},
verifyingCredential: {
description:
@@ -379,18 +434,20 @@ export const IssuersMachine = model.createMachine(
}),
loadKeyPair: assign({
- publicKey: (_, event) => event.publicKey,
+ publicKey: (_, event) => event.response?.publicKey,
privateKey: (context, event) =>
- event.privateKey ? event.privateKey : context.privateKey,
+ event.response?.privateKey
+ ? event.response.privateKey
+ : context.privateKey,
}),
getKeyPairFromStore: send(StoreEvents.GET(Issuers_Key_Ref), {
to: context => context.serviceRefs.store,
}),
storeKeyPair: send(
- (_, event) => {
+ context => {
return StoreEvents.SET(Issuers_Key_Ref, {
- publicKey: (event.data as KeyPair).public + ``,
- privateKey: (event.data as KeyPair).private + ``,
+ publicKey: context.publicKey,
+ privateKey: context.privateKey,
});
},
{
@@ -569,9 +626,7 @@ export const IssuersMachine = model.createMachine(
},
},
guards: {
- hasKeyPair: context => {
- return context.publicKey != null;
- },
+ hasKeyPair: context => !!context.publicKey,
isInternetConnected: (_, event) => !!event.data.isConnected,
isOIDCflowCancelled: (_, event) => {
// iOS & Android have different error strings for user cancelled flow
@@ -600,6 +655,8 @@ export const IssuersMachine = model.createMachine(
},
shouldFetchIssuersAgain: context => context.issuers.length === 0,
isCustomSecureKeystore: () => isHardwareKeystoreExists,
+ hasUserCancelledBiometric: (_, event) =>
+ event.data instanceof BiometricCancellationError,
},
},
);
@@ -611,8 +668,8 @@ export function selectIssuers(state: State) {
}
export function selectErrorMessageType(state: State) {
- return state.context.errorMessage === '' ||
- state.context.errorMessage === ErrorMessage.NO_INTERNET
+ const nonGenericErrors = ['', ErrorMessage.NO_INTERNET];
+ return nonGenericErrors.includes(state.context.errorMessage)
? state.context.errorMessage
: ErrorMessage.GENERIC;
}
@@ -625,6 +682,13 @@ export function selectIsDownloadCredentials(state: State) {
return state.matches('downloadCredentials');
}
+export function selectIsBiometricCancelled(state: State) {
+ return (
+ state.matches('downloadCredentials.userCancelledBiometric') ||
+ state.matches('performAuthorization.userCancelledBiometric')
+ );
+}
+
export function selectIsDone(state: State) {
return state.matches('done');
}
diff --git a/machines/issuersMachine.typegen.ts b/machines/issuersMachine.typegen.ts
index f7f7776388..4adbc1ce0c 100644
--- a/machines/issuersMachine.typegen.ts
+++ b/machines/issuersMachine.typegen.ts
@@ -80,14 +80,17 @@ export interface Typegen0 {
services: never;
};
eventsCausingActions: {
- getKeyPairFromStore: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
- loadKeyPair: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
+ getKeyPairFromStore:
+ | 'TRY_AGAIN'
+ | 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
+ loadKeyPair: 'STORE_RESPONSE';
logDownloaded: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
resetError:
| 'RESET_ERROR'
| 'TRY_AGAIN'
| 'error.platform.issuersMachine.performAuthorization:invocation[0]';
resetLoadingReason:
+ | 'RESET_ERROR'
| 'done.invoke.checkInternet'
| 'done.invoke.issuersMachine.displayIssuers:invocation[0]'
| 'error.platform.issuersMachine.downloadCredentials:invocation[0]'
@@ -104,7 +107,11 @@ export interface Typegen0 {
| 'error.platform.issuersMachine.performAuthorization:invocation[0]';
setIssuers: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
setLoadingReasonAsDisplayIssuers: 'TRY_AGAIN';
- setLoadingReasonAsDownloadingCredentials: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
+ setLoadingReasonAsDownloadingCredentials:
+ | 'STORE_ERROR'
+ | 'STORE_RESPONSE'
+ | 'TRY_AGAIN'
+ | 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
setLoadingReasonAsSettingUp:
| 'SELECTED_ISSUER'
| 'TRY_AGAIN'
@@ -127,6 +134,7 @@ export interface Typegen0 {
eventsCausingGuards: {
canSelectIssuerAgain: 'TRY_AGAIN';
hasKeyPair: 'CHECK_KEY_PAIR';
+ hasUserCancelledBiometric: 'error.platform.issuersMachine.downloadCredentials:invocation[0]';
isCustomSecureKeystore: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
isInternetConnected: 'done.invoke.checkInternet';
isOIDCConfigError: 'error.platform.issuersMachine.performAuthorization:invocation[0]';
@@ -150,13 +158,21 @@ export interface Typegen0 {
| 'displayIssuers'
| 'done'
| 'downloadCredentials'
+ | 'downloadCredentials.idle'
+ | 'downloadCredentials.userCancelledBiometric'
| 'downloadIssuerConfig'
| 'error'
| 'generateKeyPair'
| 'idle'
| 'performAuthorization'
+ | 'performAuthorization.idle'
+ | 'performAuthorization.userCancelledBiometric'
| 'selectingIssuer'
| 'storing'
- | 'verifyingCredential';
+ | 'verifyingCredential'
+ | {
+ downloadCredentials?: 'idle' | 'userCancelledBiometric';
+ performAuthorization?: 'idle' | 'userCancelledBiometric';
+ };
tags: never;
}
diff --git a/machines/store.ts b/machines/store.ts
index 1a85034296..b9b96c91fe 100644
--- a/machines/store.ts
+++ b/machines/store.ts
@@ -25,6 +25,7 @@ import {
} from '../shared/cryptoutil/cryptoUtil';
import {VCMetadata} from '../shared/VCMetadata';
import {clearLogEntriesAsync} from 'expo-updates';
+import {BiometricCancellationError} from '../shared/error/BiometricCancellationError';
export const keyinvalidatedString =
'Key Invalidated due to biometric enrollment';
@@ -44,6 +45,7 @@ const model = createModel(
GET: (key: string) => ({key}),
DECRYPT_ERROR: () => ({}),
KEY_INVALIDATE_ERROR: () => ({}),
+ BIOMETRIC_CANCELLED: (requester?: string) => ({requester}),
SET: (key: string, value: unknown) => ({key, value}),
APPEND: (key: string, value: unknown) => ({key, value}),
PREPEND: (key: string, value: unknown) => ({key, value}),
@@ -242,6 +244,17 @@ export const storeMachine =
KEY_INVALIDATE_ERROR: {
actions: sendParent('KEY_INVALIDATE_ERROR'),
},
+ BIOMETRIC_CANCELLED: {
+ actions: [
+ send(
+ (_, event) => model.events.BIOMETRIC_CANCELLED(event.requester),
+ {
+ to: (_, event) => event.requester,
+ },
+ ),
+ sendUpdate(),
+ ],
+ },
},
},
{
@@ -272,6 +285,8 @@ export const storeMachine =
'Dummy',
);
} catch (e) {
+ sendErrorEvent(getErrorEventData('ENCRYPTION', '', e));
+
if (e.message.includes(keyinvalidatedString)) {
await clear();
callback(model.events.KEY_INVALIDATE_ERROR());
@@ -282,6 +297,13 @@ export const storeMachine =
}
callback(model.events.READY());
} else {
+ sendErrorEvent(
+ getErrorEventData(
+ 'ENCRYPTION',
+ '',
+ 'Could not get the android Key alias',
+ ),
+ );
callback(
model.events.ERROR(
new Error('Could not get the android Key alias'),
@@ -388,6 +410,14 @@ export const storeMachine =
}
callback(model.events.STORE_RESPONSE(response, event.requester));
} catch (e) {
+ sendErrorEvent(
+ getErrorEventData(
+ TelemetryConstants.FlowType.fetchData,
+ '',
+ e.message,
+ {e},
+ ),
+ );
if (e.message.includes(keyinvalidatedString)) {
await clear();
callback(model.events.KEY_INVALIDATE_ERROR());
@@ -404,6 +434,9 @@ export const storeMachine =
) {
callback(model.events.DECRYPT_ERROR());
sendUpdate();
+ } else if (e instanceof BiometricCancellationError) {
+ callback(model.events.BIOMETRIC_CANCELLED(event.requester));
+ sendUpdate();
} else {
console.error(e);
callback(model.events.STORE_ERROR(e, event.requester));
@@ -417,6 +450,13 @@ export const storeMachine =
console.log('Credentials successfully loaded for user');
callback(model.events.KEY_RECEIVED(existingCredentials.password));
} else {
+ sendErrorEvent(
+ getErrorEventData(
+ TelemetryConstants.FlowType.fetchData,
+ '',
+ 'Could not get keychain credentials',
+ ),
+ );
console.log('Credentials failed to load for user');
callback(
model.events.ERROR(
@@ -437,6 +477,13 @@ export const storeMachine =
if (hasSetCredentials) {
callback(model.events.KEY_RECEIVED(randomBytesString));
} else {
+ sendErrorEvent(
+ getErrorEventData(
+ TelemetryConstants.FlowType.fetchData,
+ '',
+ 'Could not generate keychain credentials',
+ ),
+ );
callback(
model.events.ERROR(
new Error('Could not generate keychain credentials.'),
@@ -517,6 +564,13 @@ export async function getItem(
}
if (data === null && VCMetadata.isVCKey(key)) {
await removeItem(key, data, encryptionKey);
+ sendErrorEvent(
+ getErrorEventData(
+ TelemetryConstants.FlowType.fetchData,
+ TelemetryConstants.ErrorId.tampered,
+ tamperedErrorMessageString,
+ ),
+ );
throw new Error(tamperedErrorMessageString);
} else {
return defaultValue;
@@ -526,10 +580,25 @@ export async function getItem(
e.message.includes(tamperedErrorMessageString) ||
e.message.includes(keyinvalidatedString) ||
e.message === ENOENT ||
+ e instanceof BiometricCancellationError ||
e.message.includes('Key not found') // this error happens when previous get Item calls failed due to key invalidation and data and keys are deleted
) {
+ sendErrorEvent(
+ getErrorEventData(
+ TelemetryConstants.FlowType.fetchData,
+ TelemetryConstants.ErrorId.tampered,
+ e.message,
+ ),
+ );
throw e;
}
+ sendErrorEvent(
+ getErrorEventData(
+ TelemetryConstants.FlowType.fetchData,
+ TelemetryConstants.ErrorId.tampered,
+ `Exception in getting item for ${key}: ${e}`,
+ ),
+ );
console.error(`Exception in getting item for ${key}: ${e}`);
return defaultValue;
}
diff --git a/package-lock.json b/package-lock.json
index 2367504c47..291264ee4f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -48,6 +48,7 @@
"react": "18.2.0",
"react-i18next": "^11.16.6",
"react-native": "0.71.8",
+ "react-native-android-location-services-dialog-box": "^2.8.2",
"react-native-app-auth": "^7.0.0",
"react-native-app-intro-slider": "^4.0.4",
"react-native-argon2": "^2.0.1",
@@ -24406,6 +24407,14 @@
"react": "18.2.0"
}
},
+ "node_modules/react-native-android-location-services-dialog-box": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/react-native-android-location-services-dialog-box/-/react-native-android-location-services-dialog-box-2.8.2.tgz",
+ "integrity": "sha512-zBTi0xJQoF6GdImRhXOCXezYuALlPgbctqG3eOrZu63hdrlvTBhqATkb6DB5JM9kpcMbX1skMIOI3m71zKGsPA==",
+ "peerDependencies": {
+ "react-native": ">= 0.56.0"
+ }
+ },
"node_modules/react-native-app-auth": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/react-native-app-auth/-/react-native-app-auth-7.0.0.tgz",
@@ -47244,6 +47253,12 @@
}
}
},
+ "react-native-android-location-services-dialog-box": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/react-native-android-location-services-dialog-box/-/react-native-android-location-services-dialog-box-2.8.2.tgz",
+ "integrity": "sha512-zBTi0xJQoF6GdImRhXOCXezYuALlPgbctqG3eOrZu63hdrlvTBhqATkb6DB5JM9kpcMbX1skMIOI3m71zKGsPA==",
+ "requires": {}
+ },
"react-native-app-auth": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/react-native-app-auth/-/react-native-app-auth-7.0.0.tgz",
diff --git a/package.json b/package.json
index 9ab63ad023..1b33af2204 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
"react": "18.2.0",
"react-i18next": "^11.16.6",
"react-native": "0.71.8",
+ "react-native-android-location-services-dialog-box": "^2.8.2",
"react-native-app-auth": "^7.0.0",
"react-native-app-intro-slider": "^4.0.4",
"react-native-argon2": "^2.0.1",
diff --git a/screens/Home/MyVcs/AddVcModalMachine.ts b/screens/Home/MyVcs/AddVcModalMachine.ts
index 3c9a2df483..000e7a51cb 100644
--- a/screens/Home/MyVcs/AddVcModalMachine.ts
+++ b/screens/Home/MyVcs/AddVcModalMachine.ts
@@ -330,11 +330,21 @@ export const AddVcModalMachine =
'OTP is invalid': 'invalidOtp',
'OTP has expired': 'expiredOtp',
};
- return OTP_ERRORS_MAP[message]
+
+ const otpErrorMessage = OTP_ERRORS_MAP[message]
? i18n.t(`errors.backend.${OTP_ERRORS_MAP[message]}`, {
ns: 'AddVcModal',
})
: message;
+
+ sendErrorEvent(
+ getErrorEventData(
+ TelemetryConstants.FlowType.vcDownload,
+ message,
+ otpErrorMessage,
+ ),
+ );
+ return otpErrorMessage;
},
}),
diff --git a/screens/Issuers/IssuerScreenController.tsx b/screens/Issuers/IssuerScreenController.tsx
index a22129f4af..3c1988123e 100644
--- a/screens/Issuers/IssuerScreenController.tsx
+++ b/screens/Issuers/IssuerScreenController.tsx
@@ -3,6 +3,7 @@ import {
IssuerScreenTabEvents,
IssuersMachine,
selectErrorMessageType,
+ selectIsBiometricCancelled,
selectIsDone,
selectIsDownloadCredentials,
selectIsIdle,
@@ -12,14 +13,17 @@ import {
} from '../../machines/issuersMachine';
import {ActorRefFrom} from 'xstate';
import {BOTTOM_TAB_ROUTES} from '../../routes/routesConstants';
+import {logState} from '../../shared/commonUtil';
export function useIssuerScreenController({route, navigation}) {
const service = route.params.service;
+ service.subscribe(logState);
return {
issuers: useSelector(service, selectIssuers),
errorMessageType: useSelector(service, selectErrorMessageType),
isDownloadingCredentials: useSelector(service, selectIsDownloadCredentials),
+ isBiometricsCancelled: useSelector(service, selectIsBiometricCancelled),
isDone: useSelector(service, selectIsDone),
isIdle: useSelector(service, selectIsIdle),
loadingReason: useSelector(service, selectLoadingReason),
@@ -28,7 +32,6 @@ export function useIssuerScreenController({route, navigation}) {
CANCEL: () => service.send(IssuerScreenTabEvents.CANCEL()),
SELECTED_ISSUER: id =>
service.send(IssuerScreenTabEvents.SELECTED_ISSUER(id)),
- DISMISS: () => service.send(IssuerScreenTabEvents.DISMISS()),
TRY_AGAIN: () => service.send(IssuerScreenTabEvents.TRY_AGAIN()),
RESET_ERROR: () => service.send(IssuerScreenTabEvents.RESET_ERROR()),
DOWNLOAD_ID: () => {
diff --git a/screens/Issuers/IssuersScreen.tsx b/screens/Issuers/IssuersScreen.tsx
index ad2287c646..33a51d7147 100644
--- a/screens/Issuers/IssuersScreen.tsx
+++ b/screens/Issuers/IssuersScreen.tsx
@@ -1,10 +1,10 @@
import React, {useLayoutEffect, useState} from 'react';
import {useTranslation} from 'react-i18next';
-import {FlatList, Image, View, TextInput, ScrollView} from 'react-native';
+import {FlatList, Image, TextInput, View} from 'react-native';
import {Issuer} from '../../components/openId4VCI/Issuer';
import {Error} from '../../components/ui/Error';
import {Header} from '../../components/ui/Header';
-import {Column, Row, Text} from '../../components/ui';
+import {Button, Column, Row, Text} from '../../components/ui';
import {Theme} from '../../components/ui/styleUtils';
import {RootRouteProps} from '../../routes';
import {HomeRouteProps} from '../../routes/main';
@@ -24,6 +24,7 @@ import {
} from '../../shared/telemetry/TelemetryUtils';
import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants';
import {Icon} from 'react-native-elements';
+import {MessageOverlay} from '../../components/MessageOverlay';
export const IssuersScreen: React.FC<
HomeRouteProps | RootRouteProps
@@ -111,6 +112,46 @@ export const IssuersScreen: React.FC<
);
};
+ const filterIssuers = (searchText: string) => {
+ const filteredData = issuers.filter(item => {
+ if (
+ getDisplayObjectForCurrentLanguage(item.display)
+ ?.name.toLowerCase()
+ .includes(searchText.toLowerCase())
+ ) {
+ return getDisplayObjectForCurrentLanguage(item.display);
+ }
+ });
+ setFilteredSearchData(filteredData);
+ setSearch(searchText);
+ };
+
+ if (controller.isBiometricsCancelled) {
+ return (
+
+
+
+
+
+
+ );
+ }
+
if (controller.errorMessageType) {
return (
{
- let filterdData = issuers.filter(item => {
- if (
- getDisplayObjectForCurrentLanguage(item.display)
- ?.name.toLowerCase()
- .includes(searchText.toLowerCase())
- ) {
- return getDisplayObjectForCurrentLanguage(item.display);
- }
- });
- setFilteredSearchData(filterdData);
- setSearch(searchText);
- };
-
return (
{controller.issuers.length > 0 && (
diff --git a/screens/QrLogin/QrLoginSuccessMessage.tsx b/screens/QrLogin/QrLoginSuccessMessage.tsx
index 6eda16ea2b..de46de1885 100644
--- a/screens/QrLogin/QrLoginSuccessMessage.tsx
+++ b/screens/QrLogin/QrLoginSuccessMessage.tsx
@@ -44,7 +44,7 @@ export const QrLoginSuccess: React.FC = props => {
margin="2 0 0 0"
elevation={2}>