mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-09 13:38:01 -05:00
[INJIMOB-3190]: Add deeplink support for iOS OVP flow (#1913)
* [INJIMOB-3190]: add support for ovp deeplink in ios Signed-off-by: adityankannan-tw <adityan.kannan@thoughtworks.com> * [INJIMOB-3190]: refactor ovp deeplink Signed-off-by: adityankannan-tw <adityan.kannan@thoughtworks.com> * [INJIMOB-3190]: show text for ios ovp deeplink flow Signed-off-by: adityankannan-tw <adityan.kannan@thoughtworks.com> * [INJIMOB-3190]: refactor ovp deeplink flow Signed-off-by: adityankannan-tw <adityan.kannan@thoughtworks.com> * [INJIMOB-3190]: refactor state variables and extract functions Signed-off-by: adityankannan-tw <adityan.kannan@thoughtworks.com> * [INJIMOB-3190]: fix additional message shown in android success overlay Signed-off-by: adityankannan-tw <adityan.kannan@thoughtworks.com> * [INJIMOB-3190]: refactor no sharable vc variable Signed-off-by: adityankannan-tw <adityan.kannan@thoughtworks.com> * [INJIMOB-3190]: refactor send vp screen and error modal Signed-off-by: adityankannan-tw <adityan.kannan@thoughtworks.com> * [INJIMOB-3190]: refactor send vp screen and error modal Signed-off-by: adityankannan-tw <adityan.kannan@thoughtworks.com> * [INJIMOB-3190]: refactor scan machine actions Signed-off-by: adityankannan-tw <adityan.kannan@thoughtworks.com> * [INJIMOB-3153]: fix closing brace issue Signed-off-by: Alka Prasad <prasadalka1998@gmail.com> --------- Signed-off-by: adityankannan-tw <adityan.kannan@thoughtworks.com> Signed-off-by: Alka Prasad <prasadalka1998@gmail.com> Co-authored-by: Alka Prasad <prasadalka1998@gmail.com>
This commit is contained in:
34
App.tsx
34
App.tsx
@@ -1,6 +1,6 @@
|
||||
import React, {useContext, useEffect, useState} from 'react';
|
||||
import {AppLayout} from './screens/AppLayout';
|
||||
import {useFont} from "./shared/hooks/useFont";
|
||||
import {useFont} from './shared/hooks/useFont';
|
||||
import {GlobalContextProvider} from './components/GlobalContextProvider';
|
||||
import {GlobalContext} from './shared/GlobalContext';
|
||||
import {useSelector} from '@xstate/react';
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
APP_EVENTS,
|
||||
selectIsDecryptError,
|
||||
selectIsKeyInvalidateError,
|
||||
selectIsLinkCode,
|
||||
selectIsDeepLinkDetected,
|
||||
selectIsReadError,
|
||||
selectIsReady,
|
||||
} from './machines/app';
|
||||
@@ -29,7 +29,7 @@ import i18n from './i18n';
|
||||
import {CopilotProvider} from 'react-native-copilot';
|
||||
import {CopilotTooltip} from './components/CopilotTooltip';
|
||||
import {Theme} from './components/ui/styleUtils';
|
||||
import { selectAppSetupComplete } from './machines/auth';
|
||||
import {selectAppSetupComplete} from './machines/auth';
|
||||
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
// kludge: this is a bad practice but has been done temporarily to surface
|
||||
@@ -51,15 +51,15 @@ const DecryptErrorAlert = (controller, t) => {
|
||||
const AppLayoutWrapper: React.FC = () => {
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const isDecryptError = useSelector(appService, selectIsDecryptError);
|
||||
const isQrLogin = useSelector(appService, selectIsLinkCode);
|
||||
const isDeepLinkFlow = useSelector(appService, selectIsDeepLinkDetected);
|
||||
const controller = useApp();
|
||||
const {t} = useTranslation('WelcomeScreen');
|
||||
|
||||
const authService = appService.children.get('auth');
|
||||
const isAppSetupComplete = useSelector(authService, selectAppSetupComplete);
|
||||
|
||||
const [isOverlayVisible, setOverlayVisible] = useState(isQrLogin !== '');
|
||||
|
||||
|
||||
const [isDeepLinkOverlayVisible, setDeepLinkOverlayVisible] = useState(isDeepLinkFlow);
|
||||
|
||||
useEffect(() => {
|
||||
if (AppState.currentState === 'active') {
|
||||
appService.send(APP_EVENTS.ACTIVE());
|
||||
@@ -69,8 +69,8 @@ const AppLayoutWrapper: React.FC = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setOverlayVisible(isQrLogin !== '');
|
||||
}, [isQrLogin]);
|
||||
setDeepLinkOverlayVisible(isDeepLinkFlow);
|
||||
}, [isDeepLinkFlow]);
|
||||
|
||||
if (isDecryptError) {
|
||||
DecryptErrorAlert(controller, t);
|
||||
@@ -81,10 +81,12 @@ const AppLayoutWrapper: React.FC = () => {
|
||||
<AppLayout />
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={isOverlayVisible && !isAppSetupComplete}
|
||||
title={t('qrLoginOverlay.title')}
|
||||
message={t('qrLoginOverlay.message')}
|
||||
onButtonPress={() => {setOverlayVisible(false)}}
|
||||
isVisible={isDeepLinkOverlayVisible && !isAppSetupComplete}
|
||||
title={t('errors.appSetupIncomplete.title')}
|
||||
message={t('errors.appSetupIncomplete.message')}
|
||||
onButtonPress={() => {
|
||||
setDeepLinkOverlayVisible(false);
|
||||
}}
|
||||
buttonText={t('common:ok')}
|
||||
minHeight={'auto'}
|
||||
/>
|
||||
@@ -139,7 +141,7 @@ const AppLoadingWrapper: React.FC = () => {
|
||||
|
||||
const AppInitialization: React.FC = () => {
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const hasFontsLoaded = useFont()
|
||||
const hasFontsLoaded = useFont();
|
||||
const isReady = useSelector(appService, selectIsReady);
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
@@ -153,9 +155,9 @@ const AppInitialization: React.FC = () => {
|
||||
}, [i18n.language]);
|
||||
|
||||
return isReady && hasFontsLoaded ? (
|
||||
<AppLayoutWrapper />
|
||||
<AppLayoutWrapper />
|
||||
) : (
|
||||
<AppLoadingWrapper />
|
||||
<AppLoadingWrapper />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {Header} from './Header';
|
||||
import {Theme} from './styleUtils';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
import {Modal} from './Modal';
|
||||
import {isIOS} from '../../shared/constants';
|
||||
|
||||
export const Error: React.FC<ErrorProps> = props => {
|
||||
const {t} = useTranslation('common');
|
||||
@@ -22,6 +23,7 @@ export const Error: React.FC<ErrorProps> = props => {
|
||||
alignActionsOnEnd = false,
|
||||
title,
|
||||
message,
|
||||
additionalMessage,
|
||||
helpText,
|
||||
image,
|
||||
goBack,
|
||||
@@ -45,9 +47,12 @@ export const Error: React.FC<ErrorProps> = props => {
|
||||
props.textButtonText === undefined &&
|
||||
props.primaryButtonText === undefined
|
||||
) {
|
||||
const timeout = setTimeout(() => {
|
||||
setTriggerExitFlow(true);
|
||||
}, 2000);
|
||||
const timeout = setTimeout(
|
||||
() => {
|
||||
setTriggerExitFlow(true);
|
||||
},
|
||||
isIOS() ? 4000 : 2000,
|
||||
);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}
|
||||
@@ -77,6 +82,13 @@ export const Error: React.FC<ErrorProps> = props => {
|
||||
<Text style={Theme.ErrorStyles.message} testID={`${testID}Message`}>
|
||||
{message}
|
||||
</Text>
|
||||
{additionalMessage && (
|
||||
<Text
|
||||
style={Theme.ErrorStyles.additionalMessage}
|
||||
testID={`${testID}AdditionalMessage`}>
|
||||
{additionalMessage}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
{!alignActionsOnEnd && (
|
||||
<Fragment>
|
||||
@@ -193,6 +205,7 @@ export interface ErrorProps {
|
||||
alignActionsOnEnd?: boolean;
|
||||
title: string;
|
||||
message: string;
|
||||
additionalMessage?: string;
|
||||
helpText?: string;
|
||||
image: React.ReactElement;
|
||||
goBack?: () => void;
|
||||
|
||||
@@ -1270,7 +1270,7 @@ export const DefaultTheme = {
|
||||
letterSpacing: 0,
|
||||
lineHeight: 17,
|
||||
minHeight: 50,
|
||||
}
|
||||
},
|
||||
}),
|
||||
SectionLayoutStyles: StyleSheet.create({
|
||||
headerContainer: {
|
||||
@@ -1294,7 +1294,7 @@ export const DefaultTheme = {
|
||||
backgroundColor: Colors.White,
|
||||
borderBottomLeftRadius: 6,
|
||||
borderBottomRightRadius: 6,
|
||||
}
|
||||
},
|
||||
}),
|
||||
TextEditOverlayStyles: StyleSheet.create({
|
||||
overlay: {
|
||||
@@ -1557,6 +1557,15 @@ export const DefaultTheme = {
|
||||
marginHorizontal: 26,
|
||||
color: Colors.mediumDarkGrey,
|
||||
},
|
||||
additionalMessage: {
|
||||
color: Colors.Black,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 18,
|
||||
lineHeight: 21,
|
||||
paddingTop: 4,
|
||||
textAlign: 'center',
|
||||
marginBottom: 10,
|
||||
},
|
||||
}),
|
||||
SetupLanguageScreenStyle: StyleSheet.create({
|
||||
columnStyle: {
|
||||
|
||||
@@ -1299,7 +1299,7 @@ export const PurpleTheme = {
|
||||
backgroundColor: Colors.White,
|
||||
borderBottomLeftRadius: 6,
|
||||
borderBottomRightRadius: 6,
|
||||
}
|
||||
},
|
||||
}),
|
||||
TextEditOverlayStyles: StyleSheet.create({
|
||||
overlay: {
|
||||
@@ -1562,6 +1562,15 @@ export const PurpleTheme = {
|
||||
marginHorizontal: 26,
|
||||
color: Colors.mediumDarkGrey,
|
||||
},
|
||||
additionalMessage: {
|
||||
color: Colors.Black,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 18,
|
||||
lineHeight: 21,
|
||||
paddingTop: 4,
|
||||
textAlign: 'center',
|
||||
marginBottom: 10,
|
||||
},
|
||||
}),
|
||||
SetupLanguageScreenStyle: StyleSheet.create({
|
||||
columnStyle: {
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
1E55C2072DB12044009DF38B /* IntentData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E55C2062DB12044009DF38B /* IntentData.swift */; };
|
||||
1E55C20B2DB120C2009DF38B /* RNDeepLinkIntentModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E55C20A2DB120C2009DF38B /* RNDeepLinkIntentModule.swift */; };
|
||||
1E55C20D2DB120E7009DF38B /* RNDeepLinkIntentModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E55C20C2DB120E7009DF38B /* RNDeepLinkIntentModule.m */; };
|
||||
1E6875E92CA554E80086D870 /* OpenID4VP in Frameworks */ = {isa = PBXBuildFile; productRef = 1E6875E82CA554E80086D870 /* OpenID4VP */; };
|
||||
1E6875EB2CA554FD0086D870 /* RNOpenID4VPModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E6875EA2CA554FD0086D870 /* RNOpenID4VPModule.m */; };
|
||||
1E6875ED2CA5550F0086D870 /* RNOpenID4VPModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6875EC2CA5550F0086D870 /* RNOpenID4VPModule.swift */; };
|
||||
@@ -69,6 +72,9 @@
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Inji/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Inji/main.m; sourceTree = "<group>"; };
|
||||
1D23B9CD47CFD7F3C87D202F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = Inji/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
1E55C2062DB12044009DF38B /* IntentData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentData.swift; sourceTree = "<group>"; };
|
||||
1E55C20A2DB120C2009DF38B /* RNDeepLinkIntentModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNDeepLinkIntentModule.swift; sourceTree = "<group>"; };
|
||||
1E55C20C2DB120E7009DF38B /* RNDeepLinkIntentModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNDeepLinkIntentModule.m; sourceTree = "<group>"; };
|
||||
1E6875EA2CA554FD0086D870 /* RNOpenID4VPModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNOpenID4VPModule.m; sourceTree = "<group>"; };
|
||||
1E6875EC2CA5550F0086D870 /* RNOpenID4VPModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNOpenID4VPModule.swift; sourceTree = "<group>"; };
|
||||
1EED69F82DA913D30042EAFC /* IntentData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentData.swift; sourceTree = "<group>"; };
|
||||
@@ -552,6 +558,7 @@
|
||||
files = (
|
||||
1EED69FD2DA914D00042EAFC /* RNDeepLinkIntentModule.m in Sources */,
|
||||
1E6875ED2CA5550F0086D870 /* RNOpenID4VPModule.swift in Sources */,
|
||||
1E55C20B2DB120C2009DF38B /* RNDeepLinkIntentModule.swift in Sources */,
|
||||
9C48504F2C3E59B5002ECBD5 /* RNVersionModule.swift in Sources */,
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
||||
9C0E86BB2BEE36C300E9F9F6 /* RNPixelpassModule.m in Sources */,
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
<string>io.mosip.residentapp.inji</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>openid4vp</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
|
||||
@@ -60,4 +60,4 @@ import Foundation
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
RCT_EXTERN_METHOD(getDeepLinkIntentData:(NSString *)flowType resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
||||
RCT_EXTERN_METHOD(resetDeepLinkIntentData:(NSString *)flowType)
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -21,4 +21,4 @@ class RNDeepLinkIntentModule: NSObject, RCTBridgeModule {
|
||||
@objc static func requiresMainQueueSetup() -> Bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,6 +725,7 @@
|
||||
"accepted": {
|
||||
"title": "تمت مشاركة المعرف بنجاح!",
|
||||
"message": "لقد تمت مشاركة هويتك بنجاح.",
|
||||
"additionalMessage": "يمكنك الآن العودة إلى Verifier.",
|
||||
"home": "المنزل",
|
||||
"history": "تاريخ"
|
||||
},
|
||||
@@ -884,11 +885,12 @@
|
||||
"message": "يمكنك مشاركة رخصة قيادة متنقلة أو مستند واحد فقط في كل مرة. يرجى اختيار مستند واحد فقط للمتابعة."
|
||||
}
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "ஏற்றுகிறது...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "பொருந்தக்கூடிய சரிபார்க்கக்கூடிய நற்சான்றிதழ்களின் பட்டியலைப் பெறுகிறது"
|
||||
}
|
||||
"additionalMessage": "يرجى العودة إلى تطبيق التحقق."
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "ஏற்றுகிறது...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "பொருந்தக்கூடிய சரிபார்க்கக்கூடிய நற்சான்றிதழ்களின் பட்டியலைப் பெறுகிறது"
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
|
||||
@@ -733,6 +733,7 @@
|
||||
"accepted": {
|
||||
"title": "ID shared successfully!",
|
||||
"message": "Your ID has been successfully shared.",
|
||||
"additionalMessage": "You can now go back to the Verifier.",
|
||||
"home": "Home",
|
||||
"history": "History"
|
||||
},
|
||||
@@ -826,7 +827,7 @@
|
||||
"SendVcScreen": {
|
||||
"reasonForSharing": "Reason for sharing (optional)",
|
||||
"introTitle": "Requester",
|
||||
"requestMessage":"is requesting your digital ID for the purpose",
|
||||
"requestMessage": "is requesting your digital ID for the purpose",
|
||||
"acceptRequest": "Share",
|
||||
"acceptRequestAndVerify": "Share with Selfie",
|
||||
"reject": "Reject",
|
||||
@@ -896,11 +897,12 @@
|
||||
"message": "You can share only one Mobile Driving License or document at a time. Please select just one to continue."
|
||||
}
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "Loading...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "Fetching your list of matching verifiable credentials"
|
||||
}
|
||||
"additionalMessage": "Please go back to the verifier app."
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "Loading...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "Fetching your list of matching verifiable credentials"
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
@@ -991,6 +993,10 @@
|
||||
"invalidateKeyError": {
|
||||
"title": "App was Reset",
|
||||
"message": "Due to the fingerprint / facial recognition update, app security was impacted, and downloaded cards were removed. Please download again."
|
||||
},
|
||||
"appSetupIncomplete": {
|
||||
"title": "Setup incomplete",
|
||||
"message": "Please finish the initial setup to proceed."
|
||||
}
|
||||
},
|
||||
"qrLoginOverlay": {
|
||||
|
||||
@@ -500,7 +500,7 @@
|
||||
"pending": {
|
||||
"title": "Nakabinbing Katayuan:",
|
||||
"description": "Kasalukuyang nakabinbin ang pag-verify dahil sa mga teknikal na isyu."
|
||||
},
|
||||
},
|
||||
"expired": {
|
||||
"title": "Nag-expire na Katayuan:",
|
||||
"description": "Nag-expire na ang kredensyal."
|
||||
@@ -728,6 +728,7 @@
|
||||
"accepted": {
|
||||
"title": "Matagumpay na naibahagi ang ID!",
|
||||
"message": "Ang iyong ID ay matagumpay na naibahagi",
|
||||
"additionalMessage": "Maaari ka na ngayong bumalik sa Verifier.",
|
||||
"home": "Bahay",
|
||||
"history": "Kasaysayan"
|
||||
},
|
||||
@@ -887,11 +888,12 @@
|
||||
"message": "Maaari ka lamang magbahagi ng isang Mobile Driving License o dokumento sa bawat pagkakataon. Mangyaring pumili lamang ng isa upang magpatuloy."
|
||||
}
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "Naglo-load...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "Kinukuha ang iyong listahan ng mga tumutugmang nabe-verify na kredensyal"
|
||||
}
|
||||
"additionalMessage": "Mangyaring bumalik sa verifier app."
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "Naglo-load...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "Kinukuha ang iyong listahan ng mga tumutugmang nabe-verify na kredensyal"
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
"shared": "साझा",
|
||||
"received": "प्राप्त",
|
||||
"deleted": "हटाए गए",
|
||||
"historyHeaderLabel":"इतिहास"
|
||||
"historyHeaderLabel": "इतिहास"
|
||||
},
|
||||
"SettingScreen": {
|
||||
"header": "समायोजन",
|
||||
@@ -502,7 +502,7 @@
|
||||
"pending": {
|
||||
"title": "लंबित स्थिति:",
|
||||
"description": "तकनीकी समस्याओं के कारण सत्यापन फिलहाल लंबित है।"
|
||||
},
|
||||
},
|
||||
"expired": {
|
||||
"title": "समाप्त स्थिति:",
|
||||
"description": "क्रेडेंशियल समाप्त हो गया है।"
|
||||
@@ -731,6 +731,7 @@
|
||||
"accepted": {
|
||||
"title": "आईडी सफलतापूर्वक साझा की गई!",
|
||||
"message": "आपकी आईडी सफलतापूर्वक साझा कर दी गई है.",
|
||||
"additionalMessage": "अब आप सत्यापनकर्ता के पास वापस जा सकते हैं।",
|
||||
"home": "होम",
|
||||
"history": "इतिहास"
|
||||
},
|
||||
@@ -820,7 +821,7 @@
|
||||
"SendVcScreen": {
|
||||
"reasonForSharing": "साझा करने का कारण (वैकल्पिक)",
|
||||
"introTitle": "अनुरोधकर्ता",
|
||||
"requestMessage":"आपके डिजिटल आईडी के लिए अनुरोध कर रहा है",
|
||||
"requestMessage": "आपके डिजिटल आईडी के लिए अनुरोध कर रहा है",
|
||||
"acceptRequest": "शेयर करना",
|
||||
"acceptRequestAndVerify": "सेल्फी के साथ शेयर करें",
|
||||
"consentToPhotoVerification": "मैं सत्यापन के लिए अपना फोटो लेने की अनुमति देता हूं",
|
||||
@@ -890,11 +891,12 @@
|
||||
"message": "आप एक समय में केवल एक मोबाइल ड्राइविंग लाइसेंस या दस्तावेज़ साझा कर सकते हैं। कृपया जारी रखने के लिए केवल एक का चयन करें।"
|
||||
}
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "लोड हो रहा है...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "मेल खाने वाले सत्यापन योग्य क्रेडेंशियल्स की आपकी सूची लाई जा रही है"
|
||||
}
|
||||
"additionalMessage": "कृपया सत्यापनकर्ता ऐप पर वापस जाएं।"
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "लोड हो रहा है...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "मेल खाने वाले सत्यापन योग्य क्रेडेंशियल्स की आपकी सूची लाई जा रही है"
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
|
||||
@@ -729,6 +729,7 @@
|
||||
"accepted": {
|
||||
"title": "ಐಡಿಯನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಹಂಚಿಕೊಳ್ಳಲಾಗಿದೆ!",
|
||||
"message": "ನಿಮ್ಮ ಐಡಿಯನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಹಂಚಿಕೊಳ್ಳಲಾಗಿದೆ.",
|
||||
"additionalMessage": "ನೀವು ಈಗ ಪರಿಶೀಲಕಕ್ಕೆ ಹಿಂತಿರುಗಬಹುದು.",
|
||||
"home": "ಮನೆ",
|
||||
"history": "ಇತಿಹಾಸ"
|
||||
},
|
||||
@@ -888,11 +889,12 @@
|
||||
"message": "ಒಂದು ವೇಳೆ ಒಂದು ಮೊಬೈಲ್ ಡ್ರೈವಿಂಗ್ ಲೈಸೆನ್ಸ್ ಅಥವಾ ಡಾಕ್ಯುಮೆಂಟ್ ಅನ್ನು ಮಾತ್ರ ಹಂಚಬಹುದು. ಮುಂದುವರಿಸಲು ಒಂದು ಡಾಕ್ಯುಮೆಂಟ್ ಅನ್ನು ಮಾತ್ರ ಆಯ್ಕೆಮಾಡಿ."
|
||||
}
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "ಲೋಡ್ ಆಗುತ್ತಿದೆ...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "ನಿಮ್ಮ ಹೊಂದಾಣಿಕೆಯ ಪರಿಶೀಲಿಸಬಹುದಾದ ರುಜುವಾತುಗಳ ಪಟ್ಟಿಯನ್ನು ಪಡೆಯಲಾಗುತ್ತಿದೆ"
|
||||
}
|
||||
"additionalMessage": "ದಯವಿಟ್ಟು ಪರಿಶೀಲನಾ ಅಪ್ಲಿಕೇಶನ್ಗೆ ಹಿಂತಿರುಗಿ."
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "ಲೋಡ್ ಆಗುತ್ತಿದೆ...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "ನಿಮ್ಮ ಹೊಂದಾಣಿಕೆಯ ಪರಿಶೀಲಿಸಬಹುದಾದ ರುಜುವಾತುಗಳ ಪಟ್ಟಿಯನ್ನು ಪಡೆಯಲಾಗುತ್ತಿದೆ"
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
|
||||
@@ -729,6 +729,7 @@
|
||||
"accepted": {
|
||||
"title": "ஐடி வெற்றிகரமாகப் பகிரப்பட்டது!",
|
||||
"message": "உங்கள் ஐடி வெற்றிகரமாகப் பகிரப்பட்டது.",
|
||||
"additionalMessage": "நீங்கள் இப்போது சரிபார்ப்பாளருக்குத் திரும்பலாம்.",
|
||||
"home": "வீடு",
|
||||
"history": "வரலாறு"
|
||||
},
|
||||
@@ -818,7 +819,7 @@
|
||||
"SendVcScreen": {
|
||||
"reasonForSharing": "பகிர்வதற்கான காரணம் (விரும்பினால்)",
|
||||
"introTitle": "கோரியவர்",
|
||||
"requestMessage":"உங்கள் டிஜிட்டல் ஐடியை கோருகிறார்",
|
||||
"requestMessage": "உங்கள் டிஜிட்டல் ஐடியை கோருகிறார்",
|
||||
"acceptRequest": "பகிர்",
|
||||
"acceptRequestAndVerify": "செல்ஃபியுடன் பகிரவும்",
|
||||
"reject": "நிராகரிக்கவும்",
|
||||
@@ -888,11 +889,12 @@
|
||||
"message": "ஒரே நேரத்தில் ஒரு மொபைல் டிரைவிங் லைசன்ஸ் அல்லது ஆவணத்தை மட்டுமே பகிர முடியும். தொடர ஒரு ஆவணத்தை மட்டும் தேர்ந்தெடுக்கவும்."
|
||||
}
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "ஏற்றுகிறது...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "பொருந்தக்கூடிய சரிபார்க்கக்கூடிய நற்சான்றிதழ்களின் பட்டியலைப் பெறுகிறது"
|
||||
}
|
||||
"additionalMessage": "சரிபார்ப்பு பயன்பாட்டிற்குத் திரும்பவும்."
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "ஏற்றுகிறது...",
|
||||
"subTitle": {
|
||||
"fetchingVerifiers": "பொருந்தக்கூடிய சரிபார்க்கக்கூடிய நற்சான்றிதழ்களின் பட்டியலைப் பெறுகிறது"
|
||||
}
|
||||
},
|
||||
"VerifyIdentityOverlay": {
|
||||
|
||||
@@ -570,3 +570,9 @@ export function selectIsLinkCode(state: State) {
|
||||
export function selectAuthorizationRequest(state: State) {
|
||||
return state.context.authorizationRequest;
|
||||
}
|
||||
|
||||
export function selectIsDeepLinkDetected(state: State) {
|
||||
return !!(
|
||||
state.context.authorizationRequest !== '' || state.context.linkCode !== ''
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const model = createModel(
|
||||
isOnboarding: true,
|
||||
isInitialDownload: true,
|
||||
isTourGuide: false,
|
||||
appInitialSetupDone: false,
|
||||
isAppSetupComplete: false,
|
||||
},
|
||||
{
|
||||
events: {
|
||||
@@ -66,7 +66,7 @@ export const authMachine = model.createMachine(
|
||||
actions: 'setTourGuide',
|
||||
},
|
||||
BIOMETRIC_CANCELLED: {
|
||||
target: 'init'
|
||||
target: 'init',
|
||||
},
|
||||
},
|
||||
states: {
|
||||
@@ -81,9 +81,11 @@ export const authMachine = model.createMachine(
|
||||
},
|
||||
{target: 'savingDefaults'},
|
||||
],
|
||||
BIOMETRIC_CANCELLED: [{
|
||||
target: 'init'
|
||||
}],
|
||||
BIOMETRIC_CANCELLED: [
|
||||
{
|
||||
target: 'init',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
savingDefaults: {
|
||||
@@ -124,11 +126,21 @@ export const authMachine = model.createMachine(
|
||||
on: {
|
||||
SETUP_PASSCODE: {
|
||||
target: 'authorized',
|
||||
actions: ['setPasscode', 'setLanguage', 'setAppSetupComplete', 'storeContext'],
|
||||
},
|
||||
actions: [
|
||||
'setPasscode',
|
||||
'setLanguage',
|
||||
'setAppSetupComplete',
|
||||
'storeContext',
|
||||
],
|
||||
},
|
||||
SETUP_BIOMETRICS: {
|
||||
target: 'authorized',
|
||||
actions: ['setBiometrics', 'setLanguage', 'setAppSetupComplete', 'storeContext'],
|
||||
actions: [
|
||||
'setBiometrics',
|
||||
'setLanguage',
|
||||
'setAppSetupComplete',
|
||||
'storeContext',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -159,6 +171,10 @@ export const authMachine = model.createMachine(
|
||||
},
|
||||
{
|
||||
actions: {
|
||||
setAppSetupComplete: assign({
|
||||
isAppSetupComplete: context => true,
|
||||
}),
|
||||
|
||||
requestStoredContext: send(StoreEvents.GET('auth'), {
|
||||
to: context => context.serviceRefs.store,
|
||||
}),
|
||||
@@ -194,10 +210,6 @@ export const authMachine = model.createMachine(
|
||||
selectLanguage: context => true,
|
||||
}),
|
||||
|
||||
setAppSetupComplete: assign({
|
||||
appInitialSetupDone: context => true,
|
||||
}),
|
||||
|
||||
setPasscodeSalt: assign({
|
||||
passcodeSalt: (context, event) => {
|
||||
return event.data as string;
|
||||
@@ -256,10 +268,6 @@ export function selectPasscode(state: State) {
|
||||
return state?.context?.passcode;
|
||||
}
|
||||
|
||||
export function selectAppSetupComplete(state: State) {
|
||||
return state.context.appInitialSetupDone;
|
||||
}
|
||||
|
||||
export function selectPasscodeSalt(state: State) {
|
||||
return state.context.passcodeSalt;
|
||||
}
|
||||
@@ -309,3 +317,7 @@ export function selectIsBiometricToggleFromSettings(state: State) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function selectAppSetupComplete(state: State) {
|
||||
return state.context.isAppSetupComplete;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,11 @@ export const ScanActions = (model: any) => {
|
||||
resetLinkCode: model.assign({
|
||||
linkcode: '',
|
||||
}),
|
||||
|
||||
resetAuthorizationRequest: model.assign({
|
||||
authorizationRequest: '',
|
||||
}),
|
||||
|
||||
updateShowFaceAuthConsent: model.assign({
|
||||
showFaceAuthConsent: (_, event) => {
|
||||
return event.response || event.response === null;
|
||||
@@ -104,7 +109,7 @@ export const ScanActions = (model: any) => {
|
||||
sendVPScanData: context =>
|
||||
context.OpenId4VPRef.send({
|
||||
type: 'AUTHENTICATE',
|
||||
encodedAuthRequest: context.linkCode,
|
||||
encodedAuthRequest: context.authorizationRequest,
|
||||
flowType: context.openID4VPFlowType,
|
||||
selectedVC: context.selectedVc,
|
||||
isOVPViaDeepLink: context.isOVPViaDeepLink,
|
||||
@@ -285,6 +290,12 @@ export const ScanActions = (model: any) => {
|
||||
linkCode: (_, event) => event.linkCode,
|
||||
}),
|
||||
|
||||
setAuthRequestFromDeepLink: assign({
|
||||
authorizationRequest: (_, event) => {
|
||||
return event.params ?? event.authorizationRequest;
|
||||
},
|
||||
}),
|
||||
|
||||
setIsQrLoginViaDeepLink: assign({
|
||||
isQrLoginViaDeepLink: true,
|
||||
}),
|
||||
|
||||
@@ -38,6 +38,9 @@ export const ScanGuards = () => {
|
||||
isQrLoginViaDeepLinking: context => context.isQrLoginViaDeepLink === true,
|
||||
isOVPViaDeepLink: context => context.isOVPViaDeepLink === true,
|
||||
|
||||
isFlowTypeDeepLink: context =>
|
||||
context.isOVPViaDeepLink || context.isQrLoginViaDeepLink,
|
||||
|
||||
isFlowTypeMiniViewShareWithSelfie: context =>
|
||||
context.flowType === VCShareFlowType.MINI_VIEW_SHARE_WITH_SELFIE,
|
||||
|
||||
|
||||
@@ -49,8 +49,12 @@ export const scanMachine =
|
||||
actions: [
|
||||
'removeLoggers',
|
||||
'resetFlowType',
|
||||
'resetOpenID4VPFlowType',
|
||||
'resetSelectedVc',
|
||||
'resetIsQrLoginViaDeepLink',
|
||||
'resetIsOVPViaDeepLink',
|
||||
'resetAuthorizationRequest',
|
||||
'resetLinkCode',
|
||||
],
|
||||
target: '.checkStorage',
|
||||
},
|
||||
@@ -75,7 +79,7 @@ export const scanMachine =
|
||||
},
|
||||
OVP_VIA_DEEP_LINK: {
|
||||
actions: [
|
||||
'setLinkCodeFromDeepLink',
|
||||
'setAuthRequestFromDeepLink',
|
||||
'setIsOVPViaDeepLink',
|
||||
'setOpenId4VPFlowType',
|
||||
],
|
||||
@@ -110,10 +114,6 @@ export const scanMachine =
|
||||
cond: 'isMinimumStorageRequiredForAuditEntryReached',
|
||||
target: 'restrictSharingVc',
|
||||
},
|
||||
{
|
||||
cond: 'isOVPViaDeepLink',
|
||||
target: '#scan.checkFaceAuthConsent',
|
||||
},
|
||||
{
|
||||
target: 'startPermissionCheck',
|
||||
},
|
||||
@@ -125,6 +125,10 @@ export const scanMachine =
|
||||
startPermissionCheck: {
|
||||
on: {
|
||||
START_PERMISSION_CHECK: [
|
||||
{
|
||||
cond: 'isFlowTypeDeepLink',
|
||||
target: '#scan.checkFaceAuthConsent',
|
||||
},
|
||||
{
|
||||
cond: 'uptoAndroid11',
|
||||
target: '#scan.checkBluetoothPermission',
|
||||
@@ -340,6 +344,7 @@ export const scanMachine =
|
||||
},
|
||||
{
|
||||
cond: 'isOVPViaDeepLink',
|
||||
actions: ['setOpenId4VPFlowType'],
|
||||
target: '#scan.startVPSharing',
|
||||
},
|
||||
{
|
||||
@@ -373,7 +378,7 @@ export const scanMachine =
|
||||
{
|
||||
target: 'startVPSharing',
|
||||
cond: 'isOnlineSharing',
|
||||
actions: ['setOpenId4VPFlowType', 'setLinkCode'],
|
||||
actions: ['setOpenId4VPFlowType', 'setAuthRequestFromDeepLink'],
|
||||
},
|
||||
{
|
||||
target: 'decodeQuickShareData',
|
||||
|
||||
@@ -61,7 +61,7 @@ const ScanEvents = {
|
||||
IN_PROGRESS: () => ({}),
|
||||
TIMEOUT: () => ({}),
|
||||
QRLOGIN_VIA_DEEP_LINK: (linkCode: string) => ({linkCode}),
|
||||
OVP_VIA_DEEP_LINK: (linkCode: string) => ({linkCode}),
|
||||
OVP_VIA_DEEP_LINK: (authorizationRequest: string) => ({authorizationRequest}),
|
||||
};
|
||||
|
||||
export const ScanModel = createModel(
|
||||
|
||||
@@ -15,7 +15,7 @@ import {SvgImage} from '../../components/ui/svg';
|
||||
import {View, I18nManager} from 'react-native';
|
||||
import {Text} from './../../components/ui';
|
||||
import {BannerStatusType} from '../../components/BannerNotification';
|
||||
import {LIVENESS_CHECK} from '../../shared/constants';
|
||||
import {isIOS, LIVENESS_CHECK} from '../../shared/constants';
|
||||
import {SendVPScreen} from './SendVPScreen';
|
||||
|
||||
const ScanStack = createNativeStackNavigator();
|
||||
@@ -107,21 +107,7 @@ export const ScanLayout: React.FC = () => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<ScanStack.Screen
|
||||
name={SCAN_ROUTES.ScanScreen}
|
||||
component={ScanScreen}
|
||||
options={{
|
||||
title: t('MainLayout:share'),
|
||||
headerTitle: props => (
|
||||
<View style={Theme.Styles.scanLayoutHeaderContainer}>
|
||||
<Text style={Theme.Styles.scanLayoutHeaderTitle}>
|
||||
{props.children}
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{controller.openID4VPFlowType === VCShareFlowType.OPENID4VP && (
|
||||
{controller.openID4VPFlowType === VCShareFlowType.OPENID4VP ? (
|
||||
<ScanStack.Screen
|
||||
name={SCAN_ROUTES.SendVPScreen}
|
||||
component={SendVPScreen}
|
||||
@@ -158,6 +144,21 @@ export const ScanLayout: React.FC = () => {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ScanStack.Screen
|
||||
name={SCAN_ROUTES.ScanScreen}
|
||||
component={ScanScreen}
|
||||
options={{
|
||||
title: t('MainLayout:share'),
|
||||
headerTitle: props => (
|
||||
<View style={Theme.Styles.scanLayoutHeaderContainer}>
|
||||
<Text style={Theme.Styles.scanLayoutHeaderTitle}>
|
||||
{props.children}
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ScanStack.Navigator>
|
||||
|
||||
@@ -169,6 +170,11 @@ export const ScanLayout: React.FC = () => {
|
||||
}
|
||||
title={t('status.accepted.title')}
|
||||
message={t('status.accepted.message')}
|
||||
additionalMessage={
|
||||
controller.isOVPViaDeepLink && isIOS()
|
||||
? t('status.accepted.additionalMessage')
|
||||
: ''
|
||||
}
|
||||
image={SvgImage.SuccessLogo()}
|
||||
goToHome={controller.GOTO_HOME}
|
||||
goToHistory={controller.GOTO_HISTORY}
|
||||
|
||||
@@ -307,9 +307,11 @@ export function useScanLayout() {
|
||||
if (linkCode != '') {
|
||||
scanService.send(ScanEvents.QRLOGIN_VIA_DEEP_LINK(linkCode));
|
||||
appService.send(APP_EVENTS.RESET_LINKCODE());
|
||||
} else if (authorizationRequest != '' && shareableVcsMetadata.length) {
|
||||
} else if (authorizationRequest != '') {
|
||||
scanService.send(ScanEvents.OVP_VIA_DEEP_LINK(authorizationRequest));
|
||||
appService.send(APP_EVENTS.RESET_AUTHORIZATION_REQUEST());
|
||||
if (shareableVcsMetadata.length !== 0) {
|
||||
appService.send(APP_EVENTS.RESET_AUTHORIZATION_REQUEST());
|
||||
}
|
||||
} else if (isQrLoginDoneViaDeeplink) {
|
||||
changeTabBarVisible('flex');
|
||||
navigation.navigate(BOTTOM_TAB_ROUTES.home);
|
||||
|
||||
@@ -29,7 +29,6 @@ import {VerifyIdentityOverlay} from '../VerifyIdentityOverlay';
|
||||
import {VCShareFlowType} from '../../shared/Utils';
|
||||
import {APP_EVENTS} from '../../machines/app';
|
||||
import {GlobalContext} from '../../shared/GlobalContext';
|
||||
import {OpenID4VP} from '../../shared/openID4VP/OpenID4VP';
|
||||
|
||||
export const ScanScreen: React.FC = () => {
|
||||
const {t} = useTranslation('ScanScreen');
|
||||
@@ -61,11 +60,16 @@ export const ScanScreen: React.FC = () => {
|
||||
|
||||
// TODO(kludge): skip running this hook on every render
|
||||
useEffect(() => {
|
||||
if (
|
||||
scanScreenController.isStartPermissionCheck &&
|
||||
!scanScreenController.isNoSharableVCs
|
||||
)
|
||||
scanScreenController.START_PERMISSION_CHECK();
|
||||
if (scanScreenController.isStartPermissionCheck) {
|
||||
if (
|
||||
scanScreenController.authorizationRequest !== '' &&
|
||||
scanScreenController.isNoSharableVCs
|
||||
) {
|
||||
scanScreenController.START_PERMISSION_CHECK();
|
||||
} else if (!scanScreenController.isNoSharableVCs) {
|
||||
scanScreenController.START_PERMISSION_CHECK();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -75,20 +79,14 @@ export const ScanScreen: React.FC = () => {
|
||||
useEffect(() => {
|
||||
if (
|
||||
scanScreenController.isNoSharableVCs &&
|
||||
scanScreenController.authorizationRequest != ''
|
||||
) {
|
||||
scanScreenController.linkcode !== ''
|
||||
)
|
||||
setTimeout(() => {
|
||||
OpenID4VP.initialize();
|
||||
OpenID4VP.sendErrorToVerifier(OVP_ERROR_MESSAGES.NO_MATCHING_VCS);
|
||||
BackHandler.exitApp();
|
||||
scanScreenController.GOTO_HOME();
|
||||
appService.send(APP_EVENTS.RESET_AUTHORIZATION_REQUEST());
|
||||
appService.send(APP_EVENTS.RESET_LINKCODE());
|
||||
BackHandler.exitApp();
|
||||
}, 2000);
|
||||
}
|
||||
}, [
|
||||
scanScreenController.isNoSharableVCs,
|
||||
scanScreenController.authorizationRequest,
|
||||
]);
|
||||
}, [scanScreenController.isNoSharableVCs, scanScreenController.linkcode]);
|
||||
|
||||
const openSettings = () => {
|
||||
Linking.openSettings();
|
||||
@@ -202,7 +200,10 @@ export const ScanScreen: React.FC = () => {
|
||||
}
|
||||
|
||||
function loadQRScanner() {
|
||||
if (scanScreenController.isNoSharableVCs) {
|
||||
if (
|
||||
scanScreenController.isNoSharableVCs &&
|
||||
scanScreenController.authorizationRequest === ''
|
||||
) {
|
||||
return noShareableVcText();
|
||||
}
|
||||
if (scanScreenController.selectIsInvalid) {
|
||||
@@ -274,6 +275,22 @@ export const ScanScreen: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const getPrimaryButtonText = () => {
|
||||
if (
|
||||
sendVPScreenController.errorModal.showRetryButton &&
|
||||
sendVPScreenController.openID4VPRetryCount < 3
|
||||
) {
|
||||
return t('ScanScreen:status.retry');
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getTextButtonText = () => {
|
||||
return sendVPScreenController.isOVPViaDeepLink
|
||||
? undefined
|
||||
: t('ScanScreen:status.accepted.home');
|
||||
};
|
||||
|
||||
const faceVerificationController = sendVPScreenController.flowType.startsWith(
|
||||
'OpenID4VP',
|
||||
)
|
||||
@@ -353,19 +370,10 @@ export const ScanScreen: React.FC = () => {
|
||||
message={sendVPScreenController.errorModal.message}
|
||||
image={SvgImage.PermissionDenied()}
|
||||
primaryButtonTestID={'retry'}
|
||||
primaryButtonText={
|
||||
sendVPScreenController.errorModal.showRetryButton &&
|
||||
sendVPScreenController.openID4VPRetryCount < 3
|
||||
? t('ScanScreen:status.retry')
|
||||
: undefined
|
||||
}
|
||||
primaryButtonText={getPrimaryButtonText()}
|
||||
primaryButtonEvent={sendVPScreenController.RETRY}
|
||||
textButtonTestID={'home'}
|
||||
textButtonText={
|
||||
sendVPScreenController.isOVPViaDeepLink
|
||||
? undefined
|
||||
: t('ScanScreen:status.accepted.home')
|
||||
}
|
||||
textButtonText={getTextButtonText()}
|
||||
textButtonEvent={handleTextButtonEvent}
|
||||
customImageStyles={{paddingBottom: 0, marginBottom: -6}}
|
||||
customStyles={{marginTop: '30%'}}
|
||||
|
||||
@@ -27,7 +27,7 @@ import {selectIsMinimumStorageRequiredForAuditEntryLimitReached} from '../../mac
|
||||
import {BOTTOM_TAB_ROUTES} from '../../routes/routesConstants';
|
||||
import {MainBottomTabParamList} from '../../routes/routeTypes';
|
||||
import {useNavigation, NavigationProp} from '@react-navigation/native';
|
||||
import {selectAuthorizationRequest} from '../../machines/app';
|
||||
import {selectAuthorizationRequest, selectIsLinkCode} from '../../machines/app';
|
||||
|
||||
export function useScanScreen() {
|
||||
const {t} = useTranslation('ScanScreen');
|
||||
@@ -74,7 +74,10 @@ export function useScanScreen() {
|
||||
}
|
||||
type ScanScreenNavigation = NavigationProp<MainBottomTabParamList>;
|
||||
const navigation = useNavigation<ScanScreenNavigation>();
|
||||
const GOTO_HOME = () => navigation.navigate(BOTTOM_TAB_ROUTES.home);
|
||||
const GOTO_HOME = () => {
|
||||
scanService.send(ScanEvents.RESET());
|
||||
navigation.navigate(BOTTOM_TAB_ROUTES.home);
|
||||
};
|
||||
const ALLOWED = () => scanService.send(ScanEvents.ALLOWED());
|
||||
const DENIED = () => scanService.send(ScanEvents.DENIED());
|
||||
const isLocalPermissionRational = useSelector(
|
||||
@@ -117,5 +120,6 @@ export function useScanScreen() {
|
||||
DENIED,
|
||||
isLocalPermissionRational,
|
||||
authorizationRequest: useSelector(appService, selectAuthorizationRequest),
|
||||
linkcode: useSelector(appService, selectIsLinkCode),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import {useFocusEffect} from '@react-navigation/native';
|
||||
import React, {useEffect, useLayoutEffect, useState} from 'react';
|
||||
import React, {useContext, useEffect, useLayoutEffect, useState} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {BackHandler, I18nManager, View} from 'react-native';
|
||||
import {Button, Column, Row, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {VcItemContainer} from '../../components/VC/VcItemContainer';
|
||||
import {LIVENESS_CHECK, OVP_ERROR_MESSAGES} from '../../shared/constants';
|
||||
import {
|
||||
isIOS,
|
||||
LIVENESS_CHECK,
|
||||
OVP_ERROR_MESSAGES,
|
||||
} from '../../shared/constants';
|
||||
import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants';
|
||||
import {
|
||||
getImpressionEventData,
|
||||
@@ -24,19 +28,29 @@ import {Loader} from '../../components/ui/Loader';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {ScanLayoutProps} from '../../routes/routeTypes';
|
||||
import {OpenID4VP} from '../../shared/openID4VP/OpenID4VP';
|
||||
import {GlobalContext} from '../../shared/GlobalContext';
|
||||
import {APP_EVENTS} from '../../machines/app';
|
||||
import {useScanScreen} from './ScanScreenController';
|
||||
|
||||
export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
|
||||
const {t} = useTranslation('SendVPScreen');
|
||||
const controller = useSendVPScreen();
|
||||
const scanScreenController = useScanScreen();
|
||||
|
||||
const vcsMatchingAuthRequest = controller.vcsMatchingAuthRequest;
|
||||
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const [triggerExitFlow, setTriggerExitFlow] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (controller.errorModal.show && controller.isOVPViaDeepLink) {
|
||||
const timeout = setTimeout(() => {
|
||||
setTriggerExitFlow(true);
|
||||
}, 2000);
|
||||
const timeout = setTimeout(
|
||||
() => {
|
||||
OpenID4VP.sendErrorToVerifier(OVP_ERROR_MESSAGES.NO_MATCHING_VCS);
|
||||
setTriggerExitFlow(true);
|
||||
},
|
||||
isIOS() ? 4000 : 2000,
|
||||
);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}
|
||||
@@ -44,7 +58,11 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
|
||||
|
||||
useEffect(() => {
|
||||
if (triggerExitFlow) {
|
||||
controller.RESET_LOGGED_ERROR();
|
||||
controller.GO_TO_HOME();
|
||||
controller.RESET_RETRY_COUNT();
|
||||
appService.send(APP_EVENTS.RESET_AUTHORIZATION_REQUEST());
|
||||
setTriggerExitFlow(false);
|
||||
BackHandler.exitApp();
|
||||
}
|
||||
}, [triggerExitFlow]);
|
||||
@@ -71,6 +89,19 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
|
||||
}, []),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (scanScreenController.isStartPermissionCheck) {
|
||||
if (
|
||||
scanScreenController.authorizationRequest !== '' &&
|
||||
scanScreenController.isNoSharableVCs
|
||||
) {
|
||||
scanScreenController.START_PERMISSION_CHECK();
|
||||
} else if (!scanScreenController.isNoSharableVCs) {
|
||||
scanScreenController.START_PERMISSION_CHECK();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const handleDismiss = () => {
|
||||
if (controller.isOVPViaDeepLink) {
|
||||
controller.GO_TO_HOME();
|
||||
@@ -91,6 +122,19 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
|
||||
}
|
||||
};
|
||||
|
||||
const getAdditionalMessage = () => {
|
||||
if (
|
||||
controller.isOVPViaDeepLink &&
|
||||
!(
|
||||
controller.errorModal.showRetryButton &&
|
||||
controller.openID4VPRetryCount < 3
|
||||
)
|
||||
) {
|
||||
return controller.errorModal.additionalMessage;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (controller.showLoadingScreen) {
|
||||
props.navigation.setOptions({
|
||||
@@ -163,6 +207,19 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
|
||||
return controller.overlayDetails?.primaryButtonEvent;
|
||||
};
|
||||
|
||||
const getPrimaryButtonText = () => {
|
||||
return controller.errorModal.showRetryButton &&
|
||||
controller.openID4VPRetryCount < 3
|
||||
? t('ScanScreen:status.retry')
|
||||
: undefined;
|
||||
};
|
||||
|
||||
const getTextButtonText = () => {
|
||||
return controller.isOVPViaDeepLink
|
||||
? undefined
|
||||
: t('ScanScreen:status.accepted.home');
|
||||
};
|
||||
|
||||
const getVcKey = vcData => {
|
||||
return VCMetadata.fromVcMetadataString(vcData.vcMetadata).getVcKey();
|
||||
};
|
||||
@@ -354,21 +411,13 @@ export const SendVPScreen: React.FC<ScanLayoutProps> = props => {
|
||||
isVisible={controller.errorModal.show}
|
||||
title={controller.errorModal.title}
|
||||
message={controller.errorModal.message}
|
||||
additionalMessage={getAdditionalMessage()}
|
||||
image={SvgImage.PermissionDenied()}
|
||||
primaryButtonTestID={'retry'}
|
||||
primaryButtonText={
|
||||
controller.errorModal.showRetryButton &&
|
||||
controller.openID4VPRetryCount < 3
|
||||
? t('ScanScreen:status.retry')
|
||||
: undefined
|
||||
}
|
||||
primaryButtonText={getPrimaryButtonText()}
|
||||
primaryButtonEvent={controller.RETRY}
|
||||
textButtonTestID={'home'}
|
||||
textButtonText={
|
||||
!controller.isOVPViaDeepLink
|
||||
? t('ScanScreen:status.accepted.home')
|
||||
: undefined
|
||||
}
|
||||
textButtonText={getTextButtonText()}
|
||||
textButtonEvent={handleTextButtonEvent}
|
||||
customImageStyles={{paddingBottom: 0, marginBottom: -6}}
|
||||
customStyles={{marginTop: '30%'}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {NavigationProp, useNavigation} from '@react-navigation/native';
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {useContext, useState} from 'react';
|
||||
import {useContext, useEffect, useRef, useState} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
@@ -44,6 +44,8 @@ import {VCMetadata} from '../../shared/VCMetadata';
|
||||
import {VPShareOverlayProps} from './VPShareOverlay';
|
||||
import {ActivityLogEvents} from '../../machines/activityLog';
|
||||
import {VPShareActivityLog} from '../../components/VPShareActivityLogEvent';
|
||||
import {SelectedCredentialsForVPSharing} from '../../machines/VerifiableCredential/VCMetaMachine/vc';
|
||||
import {isIOS} from '../../shared/constants';
|
||||
|
||||
type MyVcsTabNavigation = NavigationProp<RootRouteProps>;
|
||||
|
||||
@@ -62,6 +64,9 @@ export function useSendVPScreen() {
|
||||
const [selectedVCKeys, setSelectedVCKeys] = useState<Record<string, string>>(
|
||||
{},
|
||||
);
|
||||
|
||||
const hasLoggedErrorRef = useRef(false);
|
||||
|
||||
const shareableVcs = useSelector(vcMetaService, selectShareableVcs);
|
||||
|
||||
const myVcs = useSelector(vcMetaService, selectMyVcs);
|
||||
@@ -135,12 +140,19 @@ export function useSendVPScreen() {
|
||||
selectOpenID4VPRetryCount,
|
||||
);
|
||||
const noCredentialsMatchingVPRequest =
|
||||
isSelectingVCs && Object.keys(vcsMatchingAuthRequest).length === 0;
|
||||
let errorModal = {
|
||||
show: error !== '' || noCredentialsMatchingVPRequest,
|
||||
title: '',
|
||||
message: '',
|
||||
showRetryButton: false,
|
||||
isSelectingVCs &&
|
||||
(Object.keys(vcsMatchingAuthRequest).length === 0 ||
|
||||
Object.values(vcsMatchingAuthRequest).every(
|
||||
value => Array.isArray(value) && value.length === 0,
|
||||
));
|
||||
|
||||
const isOVPViaDeepLink = useSelector(
|
||||
openID4VPService,
|
||||
selectIsOVPViaDeeplink,
|
||||
);
|
||||
|
||||
const getAdditionalMessage = () => {
|
||||
return isOVPViaDeepLink && isIOS() ? t('errors.additionalMessage') : '';
|
||||
};
|
||||
|
||||
function generateAndStoreLogMessage(logType: string, errorInfo?: string) {
|
||||
@@ -158,6 +170,15 @@ export function useSendVPScreen() {
|
||||
openID4VPService,
|
||||
selectRequestedClaimsByVerifier,
|
||||
);
|
||||
|
||||
const [errorModal, setErrorModalData] = useState({
|
||||
show: false,
|
||||
title: '',
|
||||
message: '',
|
||||
additionalMessage: '',
|
||||
showRetryButton: false,
|
||||
});
|
||||
|
||||
const claimsAsString = '[' + requestedClaimsByVerifier + ']';
|
||||
if (noCredentialsMatchingVPRequest) {
|
||||
errorModal.title = t('errors.noMatchingCredentials.title');
|
||||
@@ -196,7 +217,7 @@ export function useSendVPScreen() {
|
||||
errorModal.title = t('errors.duplicateMdocCredential.title');
|
||||
errorModal.message = t('errors.duplicateMdocCredential.message');
|
||||
errorModal.showRetryButton = false;
|
||||
}else if (error.startsWith('send vp')) {
|
||||
} else if (error.startsWith('send vp')) {
|
||||
errorModal.title = t('errors.genericError.title');
|
||||
errorModal.message = t('errors.genericError.message');
|
||||
errorModal.showRetryButton = true;
|
||||
@@ -206,6 +227,102 @@ export function useSendVPScreen() {
|
||||
generateAndStoreLogMessage('TECHNICAL_ERROR');
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (noCredentialsMatchingVPRequest && !hasLoggedErrorRef.current) {
|
||||
setErrorModalData({
|
||||
show: true,
|
||||
title: t('errors.noMatchingCredentials.title'),
|
||||
message: t('errors.noMatchingCredentials.message', {
|
||||
claims: claimsAsString,
|
||||
}),
|
||||
additionalMessage: getAdditionalMessage(),
|
||||
showRetryButton: false,
|
||||
});
|
||||
generateAndStoreLogMessage(
|
||||
'NO_CREDENTIAL_MATCHING_REQUEST',
|
||||
claimsAsString,
|
||||
);
|
||||
hasLoggedErrorRef.current = true;
|
||||
} else if (
|
||||
(error.includes('Verifier authentication was unsuccessful') ||
|
||||
error.startsWith('api error')) &&
|
||||
!hasLoggedErrorRef.current
|
||||
) {
|
||||
setErrorModalData({
|
||||
show: true,
|
||||
title: t('errors.invalidVerifier.title'),
|
||||
message: t('errors.invalidVerifier.message'),
|
||||
additionalMessage: getAdditionalMessage(),
|
||||
showRetryButton: false,
|
||||
});
|
||||
generateAndStoreLogMessage('VERIFIER_AUTHENTICATION_FAILED');
|
||||
hasLoggedErrorRef.current = true;
|
||||
} else if (
|
||||
error.includes('credential mismatch detected') &&
|
||||
!hasLoggedErrorRef.current
|
||||
) {
|
||||
setErrorModalData({
|
||||
show: true,
|
||||
title: t('errors.credentialsMismatch.title'),
|
||||
message: t('errors.credentialsMismatch.message', {
|
||||
claims: claimsAsString,
|
||||
}),
|
||||
additionalMessage: getAdditionalMessage(),
|
||||
showRetryButton: false,
|
||||
});
|
||||
generateAndStoreLogMessage(
|
||||
'CREDENTIAL_MISMATCH_FROM_KEBAB',
|
||||
claimsAsString,
|
||||
);
|
||||
hasLoggedErrorRef.current = true;
|
||||
} else if (
|
||||
error.includes('none of the selected VC has image') &&
|
||||
!hasLoggedErrorRef.current
|
||||
) {
|
||||
setErrorModalData({
|
||||
show: true,
|
||||
title: t('errors.noImage.title'),
|
||||
message: t('errors.noImage.message'),
|
||||
additionalMessage: getAdditionalMessage(),
|
||||
showRetryButton: false,
|
||||
});
|
||||
generateAndStoreLogMessage('NO_SELECTED_VC_HAS_IMAGE');
|
||||
hasLoggedErrorRef.current = true;
|
||||
} else if (
|
||||
error.startsWith('vc validation') &&
|
||||
!hasLoggedErrorRef.current
|
||||
) {
|
||||
setErrorModalData({
|
||||
show: true,
|
||||
title: t('errors.invalidQrCode.title'),
|
||||
message: t('errors.invalidQrCode.message'),
|
||||
additionalMessage: getAdditionalMessage(),
|
||||
showRetryButton: false,
|
||||
});
|
||||
generateAndStoreLogMessage('INVALID_AUTH_REQUEST');
|
||||
hasLoggedErrorRef.current = true;
|
||||
} else if (error.startsWith('send vp') && !hasLoggedErrorRef.current) {
|
||||
setErrorModalData({
|
||||
show: true,
|
||||
title: t('errors.genericError.title'),
|
||||
message: t('errors.genericError.message'),
|
||||
additionalMessage: getAdditionalMessage(),
|
||||
showRetryButton: true,
|
||||
});
|
||||
hasLoggedErrorRef.current = true;
|
||||
} else if (error !== '' && !hasLoggedErrorRef.current) {
|
||||
setErrorModalData({
|
||||
show: true,
|
||||
title: t('errors.genericError.title'),
|
||||
message: t('errors.genericError.message'),
|
||||
additionalMessage: getAdditionalMessage(),
|
||||
showRetryButton: false,
|
||||
});
|
||||
generateAndStoreLogMessage('TECHNICAL_ERROR');
|
||||
hasLoggedErrorRef.current = true;
|
||||
}
|
||||
}, [error, noCredentialsMatchingVPRequest]);
|
||||
|
||||
let overlayDetails: Omit<VPShareOverlayProps, 'isVisible'> | null = null;
|
||||
let vpVerifierName = useSelector(
|
||||
openID4VPService,
|
||||
@@ -256,6 +373,16 @@ export function useSendVPScreen() {
|
||||
getSelectedVCs,
|
||||
errorModal,
|
||||
overlayDetails,
|
||||
RESET_LOGGED_ERROR: () => {
|
||||
hasLoggedErrorRef.current = false;
|
||||
setErrorModalData({
|
||||
show: false,
|
||||
title: '',
|
||||
message: '',
|
||||
additionalMessage: '',
|
||||
showRetryButton: false,
|
||||
});
|
||||
},
|
||||
scanScreenError: useSelector(scanService, selectIsSendingVPError),
|
||||
vcsMatchingAuthRequest,
|
||||
userSelectedVCs: useSelector(openID4VPService, selectSelectedVCs),
|
||||
@@ -272,7 +399,7 @@ export function useSendVPScreen() {
|
||||
openID4VPService,
|
||||
selectIsFaceVerificationConsent,
|
||||
),
|
||||
isOVPViaDeepLink: useSelector(openID4VPService, selectIsOVPViaDeeplink),
|
||||
isOVPViaDeepLink,
|
||||
credentials: useSelector(openID4VPService, selectCredentials),
|
||||
verifiableCredentialsData: useSelector(
|
||||
openID4VPService,
|
||||
@@ -290,9 +417,9 @@ export function useSendVPScreen() {
|
||||
RETRY_VERIFICATION: () =>
|
||||
openID4VPService.send(OpenID4VPEvents.RETRY_VERIFICATION()),
|
||||
GO_TO_HOME: () => {
|
||||
navigation.navigate(BOTTOM_TAB_ROUTES.home, {screen: 'HomeScreen'});
|
||||
scanService.send(ScanEvents.DISMISS());
|
||||
openID4VPService.send(OpenID4VPEvents.RESET_ERROR());
|
||||
scanService.send(ScanEvents.RESET());
|
||||
navigation.navigate(BOTTOM_TAB_ROUTES.home, {screen: 'HomeScreen'});
|
||||
changeTabBarVisible('flex');
|
||||
},
|
||||
SELECT_VC_ITEM:
|
||||
|
||||
@@ -6,6 +6,7 @@ import {Pressable, Dimensions, BackHandler} from 'react-native';
|
||||
import {Button, Column, Row, Text} from '../../components/ui';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
import {SvgImage} from '../../components/ui/svg';
|
||||
import {isIOS} from '../../shared/constants';
|
||||
|
||||
export const SharingStatusModal: React.FC<SharingStatusModalProps> = props => {
|
||||
const {t} = useTranslation('ScanScreen');
|
||||
@@ -18,9 +19,12 @@ export const SharingStatusModal: React.FC<SharingStatusModalProps> = props => {
|
||||
let timeoutId: NodeJS.Timeout | undefined;
|
||||
|
||||
if (props.isVisible && props.buttonStatus === 'none') {
|
||||
timeoutId = setTimeout(() => {
|
||||
resetAndExit();
|
||||
}, 2000);
|
||||
timeoutId = setTimeout(
|
||||
() => {
|
||||
resetAndExit();
|
||||
},
|
||||
isIOS() ? 4000 : 2000,
|
||||
);
|
||||
}
|
||||
return () => {
|
||||
if (timeoutId) {
|
||||
@@ -54,6 +58,13 @@ export const SharingStatusModal: React.FC<SharingStatusModalProps> = props => {
|
||||
color={Theme.Colors.statusMessage}>
|
||||
{props.message}
|
||||
</Text>
|
||||
<Text
|
||||
testID="sharingStatusAdditionalMessage"
|
||||
margin="20 0"
|
||||
style={Theme.TextStyles.bold}
|
||||
size={'large'}>
|
||||
{props.additionalMessage}
|
||||
</Text>
|
||||
</Column>
|
||||
{props.buttonStatus === 'homeAndHistoryIcons' ? (
|
||||
<Row
|
||||
@@ -119,6 +130,7 @@ interface SharingStatusModalProps {
|
||||
buttonStatus?: 'homeAndHistoryIcons' | 'none';
|
||||
title: String;
|
||||
message: String;
|
||||
additionalMessage?: String;
|
||||
image: React.ReactElement;
|
||||
gradientButtonTitle?: String;
|
||||
clearButtonTitle?: String;
|
||||
|
||||
@@ -9,8 +9,8 @@ export const walletMetadata = {
|
||||
],
|
||||
},
|
||||
mso_mdoc: {
|
||||
alg_values_supported: ['ES256']
|
||||
}
|
||||
alg_values_supported: ['ES256'],
|
||||
},
|
||||
},
|
||||
client_id_schemes_supported: ['redirect_uri', 'did', 'pre-registered'],
|
||||
request_object_signing_alg_values_supported: ['EdDSA'],
|
||||
|
||||
Reference in New Issue
Block a user