Merge remote-tracking branch 'upstream/develop' into inji-505-release

Co-authored-by: adityankannan-tw <109274996+adityankannan-tw@users.noreply.github.com>
Signed-off-by: Kiruthika Jeyashankar <81218987+KiruthikaJeyashankar@users.noreply
.github.com>
This commit is contained in:
Kiruthika Jeyashankar
2023-10-25 11:02:45 +05:30
167 changed files with 2448 additions and 2751 deletions

5
.env
View File

@@ -6,8 +6,6 @@ MIMOTO_HOST=https://api.qa-inji.mosip.net
ESIGNET_HOST=https://api.qa-inji.mosip.net
GOOGLE_NEARBY_MESSAGES_API_KEY=
OBSRV_HOST = https://dataset-api.obsrv.mosip.net
#Application Theme can be ( orange | purple )
@@ -19,6 +17,3 @@ DEBUG_MODE=false
#supported languages( en, fil, ar, hi, kn, ta)
APPLICATION_LANGUAGE=en
#Toggle for openID for VC
ENABLE_OPENID_FOR_VC=true

View File

@@ -104,13 +104,6 @@ jobs:
gpg2 --import ./.github/keys/mosipgpgkey_pub.gpg
gpg2 --quiet --batch --passphrase=${{secrets.gpg_secret}} --allow-secret-key-import --import ./.github/keys/mosipgpgkey_sec.gpg
- name: Setup Firebase
run: |
cd android
if [ ! -z "$FIREBASE_SECRET" ] && [ -f app/google-services.json.gpg ];then rm -f app/google-services.json;gpg2 --quiet --batch --passphrase=$FIREBASE_SECRET --pinentry-mode loopback --decrypt --output app/google-services.json app/mosip-google-services.json.gpg;fi
env:
FIREBASE_SECRET: ${{ secrets.GPG_SECRET }}
- name: Generate keystore
run: |
echo "$ANDROID_KEYSTORE_FILE" > release.keystore.b64

View File

@@ -109,20 +109,6 @@ jobs:
gpg2 --import ./.github/keys/mosipgpgkey_pub.gpg
gpg2 --quiet --batch --passphrase=${{secrets.gpg_secret}} --allow-secret-key-import --import ./.github/keys/mosipgpgkey_sec.gpg
- name: Setup Firebase
run: |
cd android
if [ ! -z "$FIREBASE_SECRET" ] && [ -f app/google-services.json.gpg ];
then
rm -f app/google-services.json;gpg2 --quiet --batch --passphrase=$FIREBASE_SECRET --pinentry-mode loopback --decrypt --output app/google-services.json app/mosip-google-services.json.gpg;
else
echo "Using Dummy Google Service JSON for firebase"
echo "$DUMMY_GOOGLE_SERVICE_JSON" > app/google-services.json
fi
env:
FIREBASE_SECRET: ${{ secrets.GPG_SECRET }}
- name: Generate keystore
run: |
keytool \

View File

@@ -92,13 +92,6 @@ jobs:
gpg2 --import ./.github/keys/mosipgpgkey_pub.gpg
gpg2 --quiet --batch --passphrase=${{secrets.gpg_secret}} --allow-secret-key-import --import ./.github/keys/mosipgpgkey_sec.gpg
- name: Setup Firebase
run: |
cd android
if [ ! -z "$FIREBASE_SECRET" ] && [ -f app/google-services.json.gpg ];then rm -f app/google-services.json;gpg2 --quiet --batch --passphrase=$FIREBASE_SECRET --pinentry-mode loopback --decrypt --output app/google-services.json app/mosip-google-services.json.gpg;fi
env:
FIREBASE_SECRET: ${{ secrets.GPG_SECRET }}
- name: Generate keystore
run: |
echo "$ANDROID_KEYSTORE_FILE" > release.keystore.b64
@@ -182,13 +175,6 @@ jobs:
gpg2 --import ./.github/keys/mosipgpgkey_pub.gpg
gpg2 --quiet --batch --passphrase=${{secrets.gpg_secret}} --allow-secret-key-import --import ./.github/keys/mosipgpgkey_sec.gpg
- name: Setup Firebase
run: |
cd android
if [ ! -z "$FIREBASE_SECRET" ] && [ -f app/google-services.json.gpg ];then rm -f app/google-services.json;gpg2 --quiet --batch --passphrase=$FIREBASE_SECRET --pinentry-mode loopback --decrypt --output app/google-services.json app/mosip-google-services.json.gpg;fi
env:
FIREBASE_SECRET: ${{ secrets.GPG_SECRET }}
- name: Generate keystore
run: |
echo "$ANDROID_KEYSTORE_FILE" > release.keystore.b64

View File

@@ -88,6 +88,9 @@ jobs:
SLACK_URL: '${{ secrets.SLACK_WEBHOOK_DEVOPS }}'
MATCH_PASSWORD: '${{ secrets.INJI_IOS_MATCH_PASSWORD }}'
APPLICATION_THEME: ${{ github.event.inputs.theme }}
CREDENTIAL_REGISTRY_EDIT: ${{ github.event.inputs.registry_edit }}
MIMOTO_HOST: ${{ github.event.inputs.mimotoBackendServiceUrl }}
ESIGNET_HOST: ${{ github.event.inputs.esignetBackendServiceUrl }}
TESTFLIGHT_INTERNAL_TESTERS_GROUP: ${{ github.event.inputs.internal-testers }}
TESTFLIGHT_BETA_APP_DESCRIPTION: ${{ github.event.inputs.buildDescription }}

View File

@@ -2,3 +2,4 @@
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
.git/hooks/bin/talisman ${DEBUG_OPTS} --githook pre-commit

59
.talismanrc Normal file
View File

@@ -0,0 +1,59 @@
fileignoreconfig:
- filename: package.json
checksum: e0b34f1a1efbdf07b40a3e9e9ecd8884dd7d6835f45c57850e30988d2c6371a2
- filename: package-lock.json
checksum: fc26d97edecbacd0b7507032c6fc1acfcea9f01f5dbe66a2a0341506e2f0daca
- filename: components/PasscodeVerify.tsx
checksum: 14654c0f038979fcd0d260170a45894a072f81e0767ca9a0e66935d33b5cc703
- filename: i18n.ts
checksum: 75dd5536038ba198aa7b12eb6d2c2e7042c9ce292e5a0d5c90883ae5b2312b1e
- filename: screens/BiometricScreenController.ts
checksum: b3d0184fb894f4a9ffa1a4d277d2975495dbc47771593895bf1999deabafeb94
- filename: screens/Home/MyVcs/IdInputModal.tsx
checksum: ec48ebeaf46b8fbc756f19ef8ffe8901ce0528aef164f3c7ffdeec36fb53ffc6
- filename: components/Passcode.tsx
checksum: db4a18001be8c63bf7ffb389359861401fa1d22261b10ad729a76fd431c019a7
- filename: screens/PasscodeScreen.tsx
checksum: 0cc5b91350e491a058ee8a1878a496c16b65f14969e989bdb1c354f6e9b8c03c
- filename: locales/spa.json
checksum: 981103ff872d6f54610468c7ead65873d9294a11092649b105c552f770f0214b
- filename: screens/AuthScreen.tsx
checksum: 2c1183b2ad8ec27adf8f389a2511805493f229fdda674064e2579d2032119d1c
- filename: screens/BiometricScreen.tsx
checksum: 7a730731aaa7540eec9d05d73de277a665dc06425666280842e0f9ba6e5b8514
- filename: screens/AuthScreenController.ts
checksum: 18af825821bc95e1056050623b804a5a8e7435b9e3383916a5d63024eeba9553
- filename: screens/WelcomeScreenController.ts
checksum: 71917c8c543a4a5b7ab61df259b785d233a80718123b0c8edf6ec81e1b3a81e0
- filename: shared/telemetry/TelemetryUtils.js
checksum: 9a61cd59a3718adf1f14faf3024fec66a3295ef373878a878a28e5cb1287afaa
- filename: ios/Podfile.lock
checksum: cc123c3e1f04d41b394ceb16843b15c08bac3ba619ae853ff322717739761a85
- filename: ios/Podfile.lock
checksum: cc123c3e1f04d41b394ceb16843b15c08bac3ba619ae853ff322717739761a85
- filename: screens/Home/IntroSlidersScreen.tsx
checksum: 72ef913857448ef05763e52e32356faa2d1f3de8130a1c638d1897f44823031f
- filename: shared/commonUtil.ts
checksum: 01c3fdcd2f1c1757eae0c6a27fe04eb70c2aaf8660c604f3301997484592b66c
- filename: screens/Home/MyVcs/GetIdInputModal.tsx
checksum: 5c736ed79a372d0ffa7c02eb33d0dc06edbbb08d120978ff287f5f06cd6c7746
- filename: shared/openId4VCI/Utils.ts
checksum: 2bd39d84099d700e5bddeebe87dfb3b0e3075edc0a4d06ce48b428b0a85e238f
- filename: machines/issuersMachine.ts
checksum: b925df13236145867fc7a1a2a703faca854e8c937dac9451008399e3937592fb
- filename: machines/issuersMachine.typegen.ts
checksum: f5e2c89f9029b1e590173e37b2cf6586d5229ecb7d4dfd4c5f050f9416d3c1cd
- filename: machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.ts
checksum: eec77ca61540327ff7cab3489ebdfd47aa373fd20b3ff87a6c322cd48d35fe8f
- filename: machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.typegen.ts
checksum: f1f504bd8c14496ee71f8eb7f40d54411cc05be03347d644dcc2cca187a20678
- filename: machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.ts
checksum: 237a2640b7db70770d65da67c79f2929581e32f1162517e50b8d37e409f3387d
- filename: shared/cryptoutil/cryptoUtil.ts
checksum: b785ff3f01ab9530119072c4d38195048bfeee6155c54ea7dd031559acb722f3
- filename: package.json
checksum: 1a9d02d94424c1266503d58038cae2318ab71261ab50cf930c5f98e07c0f3ccd
- filename: machines/store.typegen.ts
checksum: 6d22bc5c77398316b943c512c208ce0846a9fff674c1ccac79e07f21962acd5f
- filename: machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.typegen.ts
checksum: 10889302ca05646a283386be1bbe7a5fb510c02bb62a308ee45f3c2a872ae64e

16
App.tsx
View File

@@ -15,15 +15,10 @@ import {
import {DualMessageOverlay} from './components/DualMessageOverlay';
import {useApp} from './screens/AppController';
import {Alert} from 'react-native';
import {
getAppInfoData,
getTelemetryConfigData,
initializeTelemetry,
sendAppInfoEvent,
} from './shared/telemetry/TelemetryUtils';
import {configureTelemetry} from './shared/telemetry/TelemetryUtils';
import {MessageOverlay} from './components/MessageOverlay';
import SecureKeystore from 'react-native-secure-keystore';
import {isCustomSecureKeystore} from './shared/cryptoutil/cryptoUtil';
import {isHardwareKeystoreExists} from './shared/cryptoutil/cryptoUtil';
import i18n from './i18n';
import './shared/flipperConfig';
@@ -42,11 +37,6 @@ const DecryptErrorAlert = (controller, t) => {
},
]);
};
function configureTelemetry() {
const config = getTelemetryConfigData();
initializeTelemetry(config);
sendAppInfoEvent(getAppInfoData());
}
const AppLayoutWrapper: React.FC = () => {
const {appService} = useContext(GlobalContext);
@@ -101,7 +91,7 @@ const AppInitialization: React.FC = () => {
const {t} = useTranslation('common');
useEffect(() => {
if (isCustomSecureKeystore()) {
if (isHardwareKeystoreExists) {
SecureKeystore.updatePopup(
t('biometricPopup.title'),
t('biometricPopup.description'),

View File

@@ -52,15 +52,10 @@ Create a `.env.local` file using `.env` as your template:
```
MIMOTO_HOST=
GOOGLE_NEARBY_MESSAGES_API_KEY=
```
And `android/local.properties`:
```
GOOGLE_NEARBY_MESSAGES_API_KEY=
```
More info here: [Setup Google Nearby Messages in React](https://github.com/mrousavy/react-native-google-nearby-messages#usage)
### Android

View File

@@ -3,8 +3,6 @@ plugins {
}
apply plugin: "com.android.application"
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: "com.facebook.react"
import com.android.build.OutputFile
@@ -121,7 +119,6 @@ android {
manifestPlaceholders = [
APP_NAME: APP_NAME_RELEASE,
GOOGLE_NEARBY_MESSAGES_API_KEY: "${properties.getProperty('GOOGLE_NEARBY_MESSAGES_API_KEY')}",
appAuthRedirectScheme: 'io.mosip.residentapp.inji'
]
}
@@ -219,6 +216,7 @@ dependencies {
if (isGifEnabled || isWebpEnabled) {
implementation 'com.facebook.fresco:fresco:2.0.0'
implementation 'com.facebook.fresco:imagepipeline-okhttp3:2.0.0'
implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.4.1'
}
if (isGifEnabled) {
@@ -250,20 +248,8 @@ dependencies {
} else {
implementation jscFlavor
}
// Firebase
implementation 'com.google.firebase:firebase-crashlytics:17.3.1'
implementation 'com.google.firebase:firebase-analytics:18.0.2'
implementation 'com.jakewharton.timber:timber:4.7.1'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
apply from: "./eas-build.gradle"
try {
def servicesJSON = file('google-services.json')
if (servicesJSON.text) {
apply plugin: 'com.google.gms.google-services'
}
} catch(Exception e) {
logger.warn("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
}
apply from: "./eas-build.gradle"

View File

@@ -1,39 +0,0 @@
{
"project_info": {
"project_number": "259470317171",
"project_id": "mosip-resident-app",
"storage_bucket": "mosip-resident-app.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "",
"android_client_info": {
"package_name": "io.mosip.residentapp"
}
},
"oauth_client": [
{
"client_id": "",
"client_type": 3
}
],
"api_key": [
{
"current_key": ""
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

Binary file not shown.

View File

@@ -21,8 +21,6 @@ import com.facebook.soloader.SoLoader;
import timber.log.Timber;
import com.facebook.react.bridge.JSIModulePackage;
import com.google.firebase.analytics.FirebaseAnalytics;
import com.google.firebase.crashlytics.FirebaseCrashlytics;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
@@ -77,36 +75,9 @@ public class MainApplication extends Application implements ReactApplication {
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
// Setup Firebase
FirebaseAnalytics.getInstance(this);
Timber.plant(new CrashReportingTree());
ApplicationLifecycleDispatcher.onApplicationCreate(this);
}
/**
* A tree which logs important information for crash reporting.
*/
private static class CrashReportingTree extends Timber.Tree {
FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
@Override
protected void log(int priority, String tag, @NonNull String message, Throwable t) {
if (priority == Log.VERBOSE || priority == Log.DEBUG) {
return;
}
crashlytics.setCustomKey("priority", priority);
crashlytics.setCustomKey("tag", tag);
crashlytics.log(message);
if (t != null) {
if (priority == Log.ERROR) {
crashlytics.recordException(t);
}
}
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);

View File

@@ -17,8 +17,6 @@ buildscript {
}
dependencies {
classpath("com.android.tools.build:gradle:7.3.1")
classpath 'com.google.gms:google-services:4.3.5'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
classpath("com.facebook.react:react-native-gradle-plugin")
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,18 +0,0 @@
{
"VC_SHARED": "shared",
"VC_RECEIVED": "received",
"VC_RECEIVED_NOT_SAVED": "received was not saved",
"VC_DELETED": "deleted",
"VC_DOWNLOADED": "downloaded",
"VC_REVOKED": "revoked",
"VC_SHARED_WITH_VERIFICATION_CONSENT": "shared. Consent is given for presence verification",
"VC_RECEIVED_WITH_PRESENCE_VERIFIED": "received. Presence verified",
"VC_RECEIVED_BUT_PRESENCE_VERIFICATION_FAILED": "received. Presence verification failed",
"PRESENCE_VERIFIED_AND_VC_SHARED": "verified and shared",
"PRESENCE_VERIFICATION_FAILED": "verification failed",
"QRLOGIN_SUCCESFULL": "QRLogin successful",
"WALLET_BINDING_SUCCESSFULL": "Activation successful",
"WALLET_BINDING_FAILURE": "Activation failed",
"VC_REMOVED": "Removed from wallet",
"TAMPERED_VC_REMOVED": "Some cards removed due to malicious activity"
}

View File

@@ -1,13 +1,13 @@
import React, { useState } from 'react';
import { Pressable } from 'react-native';
import { Theme } from './ui/styleUtils';
import React, {useState} from 'react';
import {Pressable} from 'react-native';
import {Theme} from './ui/styleUtils';
import Clipboard from '@react-native-clipboard/clipboard';
import { Icon } from 'react-native-elements';
import { Text } from './ui';
import { useTranslation } from 'react-i18next';
import {Icon} from 'react-native-elements';
import {Text} from './ui';
import {useTranslation} from 'react-i18next';
export const CopyButton: React.FC<CopyButtonProps> = ({ content }) => {
const { t } = useTranslation('common');
export const CopyButton: React.FC<CopyButtonProps> = ({content}) => {
const {t} = useTranslation('common');
const [buttonText, setButtonText] = useState(t('clipboard.copy'));
return (
@@ -22,10 +22,12 @@ export const CopyButton: React.FC<CopyButtonProps> = ({ content }) => {
type={'material'}
name={'file-copy'}
color={Theme.Colors.Icon}
style={{ marginRight: 2 }}
style={{marginRight: 2}}
size={19}
/>
<Text style={Theme.TextStyles.semibold}>{buttonText}</Text>
<Text style={{...Theme.TextStyles.semibold, paddingTop: 3}}>
{buttonText}
</Text>
</Pressable>
);
};

View File

@@ -1,6 +0,0 @@
{
"requestedBy": "Requested by",
"sentBy": "Sent by",
"deviceRefNumber": "Device reference number",
"name": "Name"
}

View File

@@ -37,17 +37,17 @@ export const EditableListItem: React.FC<EditableListItemProps> = props => {
color={Theme.Colors.Icon}
/>
<ListItem.Content>
<ListItem.Title>
<Text weight="semibold" color={Theme.Colors.profileLabel}>
<ListItem.Title style={{paddingTop: 3}}>
<Text weight="semibold" color={props.titleColor}>
{props.title}
</Text>
</ListItem.Title>
<Text color={Theme.Colors.profileValue}>{props.content}</Text>
<Text color={Theme.Colors.textLabel}>{props.content}</Text>
</ListItem.Content>
<Icon
name="chevron-right"
size={21}
color={Theme.Colors.profileLanguageValue}
color={Theme.Colors.chevronRightColor}
/>
<Overlay
overlayStyle={{padding: 24, elevation: 6}}
@@ -120,6 +120,7 @@ interface EditableListItemProps {
onCancel: () => void;
progress?: boolean;
errorMessage?: string;
titleColor: string;
}
interface ListItemProps {

View File

@@ -1 +0,0 @@
{}

View File

@@ -26,6 +26,7 @@ import {GlobalContext} from '../shared/GlobalContext';
import {selectIsActive} from '../machines/app';
import {RotatingIcon} from './RotatingIcon';
import {Theme} from './ui/styleUtils';
import {isIOS} from '../shared/constants';
export const FaceScanner: React.FC<FaceScannerProps> = props => {
const {t} = useTranslation('FaceScanner');
@@ -129,12 +130,6 @@ export const FaceScanner: React.FC<FaceScannerProps> = props => {
</Centered>
</Row>
)}
{/* TODO: remove warning when iOS SDK is ready */}
{Platform.OS === 'ios' && (
<Text size="smaller" color={Theme.Colors.textLabel} align="center">
(face-matching in iOS is mocked)
</Text>
)}
</Centered>
</View>
);

View File

@@ -1,8 +1,9 @@
import React from 'react';
import {useInterpret} from '@xstate/react';
import {appMachine, logState} from '../machines/app';
import {appMachine} from '../machines/app';
import {GlobalContext} from '../shared/GlobalContext';
import {logState} from '../shared/commonUtil';
export const GlobalContextProvider: React.FC = props => {
const appService = useInterpret(appMachine, {devTools: __DEV__});

View File

@@ -6,7 +6,6 @@ import Storage from '../shared/storage';
import {useTranslation} from 'react-i18next';
import i18next from 'i18next';
import RNRestart from 'react-native-restart';
import {__SelectedLanguage} from '../shared/GlobalVariables';
export const LanguageSelector: React.FC<LanguageSelectorProps> = props => {
const {i18n} = useTranslation();
@@ -17,7 +16,6 @@ export const LanguageSelector: React.FC<LanguageSelectorProps> = props => {
const changeLanguage = async (language: string) => {
if (language !== i18n.language) {
await i18n.changeLanguage(language).then(async () => {
__SelectedLanguage.setValue(language);
await Storage.setItem('language', i18n.language);
const isRTL = i18next.dir(language) === 'rtl' ? true : false;
if (isRTL !== I18nManager.isRTL) {

View File

@@ -29,6 +29,7 @@ export const MessageOverlay: React.FC<MessageOverlayProps> = props => {
<Column padding="21" crossAlign="center">
{props.title && (
<Text
style={{paddingTop: 3}}
align="center"
weight="bold"
margin="0 0 10 0"

View File

@@ -1,5 +0,0 @@
{
"title": "OIDC Authentication",
"text": "To be replaced with the OIDC provider UI",
"verify": "Verify"
}

View File

@@ -1,11 +1,19 @@
import React from 'react';
import { Modal as RNModal } from 'react-native';
import { Icon } from 'react-native-elements';
import { PasscodeVerify } from '../components/PasscodeVerify';
import { Column, Text } from '../components/ui';
import { Theme } from '../components/ui/styleUtils';
import React, {useEffect} from 'react';
import {Modal as RNModal} from 'react-native';
import {Icon} from 'react-native-elements';
import {PasscodeVerify} from '../components/PasscodeVerify';
import {Column, Text} from '../components/ui';
import {Theme} from '../components/ui/styleUtils';
import {
getImpressionEventData,
sendImpressionEvent,
} from '../shared/telemetry/TelemetryUtils';
export const Passcode: React.FC<PasscodeProps> = props => {
useEffect(() => {
sendImpressionEvent(getImpressionEventData('App Login', 'Passcode'));
}, []);
export const Passcode: React.FC<PasscodeProps> = (props) => {
return (
<RNModal
animationType="slide"

View File

@@ -1,3 +0,0 @@
{
"passcodeMismatchError": "Passcode did not match."
}

View File

@@ -13,6 +13,7 @@ export const PasscodeVerify: React.FC<PasscodeVerifyProps> = props => {
useEffect(() => {
if (isVerified) {
props.onSuccess();
setIsVerified(false);
}
}, [isVerified]);
@@ -21,11 +22,17 @@ export const PasscodeVerify: React.FC<PasscodeVerifyProps> = props => {
);
async function verify(value: string) {
const hashedPasscode = await hashData(value, props.salt, argon2iConfig);
if (props.passcode === hashedPasscode) {
setIsVerified(true);
} else {
props.onError(t('passcodeMismatchError'));
try {
const hashedPasscode = await hashData(value, props.salt, argon2iConfig);
if (props.passcode === hashedPasscode) {
setIsVerified(true);
} else {
if (props.onError) {
props.onError(t('passcodeMismatchError'));
}
}
} catch (error) {
console.log('error:', error);
}
}
};

View File

@@ -1,4 +0,0 @@
{
"missingPermissionText": "This app uses the camera to scan the QR code of another device.",
"allowCameraButton": "Allow access to camera"
}

View File

@@ -1,55 +1,40 @@
import React, {useContext, useEffect, useMemo, useRef} from 'react';
import {useInterpret, useSelector} from '@xstate/react';
import {View, Pressable} from 'react-native';
import React, {useEffect} from 'react';
import {Pressable, View} from 'react-native';
import {ActorRefFrom} from 'xstate';
import {
createExistingMosipVCItemMachine,
selectVerifiableCredential,
selectGeneratedOn,
ExistingMosipVCItemEvents,
ExistingMosipVCItemMachine,
selectContext,
selectEmptyWalletBindingId,
selectIsSavingFailedInIdle,
selectKebabPopUp,
} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
import {ExistingMosipVCItemEvents} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
import {ErrorMessageOverlay} from '../../MessageOverlay';
import {Theme} from '../../ui/styleUtils';
import {GlobalContext} from '../../../shared/GlobalContext';
import {MosipVCItemContent} from './MosipVCItemContent';
import {MosipVCItemActivationStatus} from './MosipVCItemActivationStatus';
import {Row} from '../../ui';
import {KebabPopUp} from '../../KebabPopUp';
import {VCMetadata} from '../../../shared/VCMetadata';
import {format} from 'date-fns';
import {
createEsignetMosipVCItemMachine,
EsignetMosipVCItemEvents,
EsignetMosipVCItemMachine,
selectContext as esignetSelectContext,
selectEmptyWalletBindingId as esignetSelectEmptyWalletBindingId,
selectGeneratedOn as esignetSelectGeneratedOn,
selectKebabPopUp as esignetSelectKebabPopUp,
selectVerifiableCredentials as esignetSelectVerifiableCredentials,
} from '../../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
import {EsignetMosipVCItemMachine} from '../../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
import {useVcItemController} from './VcItemController';
export const MosipVCItem: React.FC<
ExistingMosipVCItemProps | EsignetMosipVCItemProps
> = props => {
const {appService} = useContext(GlobalContext);
const machine = useRef(
!props.vcMetadata.isFromOpenId4VCI()
? createExistingMosipVCItemMachine(
appService.getSnapshot().context.serviceRefs,
props.vcMetadata,
)
: createEsignetMosipVCItemMachine(
appService.getSnapshot().context.serviceRefs,
props.vcMetadata,
),
);
let {
service,
context,
verifiableCredential,
emptyWalletBindingId,
isKebabPopUp,
isSavingFailedInIdle,
storeErrorTranslationPath,
generatedOn,
const service = useInterpret(machine.current, {devTools: __DEV__});
DISMISS,
KEBAB_POPUP,
} = useVcItemController(props);
let formattedDate =
generatedOn && format(new Date(generatedOn), 'MM/dd/yyyy');
useEffect(() => {
service.send(
@@ -57,32 +42,6 @@ export const MosipVCItem: React.FC<
);
}, [props.vcMetadata]);
let context = useSelector(service, selectContext);
let verifiableCredential = useSelector(service, selectVerifiableCredential);
let emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
let isKebabPopUp = useSelector(service, selectKebabPopUp);
let DISMISS = () => service.send(ExistingMosipVCItemEvents.DISMISS());
let KEBAB_POPUP = () => service.send(ExistingMosipVCItemEvents.KEBAB_POPUP());
const isSavingFailedInIdle = useSelector(service, selectIsSavingFailedInIdle);
const storeErrorTranslationPath = 'errors.savingFailed';
let generatedOn = useSelector(service, selectGeneratedOn);
if (props.vcMetadata.isFromOpenId4VCI()) {
context = useSelector(service, esignetSelectContext);
isKebabPopUp = useSelector(service, esignetSelectKebabPopUp);
generatedOn = useSelector(service, esignetSelectGeneratedOn);
emptyWalletBindingId = useSelector(
service,
esignetSelectEmptyWalletBindingId,
);
DISMISS = () => service.send(EsignetMosipVCItemEvents.DISMISS());
KEBAB_POPUP = () => service.send(EsignetMosipVCItemEvents.KEBAB_POPUP());
verifiableCredential = useSelector(
service,
esignetSelectVerifiableCredentials,
);
}
let formattedDate =
generatedOn && format(new Date(generatedOn), 'MM/dd/yyyy');
return (
<React.Fragment>
<Pressable
@@ -105,20 +64,17 @@ export const MosipVCItem: React.FC<
iconName={props.iconName}
iconType={props.iconType}
onPress={() => props.onPress(service)}
isDownloading={props.isDownloading}
/>
<View style={Theme.Styles.horizontalLine} />
{props.isSharingVc ? null : (
<Row style={Theme.Styles.activationTab}>
{props.activeTab !== 'receivedVcsTab' &&
props.activeTab != 'sharingVcScreen' && (
<MosipVCItemActivationStatus
vcMetadata={props.vcMetadata}
verifiableCredential={verifiableCredential}
emptyWalletBindingId={emptyWalletBindingId}
onPress={() => props.onPress(service)}
showOnlyBindedVc={props.showOnlyBindedVc}
/>
)}
<MosipVCItemActivationStatus
vcMetadata={props.vcMetadata}
verifiableCredential={verifiableCredential}
emptyWalletBindingId={emptyWalletBindingId}
showOnlyBindedVc={props.showOnlyBindedVc}
/>
<View style={Theme.Styles.verticalLine} />
<Row style={Theme.Styles.kebabIcon}>
<Pressable onPress={KEBAB_POPUP} accessible={false}>
@@ -153,10 +109,10 @@ export interface ExistingMosipVCItemProps {
showOnlyBindedVc?: boolean;
onPress?: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
onShow?: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
activeTab?: string;
iconName?: string;
iconType?: string;
isSharingVc?: boolean;
isDownloading?: boolean;
}
export interface EsignetMosipVCItemProps {
@@ -167,8 +123,8 @@ export interface EsignetMosipVCItemProps {
showOnlyBindedVc?: boolean;
onPress?: (vcRef?: ActorRefFrom<typeof EsignetMosipVCItemMachine>) => void;
onShow?: (vcRef?: ActorRefFrom<typeof EsignetMosipVCItemMachine>) => void;
activeTab?: string;
iconName?: string;
iconType?: string;
isSharingVc?: boolean;
isDownloading?: boolean;
}

View File

@@ -2,8 +2,6 @@ import React from 'react';
import {useTranslation} from 'react-i18next';
import {Dimensions} from 'react-native';
import {Icon} from 'react-native-elements';
import {ActorRefFrom} from 'xstate';
import {ExistingMosipVCItemMachine} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
import {VerifiableCredential} from '../../../types/VC/ExistingMosipVC/vc';
import {Row, Text} from '../../ui';
import {Theme} from '../../ui/styleUtils';
@@ -16,9 +14,10 @@ const WalletUnverifiedIcon: React.FC = () => {
size={Theme.ICON_MID_SIZE}
type="material-community"
containerStyle={{
marginStart: 10,
marginStart: 1,
marginEnd: 1,
bottom: 1,
marginLeft: 10,
marginRight: -2,
}}
/>
);
@@ -48,13 +47,14 @@ const WalletUnverifiedActivationDetails: React.FC<
crossAlign="center"
style={{
flex: 1,
maxWidth: 255,
}}>
{props.verifiableCredential && <WalletUnverifiedIcon />}
<Text
color={Theme.Colors.Details}
testID="activationPending"
weight="regular"
margin="8 10 10 5"
margin="8"
style={
!props.verifiableCredential
? Theme.Styles.loadingTitle
@@ -106,13 +106,11 @@ export const MosipVCItemActivationStatus: React.FC<
{props.emptyWalletBindingId ? (
<WalletUnverifiedActivationDetails
verifiableCredential={props.verifiableCredential}
onPress={props.onPress}
/>
) : (
<WalletVerifiedActivationDetails
verifiableCredential={props.verifiableCredential}
showOnlyBindedVc={props.showOnlyBindedVc}
onPress={props.onPress}
/>
)}
</Row>
@@ -121,18 +119,15 @@ export const MosipVCItemActivationStatus: React.FC<
interface ExistingMosipVCItemActivationStatusProps {
showOnlyBindedVc: boolean;
onPress: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
verifiableCredential: VerifiableCredential;
emptyWalletBindingId: boolean;
}
interface WalletVerifiedDetailsProps {
showOnlyBindedVc: boolean;
onPress: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
verifiableCredential: VerifiableCredential;
}
interface WalletUnVerifiedDetailsProps {
onPress: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
verifiableCredential: VerifiableCredential;
}

View File

@@ -1,13 +1,14 @@
import React from 'react';
import {useTranslation} from 'react-i18next';
import {Image, ImageBackground, View} from 'react-native';
import {Image, ImageBackground} from 'react-native';
import {getLocalizedField} from '../../../i18n';
import {VerifiableCredential} from '../../../types/VC/ExistingMosipVC/vc';
import VerifiedIcon from '../../VerifiedIcon';
import {Column, Row, Text} from '../../ui';
import {Theme} from '../../ui/styleUtils';
import {CheckBox, Icon} from 'react-native-elements';
import testIDProps from '../../../shared/commonUtil';
import testIDProps, {getMaskedText} from '../../../shared/commonUtil';
import {logoType} from '../../../machines/issuersMachine';
const getDetails = (arg1, arg2, verifiableCredential) => {
if (arg1 === 'Status') {
@@ -83,15 +84,12 @@ const getDetails = (arg1, arg2, verifiableCredential) => {
}
};
function getIdNumber(id: string) {
return '*'.repeat(id.length - 4) + id.slice(-4);
}
const getIssuerLogo = (isOpenId4VCI: boolean, issuerLogo: string) => {
const getIssuerLogo = (isOpenId4VCI: boolean, issuerLogo: logoType) => {
if (isOpenId4VCI) {
return (
<Image
src={issuerLogo}
src={issuerLogo?.url}
alt={issuerLogo?.alt_text}
style={Theme.Styles.issuerLogo}
resizeMethod="scale"
resizeMode="contain"
@@ -111,7 +109,9 @@ const getIssuerLogo = (isOpenId4VCI: boolean, issuerLogo: string) => {
export const MosipVCItemContent: React.FC<
ExistingMosipVCItemContentProps | EsignetMosipVCItemContentProps
> = props => {
const verifiableCredential = props.vcMetadata.isFromOpenId4VCI()
const verifiableCredential = props.isDownloading
? null
: props.vcMetadata.isFromOpenId4VCI()
? props.verifiableCredential?.credential
: props.verifiableCredential;
@@ -165,7 +165,7 @@ export const MosipVCItemContent: React.FC<
</ImageBackground>
<Column margin="0 0 10 20" height={96} align="space-between">
<Column>
<Column style={{maxWidth: 230}}>
<Text
testID="fullNameTitle"
weight="regular"
@@ -239,7 +239,7 @@ export const MosipVCItemContent: React.FC<
weight="semibold"
size="extraSmall"
color={Theme.Colors.statusLabel}>
{getIdNumber(uin)}
{getMaskedText(uin)}
</Text>
</Column>
) : null}
@@ -258,7 +258,7 @@ export const MosipVCItemContent: React.FC<
weight="semibold"
size="extraSmall"
color={Theme.Colors.Details}>
{getIdNumber(vid)}
{getMaskedText(vid)}
</Text>
</Column>
) : null}
@@ -319,7 +319,7 @@ export const MosipVCItemContent: React.FC<
function faceImageSource() {
return !verifiableCredential
? Theme.ProfileIcon
? Theme.cardFaceIcon
: {
uri: props.vcMetadata.isFromOpenId4VCI()
? verifiableCredential?.credentialSubject.face
@@ -338,6 +338,7 @@ interface ExistingMosipVCItemContentProps {
iconType?: string;
service: any;
onPress?: () => void;
isDownloading?: boolean;
}
export interface EsignetMosipVCItemContentProps {
@@ -350,4 +351,5 @@ export interface EsignetMosipVCItemContentProps {
iconType?: string;
service: any;
onPress?: () => void;
isDownloading?: boolean;
}

View File

@@ -2,9 +2,9 @@ import {format, formatDistanceToNow, parse} from 'date-fns';
import React from 'react';
import * as DateFnsLocale from 'date-fns/locale';
import {useTranslation} from 'react-i18next';
import {Image, ImageBackground, View} from 'react-native';
import {Dimensions, Image, ImageBackground, View} from 'react-native';
import {Icon} from 'react-native-elements';
import {VC, CredentialSubject} from '../../../types/VC/ExistingMosipVC/vc';
import {CredentialSubject, VC} from '../../../types/VC/ExistingMosipVC/vc';
import {Button, Column, Row, Text} from '../../ui';
import {Theme} from '../../ui/styleUtils';
import {TextItem} from '../../ui/TextItem';
@@ -20,10 +20,17 @@ import {
VerifiablePresentation,
} from '../../../types/VC/EsignetMosipVC/vc';
import {WalletBindingResponse} from '../../../shared/cryptoutil/cryptoUtil';
import {logoType} from '../../../machines/issuersMachine';
const getIssuerLogo = (isOpenId4VCI: boolean, issuerLogo: string) => {
const getIssuerLogo = (isOpenId4VCI: boolean, issuerLogo: logoType) => {
if (isOpenId4VCI) {
return <Image src={issuerLogo} style={Theme.Styles.issuerLogo} />;
return (
<Image
src={issuerLogo?.url}
alt={issuerLogo?.alt_text}
style={Theme.Styles.issuerLogo}
/>
);
}
return <Image source={Theme.MosipLogo} style={Theme.Styles.vcDetailsLogo} />;
};
@@ -42,7 +49,7 @@ const getProfileImage = (
return {uri: props.vc?.credential.biometrics.face};
}
}
return Theme.ProfileIcon;
return Theme.cardFaceIcon;
};
export const MosipVCItemDetails: React.FC<
@@ -68,14 +75,14 @@ export const MosipVCItemDetails: React.FC<
}
return (
<Column margin="10">
<Column margin="10 4 10 4">
<ImageBackground
imageStyle={{width: '100%'}}
resizeMethod="scale"
resizeMode="stretch"
style={Theme.Styles.openCardBgContainer}
source={Theme.OpenCard}>
<Row align="space-between" padding="10" margin="0 10 0 10">
<Row align="space-between" padding="10" margin="0 10 0 8">
<Column align="space-evenly" crossAlign="center">
<Image
source={getProfileImage(
@@ -101,6 +108,7 @@ export const MosipVCItemDetails: React.FC<
<Text
testID="fullNameValue"
weight="semibold"
style={{maxWidth: 190}}
size="smaller"
color={Theme.Colors.Details}>
{getLocalizedField(
@@ -139,6 +147,7 @@ export const MosipVCItemDetails: React.FC<
<Text
testID="nationalCard"
weight="bold"
style={{minWidth: 65}}
size="smaller"
color={Theme.Colors.Details}>
{t('nationalCard')}
@@ -198,10 +207,11 @@ export const MosipVCItemDetails: React.FC<
</Text>
</Column>
</Column>
<Column margin="0 0 0 40">
<Column margin="0 0 0 38">
<Column margin="20 0 0 0">
<Text
testID="dateOfBirth"
style={{maxWidth: 121}}
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
@@ -215,7 +225,8 @@ export const MosipVCItemDetails: React.FC<
{formattedDateOfBirth()}
</Text>
</Column>
<Column margin="25 0 0 0">
<Column
style={{marginTop: Dimensions.get('window').height * 0.04}}>
<Text
testID="status"
weight="regular"
@@ -231,6 +242,7 @@ export const MosipVCItemDetails: React.FC<
{props.vc?.isVerified && <VerifiedIcon />}
<Text
testID="valid"
style={{maxWidth: 63}}
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
@@ -238,9 +250,11 @@ export const MosipVCItemDetails: React.FC<
</Text>
</Row>
</Column>
<Column margin="92 0 0 0">
<Column
style={{marginTop: Dimensions.get('window').height * 0.1}}>
<Text
testID="phoneNumber"
style={{maxWidth: 80}}
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>

View File

@@ -0,0 +1,79 @@
import {useContext, useRef} from 'react';
import {GlobalContext} from '../../../shared/GlobalContext';
import {
createExistingMosipVCItemMachine,
ExistingMosipVCItemEvents,
selectContext,
selectEmptyWalletBindingId,
selectGeneratedOn,
selectIsSavingFailedInIdle,
selectKebabPopUp,
selectVerifiableCredential,
} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
import {
createEsignetMosipVCItemMachine,
EsignetMosipVCItemEvents,
selectContext as esignetSelectContext,
selectEmptyWalletBindingId as esignetSelectEmptyWalletBindingId,
selectGeneratedOn as esignetSelectGeneratedOn,
selectKebabPopUp as esignetSelectKebabPopUp,
selectVerifiableCredentials as esignetSelectVerifiableCredentials,
} from '../../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
import {useInterpret, useSelector} from '@xstate/react';
import {EsignetMosipVCItemProps, ExistingMosipVCItemProps} from './MosipVCItem';
export function useVcItemController(
props: ExistingMosipVCItemProps | EsignetMosipVCItemProps,
) {
const {appService} = useContext(GlobalContext);
const machine = useRef(
!props.vcMetadata.isFromOpenId4VCI()
? createExistingMosipVCItemMachine(
appService.getSnapshot().context.serviceRefs,
props.vcMetadata,
)
: createEsignetMosipVCItemMachine(
appService.getSnapshot().context.serviceRefs,
props.vcMetadata,
),
);
const service = useInterpret(machine.current, {devTools: __DEV__});
let context = useSelector(service, selectContext);
let verifiableCredential = useSelector(service, selectVerifiableCredential);
let emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
let isKebabPopUp = useSelector(service, selectKebabPopUp);
let DISMISS = () => service.send(ExistingMosipVCItemEvents.DISMISS());
let KEBAB_POPUP = () => service.send(ExistingMosipVCItemEvents.KEBAB_POPUP());
const isSavingFailedInIdle = useSelector(service, selectIsSavingFailedInIdle);
const storeErrorTranslationPath = 'errors.savingFailed';
let generatedOn = useSelector(service, selectGeneratedOn);
if (props.vcMetadata.isFromOpenId4VCI()) {
context = useSelector(service, esignetSelectContext);
isKebabPopUp = useSelector(service, esignetSelectKebabPopUp);
generatedOn = useSelector(service, esignetSelectGeneratedOn);
emptyWalletBindingId = useSelector(
service,
esignetSelectEmptyWalletBindingId,
);
DISMISS = () => service.send(EsignetMosipVCItemEvents.DISMISS());
KEBAB_POPUP = () => service.send(EsignetMosipVCItemEvents.KEBAB_POPUP());
verifiableCredential = useSelector(
service,
esignetSelectVerifiableCredentials,
);
}
return {
service,
context,
verifiableCredential,
emptyWalletBindingId,
isKebabPopUp,
DISMISS,
KEBAB_POPUP,
isSavingFailedInIdle,
storeErrorTranslationPath,
generatedOn,
};
}

View File

@@ -1,22 +0,0 @@
{
"generatedOn": "Generated On",
"status": "Status",
"valid": "Valid",
"photo": "Photo",
"fullName": "Full Name",
"gender": "Gender",
"dateOfBirth": "Date of Birth",
"phoneNumber": "Phone Number",
"email": "Email",
"address": "Address",
"reasonForSharing": "Reason for sharing",
"idType": "ID Type",
"id": "Id",
"nationalCard": "National Card",
"uin": "UIN",
"vid": "VID",
"enableVerification": "Enable Verification",
"profileAuthenticated": "Profile is authenticated!",
"offlineAuthDisabledHeader": "Offline Authentication disabled!",
"offlineAuthDisabledMessage": "Click 'Enable Authentication' to enable this credentials to be used for offline authentication."
}

View File

@@ -9,8 +9,7 @@ export const Issuer: React.FC<IssuerProps> = (props: IssuerProps) => {
const {t} = useTranslation('IssuersScreen');
function getIssuerLogo() {
if (props.logoUrl) return {uri: props.logoUrl};
return Theme.DigitIcon;
return {uri: props.logoUrl};
}
return (

View File

@@ -45,6 +45,7 @@ export const Button: React.FC<ButtonProps> = props => {
raised={props.raised}
title={
<Text
style={{paddingTop: 3}}
weight="semibold"
align="center"
color={
@@ -77,7 +78,7 @@ export const Button: React.FC<ButtonProps> = props => {
raised={props.raised}
title={
<Text
style={Theme.TextStyles.bold}
style={{...Theme.TextStyles.bold, paddingTop: 3}}
color={
type === 'solid' || type === 'gradient' || type === 'radius'
? Theme.Colors.whiteText

View File

@@ -1,14 +1,22 @@
import React, {Fragment} from 'react';
import React, {Fragment, useEffect} from 'react';
import {useTranslation} from 'react-i18next';
import {Image, SafeAreaView, View} from 'react-native';
import {BackHandler, Image, SafeAreaView, View} from 'react-native';
import Spinner from 'react-native-spinkit';
import {Button, Centered, Column, Row, Text} from '../../components/ui';
import {Theme} from '../../components/ui/styleUtils';
import {Theme} from './styleUtils';
import testIDProps from '../../shared/commonUtil';
export const Loader: React.FC<LoaderProps> = props => {
const {t} = useTranslation('ScanScreen');
useEffect(() => {
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
() => true,
);
return () => backHandler.remove();
}, []);
return (
<Fragment>
<Row style={{backgroundColor: Theme.Colors.whiteBackgroundColor}}>
@@ -17,7 +25,7 @@ export const Loader: React.FC<LoaderProps> = props => {
fill
align={'flex-start'}
style={Theme.LoaderStyles.titleContainer}>
<View style={Theme.issuersScreenStyles.loaderHeadingText}>
<View style={Theme.LoaderStyles.heading}>
<Text
style={Theme.TextStyles.semiBoldHeader}
testID="loaderTitle">
@@ -26,7 +34,7 @@ export const Loader: React.FC<LoaderProps> = props => {
{props.subTitle && (
<Text
style={Theme.TextStyles.subHeader}
color={Theme.Colors.profileValue}
color={Theme.Colors.textLabel}
testID="loaderSubTitle">
{props.subTitle}
</Text>
@@ -81,7 +89,7 @@ export const Loader: React.FC<LoaderProps> = props => {
export interface LoaderProps {
isVisible: boolean;
title?: string;
title: string;
subTitle?: string;
label?: string;
hint?: string;

View File

@@ -57,7 +57,7 @@ export const Modal: React.FC<ModalProps> = props => {
color={
props.headerLabelColor
? props.headerLabelColor
: Theme.Colors.profileLanguageValue
: Theme.Colors.textLabel
}>
{props.headerLabel}
</Text>

View File

@@ -40,7 +40,7 @@ export const SetupPicker: Picker = (props: PickerProps<unknown>) => {
onPress={() => selectItem(index)}
key={index}>
<ListItem.Content>
<ListItem.Title>
<ListItem.Title style={{paddingTop: 3}}>
<Text
color={selectedIndex === index ? Theme.Colors.Icon : null}
weight={selectedIndex === index ? 'semibold' : 'regular'}>

View File

@@ -1,6 +1,7 @@
/* eslint-disable sonarjs/no-duplicate-string */
import {Dimensions, Platform, StyleSheet, ViewStyle} from 'react-native';
import {Dimensions, StyleSheet, ViewStyle} from 'react-native';
import {Spacing} from '../styleUtils';
import {isIOS} from '../../../shared/constants';
const Colors = {
Black: '#000000',
@@ -61,11 +62,7 @@ export const DefaultTheme = {
borderBottomColor: Colors.Grey6,
whiteBackgroundColor: Colors.White,
lightGreyBackgroundColor: Colors.LightGrey,
profileLanguageValue: Colors.Grey,
aboutVersion: Colors.Gray40,
profileAuthFactorUnlock: Colors.Grey,
profileLabel: Colors.Black,
profileValue: Colors.Grey,
switchHead: Colors.Orange,
switchTrackTrue: Colors.LightOrange,
switchTrackFalse: Colors.Grey,
@@ -82,7 +79,6 @@ export const DefaultTheme = {
VerifiedIcon: Colors.Green,
whiteText: Colors.White,
flipCameraIcon: Colors.Black,
IdInputModalBorder: Colors.Grey,
RetrieveIdLabel: Colors.ShadeOfGrey,
inputSelection: Colors.Orange,
checkCircleIcon: Colors.White,
@@ -90,7 +86,6 @@ export const DefaultTheme = {
OnboardingCloseIcon: Colors.White,
WarningIcon: Colors.Warning,
DefaultToggle: Colors.LightOrange,
ProfileIconBg: Colors.LightOrange,
GrayText: Colors.GrayText,
errorGrayText: Colors.mediumDarkGrey,
gradientBtn: ['#F59B4B', '#E86E04'],
@@ -109,6 +104,8 @@ export const DefaultTheme = {
statusMessage: Colors.Gray40,
blackIcon: Colors.Black,
uncheckedIcon: Colors.uncheckedIcon,
settingsLabel: Colors.Black,
chevronRightColor: Colors.Grey,
},
Styles: StyleSheet.create({
title: {
@@ -177,8 +174,8 @@ export const DefaultTheme = {
cardDetailsContainer: {},
bottomTabIconStyle: {
padding: 4,
width: 36,
height: 36,
width: Dimensions.get('window').width * 0.12,
height: Dimensions.get('window').height * 0.045,
borderRadius: 6,
backgroundColor: Colors.LightOrange,
},
@@ -226,8 +223,8 @@ export const DefaultTheme = {
height: 30,
backgroundColor: Colors.Grey,
marginVertical: 8,
marginLeft: -45,
marginRight: 22,
marginLeft: -25,
marginRight: 12,
},
closeCardBgContainer: {
borderRadius: 10,
@@ -492,6 +489,22 @@ export const DefaultTheme = {
fontSize: 15,
fontFamily: 'Inter_700Bold',
},
idInputContainer: {
marginTop: 20,
marginRight: Dimensions.get('window').width * 0.26,
},
idInputPicker: {
width: Dimensions.get('window').width * 0.32,
borderBottomWidth: 1,
marginBottom: 2,
borderColor: isIOS() ? 'transparent' : Colors.Grey,
bottom: isIOS() ? 50 : 24,
height: isIOS() ? 100 : 'auto',
},
idInputBottom: {
borderBottomColor: Colors.Orange,
borderBottomWidth: 1,
},
getId: {
justifyContent: 'center',
alignItems: 'center',
@@ -511,12 +524,11 @@ export const DefaultTheme = {
borderBottomColor: Colors.platinumGrey,
borderBottomWidth: 1.3,
},
downloadFabIcon: {
downloadFabIconContainer: {
height: 70,
width: 70,
borderRadius: 200,
padding: 10,
backgroundColor: Colors.Orange,
shadowColor: '#000',
shadowOpacity: 0.4,
elevation: 5,
@@ -524,6 +536,21 @@ export const DefaultTheme = {
bottom: Dimensions.get('window').width * 0.1,
right: Dimensions.get('window').width * 0.1,
},
downloadFabIconNormal: {
borderRadius: 200,
height: 70,
width: 70,
justifyContent: 'center',
position: 'absolute',
},
downloadFabIconPressed: {
borderRadius: 200,
height: 70,
width: 70,
backgroundColor: Colors.Orange,
justifyContent: 'center',
position: 'absolute',
},
boxShadow: generateBoxShadowStyle(),
}),
QrCodeStyles: StyleSheet.create({
@@ -598,14 +625,14 @@ export const DefaultTheme = {
fontFamily: 'Inter_700Bold',
fontSize: 18,
lineHeight: 19,
paddingTop: 4,
paddingTop: 5,
},
subHeader: {
color: Colors.mediumLightGrayText,
fontFamily: 'Inter_600SemiBold',
lineHeight: 19,
fontSize: 13,
paddingTop: 3,
paddingTop: 4,
},
semiBoldHeader: {
color: Colors.Black,
@@ -717,6 +744,10 @@ export const DefaultTheme = {
marginBottom: 17,
marginTop: 22,
},
heading: {
flex: 1,
flexDirection: 'column',
},
}),
ButtonStyles: StyleSheet.create({
fill: {
@@ -1162,6 +1193,7 @@ export const DefaultTheme = {
lineHeight: 17,
paddingHorizontal: 3,
paddingBottom: 4,
paddingTop: 1.7,
},
issuerDescription: {
fontSize: 11,
@@ -1169,6 +1201,7 @@ export const DefaultTheme = {
color: Colors.ShadeOfGrey,
paddingVertical: 5,
paddingHorizontal: 3,
paddingTop: 1.4,
},
issuerIcon: {
resizeMode: 'contain',
@@ -1178,10 +1211,6 @@ export const DefaultTheme = {
marginTop: 8,
marginLeft: 2.5,
},
loaderHeadingText: {
flex: 1,
flexDirection: 'column',
},
}),
ErrorStyles: StyleSheet.create({
image: {marginTop: -60, paddingBottom: 26},
@@ -1204,6 +1233,15 @@ export const DefaultTheme = {
color: Colors.mediumDarkGrey,
},
}),
SetupLanguageScreenStyle: StyleSheet.create({
columnStyle: {
flex: 1,
alignItems: 'center',
justifyContent: 'space-around',
backgroundColor: Colors.White,
maxHeight: Dimensions.get('window').height,
},
}),
ICON_SMALL_SIZE: 16,
ICON_MID_SIZE: 22,
@@ -1212,7 +1250,7 @@ export const DefaultTheme = {
CardBackground: require('../../../assets/card_bg.png'),
OpenCard: require('../../../assets/card_bg.png'),
activationPending: require('../../../assets/pending_activation.png'),
ProfileIcon: require('../../../assets/placeholder-photo.png'),
cardFaceIcon: require('../../../assets/placeholder-photo.png'),
MosipSplashLogo: require('../../../assets/icon.png'),
MosipLogo: require('../../../assets/mosip-logo.png'),
CameraFlipIcon: require('../../../assets/camera-flip-icon.png'),
@@ -1230,14 +1268,13 @@ export const DefaultTheme = {
InjiHomeLogo: require('../../../assets/inji-home-logo.png'),
MagnifierZoom: require('../../../assets/magnifier-zoom.png'),
HelpIcon: require('../../../assets/help-icon.png'),
sharingIntro: require('../../../assets/Secure-Sharing.png'),
sharingIntro: require('../../../assets/intro-secure-sharing.png'),
walletIntro: require('../../../assets/intro-wallet-binding.png'),
IntroScanner: require('../../../assets/intro-scanner.png'),
injiSmallLogo: require('../../../assets/inji_small_logo.png'),
protectPrivacy: require('../../../assets/phone_mockup_1.png'),
protectPrivacy: require('../../../assets/intro-unlock-method.png'),
NoInternetConnection: require('../../../assets/no-internet-connection.png'),
SomethingWentWrong: require('../../../assets/something-went-wrong.png'),
DigitIcon: require('../../../assets/digit-icon.png'),
elevation(level: ElevationLevel): ViewStyle {
// https://ethercreative.github.io/react-native-shadow-generator/
@@ -1283,17 +1320,16 @@ export const DefaultTheme = {
};
function generateBoxShadowStyle() {
if (Platform.OS === 'ios') {
if (isIOS()) {
return {
shadowColor: '#000',
shadowOffset: {width: 1, height: 1.2},
shadowOpacity: 0.3,
shadowRadius: 2.5,
};
} else if (Platform.OS === 'android') {
return {
elevation: 4,
shadowColor: '#000',
};
}
return {
elevation: 4,
shadowColor: '#000',
};
}

View File

@@ -1,6 +1,7 @@
/* eslint-disable sonarjs/no-duplicate-string */
import {Dimensions, Platform, StyleSheet, ViewStyle} from 'react-native';
import {Dimensions, StyleSheet, ViewStyle} from 'react-native';
import {Spacing} from '../styleUtils';
import {isIOS} from '../../../shared/constants';
const Colors = {
Black: '#231F20',
@@ -64,11 +65,7 @@ export const PurpleTheme = {
whiteBackgroundColor: Colors.White,
lightGreyBackgroundColor: Colors.LightGrey,
errorGrayText: Colors.mediumDarkGrey,
profileLanguageValue: Colors.Grey,
aboutVersion: Colors.Gray40,
profileAuthFactorUnlock: Colors.Grey,
profileLabel: Colors.Black,
profileValue: Colors.Grey,
switchHead: Colors.Purple,
switchTrackTrue: Colors.LightPurple,
switchTrackFalse: Colors.Grey,
@@ -85,7 +82,6 @@ export const PurpleTheme = {
VerifiedIcon: Colors.Green,
whiteText: Colors.White,
flipCameraIcon: Colors.Black,
IdInputModalBorder: Colors.Grey,
RetrieveIdLabel: Colors.ShadeOfGrey,
inputSelection: Colors.Purple,
checkCircleIcon: Colors.White,
@@ -93,7 +89,6 @@ export const PurpleTheme = {
OnboardingCloseIcon: Colors.White,
WarningIcon: Colors.Warning,
DefaultToggle: Colors.LightPurple,
ProfileIconBg: Colors.LightPurple,
GrayText: Colors.GrayText,
gradientBtn: Colors.GradientColors,
dotColor: Colors.dorColor,
@@ -111,6 +106,8 @@ export const PurpleTheme = {
statusMessage: Colors.Gray40,
blackIcon: Colors.Black,
uncheckedIcon: Colors.uncheckedIcon,
settingsLabel: Colors.Black,
chevronRightColor: Colors.Grey,
},
Styles: StyleSheet.create({
title: {
@@ -179,8 +176,8 @@ export const PurpleTheme = {
cardDetailsContainer: {},
bottomTabIconStyle: {
padding: 4,
width: 36,
height: 36,
width: Dimensions.get('window').width * 0.12,
height: Dimensions.get('window').height * 0.045,
borderRadius: 6,
backgroundColor: Colors.LightPurple,
},
@@ -228,8 +225,8 @@ export const PurpleTheme = {
height: 30,
backgroundColor: Colors.Grey,
marginVertical: 8,
marginLeft: -45,
marginRight: 22,
marginLeft: -25,
marginRight: 12,
},
closeCardBgContainer: {
borderRadius: 10,
@@ -494,6 +491,22 @@ export const PurpleTheme = {
fontSize: 15,
fontFamily: 'Inter_700Bold',
},
idInputContainer: {
marginTop: 20,
marginRight: Dimensions.get('window').width * 0.26,
},
idInputPicker: {
width: Dimensions.get('window').width * 0.32,
borderBottomWidth: 1,
marginBottom: 2,
borderColor: isIOS() ? 'transparent' : Colors.Grey,
bottom: isIOS() ? 50 : 24,
height: isIOS() ? 100 : 'auto',
},
idInputBottom: {
borderBottomColor: Colors.Orange,
borderBottomWidth: 1,
},
getId: {
justifyContent: 'center',
alignItems: 'center',
@@ -513,12 +526,11 @@ export const PurpleTheme = {
borderBottomColor: Colors.platinumGrey,
borderBottomWidth: 1.3,
},
downloadFabIcon: {
downloadFabIconContainer: {
height: 70,
width: 70,
borderRadius: 200,
padding: 10,
backgroundColor: Colors.Purple,
shadowColor: '#000',
shadowOpacity: 0.4,
elevation: 5,
@@ -526,6 +538,21 @@ export const PurpleTheme = {
bottom: Dimensions.get('window').width * 0.1,
right: Dimensions.get('window').width * 0.1,
},
downloadFabIconNormal: {
borderRadius: 200,
height: 70,
width: 70,
justifyContent: 'center',
position: 'absolute',
},
downloadFabIconPressed: {
borderRadius: 200,
height: 70,
width: 70,
backgroundColor: Colors.Purple,
justifyContent: 'center',
position: 'absolute',
},
boxShadow: generateBoxShadowStyle(),
}),
QrCodeStyles: StyleSheet.create({
@@ -600,14 +627,14 @@ export const PurpleTheme = {
fontFamily: 'Inter_700Bold',
fontSize: 18,
lineHeight: 19,
paddingTop: 4,
paddingTop: 5,
},
subHeader: {
color: Colors.mediumLightGrayText,
fontFamily: 'Inter_600SemiBold',
lineHeight: 19,
fontSize: 13,
paddingTop: 3,
paddingTop: 4,
},
semiBoldHeader: {
color: Colors.Black,
@@ -719,6 +746,10 @@ export const PurpleTheme = {
marginBottom: 17,
marginTop: 22,
},
heading: {
flex: 1,
flexDirection: 'column',
},
}),
ButtonStyles: StyleSheet.create({
fill: {
@@ -1162,11 +1193,13 @@ export const PurpleTheme = {
fontFamily: 'Inter_600SemiBold',
fontSize: 14,
lineHeight: 17,
paddingTop: 1.7,
},
issuerDescription: {
fontSize: 11,
lineHeight: 14,
color: Colors.ShadeOfGrey,
paddingTop: 1.4,
},
issuerIcon: {
resizeMode: 'contain',
@@ -1176,10 +1209,6 @@ export const PurpleTheme = {
marginTop: 8,
marginLeft: 2.5,
},
loaderHeadingText: {
flex: 1,
flexDirection: 'column',
},
}),
ErrorStyles: StyleSheet.create({
image: {marginTop: -60, paddingBottom: 26},
@@ -1202,6 +1231,16 @@ export const PurpleTheme = {
color: Colors.mediumDarkGrey,
},
}),
SetupLanguageScreenStyle: StyleSheet.create({
columnStyle: {
flex: 1,
alignItems: 'center',
justifyContent: 'space-around',
backgroundColor: Colors.White,
maxHeight: Dimensions.get('window').height,
},
}),
ICON_SMALL_SIZE: 16,
ICON_MID_SIZE: 22,
PinIcon: require('../../../assets/pin_icon.png'),
@@ -1209,7 +1248,7 @@ export const PurpleTheme = {
CardBackground: require('../../../assets/card_bg.png'),
OpenCard: require('../../../assets/card_bg.png'),
activationPending: require('../../../assets/pending_activation.png'),
ProfileIcon: require('../../../purpleAssets/profile_icon.png'),
cardFaceIcon: require('../../../purpleAssets/profile_icon.png'),
MosipSplashLogo: require('../../../assets/icon.png'),
MosipLogo: require('../../../assets/mosip-logo.png'),
CameraFlipIcon: require('../../../assets/camera-flip-icon.png'),
@@ -1227,14 +1266,13 @@ export const PurpleTheme = {
InjiHomeLogo: require('../../../assets/inji-home-logo.png'),
MagnifierZoom: require('../../../assets/magnifier-zoom.png'),
HelpIcon: require('../../../assets/help-icon.png'),
sharingIntro: require('../../../assets/Secure-Sharing.png'),
sharingIntro: require('../../../assets/intro-secure-sharing.png'),
walletIntro: require('../../../assets/intro-wallet-binding.png'),
IntroScanner: require('../../../assets/intro-scanner.png'),
injiSmallLogo: require('../../../assets/inji_small_logo.png'),
protectPrivacy: require('../../../assets/phone_mockup_1.png'),
protectPrivacy: require('../../../assets/intro-unlock-method.png'),
NoInternetConnection: require('../../../assets/no-internet-connection.png'),
SomethingWentWrong: require('../../../assets/something-went-wrong.png'),
DigitIcon: require('../../../assets/digit-icon.png'),
elevation(level: ElevationLevel): ViewStyle {
// https://ethercreative.github.io/react-native-shadow-generator/
@@ -1280,17 +1318,16 @@ export const PurpleTheme = {
};
function generateBoxShadowStyle() {
if (Platform.OS === 'ios') {
if (isIOS()) {
return {
shadowColor: '#000',
shadowOffset: {width: 1, height: 1.2},
shadowOpacity: 0.3,
shadowRadius: 2.5,
};
} else if (Platform.OS === 'android') {
return {
elevation: 4,
shadowColor: '#000',
};
}
return {
elevation: 4,
shadowColor: '#000',
};
}

57
i18n.ts
View File

@@ -13,11 +13,9 @@ import Storage from './shared/storage';
import {iso6393To1} from 'iso-639-3';
import {LocalizedField} from './types/VC/ExistingMosipVC/vc';
import {APPLICATION_LANGUAGE} from 'react-native-dotenv';
const resources = {en, fil, ar, hi, kn, ta};
const locale = Localization.locale;
const languageCodeMap = {};
const languageCodeMap = {} as {[key: string]: string};
export const SUPPORTED_LANGUAGES = {
en: 'English',
@@ -56,19 +54,32 @@ export function getLanguageCode(code: string) {
return language;
}
export function getVCDetailsForCurrentLanguage(locales) {
export function getValueForCurrentLanguage(
localizedData: LocalizedField[] | Object,
defaultLanguage = '@none',
) {
const currentLanguage = i18next.language;
const vcDetailsForCurrentLanguage = locales.filter(
obj => obj.language === languageCodeMap[currentLanguage],
);
return vcDetailsForCurrentLanguage[0]?.value
? vcDetailsForCurrentLanguage[0].value
: locales[0]?.value;
const currentLanguageCode = languageCodeMap[currentLanguage];
if (Array.isArray(localizedData)) {
const valueForCurrentLanguage = localizedData.filter(
obj => obj.language === currentLanguageCode,
);
return valueForCurrentLanguage[0]?.value
? valueForCurrentLanguage[0].value
: localizedData[0]?.value;
} else {
const localizedDataObject = localizedData as {[key: string]: string};
return localizedDataObject.hasOwnProperty(currentLanguageCode)
? localizedDataObject[currentLanguageCode]
: localizedDataObject[defaultLanguage];
}
}
// This method gets the value from iso-639-3 package, which contains key value pairs of three letter language codes[key] and two letter langugae code[value]. These values are according to iso standards.
// The response received from the server is three letter language code and the value in the inji code base is two letter language code. Hence the conversion is done.
function getThreeLetterLanguageCode(twoLetterLanguageCode) {
function getThreeLetterLanguageCode(twoLetterLanguageCode: string) {
return Object.keys(iso6393To1).find(
key => iso6393To1[key] === twoLetterLanguageCode,
);
@@ -86,19 +97,33 @@ function populateLanguageCodeMap() {
});
}
export function getLocalizedField(rawField: string | LocalizedField[]) {
export function getLocalizedField(
rawField: string | LocalizedField[] | Object,
) {
if (typeof rawField === 'string') {
return rawField;
}
if (Array.isArray(rawField)) {
try {
if (rawField.length == 1) return rawField[0]?.value;
return getValueForCurrentLanguage(rawField);
} catch (e) {
return '';
}
}
try {
const locales: LocalizedField[] = JSON.parse(JSON.stringify(rawField));
if (locales.length == 1) return locales[0]?.value;
return getVCDetailsForCurrentLanguage(locales);
if (Object.keys(rawField).length === 1) {
return Object.values(rawField)[0];
}
return getValueForCurrentLanguage(rawField);
} catch (e) {
return '';
}
}
function isTwoLetterLanguageCode(languageCode) {
function isTwoLetterLanguageCode(languageCode: string) {
return languageCode.length == 2;
}

View File

@@ -6,6 +6,8 @@ PODS:
- AppAuth/ExternalUserAgent (1.6.2):
- AppAuth/Core
- ASN1Decoder (1.8.0)
- BiometricSdk (0.5.4):
- TensorFlowLiteObjC (= 2.12.0)
- boost (1.76.0)
- BVLinearGradient (2.8.2):
- React-Core
@@ -78,7 +80,8 @@ PODS:
- MMKV (1.2.13):
- MMKVCore (~> 1.2.13)
- MMKVCore (1.2.16)
- mosip-inji-face-sdk (0.1.12):
- mosip-mobileid-sdk (0.1.0):
- BiometricSdk (= 0.5.4)
- React-Core
- Permission-BluetoothPeripheral (3.8.4):
- RNPermissions
@@ -481,6 +484,13 @@ PODS:
- React
- RNSVG (13.4.0):
- React-Core
- TensorFlowLiteC (2.12.0):
- TensorFlowLiteC/Core (= 2.12.0)
- TensorFlowLiteC/Core (2.12.0)
- TensorFlowLiteObjC (2.12.0):
- TensorFlowLiteObjC/Core (= 2.12.0)
- TensorFlowLiteObjC/Core (2.12.0):
- TensorFlowLiteC (= 2.12.0)
- Yoga (1.14.0)
- ZXingObjC/Core (3.6.5)
- ZXingObjC/OneD (3.6.5):
@@ -516,7 +526,7 @@ DEPENDENCIES:
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- libevent (~> 2.1.12)
- mosip-inji-face-sdk (from `../node_modules/mosip-inji-face-sdk`)
- mosip-mobileid-sdk (from `../node_modules/mosip-mobileid-sdk`)
- Permission-BluetoothPeripheral (from `../node_modules/react-native-permissions/ios/BluetoothPeripheral`)
- Permission-Camera (from `../node_modules/react-native-permissions/ios/Camera`)
- Permission-LocationAccuracy (from `../node_modules/react-native-permissions/ios/LocationAccuracy`)
@@ -579,6 +589,7 @@ SPEC REPOS:
trunk:
- AppAuth
- ASN1Decoder
- BiometricSdk
- CatCrypto
- CrcSwift
- fmt
@@ -586,6 +597,8 @@ SPEC REPOS:
- libevent
- MMKV
- MMKVCore
- TensorFlowLiteC
- TensorFlowLiteObjC
- ZXingObjC
EXTERNAL SOURCES:
@@ -641,8 +654,8 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
hermes-engine:
:podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
mosip-inji-face-sdk:
:path: "../node_modules/mosip-inji-face-sdk"
mosip-mobileid-sdk:
:path: "../node_modules/mosip-mobileid-sdk"
Permission-BluetoothPeripheral:
:path: "../node_modules/react-native-permissions/ios/BluetoothPeripheral"
Permission-Camera:
@@ -759,6 +772,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570
ASN1Decoder: 6110fdeacfdb41559b1481457a1645be716610aa
BiometricSdk: 1d16c395413164d92a280b78006abba4307063e6
boost: 57d2868c099736d80fcd648bf211b4431e51a558
BVLinearGradient: 916632041121a658c704df89d99f04acb038de0f
CatCrypto: a477899b6be4954e75be4897e732da098cc0a5a8
@@ -792,7 +806,7 @@ SPEC CHECKSUMS:
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
MMKV: aac95d817a100479445633f2b3ed8961b4ac5043
MMKVCore: 9cfef4c48c6c46f66226fc2e4634d78490206a48
mosip-inji-face-sdk: d5bc0fb66721c25450f92d3297efcb2cf0117271
mosip-mobileid-sdk: 6b8182276896194fdcacdde148b02d5d2d16b02a
Permission-BluetoothPeripheral: 2b88a131074edafd8a46a5cda4ba610ec986d2fb
Permission-Camera: 7ec9ee99704766ff9b90198183387a7f5d82b0c1
Permission-LocationAccuracy: a38ddb5c5d0b8e656f3c86e4a500f9bb88bc099d
@@ -848,6 +862,8 @@ SPEC CHECKSUMS:
RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
RNSecureRandom: 07efbdf2cd99efe13497433668e54acd7df49fef
RNSVG: 07dbd870b0dcdecc99b3a202fa37c8ca163caec2
TensorFlowLiteC: 20785a69299185a379ba9852b6625f00afd7984a
TensorFlowLiteObjC: 9a46a29a76661c513172cfffd3bf712b11ef25c3
Yoga: 065f0b74dba4832d6e328238de46eb72c5de9556
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb

View File

@@ -1,4 +0,0 @@
export default async function faceAuth(capturedImage: string, vcImage: string) {
// TODO: iOS implementation
return Promise.resolve(true);
}

View File

@@ -1,3 +0,0 @@
import { faceAuth } from 'mosip-inji-face-sdk';
export default faceAuth;

View File

@@ -11,7 +11,7 @@
"VC_RECEIVED_BUT_PRESENCE_VERIFICATION_FAILED": "الواردة. فشل التحقق من التواجد",
"PRESENCE_VERIFIED_AND_VC_SHARED": "التحقق منها ومشاركتها",
"PRESENCE_VERIFICATION_FAILED": "فشل التحقق",
"QRLOGIN_SUCCESFULL": "QRL تم تسجيل الدخول بنجاح",
"QRLOGIN_SUCCESFULL": "QR تم تسجيل الدخول بنجاح",
"WALLET_BINDING_SUCCESSFULL": "تفعيل ناجحة",
"WALLET_BINDING_FAILURE": "فشل التفعيل",
"VC_REMOVED": "تمت إزالته من المحفظة",
@@ -159,13 +159,15 @@
},
"IssuersScreen": {
"title": "أضف بطاقة جديدة",
"header": "يرجى تحديد الطريقة المفضلة من الأسفل لإضافة بطاقة جديدة",
"header": "يرجى اختيار جهة الإصدار المفضلة لديك من الخيارات أدناه لإضافة بطاقة جديدة.",
"itemHeading": "التنزيل عبر {{issuer}}",
"itemSubHeading": "أدخل رقم الهوية الوطنية الخاص بك لتنزيل بطاقتك.",
"loaders": {
"loading": "جار التحميل...",
"subTitle": {
"displayIssuers": "Fetching Issuers",
"settingUp": "Setting up",
"downloadingCredentials": "Downloading Credentials",
"displayIssuers": "جلب جهات الإصدار",
"settingUp": "اعداد",
"downloadingCredentials": "تنزيل بيانات الاعتماد"
}
},
"errors":{
@@ -202,6 +204,12 @@
},
"AddVcModal": {
"requestingCredential": "جارٍ طلب بيانات الاعتماد...",
"confirmationDialog": {
"title": "هل تريد إلغاء التنزيل؟",
"message": "بمجرد الإلغاء، لن يتم تنزيل بطاقتك وستحتاج إلى إعادة بدء التنزيل.",
"wait": "لا، سأنتظر",
"cancel": "نعم، إلغاء"
},
"errors": {
"input": {
"empty": "فارغًا",
@@ -297,6 +305,10 @@
"title": "لا يوجد اتصال بالإنترنت",
"message": "الرجاء التحقق من اتصالك وإعادة المحاولة"
},
"downloadLimitExpires":{
"title": "الخطأ تحميل",
"message": "حدثت مشكلة أثناء تنزيل البطاقات التالية. حاول مرة اخرى"
}
}
},
"OnboardingOverlay": {
@@ -353,12 +365,7 @@
"confirmPasscode": "قم بتأكيد رمز المرور",
"enterPasscode": "أدخل رمز المرور الخاص"
},
"AppMetaData": {
"header": "حول إنجي",
"version": "الإصدار",
"useBle": "مدعوم من BLE"
},
"QrScreen": {
"QrLogin": {
"title": "QR تسجيل الدخول",
"alignQr": "قم بمحاذاة رمز الاستجابة السريعة داخل الإطار للمسح الضوئي",
"confirmation": "تأكيد",
@@ -375,8 +382,8 @@
"domainWarning": "يرجى تأكيد مجال موقع الويب الذي تقوم بمسح رمز الاستجابة السريعة منه على النحو التالي",
"access": " يطلب الوصول إلى",
"status": "حالة",
"successMessage": "لقد قمت بتسجيل الدخول بنجاح ",
"okay": "تمام",
"successMessage": "لقد تم تسجيل دخولك بنجاح ",
"ok": "نعم",
"allow": "يسمح",
"cancel": "يلغي",
"essentialClaims": "الادعاءات الأساسية",
@@ -457,7 +464,7 @@
},
"ScanScreen": {
"header": "مسح رمز الاستجابة السريعة",
"noShareableVcs": "لا تتوفر بطاقات قابلة للمشاركة.",
"noShareableVcs": "لا تتوفر بطاقات قابلة للمشاركة",
"bluetoothStateAndroid": "يرجى تشغيل البلوتوث من الإعدادات السريعة لدعم المشاركة المحلية",
"bluetoothStateIos": "يرجى تشغيل البلوتوث من مركز التحكم لدعم المشاركة المحلية",
"enableBluetoothMessage": "يرجى تمكين أذونات البلوتوث لدعم المشاركة المحلية",
@@ -560,8 +567,7 @@
},
"WelcomeScreen": {
"title": "حل هوية مفتوحة المصدر",
"getStarted": "البدء",
"unlockApp": "فتح التطبيق",
"unlockApplication": "فتح التطبيق",
"failedToReadKeys": "فشلت قراءة المفاتيح",
"retryRead": "هل تريد إعادة المحاولة؟",
"errors": {

View File

@@ -160,13 +160,9 @@
},
"IssuersScreen": {
"title": "Add new card",
"header": "Please select a preferred method from below to add a new card",
"header": "Please choose your preferred issuer from the options below to add a new card.",
"itemHeading": "Download via {{issuer}}",
"itemSubHeading": "Enter the mentioned ID and get your card",
"modal": {
"title": "In Progress",
"hint": "downloading your credential from issuer"
},
"itemSubHeading": "Enter your national ID to download your card.",
"loaders": {
"loading": "Loading...",
"subTitle": {
@@ -209,6 +205,12 @@
},
"AddVcModal": {
"requestingCredential": "Requesting credential...",
"confirmationDialog": {
"title": "Do you want to cancel downloading?",
"message": "Once cancelled, your card will not be downloaded and you need to reinitiate the download.",
"wait": "No, Ill wait",
"cancel": "Yes, Cancel"
},
"errors": {
"input": {
"empty": "The input cannot be empty",
@@ -303,6 +305,10 @@
"noInternetConnection": {
"title": "No internet connection",
"message": "Please check your connection and retry"
},
"downloadLimitExpires":{
"title": "Download Error",
"message": "There was an issue downloading following cards. Please try again"
}
}
},
@@ -360,12 +366,7 @@
"confirmPasscode": "Confirm passcode",
"enterPasscode": "Enter your passcode"
},
"AppMetaData": {
"header": "About Inji",
"version": "Version",
"useBle": "Powered by BLE"
},
"QrScreen": {
"QrLogin": {
"title": "QR Login",
"alignQr": "Align the QR code within the frame to scan",
"confirmation": "Confirmation",
@@ -382,8 +383,8 @@
"domainWarning": "Please confirm the domain of the website you are scanning the QR code from below",
"access": " is requesting access to",
"status": "Status",
"successMessage": "You are successfully logged in ",
"okay": "All right",
"successMessage": "You are successfully logged in to ",
"ok": "OK",
"allow": "Allow",
"cancel": "Cancel",
"essentialClaims": "Important Claims",
@@ -464,7 +465,7 @@
},
"ScanScreen": {
"header": "Scan QR Code",
"noShareableVcs": "No shareable card are available.",
"noShareableVcs": "No shareable cards are available.",
"sharingVc": "Sharing card",
"bluetoothStateIos": "Bluetooth is turned OFF, please turn it ON from Control center",
"bluetoothStateAndroid": "Bluetooth is turned OFF, please turn it ON from Quick settings menu",
@@ -567,18 +568,17 @@
},
"WelcomeScreen": {
"title": "Open Source Identity Solution",
"getStarted": "Get started",
"unlockApp": "Unlock application",
"unlockApplication": "Unlock Application",
"failedToReadKeys": "Failed to read keys",
"retryRead": "Want to retry?",
"ignore": "Ignore",
"errors": {
"decryptionFailed": "Failed to decrypt data",
"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."
}
}
},
"ignore": "Ignore"
},
"SetupLanguage": {
"header": "Choose Language",

View File

@@ -158,13 +158,15 @@
},
"IssuersScreen": {
"title": "Magdagdag ng bagong card",
"header": "Mangyaring pumili ng gustong paraan mula sa ibaba upang magdagdag ng bagong card",
"header": "Mangyaring piliin ang iyong gustong tagabigay mula sa mga opsyon sa ibaba upang magdagdag ng bagong card.",
"itemHeading": "I-download sa pamamagitan ng {{issuer}}",
"itemSubHeading": "Ilagay ang iyong national ID para i-download ang iyong card.",
"loaders": {
"loading": "Naglo-load...",
"subTitle": {
"displayIssuers": "Fetching Issuers",
"settingUp": "Setting up",
"downloadingCredentials": "Downloading Credentials",
"displayIssuers": "Kinukuha ang mga Isyu",
"settingUp": "Inaayos",
"downloadingCredentials": "Nagda-download ng Mga Kredensyal"
}
},
"errors": {
@@ -201,6 +203,12 @@
},
"AddVcModal": {
"requestingCredential": "Humihiling ng kredensyal...",
"confirmationDialog": {
"title": "Gusto mo bang kanselahin ang pag-download?",
"message": "Kapag nakansela, hindi na mada-download ang iyong card at kailangan mong simulan muli ang pag-download.",
"wait": "Hindi, maghihintay ako",
"cancel": "Oo, Kanselahin"
},
"errors": {
"input": {
"empty": "Hindi maaaring walang laman ang input",
@@ -295,6 +303,10 @@
"noInternetConnection": {
"title": "Pakisuri ang iyong koneksyon at subukang muli",
"message": "Mangyaring kumonekta sa internet at subukang muli."
},
"downloadLimitExpires":{
"title": "Error sa Pag-download",
"message": "Nagkaroon ng isyu sa pag-download ng mga sumusunod na card. Pakisubukang muli"
}
}
},
@@ -352,12 +364,7 @@
"confirmPasscode": "Kumpirmahin ang passcode",
"enterPasscode": "Ilagay ang iyong passcode"
},
"AppMetaData": {
"header": "Tungkol kay Inji",
"version": "Bersyon",
"useBle": "Pinapatakbo ng BLE"
},
"QrScreen": {
"QrLogin": {
"title": "QR Login",
"alignQr": "I-align ang QR code sa loob ng frame para i-scan",
"confirmation": "Kumpirmasyon",
@@ -374,8 +381,8 @@
"domainWarning": "Pakikumpirma ang domain ng website kung saan mo ini-scan ang QR code mula sa ibaba",
"access": " ay humihiling ng access sa",
"status": "Katayuan",
"successMessage": "Ikaw ay Matagumpay na Naka-log In ",
"okay": "Sige",
"successMessage": "Matagumpay kang naka-log in sa ",
"ok": "OK",
"allow": "Payagan",
"cancel": "Kanselahin",
"essentialClaims": "Mahahalagang Claim",
@@ -460,7 +467,7 @@
},
"ScanScreen": {
"header": "I-scan ang QR Code",
"noShareableVcs": "Walang magagamit na maibabahaging card..",
"noShareableVcs": "Walang magagamit na mga card na maibabahagi.",
"sharingVc": "Pagbabahagi ng card",
"bluetoothStateIos": "Naka-OFF ang Bluetooth, paki-ON ito mula sa control center",
"bluetoothStateAndroid": "Naka-OFF ang Bluetooth, paki-ON ito mula sa control center",
@@ -563,8 +570,7 @@
},
"WelcomeScreen": {
"title": "Open Source Identity Solution",
"getStarted": "Magsimula",
"unlockApp": "Buksan ang aplikasyon",
"unlockApplication": "I-unlock ang Application",
"failedToReadKeys": "Nabigong basahin ang mga susi",
"retryRead": "Gustong subukang muli?",
"errors": {

View File

@@ -156,8 +156,9 @@
},
"IssuersScreen": {
"title": "नया कार्ड जोड़ें",
"header": "नया कार्ड जोड़ने के लिए कृपया नीचे से कोई पसंदीदा विधि चुनें",
"header": "नया कार्ड जोड़ने के लिए कृपया नीचे दिए गए विकल्पों में से अपना पसंदीदा जारीकर्ता चुनें",
"itemHeading": "{{issuer}} के माध्यम से डाउनलोड करें",
"itemSubHeading": "अपना कार्ड डाउनलोड करने के लिए अपनी राष्ट्रीय आईडी दर्ज करें।",
"loaders": {
"loading": "लोड हो रहा है...",
"subTitle": {
@@ -200,6 +201,12 @@
},
"AddVcModal": {
"requestingCredential": "क्रेडेंशियल का अनुरोध कर रहा है...",
"confirmationDialog": {
"title": "क्या आप डाउनलोडिंग रद्द करना चाहते हैं?",
"message": "एक बार रद्द होने पर, आपका कार्ड डाउनलोड नहीं किया जाएगा और आपको डाउनलोड फिर से शुरू करना होगा।",
"wait": "नहीं, मैं इंतजार करूंगा",
"cancel": "हाँ, रद्द करें"
},
"errors": {
"input": {
"empty": "इनपुट खाली नहीं हो सकता",
@@ -296,6 +303,10 @@
"title": "कोई इंटरनेट कनेक्शन नहीं",
"message": "कृपया अपना कनेक्शन जांचें और पुनः प्रयास करें"
},
"downloadLimitExpires":{
"title": "डाउनलोड त्रुटि",
"message": "निम्नलिखित कार्ड डाउनलोड करने में एक समस्या थी। कृपया पुन: प्रयास करें"
}
}
},
"OnboardingOverlay": {
@@ -307,7 +318,7 @@
"stepThreeText": "परेशानी मुक्त तरीके से अपने कार्ड सुरक्षित रूप से साझा करें और विभिन्न सेवाओं का लाभ उठाएं।",
"stepFourTitle": "परेशानी मुक्त प्रमाणीकरण",
"stepFourText": "संग्रहीत डिजिटल क्रेडेंशियल का उपयोग करके आसानी से स्वयं को प्रमाणित करें।",
"getStarted": "शुरू करें",
"getStarted": "शुरू हो जाओ",
"goBack": "वापस जाओ",
"back": "पीछे",
"skip": "छोडना",
@@ -352,12 +363,7 @@
"confirmPasscode": "पासकोड की पुष्टि करें",
"enterPasscode": "अपना पासकोड प्रविष्ट करें"
},
"AppMetaData": {
"header": "इंजी के बारे में",
"version": "संस्करण",
"useBle": "BLE द्वारा संचालित"
},
"QrScreen": {
"QrLogin": {
"title": "क्यूआर लॉगिन",
"alignQr": "स्कैन करने के लिए फ्रेम के भीतर क्यूआर कोड को संरेखित करें",
"confirmation": "पुष्टीकरण",
@@ -374,8 +380,8 @@
"domainWarning": "कृपया उस वेबसाइट के डोमेन की पुष्टि करें जिसे आप नीचे दिए गए क्यूआर कोड से स्कैन कर रहे हैं",
"access": " तक पहुंच का अनुरोध कर रहा है",
"status": "दर्जा",
"successMessage": "आपने सफलतापूर्वक लॉग इन कर लिया है ",
"okay": "ठीक",
"successMessage": "आप सफलतापूर्वक लॉग इन हो गए है ",
"ok": "ठीक है",
"allow": "अनुमति देना",
"cancel": "रद्द करना",
"essentialClaims": "आवश्यक दावे",
@@ -458,7 +464,7 @@
},
"ScanScreen": {
"header": "स्कैन क्यू आर कोड",
"noShareableVcs": "कोई साझा करने योग्य कार्ड उपलब्ध नहीं है।",
"noShareableVcs": "कोई साझा करने योग्य कार्ड उपलब्ध नहीं है।",
"sharingVc": "कार्ड साझा करना",
"bluetoothStateIos": "ब्लूटूथ बंद है, कृपया इसे नियंत्रण केंद्र से चालू करें",
"bluetoothStateAndroid": "ब्लूटूथ बंद है, कृपया इसे त्वरित सेटिंग मेनू से चालू करें",
@@ -561,8 +567,7 @@
},
"WelcomeScreen": {
"title": "ओपन सोर्स आइडेंटिटी सॉल्यूशन",
"getStarted": "आरंभ करें",
"unlockApp": "एप्लिकेशन अनलॉक करें",
"unlockApplication": "एप्लिकेशन अनलॉक करें",
"failedToReadKeys": "कुंजियाँ पढ़ने में विफल",
"retryRead": "पुनः प्रयास करना चाहते हैं?",
"errors": {

View File

@@ -155,13 +155,15 @@
},
"IssuersScreen": {
"title": "ಹೊಸ ಕಾರ್ಡ್ ಸೇರಿಸಿ",
"header": "ದಯವಿಟ್ಟು ಹೊಸ ಕಾರ್ಡ್ ಸೇರಿಸಲು ಕೆಳಗಿನಿಂದ ಆದ್ಯತೆಯ ವಿಧಾನವನ್ನು ಆಯ್ಕೆಮಾಡಿ",
"header": "ದಯವಿಟ್ಟು ಹೊಸ ಕಾರ್ಡ್ ಸೇರಿಸಲು ಕೆಳಗಿನ ಆಯ್ಕೆಗಳಿಂದ ನಿಮ್ಮ ಆದ್ಯತೆ ನೀಡುವವರನ್ನು ಆಯ್ಕೆಮಾಡಿ.",
"itemHeading": "{{issuer}} ಮೂಲಕ ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ",
"itemSubHeading": "ನಿಮ್ಮ ಕಾರ್ಡ್ ಅನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಲು ನಿಮ್ಮ ರಾಷ್ಟ್ರೀಯ ID ಅನ್ನು ನಮೂದಿಸಿ.",
"loaders": {
"loading": "ಲೋಡ್ ಆಗುತ್ತಿದೆ...",
"subTitle": {
"displayIssuers": "Fetching Issuers",
"settingUp": "Setting up",
"downloadingCredentials": "Downloading Credentials"
"displayIssuers": "ವಿತರಕರನ್ನು ಪಡೆಯಲಾಗುತ್ತಿದೆ",
"settingUp": "ಸ್ಥಾಪನೆಗೆ",
"downloadingCredentials": "ರುಜುವಾತುಗಳನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ"
}
},
"errors": {
@@ -198,6 +200,12 @@
},
"AddVcModal": {
"requestingCredential": "ರುಜುವಾತುಗಳನ್ನು ವಿನಂತಿಸಲಾಗುತ್ತಿದೆ...",
"confirmationDialog": {
"title": "ನೀವು ಡೌನ್‌ಲೋಡ್ ಮಾಡುವುದನ್ನು ರದ್ದುಗೊಳಿಸಲು ಬಯಸುವಿರಾ?",
"message": "ಒಮ್ಮೆ ರದ್ದುಗೊಳಿಸಿದರೆ, ನಿಮ್ಮ ಕಾರ್ಡ್ ಡೌನ್‌ಲೋಡ್ ಆಗುವುದಿಲ್ಲ ಮತ್ತು ನೀವು ಡೌನ್‌ಲೋಡ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಬೇಕಾಗುತ್ತದೆ.",
"wait": "ಇಲ್ಲ, ನಾನು ಕಾಯುತ್ತೇನೆ",
"cancel": "ಹೌದು, ರದ್ದುಮಾಡಿ"
},
"errors": {
"input": {
"empty": "ಇನ್‌ಪುಟ್ ಖಾಲಿ ಇರುವಂತಿಲ್ಲ",
@@ -293,6 +301,10 @@
"title": "ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವಿಲ್ಲ",
"message": "ದಯವಿಟ್ಟು ನಿಮ್ಮ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ ಮತ್ತು ಮರುಪ್ರಯತ್ನಿಸಿ"
},
"downloadLimitExpires":{
"title": "ಡೌನ್‌ಲೋಡ್ ದೋಷ",
"message": "ಕೆಳಗಿನ ಕಾರ್ಡ್‌ಗಳನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡುವಲ್ಲಿ ಸಮಸ್ಯೆ ಕಂಡುಬಂದಿದೆ. ದಯವಿಟ್ಟು ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"
}
}
},
"OnboardingOverlay": {
@@ -336,8 +348,8 @@
}
},
"MainLayout": {
"home": "ಲೇಔಟ್",
"scan": "ಮನೆ",
"home": "ಮನೆ",
"scan": "ಸ್ಕ್ಯಾನ್",
"history": "ಇತಿಹಾಸ",
"request": "ವಿನಂತಿ",
"settings": "ಸೆಟ್ಟಿಂಗ್‌ಗಳು"
@@ -349,12 +361,7 @@
"confirmPasscode": "ನಿಮ್ಮ ಪಾಸ್ಕೋಡ್ ಅನ್ನು ದೃಢೀಕರಿಸಿ",
"enterPasscode": "ನಿಮ್ಮ ಪಾಸ್ಕೋಡ್ ನಮೂದಿಸಿ"
},
"AppMetaData": {
"header": "ಇಂಜಿ ಬಗ್ಗೆ",
"version": "ಆವೃತ್ತಿ",
"useBle": "BLE ನಿಂದ ನಡೆಸಲ್ಪಡುತ್ತಿದೆ"
},
"QrScreen": {
"QrLogin": {
"title": "QR ಲಾಗಿನ್",
"alignQr": "ಸ್ಕ್ಯಾನ್ ಮಾಡಲು ಚೌಕಟ್ಟಿನೊಳಗೆ QR ಕೋಡ್ ಅನ್ನು ಹೊಂದಿಸಿ",
"confirmation": "ದೃಢೀಕರಣ",
@@ -372,7 +379,7 @@
"access": " ಗೆ ಪ್ರವೇಶವನ್ನು ವಿನಂತಿಸುತ್ತಿದೆ",
"status": "ಸ್ಥಿತಿ",
"successMessage": "ನೀವು ಯಶಸ್ವಿಯಾಗಿ ಲಾಗ್ ಇನ್ ಆಗಿರುವಿರಿ ",
"okay": "ಸರಿ",
"ok": "ಸರಿ",
"allow": "ಅನುಮತಿಸಿ",
"cancel": "ರದ್ದುಮಾಡು",
"essentialClaims": "ಅಗತ್ಯ ಹಕ್ಕುಗಳು",
@@ -455,7 +462,7 @@
},
"ScanScreen": {
"header": "QR ಕೋಡ್ ಅನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಿ",
"noShareableVcs": "ಯಾವುದೇ ಹಂಚಿಕೊಳ್ಳಬಹುದಾದ ಕಾರ್ಡ್ ಲಭ್ಯವಿಲ್ಲ.",
"noShareableVcs": "ಹಂಚಿಕೊಳ್ಳಬಹುದಾದ ಯಾವುದೇ ಕಾರ್ಡ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ.",
"sharingVc": "ಕಾರ್ಡ್ ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ",
"bluetoothStateIos": "ಬ್ಲೂಟೂತ್ ಆಫ್ ಆಗಿದೆ, ದಯವಿಟ್ಟು ಅದನ್ನು ನಿಯಂತ್ರಣ ಕೇಂದ್ರದಿಂದ ಆನ್ ಮಾಡಿ",
"bluetoothStateAndroid": "ಬ್ಲೂಟೂತ್ ಆಫ್ ಆಗಿದೆ, ದಯವಿಟ್ಟು ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಮೆನುವಿನಿಂದ ಅದನ್ನು ಆನ್ ಮಾಡಿ",
@@ -558,8 +565,7 @@
},
"WelcomeScreen": {
"title": "ಓಪನ್ ಸೋರ್ಸ್ ಐಡೆಂಟಿಟಿ ಸೊಲ್ಯೂಷನ್",
"getStarted": "ಪ್ರಾರಂಭಿಸಿ",
"unlockApp": "ಅಪ್ಲಿಕೇಶನ್ ಅನ್ಲಾಕ್ ಮಾಡಿ",
"unlockApplication": "ಪ್ಲಿಕೇಶನ್ ಅನ್ಲಾಕ್ ಮಾಡಿ",
"failedToReadKeys": "ಕೀಗಳನ್ನು ಓದಲು ವಿಫಲವಾಗಿದೆ",
"retryRead": "ಮರುಪ್ರಯತ್ನಿಸಲು ಬಯಸುವಿರಾ?",
"errors": {

View File

@@ -117,6 +117,12 @@
},
"AddVcModal": {
"requestingCredential": "Solicitando credencial...",
"confirmationDialog": {
"title": "Quieres cancelar la descarga?",
"message": "OUna vez cancelada, su tarjeta no se descargará y deberá reiniciar la descarga.",
"wait": "No, esperaré",
"cancel": "Sí, cancelar"
},
"errors": {
"input": {
"empty": "El campo no puede estar vacío",
@@ -206,6 +212,10 @@
"title": "Sin conexión a Internet",
"message": "Por favor verifique su conexión y vuelva a intentarlo"
},
"downloadLimitExpires":{
"title": "Error de descarga",
"message": "Hubo un problema al descargar las siguientes tarjetas. Inténtalo de nuevo"
}
}
},
"OnboardingOverlay": {
@@ -250,11 +260,6 @@
"confirmPasscode": "Confirma tu código de acceso",
"enterPasscode": "Ingresa tu código de acceso"
},
"AppMetaData": {
"header": "Acerca de Inji",
"version": "Versión",
"useBle": "Impulsado por BLE"
},
"SettingScreen": {
"header": "Ajustes",
"injiAsVerifierApp": "Inji como aplicación verificadora",
@@ -267,7 +272,31 @@
"logout": "Cerrar sesión",
"resetInjiProps": "Restableciendo accesorios Inji..."
},
"QrScreen": {
"IssuersScreen": {
"title": "Agregar nueva tarjeta",
"header": "Elija su emisor preferido entre las opciones siguientes para agregar una nueva tarjeta.",
"itemHeading": "Descargar vía {{issuer}}",
"itemSubHeading": "Introduce tu DNI para descargar tu tarjeta.",
"loaders": {
"loading": "Cargando...",
"subTitle": {
"displayIssuers": "Obteniendo emisores",
"settingUp": "Configurando",
"downloadingCredentials": "Descarga de credenciales"
}
},
"errors": {
"noInternetConnection": {
"title": "Sin conexión a Internet",
"message": "Por favor verifique su conexión y vuelva a intentarlo"
},
"generic": {
"title": "¡Algo salió mal!",
"message": "Estamos teniendo algunos problemas con su solicitud. Inténtalo de nuevo."
}
}
},
"QrLogin": {
"title": "Inicio de sesión con código QR",
"alignQr": "Alinea el código QR dentro del marco para escanearlo",
"confirmation": "Confirmación",
@@ -285,7 +314,7 @@
"access": "está solicitando acceso a",
"status": "Estado",
"successMessage": "Has iniciado sesión correctamente en",
"okay": "Aceptar",
"ok": "Aceptar",
"allow": "Permitir",
"cancel": "Cancelar",
"essentialClaims": "Reclamos Esenciales",
@@ -453,14 +482,16 @@
},
"WelcomeScreen": {
"title": "Solución de identidad de código abierto",
"getStarted": "Empezar",
"unlockApp": "Desbloquear aplicación",
"unlockApplication": "Desbloquear aplicación",
"failedToReadKeys": "No se pudieron leer las claves",
"retryRead": "¿Quieres volver a intentarlo?",
"errors": {
"invalidateKeyError": {
"title": "La aplicación se restableció",
"message": "Debido a la actualización de reconocimiento facial/huella digital, la seguridad de la aplicación se vio afectada y se eliminaron las tarjetas descargadas. Por favor descargue nuevamente."
}
}
},
"ignore": "Ignorar"
},
"common": {
"cancel": "Cancelar",
@@ -728,11 +759,6 @@
"confirmPasscode": "Confirma tu código de acceso",
"enterPasscode": "Ingresa tu código de acceso"
},
"AppMetaData": {
"header": "Acerca de Inji",
"version": "Versión",
"useBle": "Impulsado por BLE"
},
"SettingScreen": {
"header": "Ajustes",
"injiAsVerifierApp": "Inji como aplicación verificadora",
@@ -757,7 +783,7 @@
}
}
},
"QrScreen": {
"QrLogin": {
"title": "Inicio de sesión con código QR",
"alignQr": "Alinea el código QR dentro del marco para escanearlo",
"confirmation": "Confirmación",
@@ -774,8 +800,8 @@
"domainWarning": "Por favor, confirma que el dominio del sitio web desde el cual estás escaneando el código QR es el siguiente",
"access": "está solicitando acceso a",
"status": "Estado",
"successMessage": "Has iniciado sesión correctamente en",
"okay": "Aceptar",
"successMessage": "Has iniciado sesión correctamente en ",
"okay": "De acuerdo",
"allow": "Permitir",
"cancel": "Cancelar",
"essentialClaims": "Reclamos Esenciales",
@@ -936,17 +962,6 @@
}
}
},
"WelcomeScreen": {
"title": "Solución de identidad de código abierto",
"getStarted": "Empezar",
"unlockApp": "Desbloquear aplicación",
"errors": {
"invalidateKeyError": {
"title": "Algunas pruebas de identidad se eliminan debido a problemas de seguridad.",
"message": "Vuelva a descargar."
}
}
},
"common": {
"cancel": "Cancelar",
"save": "Guardar",

View File

@@ -58,7 +58,7 @@
"idType": "ஐடி வகை",
"id": "ஐடி",
"qrCodeHeader": "க்யு ஆர் குறியீடு",
"nationalCard": "தேசிய அட்டை",
"nationalCard": "தேசிய அட்டை",
"uin": "UIN",
"vid": "VID",
"enableVerification": "செயல்படுத்த",
@@ -155,13 +155,15 @@
},
"IssuersScreen": {
"title": "புதிய அட்டையைச் சேர்க்கவும்",
"header": "புதிய கார்டைச் சேர்க்க கீழே இருந்து விருப்பமான முறையைத் தேர்ந்தெடுக்கவும்",
"header": "புதிய கார்டைச் சேர்க்க, கீழே உள்ள விருப்பங்களிலிருந்து உங்களுக்கு விருப்பமான வழங்குபவரைத் தேர்ந்தெடுக்கவும்.",
"itemHeading": "{{issuer}} மூலம் பதிவிறக்கவும்",
"itemSubHeading": "உங்கள் கார்டைப் பதிவிறக்க உங்கள் தேசிய ஐடியை உள்ளிடவும்.",
"loaders": {
"loading": "ஏற்றுகிறது...",
"subTitle": {
"displayIssuers": "Fetching Issuers",
"settingUp": "Setting up",
"downloadingCredentials": "Downloading Credentials",
"displayIssuers": "வழங்குபவர்களைப் பெறுதல்",
"settingUp": "அமைக்கிறது",
"downloadingCredentials": "நற்சான்றிதழ்களைப் பதிவிறக்குகிறது"
}
},
"errors": {
@@ -198,6 +200,12 @@
},
"AddVcModal": {
"requestingCredential": "நற்சான்றிதழைக் கோருகிறது...",
"confirmationDialog": {
"title": "பதிவிறக்குவதை ரத்துசெய்ய விரும்புகிறீர்களா?",
"message": "ரத்துசெய்யப்பட்டதும், உங்கள் கார்டு பதிவிறக்கம் செய்யப்படாது மேலும் நீங்கள் பதிவிறக்கத்தை மீண்டும் தொடங்க வேண்டும்.",
"wait": "இல்லை, நான் காத்திருப்பேன்",
"cancel": "ஆம், ரத்துசெய்"
},
"errors": {
"input": {
"empty": "உள்ளீடு காலியாக இருக்க முடியாது",
@@ -292,6 +300,10 @@
"noInternetConnection": {
"title": "இணைய இணைப்பு இல்லை",
"message": "உங்கள் இணைப்பைச் சரிபார்த்து மீண்டும் முயற்சிக்கவும்"
},
"downloadLimitExpires":{
"title": "பதிவிறக்கப் பிழை",
"message": "பின்வரும் கார்டுகளைப் பதிவிறக்குவதில் சிக்கல் ஏற்பட்டது. தயவு செய்து மீண்டும் முயற்சிக்கவும்"
}
}
},
@@ -304,7 +316,7 @@
"stepThreeText": "தொந்தரவு இல்லாத வகையில் உங்கள் கார்டுகளைப் பாதுகாப்பாகப் பகிர்ந்து, பல்வேறு சேவைகளைப் பெறுங்கள்.",
"stepFourTitle": "தொந்தரவு இல்லாத அங்கீகாரம்",
"stepFourText": "சேமிக்கப்பட்ட டிஜிட்டல் நற்சான்றிதழைப் பயன்படுத்தி உங்களை எளிதாக அங்கீகரிக்கவும்.",
"getStarted": "தொடங்குங்கள்",
"getStarted": "தொடங்கு",
"goBack": "திரும்பி போ",
"back": "மீண்டும்",
"skip": "தவிர்க்கவும்",
@@ -349,12 +361,7 @@
"confirmPasscode": "கடவுக்குறியீட்டை உறுதிப்படுத்தவும்",
"enterPasscode": "உங்கள் கடவுக்குறியீட்டை உள்ளிடவும்"
},
"AppMetaData": {
"header": "இன்ஜி பற்றி",
"version": "பதிப்பு",
"useBle": "BLE ஆல் இயக்கப்படுகிறது"
},
"QrScreen": {
"QrLogin": {
"title": "QR உள்நுழைவு",
"alignQr": "ஸ்கேன் செய்ய ஃப்ரேமுக்குள் QR குறியீட்டை சீரமைக்கவும்",
"confirmation": "உறுதிப்படுத்தல்",
@@ -372,7 +379,7 @@
"access": " அணுகலைக் கோருகிறது",
"status": "நிலை",
"successMessage": "நீங்கள் வெற்றிகரமாக உள்நுழைந்துள்ளீர்கள் ",
"okay": "சரி",
"ok": "சரி",
"allow": "அனுமதி",
"cancel": "ரத்து செய்",
"essentialClaims": "அத்தியாவசிய உரிமைகோரல்கள்",
@@ -455,7 +462,7 @@
},
"ScanScreen": {
"header": "QR குறியீட்டை ஸ்கேன் செய்யவும்",
"noShareableVcs": "பகிரக்கூடிய அட்டை எதுவும் இல்லை.",
"noShareableVcs": "பகிரக்கூடிய அட்டைகள் எதுவும் இல்லை.",
"sharingVc": "பகிர்கிறது அட்டை",
"bluetoothStateIos": "புளூடூத் முடக்கப்பட்டுள்ளது, கட்டுப்பாட்டு மையத்திலிருந்து அதை இயக்கவும்",
"bluetoothStateAndroid": "புளூடூத் முடக்கப்பட்டுள்ளது, விரைவு அமைப்புகள் மெனுவிலிருந்து அதை இயக்கவும்",
@@ -558,8 +565,7 @@
},
"WelcomeScreen": {
"title": "திறந்த மூல அடையாள தீர்வு",
"getStarted": "தொடங்கு",
"unlockApp": "பயன்பாட்டைத் திற",
"unlockApplication": "பயன்பாட்டைத் திறக்கவும்",
"failedToReadKeys": "விசைகளைப் படிக்க முடியவில்லை",
"retryRead": "மீண்டும் முயற்சிக்க வேண்டுமா?",
"errors": {

View File

@@ -12,14 +12,20 @@ import {MY_VCS_STORE_KEY, ESIGNET_BASE_URL} from '../shared/constants';
import {StoreEvents} from './store';
import {linkTransactionResponse, VC} from '../types/VC/ExistingMosipVC/vc';
import {request} from '../shared/request';
import {getJwt, isCustomSecureKeystore} from '../shared/cryptoutil/cryptoUtil';
import {
getJwt,
isHardwareKeystoreExists,
} from '../shared/cryptoutil/cryptoUtil';
import {
getBindingCertificateConstant,
getPrivateKey,
} from '../shared/keystore/SecureKeystore';
import i18n from '../i18n';
import {parseMetadatas, VCMetadata} from '../shared/VCMetadata';
import {getEndData, sendEndEvent} from '../shared/telemetry/TelemetryUtils';
import {
getEndEventData,
sendEndEvent,
} from '../shared/telemetry/TelemetryUtils';
const model = createModel(
{
@@ -31,7 +37,7 @@ const model = createModel(
linkTransactionResponse: {} as linkTransactionResponse,
authFactors: [],
authorizeScopes: null,
clientName: '',
clientName: {},
configs: {},
essentialClaims: [],
linkTransactionId: '',
@@ -224,7 +230,7 @@ export const qrLoginMachine =
},
},
success: {
entry: [() => sendEndEvent(getEndData('QR login'))],
entry: [() => sendEndEvent(getEndEventData('QR login', 'SUCCESS'))],
on: {
CONFIRM: {
target: 'done',
@@ -345,7 +351,7 @@ export const qrLoginMachine =
linkTransaction: async context => {
const response = await request(
'POST',
'/v1/esignet/linked-authorization/link-transaction',
'/v1/esignet/linked-authorization/v2/link-transaction',
{
requestTime: String(new Date().toISOString()),
request: {
@@ -360,7 +366,7 @@ export const qrLoginMachine =
sendAuthenticate: async context => {
let privateKey;
const individualId = context.selectedVc.vcMetadata.id;
if (!isCustomSecureKeystore()) {
if (!isHardwareKeystoreExists) {
privateKey = await getPrivateKey(
context.selectedVc.walletBindingResponse?.walletBindingId,
);
@@ -394,7 +400,7 @@ export const qrLoginMachine =
sendConsent: async context => {
let privateKey;
const individualId = context.selectedVc.vcMetadata.id;
if (!isCustomSecureKeystore()) {
if (!isHardwareKeystoreExists) {
privateKey = await getPrivateKey(
context.selectedVc.walletBindingResponse?.walletBindingId,
);

View File

@@ -1,83 +0,0 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
'done.invoke.QrLogin.linkTransaction:invocation[0]': {
type: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.QrLogin.sendingAuthenticate:invocation[0]': {
type: 'done.invoke.QrLogin.sendingAuthenticate:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'error.platform.QrLogin.linkTransaction:invocation[0]': {
type: 'error.platform.QrLogin.linkTransaction:invocation[0]';
data: unknown;
};
'error.platform.QrLogin.sendingAuthenticate:invocation[0]': {
type: 'error.platform.QrLogin.sendingAuthenticate:invocation[0]';
data: unknown;
};
'error.platform.QrLogin.sendingConsent:invocation[0]': {
type: 'error.platform.QrLogin.sendingConsent:invocation[0]';
data: unknown;
};
'xstate.init': {type: 'xstate.init'};
};
invokeSrcNameMap: {
linkTransaction: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
sendAuthenticate: 'done.invoke.QrLogin.sendingAuthenticate:invocation[0]';
sendConsent: 'done.invoke.QrLogin.sendingConsent:invocation[0]';
};
missingImplementations: {
actions: never;
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
SetErrorMessage:
| 'error.platform.QrLogin.linkTransaction:invocation[0]'
| 'error.platform.QrLogin.sendingAuthenticate:invocation[0]'
| 'error.platform.QrLogin.sendingConsent:invocation[0]';
expandLinkTransResp: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
forwardToParent: 'DISMISS';
loadMyVcs: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
loadThumbprint: 'CONFIRM';
resetLinkTransactionId: 'GET';
resetSelectedVoluntaryClaims: 'GET';
setClaims: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
setConsentClaims: 'TOGGLE_CONSENT_CLAIM';
setLinkedTransactionId: 'done.invoke.QrLogin.sendingAuthenticate:invocation[0]';
setMyVcs: 'STORE_RESPONSE';
setScanData: 'GET';
setSelectedVc: 'SELECT_VC';
setThumbprint: 'STORE_RESPONSE';
setlinkTransactionResponse: 'done.invoke.QrLogin.linkTransaction:invocation[0]';
};
eventsCausingDelays: {};
eventsCausingGuards: {};
eventsCausingServices: {
linkTransaction: 'GET';
sendAuthenticate: never;
sendConsent: 'STORE_RESPONSE';
};
matchesStates:
| 'ShowError'
| 'done'
| 'faceAuth'
| 'invalidIdentity'
| 'linkTransaction'
| 'loadMyVcs'
| 'loadingThumbprint'
| 'requestConsent'
| 'sendingAuthenticate'
| 'sendingConsent'
| 'showvcList'
| 'success'
| 'waitingForData';
tags: never;
}

View File

@@ -5,11 +5,11 @@ import {VCMetadata} from '../../../shared/VCMetadata';
import {VC} from '../../../types/VC/ExistingMosipVC/vc';
import {
generateKeys,
isCustomSecureKeystore,
isHardwareKeystoreExists,
WalletBindingResponse,
} from '../../../shared/cryptoutil/cryptoUtil';
import {log} from 'xstate/lib/actions';
import {OpenId4VCIProtocol} from '../../../shared/openId4VCI/Utils';
import {Protocols} from '../../../shared/openId4VCI/Utils';
import {StoreEvents} from '../../../machines/store';
import {MIMOTO_BASE_URL, MY_VCS_STORE_KEY} from '../../../shared/constants';
import {VcEvents} from '../../../machines/vc';
@@ -23,6 +23,7 @@ import {ActivityLogEvents} from '../../../machines/activityLog';
import {request} from '../../../shared/request';
import SecureKeystore from 'react-native-secure-keystore';
import {VerifiableCredential} from './vc';
import {API_URLS} from '../../../shared/api';
const model = createModel(
{
@@ -500,7 +501,7 @@ export const EsignetMosipVCItemMachine = model.createMachine(
context => ({
type: 'GET_VC_ITEM',
vcMetadata: context.vcMetadata,
protocol: OpenId4VCIProtocol,
protocol: Protocols.OpenId4VCI,
}),
{
to: context => context.serviceRefs.vc,
@@ -632,7 +633,7 @@ export const EsignetMosipVCItemMachine = model.createMachine(
),
setPublicKey: assign({
publicKey: (context, event) => {
if (!isCustomSecureKeystore()) {
if (!isHardwareKeystoreExists) {
return (event.data as KeyPair).public;
}
return event.data as string;
@@ -751,8 +752,8 @@ export const EsignetMosipVCItemMachine = model.createMachine(
},
addWalletBindnigId: async context => {
const response = await request(
'POST',
'/residentmobileapp/wallet-binding',
API_URLS.walletBinding.method,
API_URLS.walletBinding.buildURL(),
{
requestTime: String(new Date().toISOString()),
request: {
@@ -788,7 +789,7 @@ export const EsignetMosipVCItemMachine = model.createMachine(
return walletResponse;
},
generateKeyPair: async context => {
if (!isCustomSecureKeystore()) {
if (!isHardwareKeystoreExists) {
return await generateKeys();
}
const isBiometricsEnabled = SecureKeystore.hasBiometricsEnabled();
@@ -800,8 +801,8 @@ export const EsignetMosipVCItemMachine = model.createMachine(
},
requestBindingOtp: async context => {
const response = await request(
'POST',
'/residentmobileapp/binding-otp',
API_URLS.bindingOtp.method,
API_URLS.bindingOtp.buildURL(),
{
requestTime: String(new Date().toISOString()),
request: {
@@ -825,7 +826,7 @@ export const EsignetMosipVCItemMachine = model.createMachine(
return vc != null;
},
isCustomSecureKeystore: () => isCustomSecureKeystore(),
isCustomSecureKeystore: () => isHardwareKeystoreExists,
},
},
);

View File

@@ -128,7 +128,7 @@ export interface Typegen0 {
sendWalletBindingSuccess:
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]'
| 'done.invoke.vc-item-openid4vci.kebabPopUp.updatingPrivateKey:invocation[0]';
setContext: 'STORE_RESPONSE';
setContext: 'GET_VC_RESPONSE' | 'STORE_RESPONSE';
setGeneratedOn: 'GET_VC_RESPONSE';
setOtp: 'INPUT_OTP';
setPinCard: 'PIN_CARD';

View File

@@ -4,10 +4,10 @@ import {MIMOTO_BASE_URL, MY_VCS_STORE_KEY} from '../../../shared/constants';
import {AppServices} from '../../../shared/GlobalContext';
import {CredentialDownloadResponse, request} from '../../../shared/request';
import {
VC,
VerifiableCredential,
VcIdType,
DecodedCredential,
VC,
VcIdType,
VerifiableCredential,
} from '../../../types/VC/ExistingMosipVC/vc';
import {StoreEvents} from '../../store';
import {ActivityLogEvents} from '../../activityLog';
@@ -15,7 +15,7 @@ import {verifyCredential} from '../../../shared/vcjs/verifyCredential';
import {log} from 'xstate/lib/actions';
import {
generateKeys,
isCustomSecureKeystore,
isHardwareKeystoreExists,
WalletBindingResponse,
} from '../../../shared/cryptoutil/cryptoUtil';
import {KeyPair} from 'react-native-rsa-native';
@@ -31,11 +31,12 @@ import i18n from '../../../i18n';
import SecureKeystore from 'react-native-secure-keystore';
import {VCMetadata} from '../../../shared/VCMetadata';
import {
sendStartEvent,
getData,
getEndData,
getEndEventData,
getStartEventData,
sendEndEvent,
sendStartEvent,
} from '../../../shared/telemetry/TelemetryUtils';
import {API_URLS} from '../../../shared/api';
const model = createModel(
{
@@ -173,7 +174,10 @@ export const ExistingMosipVCItemMachine =
actions: ['setMaxDownloadCount', 'setDownloadInterval'],
},
onError: {
actions: log((_, event) => (event.data as Error).message),
actions: [
log((_, event) => (event.data as Error).message),
'sendDownloadLimitExpire',
],
target: 'checkingStatus',
},
},
@@ -216,6 +220,7 @@ export const ExistingMosipVCItemMachine =
'setVerifiableCredential',
'updateVc',
'logDownloaded',
'sendTelemetryEvents',
'removeVcFromInProgressDownloads',
],
target: '#vc-item.checkingVerificationStatus',
@@ -301,7 +306,9 @@ export const ExistingMosipVCItemMachine =
showBindingWarning: {
on: {
CONFIRM: {
actions: [() => sendStartEvent(getData('VC activation'))],
actions: [
() => sendStartEvent(getStartEventData('VC activation')),
],
target: '#vc-item.kebabPopUp.requestingBindingOtp',
},
CANCEL: {
@@ -416,7 +423,8 @@ export const ExistingMosipVCItemMachine =
'setWalletBindingErrorEmpty',
'sendWalletBindingSuccess',
'logWalletBindingSuccess',
() => sendEndEvent(getEndData('VC activation')),
() =>
sendEndEvent(getEndEventData('VC activation', 'SUCCESS')),
],
target: '#vc-item.kebabPopUp',
},
@@ -633,7 +641,7 @@ export const ExistingMosipVCItemMachine =
showBindingWarning: {
on: {
CONFIRM: {
actions: () => sendStartEvent(getData('VC activation')),
actions: () => sendStartEvent(getStartEventData('VC activation')),
target: 'requestingBindingOtp',
},
CANCEL: {
@@ -743,7 +751,7 @@ export const ExistingMosipVCItemMachine =
'setWalletBindingErrorEmpty',
'setWalletBindingSuccess',
'logWalletBindingSuccess',
() => sendEndEvent(getEndData('VC activation')),
() => sendEndEvent(getEndEventData('VC activation', 'SUCCESS')),
],
target: 'idle',
},
@@ -804,6 +812,18 @@ export const ExistingMosipVCItemMachine =
},
),
sendDownloadLimitExpire: send(
(_context, event) => {
return {
type: 'DOWNLOAD_LIMIT_EXPIRED',
vcMetadata: _context.vcMetadata,
};
},
{
to: context => context.serviceRefs.vc,
},
),
setWalletBindingError: assign({
walletBindingError: () =>
i18n.t(`errors.genericError`, {
@@ -835,7 +855,7 @@ export const ExistingMosipVCItemMachine =
),
setPublicKey: assign({
publicKey: (context, event) => {
if (!isCustomSecureKeystore()) {
if (!isHardwareKeystoreExists) {
return (event.data as KeyPair).public;
}
return event.data as string;
@@ -1000,7 +1020,11 @@ export const ExistingMosipVCItemMachine =
};
case 'GET_VC_RESPONSE':
case 'CREDENTIAL_DOWNLOADED':
return {...context, ...event.vc, vcMetadata: context.vcMetadata};
return {
...context,
...event.vc,
vcMetadata: context.vcMetadata,
};
}
}),
@@ -1019,6 +1043,9 @@ export const ExistingMosipVCItemMachine =
to: context => context.serviceRefs.activityLog,
},
),
sendTelemetryEvents: () => {
sendEndEvent({type: 'VC Download', status: 'SUCCESS'});
},
logWalletBindingSuccess: send(
context =>
@@ -1177,8 +1204,8 @@ export const ExistingMosipVCItemMachine =
addWalletBindnigId: async context => {
const response = await request(
'POST',
'/residentmobileapp/wallet-binding',
API_URLS.walletBinding.method,
API_URLS.walletBinding.buildURL(),
{
requestTime: String(new Date().toISOString()),
request: {
@@ -1224,7 +1251,7 @@ export const ExistingMosipVCItemMachine =
},
generateKeyPair: async context => {
if (!isCustomSecureKeystore()) {
if (!isHardwareKeystoreExists) {
return await generateKeys();
}
const isBiometricsEnabled = SecureKeystore.hasBiometricsEnabled();
@@ -1237,8 +1264,8 @@ export const ExistingMosipVCItemMachine =
requestBindingOtp: async context => {
const response = await request(
'POST',
'/residentmobileapp/binding-otp',
API_URLS.bindingOtp.method,
API_URLS.bindingOtp.buildURL(),
{
requestTime: String(new Date().toISOString()),
request: {
@@ -1261,8 +1288,10 @@ export const ExistingMosipVCItemMachine =
onReceive(async event => {
if (event.type === 'POLL_STATUS') {
const response = await request(
'GET',
`/residentmobileapp/credentialshare/request/status/${context.vcMetadata.requestId}`,
API_URLS.credentialStatus.method,
API_URLS.credentialStatus.buildURL(
context.vcMetadata.requestId,
),
);
switch (response.response?.statusCode) {
case 'NEW':
@@ -1287,8 +1316,8 @@ export const ExistingMosipVCItemMachine =
onReceive(async event => {
if (event.type === 'POLL_DOWNLOAD') {
const response: CredentialDownloadResponse = await request(
'POST',
'/residentmobileapp/credentialshare/download',
API_URLS.credentialDownload.method,
API_URLS.credentialDownload.buildURL(),
{
individualId: context.vcMetadata.id,
requestId: context.vcMetadata.requestId,
@@ -1322,12 +1351,16 @@ export const ExistingMosipVCItemMachine =
requestOtp: async context => {
try {
return request('POST', '/residentmobileapp/req/otp', {
individualId: context.vcMetadata.id,
individualIdType: context.vcMetadata.idType,
otpChannel: ['EMAIL', 'PHONE'],
transactionID: context.transactionId,
});
return request(
API_URLS.requestOtp.method,
API_URLS.requestOtp.buildURL(),
{
individualId: context.vcMetadata.id,
individualIdType: context.vcMetadata.idType,
otpChannel: ['EMAIL', 'PHONE'],
transactionID: context.transactionId,
},
);
} catch (error) {
console.error(error);
}
@@ -1337,8 +1370,8 @@ export const ExistingMosipVCItemMachine =
let response = null;
if (context.locked) {
response = await request(
'POST',
'/residentmobileapp/req/auth/unlock',
API_URLS.authUnLock.method,
API_URLS.authUnLock.buildURL(),
{
individualId: context.vcMetadata.id,
individualIdType: context.vcMetadata.idType,
@@ -1350,8 +1383,8 @@ export const ExistingMosipVCItemMachine =
);
} else {
response = await request(
'POST',
'/residentmobileapp/req/auth/lock',
API_URLS.authLock.method,
API_URLS.authLock.buildURL(),
{
individualId: context.vcMetadata.id,
individualIdType: context.vcMetadata.idType,
@@ -1367,8 +1400,8 @@ export const ExistingMosipVCItemMachine =
requestRevoke: async context => {
try {
return request(
'PATCH',
`/residentmobileapp/vid/${context.vcMetadata.id}`,
API_URLS.requestRevoke.method,
API_URLS.requestRevoke.buildURL(context.vcMetadata.id),
{
transactionID: context.transactionId,
vidStatus: 'REVOKED',
@@ -1399,7 +1432,7 @@ export const ExistingMosipVCItemMachine =
return context.isVerified;
},
isCustomSecureKeystore: () => isCustomSecureKeystore(),
isCustomSecureKeystore: () => isHardwareKeystoreExists,
},
},
);

View File

@@ -208,15 +208,19 @@ export interface Typegen0 {
| 'done.invoke.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]'
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
markVcValid: 'done.invoke.vc-item.verifyingCredential:invocation[0]';
refreshMyVcs: 'STORE_RESPONSE';
removeTamperedVcItem: 'TAMPERED_VC';
removeVcFromInProgressDownloads: 'STORE_RESPONSE';
removeVcItem: 'CONFIRM';
removeVcMetaDataFromStorage: 'STORE_ERROR';
removeVcMetaDataFromVcMachine: 'DISMISS';
removedVc: 'STORE_RESPONSE';
requestStoredContext: 'GET_VC_RESPONSE' | 'REFRESH';
requestVcContext: 'DISMISS' | 'xstate.init';
resetWalletBindingSuccess: 'DISMISS';
revokeVID: 'done.invoke.vc-item.requestingRevoke:invocation[0]';
sendDownloadLimitExpire: 'error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
sendTamperedVc: 'TAMPERED_VC';
sendTelemetryEvents: 'STORE_RESPONSE';
sendVcUpdated: 'PIN_CARD';
sendWalletBindingSuccess:
| 'done.invoke.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]'

View File

@@ -1,17 +1,13 @@
import NetInfo, {NetInfoStateType} from '@react-native-community/netinfo';
import {AppState, AppStateStatus, Platform} from 'react-native';
import {
getDeviceId,
getDeviceName,
getDeviceNameSync,
} from 'react-native-device-info';
import {EventFrom, spawn, StateFrom, send, assign, AnyState} from 'xstate';
import {getDeviceId, getDeviceName} from 'react-native-device-info';
import {assign, EventFrom, send, spawn, StateFrom} from 'xstate';
import {createModel} from 'xstate/lib/model';
import {authMachine, createAuthMachine} from './auth';
import {createSettingsMachine, settingsMachine} from './settings';
import {StoreEvents, storeMachine} from './store';
import {createVcMachine, vcMachine} from './vc';
import {createActivityLogMachine, activityLogMachine} from './activityLog';
import {activityLogMachine, createActivityLogMachine} from './activityLog';
import {
createRequestMachine,
requestMachine,
@@ -20,19 +16,19 @@ import {createScanMachine, scanMachine} from './bleShare/scan/scanMachine';
import {createRevokeMachine, revokeVidsMachine} from './revoke';
import {pure, respond} from 'xstate/lib/actions';
import {AppServices} from '../shared/GlobalContext';
import {request} from '../shared/request';
import {
changeCrendetialRegistry,
SETTINGS_STORE_KEY,
MIMOTO_BASE_URL,
ESIGNET_BASE_URL,
changeEsignetUrl,
ESIGNET_BASE_URL,
isAndroid,
MIMOTO_BASE_URL,
SETTINGS_STORE_KEY,
} from '../shared/constants';
import {logState} from '../shared/commonUtil';
const model = createModel(
{
info: {} as AppInfo,
backendInfo: {} as BackendInfo,
serviceRefs: {} as AppServices,
isReadError: false,
isDecryptError: false,
@@ -51,7 +47,6 @@ const model = createModel(
REQUEST_DEVICE_INFO: () => ({}),
READY: (data?: unknown) => ({data}),
APP_INFO_RECEIVED: (info: AppInfo) => ({info}),
BACKEND_INFO_RECEIVED: (info: BackendInfo) => ({info}),
STORE_RESPONSE: (response: unknown) => ({response}),
RESET_KEY_INVALIDATE_ERROR_DISMISS: () => ({}),
},
@@ -135,19 +130,8 @@ export const appMachine = model.createMachine(
},
on: {
APP_INFO_RECEIVED: {
target: 'devinfo',
actions: ['setAppInfo'],
},
},
},
devinfo: {
invoke: {
src: 'getBackendInfo',
},
on: {
BACKEND_INFO_RECEIVED: {
target: '#ready',
actions: ['setBackendInfo'],
actions: ['setAppInfo'],
},
},
},
@@ -286,7 +270,7 @@ export const appMachine = model.createMachine(
scanMachine.id,
);
if (Platform.OS === 'android') {
if (isAndroid()) {
serviceRefs.request = spawn(
createRequestMachine(serviceRefs),
requestMachine.id,
@@ -310,7 +294,7 @@ export const appMachine = model.createMachine(
context.serviceRefs.activityLog.subscribe(logState);
context.serviceRefs.scan.subscribe(logState);
if (Platform.OS === 'android') {
if (isAndroid()) {
context.serviceRefs.request.subscribe(logState);
}
@@ -322,10 +306,6 @@ export const appMachine = model.createMachine(
info: (_, event) => event.info,
}),
setBackendInfo: model.assign({
backendInfo: (_, event) => event.info,
}),
loadCredentialRegistryHostFromStorage: send(
StoreEvents.GET(SETTINGS_STORE_KEY),
{
@@ -363,23 +343,6 @@ export const appMachine = model.createMachine(
callback(model.events.APP_INFO_RECEIVED(appInfo));
},
getBackendInfo: () => async callback => {
let backendInfo = {
application: {
name: '',
version: '',
},
build: {},
config: {},
};
try {
backendInfo = await request('GET', '/residentmobileapp/info');
callback(model.events.BACKEND_INFO_RECEIVED(backendInfo));
} catch {
callback(model.events.BACKEND_INFO_RECEIVED(backendInfo));
}
},
checkFocusState: () => callback => {
const changeHandler = (newState: AppStateStatus) => {
switch (newState) {
@@ -398,8 +361,7 @@ export const appMachine = model.createMachine(
AppState.addEventListener('change', changeHandler);
// android only
if (Platform.OS === 'android') {
if (isAndroid()) {
AppState.addEventListener('blur', blurHandler);
AppState.addEventListener('focus', focusHandler);
}
@@ -407,7 +369,7 @@ export const appMachine = model.createMachine(
return () => {
AppState.removeEventListener('change', changeHandler);
if (Platform.OS === 'android') {
if (isAndroid()) {
AppState.removeEventListener('blur', blurHandler);
AppState.removeEventListener('focus', focusHandler);
}
@@ -432,25 +394,11 @@ interface AppInfo {
deviceName: string;
}
interface BackendInfo {
application: {
name: string;
version: string;
};
build: object;
config: object;
}
type State = StateFrom<typeof appMachine>;
export function selectAppInfo(state: State) {
return state.context.info;
}
export function selectBackendInfo(state: State) {
return state.context.backendInfo;
}
export function selectIsReady(state: State) {
return state.matches('ready');
}
@@ -467,28 +415,6 @@ export function selectIsFocused(state: State) {
return state.matches('ready.focus');
}
export function logState(state: AnyState) {
const data = JSON.stringify(
state.event,
(key, value) => {
if (key === 'type') return undefined;
if (typeof value === 'string' && value.length >= 100) {
return value.slice(0, 100) + '...';
}
return value;
},
2,
);
console.log(
`[${getDeviceNameSync()}] ${state.machine.id}: ${
state.event.type
} -> ${state.toStrings().pop()}\n${
data.length > 300 ? data.slice(0, 300) + '...' : data
}
`,
);
}
export function selectIsReadError(state: State) {
return state.context.isReadError;
}

View File

@@ -1,4 +1,3 @@
import {init} from 'mosip-inji-face-sdk';
import {assign, ContextFrom, EventFrom, send, StateFrom} from 'xstate';
import {createModel} from 'xstate/lib/model';
import getAllConfigurations, {

View File

@@ -1,64 +1,47 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
'': {type: ''};
'done.invoke.auth.authorized:invocation[0]': {
type: 'done.invoke.auth.authorized:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.auth.introSlider:invocation[0]': {
type: 'done.invoke.auth.introSlider:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'xstate.init': {type: 'xstate.init'};
};
invokeSrcNameMap: {
downloadFaceSdkModel: 'done.invoke.auth.authorized:invocation[0]';
generatePasscodeSalt: 'done.invoke.auth.introSlider:invocation[0]';
};
missingImplementations: {
actions: never;
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
requestStoredContext: 'xstate.init';
setBiometrics: 'SETUP_BIOMETRICS';
setContext: 'STORE_RESPONSE';
setLanguage: 'SETUP_BIOMETRICS' | 'SETUP_PASSCODE';
setPasscode: 'SETUP_PASSCODE';
setPasscodeSalt: 'done.invoke.auth.introSlider:invocation[0]';
storeContext:
| 'SETUP_BIOMETRICS'
| 'SETUP_PASSCODE'
| 'STORE_RESPONSE'
| 'done.invoke.auth.authorized:invocation[0]'
| 'done.invoke.auth.introSlider:invocation[0]';
};
eventsCausingDelays: {};
eventsCausingGuards: {
hasBiometricSet: '';
hasData: 'STORE_RESPONSE';
hasLanguageset: '';
hasPasscodeSet: '';
};
eventsCausingServices: {
downloadFaceSdkModel: 'LOGIN' | 'SETUP_PASSCODE';
generatePasscodeSalt: 'SELECT';
};
matchesStates:
| 'authorized'
| 'checkingAuth'
| 'init'
| 'introSlider'
| 'languagesetup'
| 'savingDefaults'
| 'settingUp'
| 'unauthorized';
tags: never;
}
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
"": { type: "" };
"done.invoke.auth.authorized:invocation[0]": { type: "done.invoke.auth.authorized:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.auth.introSlider:invocation[0]": { type: "done.invoke.auth.introSlider:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
"downloadFaceSdkModel": "done.invoke.auth.authorized:invocation[0]";
"generatePasscodeSalt": "done.invoke.auth.introSlider:invocation[0]";
};
missingImplementations: {
actions: never;
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
"requestStoredContext": "xstate.init";
"setBiometrics": "SETUP_BIOMETRICS";
"setContext": "STORE_RESPONSE";
"setLanguage": "SETUP_BIOMETRICS" | "SETUP_PASSCODE";
"setPasscode": "SETUP_PASSCODE";
"setPasscodeSalt": "done.invoke.auth.introSlider:invocation[0]";
"storeContext": "SETUP_BIOMETRICS" | "SETUP_PASSCODE" | "STORE_RESPONSE" | "done.invoke.auth.authorized:invocation[0]" | "done.invoke.auth.introSlider:invocation[0]";
};
eventsCausingDelays: {
};
eventsCausingGuards: {
"hasBiometricSet": "";
"hasData": "STORE_RESPONSE";
"hasLanguageset": "";
"hasPasscodeSet": "";
};
eventsCausingServices: {
"downloadFaceSdkModel": "LOGIN" | "SETUP_PASSCODE";
"generatePasscodeSalt": "SELECT";
};
matchesStates: "authorized" | "checkingAuth" | "init" | "introSlider" | "languagesetup" | "savingDefaults" | "settingUp" | "unauthorized";
tags: never;
}

View File

@@ -1,7 +1,8 @@
import { createModel } from 'xstate/lib/model';
import {createModel} from 'xstate/lib/model';
import * as LocalAuthentication from 'expo-local-authentication';
import { EventFrom, MetaObject, StateFrom } from 'xstate';
import { Platform } from 'react-native';
import {EventFrom, MetaObject, StateFrom} from 'xstate';
import {Platform} from 'react-native';
import {isAndroid} from '../shared/constants';
// ----- CREATE MODEL ---------------------------------------------------------
const model = createModel(
@@ -11,18 +12,19 @@ const model = createModel(
isEnrolled: false,
status: null,
retry: false,
error: {},
},
{
events: {
SET_IS_AVAILABLE: (data: boolean) => ({ data }),
SET_AUTH: (data: unknown[]) => ({ data }),
SET_IS_ENROLLED: (data: boolean) => ({ data }),
SET_STATUS: (data: boolean) => ({ data }),
SET_IS_AVAILABLE: (data: boolean) => ({data}),
SET_AUTH: (data: unknown[]) => ({data}),
SET_IS_ENROLLED: (data: boolean) => ({data}),
SET_STATUS: (data: boolean) => ({data}),
AUTHENTICATE: () => ({}),
RETRY_AUTHENTICATE: () => ({}),
},
}
},
);
// ----------------------------------------------------------------------------
@@ -98,7 +100,7 @@ export const biometricsMachine = model.createMachine(
authenticating: {
invoke: {
src: () => async () => {
if (Platform.OS === 'android') {
if (isAndroid()) {
await LocalAuthentication.cancelAuthenticate();
}
const res = await LocalAuthentication.authenticateAsync({
@@ -108,9 +110,20 @@ export const biometricsMachine = model.createMachine(
// disableDeviceFallback: true,
// fallbackLabel: 'Invalid fingerprint attempts, Please try again.'
});
if (res.error) {
throw new Error(JSON.stringify(res));
}
return res.success;
},
onError: 'failure',
onError: [
{
target: 'failure',
actions: ['sendFailedEndEvent'],
},
],
onDone: {
target: 'authentication',
actions: ['setStatus'],
@@ -192,6 +205,7 @@ export const biometricsMachine = model.createMachine(
meta: {
message: 'errors.generic',
},
exit: 'resetError',
},
},
on: {
@@ -222,15 +236,26 @@ export const biometricsMachine = model.createMachine(
setRetry: model.assign({
retry: () => true,
}),
sendFailedEndEvent: model.assign({
error: (_context, event) => {
const res = JSON.parse((event.data as Error).message);
return {res: res, stacktrace: event};
},
}),
resetError: model.assign({
error: () => null,
}),
},
guards: {
isStatusSuccess: (ctx) => ctx.status,
isStatusFail: (ctx) => !ctx.status,
checkIfAvailable: (ctx) => ctx.isAvailable && ctx.isEnrolled,
checkIfUnavailable: (ctx) => !ctx.isAvailable,
checkIfUnenrolled: (ctx) => !ctx.isEnrolled,
isStatusSuccess: ctx => ctx.status,
isStatusFail: ctx => !ctx.status,
checkIfAvailable: ctx => ctx.isAvailable && ctx.isEnrolled,
checkIfUnavailable: ctx => !ctx.isAvailable,
checkIfUnenrolled: ctx => !ctx.isEnrolled,
},
}
},
);
// ----------------------------------------------------------------------------
@@ -253,7 +278,7 @@ export function selectFailMessage(state: State) {
}
export function selectIsEnabled(state: State) {
return state.matches('available') || state.matches({ failure: 'unenrolled' });
return state.matches('available') || state.matches({failure: 'unenrolled'});
}
export function selectIsAvailable(state: State) {
@@ -261,11 +286,11 @@ export function selectIsAvailable(state: State) {
}
export function selectIsUnvailable(state: State) {
return state.matches({ failure: 'unavailable' });
return state.matches({failure: 'unavailable'});
}
export function selectIsUnenrolled(state: State) {
return state.matches({ failure: 'unenrolled' });
return state.matches({failure: 'unenrolled'});
}
export function selectIsSuccess(state: State) {
@@ -273,11 +298,15 @@ export function selectIsSuccess(state: State) {
}
export function selectError(state: State) {
return state.matches({ failure: 'error' }) ? selectFailMessage(state) : null;
return state.matches({failure: 'error'}) ? selectFailMessage(state) : null;
}
export function selectUnenrolledNotice(state: State) {
return state.matches({ failure: 'unenrolled' }) && state.context.retry
return state.matches({failure: 'unenrolled'}) && state.context.retry
? selectFailMessage(state)
: null;
}
export function selectErrorResponse(state: State) {
return state.context.error;
}

View File

@@ -14,7 +14,7 @@ import {getDeviceNameSync} from 'react-native-device-info';
import {StoreEvents} from '../../store';
import {VC} from '../../../types/VC/ExistingMosipVC/vc';
import {AppServices} from '../../../shared/GlobalContext';
import {RECEIVED_VCS_STORE_KEY} from '../../../shared/constants';
import {isAndroid, RECEIVED_VCS_STORE_KEY} from '../../../shared/constants';
import {ActivityLogEvents, ActivityLogType} from '../../activityLog';
import {VcEvents} from '../../vc';
import {subscribe} from '../../../shared/openIdBLE/verifierEventHandler';
@@ -736,7 +736,7 @@ export const requestMachine =
},
checkNearByDevicesPermission: () => callback => {
if (Platform.OS === 'android' && Platform.Version >= 31) {
if (isAndroid() && Platform.Version >= 31) {
const result = checkMultiple([
PERMISSIONS.ANDROID.BLUETOOTH_ADVERTISE,
PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,

View File

@@ -17,7 +17,7 @@ import {getDeviceNameSync} from 'react-native-device-info';
import {VC, VerifiablePresentation} from '../../../types/VC/ExistingMosipVC/vc';
import {AppServices} from '../../../shared/GlobalContext';
import {ActivityLogEvents, ActivityLogType} from '../../activityLog';
import {MY_LOGIN_STORE_KEY} from '../../../shared/constants';
import {isAndroid, isIOS, MY_LOGIN_STORE_KEY} from '../../../shared/constants';
import {subscribe} from '../../../shared/openIdBLE/walletEventHandler';
import {
check,
@@ -38,14 +38,14 @@ import {StoreEvents} from '../../store';
import {WalletDataEvent} from 'react-native-tuvali/lib/typescript/types/events';
import {BLEError} from '../types';
import Storage from '../../../shared/storage';
import {logState} from '../../app';
import {VCMetadata} from '../../../shared/VCMetadata';
import {
getData,
getEndData,
getStartEventData,
getEndEventData,
sendStartEvent,
sendEndEvent,
} from '../../../shared/telemetry/TelemetryUtils';
import {logState} from '../../../shared/commonUtil';
const {wallet, EventTypes, VerificationStatus} = tuvali;
@@ -432,7 +432,10 @@ export const scanMachine =
},
navigatingToHistory: {},
},
entry: ['sendScanData', () => sendStartEvent(getData('QR login'))],
entry: [
'sendScanData',
() => sendStartEvent(getStartEventData('QR login')),
],
},
connecting: {
invoke: {
@@ -567,7 +570,10 @@ export const scanMachine =
},
},
accepted: {
entry: ['logShared', () => sendEndEvent(getEndData('VC share'))],
entry: [
'logShared',
() => sendEndEvent(getEndEventData('VC share', 'SUCCESS')),
],
on: {
DISMISS: {
target: 'navigatingToHome',
@@ -715,7 +721,7 @@ export const scanMachine =
value: context.linkCode,
}),
openBluetoothSettings: () => {
Platform.OS === 'android'
isAndroid()
? BluetoothStateManager.openSettings().catch()
: Linking.openURL('App-Prefs:Bluetooth');
},
@@ -899,7 +905,7 @@ export const scanMachine =
// Passing Granted for android since permission status is always granted even if its denied.
let response: PermissionStatus = RESULTS.GRANTED;
if (Platform.OS === 'ios') {
if (isIOS()) {
response = await check(PERMISSIONS.IOS.BLUETOOTH_PERIPHERAL);
}
@@ -1006,7 +1012,7 @@ export const scanMachine =
},
startConnection: context => callback => {
sendStartEvent(getData('VC share'));
sendStartEvent(getStartEventData('VC share'));
wallet.startConnection(context.openId4VpUri);
const statusCallback = (event: WalletDataEvent) => {
if (event.type === EventTypes.onSecureChannelEstablished) {
@@ -1101,9 +1107,9 @@ export const scanMachine =
}
},
uptoAndroid11: () => Platform.OS === 'android' && Platform.Version < 31,
uptoAndroid11: () => isAndroid() && Platform.Version < 31,
isIOS: () => Platform.OS === 'ios',
isIOS: () => isIOS(),
isMinimumStorageRequiredForAuditEntryReached: (_context, event) =>
Boolean(event.data),

View File

@@ -1,10 +1,10 @@
import { Camera, CameraCapturedPicture, PermissionResponse } from 'expo-camera';
import { CameraType, Face, ImageType } from 'expo-camera/build/Camera.types';
import { Linking } from 'react-native';
import { assign, EventFrom, StateFrom } from 'xstate';
import { createModel } from 'xstate/lib/model';
import {Camera, CameraCapturedPicture, PermissionResponse} from 'expo-camera';
import {CameraType, Face, ImageType} from 'expo-camera/build/Camera.types';
import {Linking} from 'react-native';
import {assign, EventFrom, StateFrom} from 'xstate';
import {createModel} from 'xstate/lib/model';
import mosipFaceAuth from '../lib/mosip-inji-face-sdk/faceAuth';
import {faceAuth} from 'mosip-mobileid-sdk';
const model = createModel(
{
@@ -19,15 +19,15 @@ const model = createModel(
},
{
events: {
READY: (cameraRef: Camera) => ({ cameraRef }),
READY: (cameraRef: Camera) => ({cameraRef}),
FLIP_CAMERA: () => ({}),
CAPTURE: () => ({}),
DENIED: (response: PermissionResponse) => ({ response }),
DENIED: (response: PermissionResponse) => ({response}),
GRANTED: () => ({}),
OPEN_SETTINGS: () => ({}),
APP_FOCUSED: () => ({}),
},
}
},
);
export const FaceScannerEvents = model.events;
@@ -176,7 +176,7 @@ export const createFaceScannerMachine = (vcImage: string) =>
}),
flipWhichCamera: model.assign({
whichCamera: (context) =>
whichCamera: context =>
context.whichCamera === Camera.Constants.Type.front
? Camera.Constants.Type.back
: Camera.Constants.Type.front,
@@ -190,7 +190,7 @@ export const createFaceScannerMachine = (vcImage: string) =>
},
services: {
checkPermission: () => async (callback) => {
checkPermission: () => async callback => {
const result = await Camera.getCameraPermissionsAsync();
if (result.granted) {
callback(FaceScannerEvents.GRANTED());
@@ -199,7 +199,7 @@ export const createFaceScannerMachine = (vcImage: string) =>
}
},
requestPermission: () => async (callback) => {
requestPermission: () => async callback => {
const result = await Camera.requestCameraPermissionsAsync();
if (result.granted) {
callback(FaceScannerEvents.GRANTED());
@@ -208,19 +208,19 @@ export const createFaceScannerMachine = (vcImage: string) =>
}
},
captureImage: (context) => {
captureImage: context => {
return context.cameraRef.takePictureAsync({
base64: true,
imageType: ImageType.jpg,
});
},
verifyImage: (context) => {
verifyImage: context => {
context.cameraRef.pausePreview();
const rxDataURI =
/data:(?<mime>[\w/\-.]+);(?<encoding>\w+),(?<data>.*)/;
const matches = rxDataURI.exec(vcImage).groups;
return mosipFaceAuth(context.capturedImage.base64, matches.data);
return faceAuth(context.capturedImage.base64, matches.data);
},
},
@@ -231,7 +231,7 @@ export const createFaceScannerMachine = (vcImage: string) =>
doesFaceMatch: (_context, event) => event.data,
},
}
},
);
type State = StateFrom<ReturnType<typeof createFaceScannerMachine>>;

View File

@@ -1,13 +1,17 @@
import {authorize, AuthorizeResult} from 'react-native-app-auth';
import {assign, EventFrom, send, sendParent, StateFrom} from 'xstate';
import {createModel} from 'xstate/lib/model';
import {MY_VCS_STORE_KEY} from '../shared/constants';
import {
MY_VCS_STORE_KEY,
NETWORK_REQUEST_FAILED,
REQUEST_TIMEOUT,
} from '../shared/constants';
import {StoreEvents} from './store';
import {AppServices} from '../shared/GlobalContext';
import NetInfo from '@react-native-community/netinfo';
import {
generateKeys,
isCustomSecureKeystore,
isHardwareKeystoreExists,
} from '../shared/cryptoutil/cryptoUtil';
import SecureKeystore from 'react-native-secure-keystore';
import {KeyPair} from 'react-native-rsa-native';
@@ -20,9 +24,18 @@ import {
vcDownloadTimeout,
OIDCErrors,
ErrorMessage,
updateCredentialInformation,
constructAuthorizationConfiguration,
getVCMetadata,
Issuers_Key_Ref,
} from '../shared/openId4VCI/Utils';
import {NETWORK_REQUEST_FAILED, REQUEST_TIMEOUT} from '../shared/constants';
import {VCMetadata} from '../shared/VCMetadata';
import {
getEndEventData,
getImpressionEventData,
sendEndEvent,
sendImpressionEvent,
} from '../shared/telemetry/TelemetryUtils';
import {
CredentialWrapper,
VerifiableCredential,
@@ -62,7 +75,6 @@ const model = createModel(
export const IssuerScreenTabEvents = model.events;
export const Issuer_Tab_Ref_Id = 'issuersMachine';
export const Issuers_Key_Ref = 'OpenId4VCI';
export const IssuersMachine = model.createMachine(
{
/** @xstate-layout N4IgpgJg5mDOIC5QEtawK5gE6wLIEMBjAC2QDswA6CVABwBt8BPASTUxwGIIB7Cy8gDceAayqoM2PEVL8asBszaScCIT0L4ALsj4BtAAwBdQ0cShaPWMh19zIAB6IALAEYAnJWfuAzM4BsAKwAHN7BwQDsfgA0IEyIAEw+Ea6UBgkRwT7uEekGrhHOAL5FsRIc0iTkVPKKrOxSnNhYPFiUiloAZq0AtgINOARVcnSM9SqwamTCmrZkpqb2ltZz9k4Izgme3n5Boe7hUc6x8QgJ3gZpgT6BgRH+yYWBJWUDlbJUza2cACoASgBNAD6AEEAOIglgAOUWSBAyxsujIaxcWy8vgCITCkRicUSIQSlGyCQMETu7n84QKLxA5SkQw+lC+WF+gNBEOhelcZjhCNWcPWrgSJ0QQppdMGMmqTKwLRZ-2B4MhMISPIsVkRdgFooMvjS-jc9wezl1ARFZwiCUClFcN3czmcKWuPnFbwZ0uZnD+AFEAMren5A71-P4AeT+sPVKyRKIQrgMwX8lASCWCgVcgW8lt15oyEU8CSCloz6WcN38rom7v4sDA9DAhB0ZCgyg4nAAIqGAOpQgAyoZB7aBLHbkfhGv5oHWPgMBmclEigQM-lTVpyc9z+R8yfzriyPhTS4zlYq1aotfrjfILYGnH9ve9AGEft6hyxfb6AKrBsd8mPahBAmFPEEB8VwhS8XVkgKA5dXcE96SlOQeAAdzIegeHwCBW2wR8+E6ZAoG4PhxGmURxDdJCalQ9DMOwgY8LIAioCmGZtCRBZjCWCd-ynUVgNOMVSlpSjhmotCMKwnCsEY5imllVp2kYLpen6KsqOoGjJPolRZMI1iNHY-RjF-HitT4jYUwXZwgMpFMfHCGcIlze15zuFdghyA5UweBDJTEygSAbEQWDILRsAoLRiP4dQxEC4hgtC8KsEi0zo3MxxRVuQlLUCdxdUzbIhX8c14w8Iky1ucI1zTYI-PeaUgsIEKwoisAot4GKyLipqWuS1LuW49LkQA4I0SFdxwOXO1-HSUrbUuLZSX8XwMn8Vwy3qs94sS1qUva+S5SU7RuiwPpeqStqtDSzURos4IIMpB5i1moCHPNSbgi8QIDXcPLcltDIto0i69simU5QSVlFQ5GEuN5My7syuNkiTfJJvyDwiyCUrZ2tPwbimoDMhuYGAtobBTp6EF0C0YhWmQAAvIyyGi0jhDiiUGv4CmsCpmm6YZ5m5gM2YOJM+Go1u2NbJtFbJoyN7U3cc0V2tbxXH8FdHSPe0ycZXn+dp+msCZlnDsUjoqbU08NMN3oBZNs2RfUMXjJMSXx2GmXNbl9wFbJJJlfNB6bUiLYbiW-KIgifXpXts7HaF83mWOlSzptxDycph3jeTl2yLd+YTMGhHvYA64k01-2hUDg9PNK5J52CAwZ3TQpwNj4Sue23qAGkwCYAAFfBkBZR8AAknz7oE++9YEh8hCNPb-DLBTnKuPD+h15cKc0qptDaMcibxnm70TGX7weR7HzhJ+n2f56BReWAjUupcnZH4x8Tf-czAJ-Z7xAskS4YFiQeXOJ5F0591IBRgBQLA2gwAD2HqPFknV2bkUzv5Rk8DsBIJQTfLAosWacTVF7aWAECjZEoPlTy6Z1o5HOOaPwqRqoOQyGNTMKY478DwYg8KhC0FswEN1CisDcFgAQQQ6+aCSFzAWO-Chn9BRRE8HQv6msPCWmOCBFMhI0xBH8DHMkOQyS8PErRLCj4sCQCkTofA9BYAiNiuI22AVeASTojYuxYVkCOMmK7UhEtyGryRusGyER9SWgpIVCkYFAi5j3EmG4CZW4OSbsUGB7jGSeKsRAHxEB7H+KcRbNoVtVI9w0nk7ShTikBPkeLD2oTEaxkidErYQQyzxIzLmThlBKSrQzJsKkFjNJeOsbYopfiAl3xBFCR83pew3RUS4R0HTYndIeL0kCe4ogVSyB4ByMcNpjMENgZAnQmDXjqTM+gLixHYO5lQc5psrk3KmfU+gjT3YrN4l-eMnguF3HOAkDMDlEl6ITFXYxDosjnCONA14EjpSvMudc5styHH3NThUjOVSApoveZiz5dyfnF2aUNShFkppAqtCCzY4K0x9IMNafMDCDgPBJAeMZyAID1jvqGXAQ8HwvlHCvVpVC1ZeG5VsVMUQ9l9JNAM5wY0W6pnjJEXl-KwBzIWUsv5a9RTSs2K3OVOJFV6NtKkG4eVUytx+jymkZAeBFPgHCAlHwqWrIQAAWh-uaX1ZIw6WnuJVKIK0EhjNqGMaS7qP7-PWJaXMP0okmhTPmGOFJ4wVmyVnRkzJvWJsQC3IFOQNqJkKPmG4uZMiXF1JSTYhZ0i7DGReBsTYbwqCLUauMu4iRUh+pmIUG0km2gGQURMqNNibTzTg6UNS6LST0lAHt4TEDGJcoWWhI6xreACGSXNyKcmNQSs1S6+0tBrtjGGgZU6XqsqSMEc0ZJtwFRNAUHeGYz7Hvzae3a-V2oQ1aAka9VCwVROhS3PKfhlwFA+mNZMjxcgORskkI9IkUU8xzonPOpthb-LCW0gSoofB+C8GNFaLd4zLhjmMq+qCx5gYsoehcMcbi7g2imVW5H0yqrGjcPwlo6pzueZQfhMjGNYGY1-PwhISQFGeoJlILCwJeHWhmBVFahK-vnchCZBTSXYvjco4tCAch9LU7Va48ZHSUjORc4lUAsUlJk+sQZyYqrrgNFrZ9VrKR3uMR5TWZGf5tq0AzZsbnSO+0NNkNwbhHKQsEv7bcar8pgW8FsUI2r6zRZRhmNIMdVy5Dyuqyz84AhPXsqqlI0aSL5ZgjawdXSR26NOCmX2WxHS6k-Xxs+JQgA */
@@ -82,7 +94,11 @@ export const IssuersMachine = model.createMachine(
invoke: {
src: 'downloadIssuersList',
onDone: {
actions: ['setIssuers', 'resetLoadingReason'],
actions: [
'sendImpressionEvent',
'setIssuers',
'resetLoadingReason',
],
target: 'selectingIssuer',
},
onError: {
@@ -109,7 +125,7 @@ export const IssuersMachine = model.createMachine(
},
{
description: 'not fetched issuers config yet',
actions: ['setLoadingReasonAsDisplayIssuers', 'resetError'],
actions: ['setLoadingReasonAsSettingUp', 'resetError'],
target: 'downloadIssuerConfig',
},
],
@@ -126,7 +142,7 @@ export const IssuersMachine = model.createMachine(
actions: sendParent('DOWNLOAD_ID'),
},
SELECTED_ISSUER: {
actions: 'setSelectedIssuerId',
actions: ['setSelectedIssuerId', 'setLoadingReasonAsSettingUp'],
target: 'downloadIssuerConfig',
},
},
@@ -161,7 +177,8 @@ export const IssuersMachine = model.createMachine(
},
],
onError: {
actions: () => console.log('checkInternet error caught'),
actions: () =>
console.log('Error Occurred while checking Internet'),
target: 'error',
},
},
@@ -195,7 +212,11 @@ export const IssuersMachine = model.createMachine(
actions: [
'setError',
'resetLoadingReason',
(_, event) => console.log('error in invokeAuth - ', event.data),
(_, event) =>
console.log(
'Error Occurred while invoking Auth - ',
event.data,
),
],
target: 'error',
},
@@ -204,7 +225,7 @@ export const IssuersMachine = model.createMachine(
},
checkKeyPair: {
description: 'checks whether key pair is generated',
entry: [send('CHECK_KEY_PAIR')],
entry: ['setLoadingReasonAsSettingUp', send('CHECK_KEY_PAIR')],
on: {
CHECK_KEY_PAIR: [
{
@@ -272,12 +293,16 @@ export const IssuersMachine = model.createMachine(
src: 'verifyCredential',
onDone: [
{
actions: ['sendSuccessEndEvent'],
target: 'storing',
},
],
onError: [
{
actions: log((_, event) => (event.data as Error).message),
actions: [
log((_, event) => (event.data as Error).message),
'sendErrorEndEvent',
],
//TODO: Move to state according to the required flow when verification of VC fails
target: 'idle',
},
@@ -435,7 +460,7 @@ export const IssuersMachine = model.createMachine(
}),
setPublicKey: assign({
publicKey: (_, event) => {
if (!isCustomSecureKeystore()) {
if (!isHardwareKeystoreExists) {
return (event.data as KeyPair).public;
}
return event.data as string;
@@ -460,6 +485,17 @@ export const IssuersMachine = model.createMachine(
to: context => context.serviceRefs.activityLog,
},
),
sendSuccessEndEvent: () => {
sendEndEvent(getEndEventData('VC Download', 'SUCCESS'));
},
sendErrorEndEvent: () => {
sendEndEvent(getEndEventData('VC Download', 'FAILURE'));
},
sendImpressionEvent: () => {
sendImpressionEvent(
getImpressionEventData('VC Download', 'Issuer List'),
);
},
},
services: {
downloadIssuersList: async () => {
@@ -474,7 +510,7 @@ export const IssuersMachine = model.createMachine(
const downloadTimeout = await vcDownloadTimeout();
let credential = await request(
'POST',
context.selectedIssuer.serviceConfiguration.credentialEndpoint,
context.selectedIssuer.credential_endpoint,
body,
'',
{
@@ -490,10 +526,18 @@ export const IssuersMachine = model.createMachine(
return credential;
},
invokeAuthorization: async context => {
return await authorize(context.selectedIssuer);
sendImpressionEvent(
getImpressionEventData(
'VC Download',
context.selectedIssuer.credential_issuer + ' Web View Page',
),
);
return await authorize(
constructAuthorizationConfiguration(context.selectedIssuer),
);
},
generateKeyPair: async context => {
if (!isCustomSecureKeystore()) {
generateKeyPair: async () => {
if (!isHardwareKeystoreExists) {
return await generateKeys();
}
const isBiometricsEnabled = SecureKeystore.hasBiometricsEnabled();
@@ -538,7 +582,7 @@ export const IssuersMachine = model.createMachine(
);
},
shouldFetchIssuersAgain: context => context.issuers.length === 0,
isCustomSecureKeystore: () => isCustomSecureKeystore(),
isCustomSecureKeystore: () => isHardwareKeystoreExists,
},
},
);
@@ -576,32 +620,27 @@ export function selectStoring(state: State) {
return state.matches('storing');
}
interface issuerType {
id: string;
displayName: string;
logoUrl: string;
export interface logoType {
url: string;
alt_text: string;
}
const updateCredentialInformation = (context, credential) => {
let credentialWrapper: CredentialWrapper = {};
credentialWrapper.verifiableCredential = credential;
credentialWrapper.verifiableCredential.issuerLogo =
context.selectedIssuer.logoUrl;
credentialWrapper.identifier = getIdentifier(context, credential);
credentialWrapper.generatedOn = new Date();
credentialWrapper.issuerLogo = context.selectedIssuer.logoUrl;
return credentialWrapper;
};
const getVCMetadata = context => {
const [issuer, protocol, requestId] =
context.credentialWrapper?.identifier.split(':');
return VCMetadata.fromVC({
requestId: requestId ? requestId : null,
issuer: issuer,
protocol: protocol,
id: context.verifiableCredential?.credential.credentialSubject.UIN
? context.verifiableCredential?.credential.credentialSubject.UIN
: context.verifiableCredential?.credential.credentialSubject.VID,
});
};
export interface displayType {
name: string;
logo: logoType;
language: string;
}
export interface issuerType {
credential_issuer: string;
protocol: string;
client_id: string;
'.well-known': string;
redirect_uri: string;
scopes_supported: [string];
additional_headers: object;
authorization_endpoint: string;
token_endpoint: string;
credential_endpoint: string;
credential_audience: string;
display: [displayType];
}

View File

@@ -58,6 +58,10 @@ export interface Typegen0 {
type: 'error.platform.issuersMachine.performAuthorization:invocation[0]';
data: unknown;
};
'error.platform.issuersMachine.verifyingCredential:invocation[0]': {
type: 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
data: unknown;
};
'xstate.init': {type: 'xstate.init'};
};
invokeSrcNameMap: {
@@ -89,6 +93,9 @@ export interface Typegen0 {
| 'error.platform.issuersMachine.downloadCredentials:invocation[0]'
| 'error.platform.issuersMachine.downloadIssuerConfig:invocation[0]'
| 'error.platform.issuersMachine.performAuthorization:invocation[0]';
sendErrorEndEvent: 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
sendImpressionEvent: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
sendSuccessEndEvent: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
setCredentialWrapper: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]';
setError:
| 'error.platform.issuersMachine.displayIssuers:invocation[0]'
@@ -98,7 +105,10 @@ export interface Typegen0 {
setIssuers: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
setLoadingReasonAsDisplayIssuers: 'TRY_AGAIN';
setLoadingReasonAsDownloadingCredentials: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
setLoadingReasonAsSettingUp: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
setLoadingReasonAsSettingUp:
| 'SELECTED_ISSUER'
| 'TRY_AGAIN'
| 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
setNoInternet: 'done.invoke.checkInternet';
setOIDCConfigError: 'error.platform.issuersMachine.performAuthorization:invocation[0]';
setPrivateKey: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';

View File

@@ -1,5 +1,5 @@
import {TextInput} from 'react-native';
import {assign, ErrorPlatformEvent, StateFrom, send, EventFrom} from 'xstate';
import {assign, ErrorPlatformEvent, EventFrom, send, StateFrom} from 'xstate';
import {log} from 'xstate/lib/actions';
import i18n from '../i18n';
@@ -11,6 +11,7 @@ import {request} from '../shared/request';
import {VcIdType} from '../types/VC/ExistingMosipVC/vc';
import {MY_VCS_STORE_KEY} from '../shared/constants';
import {VCMetadata} from '../shared/VCMetadata';
import {API_URLS} from '../shared/api';
const model = createModel(
{
@@ -236,12 +237,16 @@ export const revokeVidsMachine =
services: {
requestOtp: async context => {
const transactionId = String(new Date().valueOf()).substring(3, 13);
return request('POST', '/residentmobileapp/req/otp', {
individualId: context.VIDsMetadata[0].id,
individualIdType: 'VID',
otpChannel: ['EMAIL', 'PHONE'],
transactionID: transactionId,
});
return request(
API_URLS.requestOtp.method,
API_URLS.requestOtp.buildURL(),
{
individualId: context.VIDsMetadata[0].id,
individualIdType: 'VID',
otpChannel: ['EMAIL', 'PHONE'],
transactionID: transactionId,
},
);
},
requestRevoke: context => async callback => {
@@ -253,8 +258,8 @@ export const revokeVidsMachine =
13,
);
return request(
'PATCH',
`/residentmobileapp/vid/${metadata.id}`,
API_URLS.requestRevoke.method,
API_URLS.requestRevoke.buildURL(metadata.id),
{
transactionID: transactionId,
vidStatus: 'REVOKED',

View File

@@ -2,7 +2,7 @@
export interface Typegen0 {
'@@xstate/typegen': true;
'internalEvents': {
internalEvents: {
'done.invoke.RevokeVids.acceptingVIDs.requestingOtp:invocation[0]': {
type: 'done.invoke.RevokeVids.acceptingVIDs.requestingOtp:invocation[0]';
data: unknown;
@@ -12,19 +12,19 @@ export interface Typegen0 {
type: 'error.platform.RevokeVids.acceptingVIDs.requestingOtp:invocation[0]';
data: unknown;
};
'xstate.init': { type: 'xstate.init' };
'xstate.init': {type: 'xstate.init'};
};
'invokeSrcNameMap': {
invokeSrcNameMap: {
requestOtp: 'done.invoke.RevokeVids.acceptingVIDs.requestingOtp:invocation[0]';
requestRevoke: 'done.invoke.RevokeVids.requestingRevoke:invocation[0]';
};
'missingImplementations': {
missingImplementations: {
actions: never;
delays: never;
guards: never;
services: never;
};
'eventsCausingActions': {
eventsCausingActions: {
clearOtp:
| 'DISMISS'
| 'ERROR'
@@ -39,13 +39,13 @@ export interface Typegen0 {
setTransactionId: 'DISMISS' | 'REVOKE_VCS' | 'xstate.init';
setVIDs: 'REVOKE_VCS';
};
'eventsCausingDelays': {};
'eventsCausingGuards': {};
'eventsCausingServices': {
eventsCausingDelays: {};
eventsCausingGuards: {};
eventsCausingServices: {
requestOtp: never;
requestRevoke: 'INPUT_OTP';
};
'matchesStates':
matchesStates:
| 'acceptingOtpInput'
| 'acceptingVIDs'
| 'acceptingVIDs.idle'
@@ -57,6 +57,6 @@ export interface Typegen0 {
| 'loggingRevoke'
| 'requestingRevoke'
| 'revokingVc'
| { acceptingVIDs?: 'idle' | 'requestingOtp'; invalid?: 'backend' | 'otp' };
'tags': never;
| {acceptingVIDs?: 'idle' | 'requestingOtp'; invalid?: 'backend' | 'otp'};
tags: never;
}

View File

@@ -17,7 +17,7 @@ import getAllConfigurations, {
import Storage from '../shared/storage';
import ShortUniqueId from 'short-unique-id';
import {__AppId} from '../shared/GlobalVariables';
import {isCustomSecureKeystore} from '../shared/cryptoutil/cryptoUtil';
import {isHardwareKeystoreExists} from '../shared/cryptoutil/cryptoUtil';
const model = createModel(
{
@@ -93,7 +93,6 @@ export const settingsMachine = model.createMachine(
},
},
idle: {
entry: ['injiTourGuide'],
on: {
TOGGLE_BIOMETRIC_UNLOCK: {
actions: ['toggleBiometricUnlock', 'storeContext'],
@@ -114,6 +113,9 @@ export const settingsMachine = model.createMachine(
CANCEL: {
actions: ['resetCredentialRegistry'],
},
INJI_TOUR_GUIDE: {
target: 'showInjiTourGuide',
},
ACCEPT_HARDWARE_SUPPORT_NOT_EXISTS: {
actions: [
'updateUserShownWithHardwareKeystoreNotExists',
@@ -146,13 +148,6 @@ export const settingsMachine = model.createMachine(
},
},
},
injiTourGuide: {
on: {
INJI_TOUR_GUIDE: {
target: 'showInjiTourGuide',
},
},
},
showInjiTourGuide: {
on: {
BACK: {
@@ -274,7 +269,7 @@ function generateAppId() {
}
function deviceSupportsHardwareKeystore() {
return isIOS() ? true : isCustomSecureKeystore();
return isIOS() ? true : isHardwareKeystoreExists;
}
type State = StateFrom<typeof settingsMachine>;

View File

@@ -16,15 +16,15 @@ import {MY_VCS_STORE_KEY} from '../shared/constants';
import SecureKeystore from 'react-native-secure-keystore';
import {
AUTH_TIMEOUT,
clear,
decryptJson,
DUMMY_KEY_FOR_BIOMETRIC_ALIAS,
ENCRYPTION_ID,
encryptJson,
HMAC_ALIAS,
isCustomSecureKeystore,
isHardwareKeystoreExists,
} from '../shared/cryptoutil/cryptoUtil';
import {VCMetadata} from '../shared/VCMetadata';
import FileStorage, {getFilePath} from '../shared/fileStorage';
export const keyinvalidatedString =
'Key Invalidated due to biometric enrollment';
@@ -81,7 +81,7 @@ export const storeMachine =
events: {} as EventFrom<typeof model>,
},
id: 'store',
initial: !isCustomSecureKeystore()
initial: !isHardwareKeystoreExists
? 'gettingEncryptionKey'
: 'checkEncryptionKey',
states: {
@@ -104,7 +104,7 @@ export const storeMachine =
},
on: {
KEY_RECEIVED: {
actions: ['setEncryptionKey', 'logKey'],
actions: ['setEncryptionKey'],
target: 'ready',
},
ERROR: {
@@ -173,7 +173,7 @@ export const storeMachine =
},
},
ready: {
entry: 'notifyParent',
entry: ['notifyParent', 'cacheVCFilesData'],
invoke: {
src: 'store',
id: '_store',
@@ -259,10 +259,22 @@ export const storeMachine =
setEncryptionKey: model.assign({
encryptionKey: (_, event) => event.key,
}),
cacheVCFilesData: context => {
getItem(MY_VCS_STORE_KEY, [], context.encryptionKey).then(vcList => {
if (vcList) {
vcList?.forEach((vcMetadataStr: string) => {
const vcKey =
VCMetadata.fromVcMetadataString(vcMetadataStr).getVcKey();
FileStorage.readAndCacheFile(getFilePath(vcKey));
});
}
});
},
},
services: {
clear,
clear: () => clear(),
hasAndroidEncryptionKey: () => async callback => {
const hasSetCredentials = SecureKeystore.hasAlias(ENCRYPTION_ID);
if (hasSetCredentials) {
@@ -289,6 +301,7 @@ export const storeMachine =
);
}
},
checkStorageInitialisedOrNot: () => async callback => {
const isDirectoryExist = await Storage.isVCStorageInitialised();
if (!isDirectoryExist) {
@@ -427,7 +440,7 @@ export const storeMachine =
generateEncryptionKey: () => async callback => {
const randomBytes = await generateSecureRandom(32);
const randomBytesString = binaryToBase64(randomBytes);
if (!isCustomSecureKeystore()) {
if (!isHardwareKeystoreExists) {
const hasSetCredentials = await Keychain.setGenericPassword(
ENCRYPTION_ID,
randomBytesString,
@@ -461,7 +474,7 @@ export const storeMachine =
},
guards: {
isCustomSecureKeystore: () => isCustomSecureKeystore(),
isCustomSecureKeystore: () => isHardwareKeystoreExists,
},
},
);
@@ -616,6 +629,18 @@ export async function removeVCMetaData(
}
}
export async function clear() {
try {
console.log('clearing entire storage');
if (isHardwareKeystoreExists) {
SecureKeystore.clearKeys();
}
await Storage.clear();
} catch (e) {
console.error('error clear:', e);
throw e;
}
}
export async function removeItems(
key: string,
values: string[],

View File

@@ -1,54 +1,5 @@
// This file was automatically generated. Edits will be overwritten
<<<<<<< HEAD
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
"done.invoke._store": { type: "done.invoke._store"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.store.resettingStorage:invocation[0]": { type: "done.invoke.store.resettingStorage:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"error.platform._store": { type: "error.platform._store"; data: unknown };
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
"checkStorageInitialisedOrNot": "done.invoke.store.checkStorageInitialisation:invocation[0]";
"clear": "done.invoke.store.resettingStorage:invocation[0]";
"generateEncryptionKey": "done.invoke.store.generatingEncryptionKey:invocation[0]";
"getEncryptionKey": "done.invoke.store.gettingEncryptionKey:invocation[0]";
"hasAndroidEncryptionKey": "done.invoke.store.checkEncryptionKey:invocation[0]";
"store": "done.invoke._store";
};
missingImplementations: {
actions: "logKey";
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
"forwardStoreRequest": "APPEND" | "CLEAR" | "GET" | "PREPEND" | "REMOVE" | "REMOVE_ITEMS" | "REMOVE_VC_METADATA" | "SET" | "UPDATE";
"logKey": "KEY_RECEIVED";
"notifyParent": "KEY_RECEIVED" | "READY" | "done.invoke.store.resettingStorage:invocation[0]";
"setEncryptionKey": "KEY_RECEIVED";
};
eventsCausingDelays: {
};
eventsCausingGuards: {
"isCustomSecureKeystore": "KEY_RECEIVED";
};
eventsCausingServices: {
"checkStorageInitialisedOrNot": "ERROR";
"clear": "KEY_RECEIVED";
"generateEncryptionKey": "ERROR" | "IGNORE" | "READY";
"getEncryptionKey": "TRY_AGAIN";
"hasAndroidEncryptionKey": never;
"store": "KEY_RECEIVED" | "READY" | "done.invoke.store.resettingStorage:invocation[0]";
};
matchesStates: "checkEncryptionKey" | "checkStorageInitialisation" | "failedReadingKey" | "generatingEncryptionKey" | "gettingEncryptionKey" | "ready" | "resettingStorage";
tags: never;
}
=======
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
@@ -74,12 +25,16 @@ export interface Typegen0 {
store: 'done.invoke._store';
};
missingImplementations: {
actions: 'logKey';
actions: never;
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
cacheVCFilesData:
| 'KEY_RECEIVED'
| 'READY'
| 'done.invoke.store.resettingStorage:invocation[0]';
forwardStoreRequest:
| 'APPEND'
| 'CLEAR'
@@ -90,14 +45,11 @@ export interface Typegen0 {
| 'REMOVE_VC_METADATA'
| 'SET'
| 'UPDATE';
logKey: 'KEY_RECEIVED';
notifyParent:
| 'KEY_RECEIVED'
| 'READY'
| 'done.invoke.store.resettingStorage:invocation[0]';
resetIsTamperedVc: 'RESET_IS_TAMPERED';
setEncryptionKey: 'KEY_RECEIVED';
setIsTamperedVc: 'TAMPERED_VC';
};
eventsCausingDelays: {};
eventsCausingGuards: {
@@ -124,4 +76,3 @@ export interface Typegen0 {
| 'resettingStorage';
tags: never;
}
>>>>>>> 5b53b069 (refactor(INJI-205): Check Internet before OIDC AuthZ)

View File

@@ -7,7 +7,7 @@ import {log, respond} from 'xstate/lib/actions';
import {ExistingMosipVCItemEvents} from './VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
import {MY_VCS_STORE_KEY, RECEIVED_VCS_STORE_KEY} from '../shared/constants';
import {parseMetadatas, VCMetadata} from '../shared/VCMetadata';
import {OpenId4VCIProtocol} from '../shared/openId4VCI/Utils';
import {Protocols} from '../shared/openId4VCI/Utils';
import {EsignetMosipVCItemEvents} from './VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
import {ActivityLogEvents} from './activityLog';
import {ActivityLog} from '../components/ActivityLogEvent';
@@ -22,6 +22,7 @@ const model = createModel(
areAllVcsDownloaded: false as boolean,
walletBindingSuccess: false,
tamperedVcs: [] as VCMetadata[],
downloadingFailedVcs: [] as VCMetadata[],
},
{
events: {
@@ -52,6 +53,8 @@ const model = createModel(
RESET_ARE_ALL_VCS_DOWNLOADED: () => ({}),
TAMPERED_VC: (VC: VCMetadata) => ({VC}),
REMOVE_TAMPERED_VCS: () => ({}),
DOWNLOAD_LIMIT_EXPIRED: (vcMetadata: VCMetadata) => ({vcMetadata}),
DELETE_VC: () => ({}),
},
},
);
@@ -201,6 +204,13 @@ export const vcMachine =
actions: 'setTamperedVcs',
target: 'tamperedVCs',
},
DOWNLOAD_LIMIT_EXPIRED: {
actions: [
'removeVcFromInProgressDownlods',
'setDownloadingFailedVcs',
],
target: 'downloadLimitExpired',
},
},
},
tamperedVCs: {
@@ -211,6 +221,25 @@ export const vcMachine =
},
},
},
downloadLimitExpired: {
on: {
DELETE_VC: {
target: 'deletingFailedVcs',
},
},
},
deletingFailedVcs: {
entry: 'removeDownloadFailedVcsFromStorage',
on: {
STORE_RESPONSE: {
actions: [
'removeDownloadingFailedVcsFromMyVcs',
'resetDownloadFailedVcs',
],
target: '#vc.ready.myVcs.refreshing',
},
},
},
},
},
{
@@ -222,7 +251,7 @@ export const vcMachine =
getVcItemResponse: respond((context, event) => {
const vc = context.vcs[event.vcMetadata?.getVcKey()];
if (event.protocol === OpenId4VCIProtocol) {
if (event.protocol === Protocols.OpenId4VCI) {
return EsignetMosipVCItemEvents.GET_VC_RESPONSE(vc);
}
return ExistingMosipVCItemEvents.GET_VC_RESPONSE(vc);
@@ -252,6 +281,17 @@ export const vcMachine =
tamperedVcs: (context, event) => [event.VC, ...context.tamperedVcs],
}),
setDownloadingFailedVcs: model.assign({
downloadingFailedVcs: (context, event) => [
event.vcMetadata,
...context.downloadingFailedVcs,
],
}),
resetDownloadFailedVcs: model.assign({
downloadingFailedVcs: (context, event) => [],
}),
setDownloadedVc: (context, event) => {
const vcUniqueId = VCMetadata.fromVC(event.vc).getVcKey();
context.vcs[vcUniqueId] = event.vc;
@@ -271,7 +311,10 @@ export const vcMachine =
inProgressVcDownloads: (context, event) => {
let paresedInProgressList: Set<string> =
context.inProgressVcDownloads;
const removeVcRequestID = event.requestId;
const removeVcRequestID =
event.type === 'REMOVE_VC_FROM_IN_PROGRESS_DOWNLOADS'
? event.requestId
: event.vcMetadata.requestId;
paresedInProgressList.delete(removeVcRequestID);
return paresedInProgressList;
},
@@ -323,6 +366,26 @@ export const vcMachine =
),
}),
removeDownloadingFailedVcsFromMyVcs: model.assign({
myVcs: (context, event) =>
context.myVcs.filter(
value =>
!context.downloadingFailedVcs.some(item => item?.equals(value)),
),
}),
removeDownloadFailedVcsFromStorage: send(
context => {
return StoreEvents.REMOVE_ITEMS(
MY_VCS_STORE_KEY,
context.downloadingFailedVcs.map(m => m.getVcKey()),
);
},
{
to: context => context.serviceRefs.store,
},
),
removeTamperedVcs: model.assign({
myVcs: (context, event) =>
context.myVcs.filter(
@@ -430,8 +493,8 @@ export function selectAreAllVcsDownloaded(state: State) {
return state.context.areAllVcsDownloaded;
}
export function selectInProgressVcDownloadsCount(state: State) {
return state.context.inProgressVcDownloads.size;
export function selectInProgressVcDownloads(state: State) {
return state.context.inProgressVcDownloads;
}
function getUpdatedVCMetadatas(
@@ -462,3 +525,11 @@ export function selectWalletBindingSuccess(state: State) {
export function selectIsTampered(state: State) {
return state.matches('tamperedVCs');
}
export function selectIsDownloadLimitExpired(state: State) {
return state.matches('downloadLimitExpired');
}
export function selectDownloadingFailedVcs(state: State) {
return state.context.downloadingFailedVcs;
}

203
package-lock.json generated
View File

@@ -42,7 +42,7 @@
"i18next": "^21.6.16",
"iso-639-3": "^3.0.1",
"jwt-decode": "^3.1.2",
"mosip-inji-face-sdk": "^0.1.12",
"mosip-mobileid-sdk": "git://github.com/mosip/mosip-mobileid-sdk.git#IrisScan",
"node-forge": "^1.3.1",
"node-jose": "^2.2.0",
"patch-package": "^6.5.1",
@@ -84,7 +84,7 @@
"react-native-vector-icons": "^10.0.0",
"short-unique-id": "^4.4.4",
"simple-pem2jwk": "^0.2.4",
"telemetry-sdk": "git://github.com/mosip/sunbird-telemetry-sdk.git#develop",
"telemetry-sdk": "git://github.com/mosip/sunbird-telemetry-sdk.git#f762be5732ee552c0c70bdd540aa4e2701554c71",
"xstate": "^4.35.0"
},
"devDependencies": {
@@ -9623,7 +9623,7 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@svgr/core/node_modules/typescript": {
"node_modules/@svgr/core/node_modules/typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
@@ -9742,7 +9742,7 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@svgr/plugin-svgo/node_modules/typescript": {
"node_modules/@svgr/plugin-svgo/node_modules/typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
@@ -10269,7 +10269,7 @@
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="
},
"node_modules/abbrev": {
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
@@ -10552,7 +10552,7 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/array-each": {
"node_modules/array-each": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
"integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==",
@@ -12872,7 +12872,7 @@
"node": ">=4.0"
}
},
"node_modules/dateformat": {
"node_modules/dateformat": {
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
"integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
@@ -13223,7 +13223,7 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/detect-file": {
"node_modules/detect-file": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
"integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==",
@@ -13460,7 +13460,7 @@
"node": ">= 0.8"
}
},
"node_modules/encoding": {
"node_modules/encoding": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
@@ -14347,7 +14347,7 @@
"node": ">=6"
}
},
"node_modules/eventemitter2": {
"node_modules/eventemitter2": {
"version": "0.4.14",
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
"integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==",
@@ -14402,7 +14402,7 @@
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
"integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
"engines": {
"engines": {
"node": ">= 0.8.0"
}
},
@@ -14591,7 +14591,7 @@
"node": ">=0.10"
}
},
"node_modules/expand-tilde": {
"node_modules/expand-tilde": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
"integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==",
@@ -15670,7 +15670,7 @@
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.5.1.tgz",
"integrity": "sha512-yt5a1VCp2BF9CrsO689PCD5oXKP14MMhnOanQMvDn4BDpURYfzAlDVGC5fZrNQKtwn/eq3bcrxIwZ7D9QjVVRg==",
"peer": true,
"peer": true,
"dependencies": {
"@expo/config": "~8.1.0",
"chalk": "^4.1.0",
@@ -15687,7 +15687,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"peer": true,
"peer": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -15702,7 +15702,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"peer": true,
"peer": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -15718,7 +15718,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"peer": true,
"peer": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -15736,7 +15736,7 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"peer": true,
"peer": true,
"engines": {
"node": ">= 10"
}
@@ -15745,7 +15745,7 @@
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"peer": true,
"peer": true,
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
@@ -15760,7 +15760,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"peer": true,
"peer": true,
"engines": {
"node": ">=8"
}
@@ -15769,7 +15769,7 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"peer": true,
"peer": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -15781,7 +15781,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
"peer": true,
"peer": true,
"engines": {
"node": ">= 10.0.0"
}
@@ -16771,7 +16771,7 @@
"node": ">=8"
}
},
"node_modules/findup-sync": {
"node_modules/findup-sync": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz",
"integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==",
@@ -16878,7 +16878,7 @@
"node": ">=0.10.0"
}
},
"node_modules/for-own": {
"node_modules/for-own": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
"integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==",
@@ -17092,7 +17092,7 @@
"node": ">=6"
}
},
"node_modules/getobject": {
"node_modules/getobject": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz",
"integrity": "sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg==",
@@ -17154,7 +17154,7 @@
"node": ">=10"
}
},
"node_modules/global-modules": {
"node_modules/global-modules": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
"integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
@@ -17291,7 +17291,7 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/grunt": {
"node_modules/grunt": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/grunt/-/grunt-1.6.1.tgz",
"integrity": "sha512-/ABUy3gYWu5iBmrUSRBP97JLpQUm0GgVveDCp6t3yRNIoltIYw7rEj3g5y1o2PGPR2vfTRGa7WC/LZHLTXnEzA==",
@@ -17367,7 +17367,7 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
"integrity": "sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ=="
},
"node_modules/grunt-known-options": {
"node_modules/grunt-known-options": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz",
"integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==",
@@ -17777,7 +17777,7 @@
"react-is": "^16.7.0"
}
},
"node_modules/homedir-polyfill": {
"node_modules/homedir-polyfill": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
"integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
@@ -18161,7 +18161,7 @@
"node": ">= 0.4"
}
},
"node_modules/interpret": {
"node_modules/interpret": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
"integrity": "sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA==",
@@ -18196,7 +18196,7 @@
"node": ">= 0.10"
}
},
"node_modules/is-absolute": {
"node_modules/is-absolute": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
"integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
@@ -18610,7 +18610,7 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-relative": {
"node_modules/is-relative": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
"integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
@@ -18713,7 +18713,7 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-unc-path": {
"node_modules/is-unc-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
"integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
@@ -22324,7 +22324,7 @@
"node": ">= 0.8.0"
}
},
"node_modules/liftup": {
"node_modules/liftup": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz",
"integrity": "sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw==",
@@ -22402,7 +22402,7 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-x64": {
"node_modules/lightningcss-darwin-x64": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.19.0.tgz",
"integrity": "sha512-Lif1wD6P4poaw9c/4Uh2z+gmrWhw/HtXFoeZ3bEsv6Ia4tt8rOJBdkfVaUJ6VXmpKHALve+iTyP2+50xY1wKPw==",
@@ -23006,7 +23006,7 @@
"semver": "bin/semver"
}
},
"node_modules/make-iterator": {
"node_modules/make-iterator": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
"integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
@@ -24420,14 +24420,16 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/mosip-inji-face-sdk": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/mosip-inji-face-sdk/-/mosip-inji-face-sdk-0.1.12.tgz",
"integrity": "sha512-lnoaa2lL0pS9u/9fwH4Q9OU2U56oqjXf2z4JjGxX80DQOqJGWvj49PfvPMYGFEfVucqIJyQBZUfvfpqfYO0+vw==",
"node_modules/mosip-mobileid-sdk": {
"version": "0.1.0",
"resolved": "git+ssh://git@github.com/mosip/mosip-mobileid-sdk.git#ff4647a6c949b0acf8f36ae968dcbd407df7fb83",
"license": "MIT",
"engines": {
"node": ">= 16.0.0"
},
"peerDependencies": {
"react": "*",
"react-native": "*",
"react-native-fs": "*"
"react-native": "*"
}
},
"node_modules/ms": {
@@ -24709,7 +24711,7 @@
"url": "https://github.com/sponsors/antelle"
}
},
"node_modules/nopt": {
"node_modules/nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
"integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==",
@@ -24959,7 +24961,7 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/object.defaults": {
"node_modules/object.defaults": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
"integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==",
@@ -25027,7 +25029,7 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/object.map": {
"node_modules/object.map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
"integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==",
@@ -25365,7 +25367,7 @@
"node": ">=6"
}
},
"node_modules/parse-filepath": {
"node_modules/parse-filepath": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
"integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==",
@@ -25391,7 +25393,7 @@
"node": ">=4"
}
},
"node_modules/parse-passwd": {
"node_modules/parse-passwd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
"integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==",
@@ -25703,7 +25705,7 @@
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/path-root": {
"node_modules/path-root": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
"integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==",
@@ -26413,7 +26415,7 @@
}
}
},
"node_modules/react-dom": {
"node_modules/react-dom": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
"integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
@@ -26750,7 +26752,7 @@
"react-native": ">0.62.0"
}
},
"node_modules/react-native-flipper-xstate/node_modules/ws": {
"node_modules/react-native-flipper-xstate/node_modules/ws": {
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
@@ -27613,7 +27615,7 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/rechoir": {
"node_modules/rechoir": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
"integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
@@ -27864,7 +27866,7 @@
"node": ">=8"
}
},
"node_modules/resolve-dir": {
"node_modules/resolve-dir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
"integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==",
@@ -29562,9 +29564,9 @@
}
},
"node_modules/telemetry-sdk": {
"name": "@project-sunbird/telemetry-sdk",
"version": "0.0.21",
"resolved": "git+ssh://git@github.com/mosip/sunbird-telemetry-sdk.git#7ff643a81acd54ddb2ad8ad588314004513a3749",
"resolved": "git+ssh://git@github.com/mosip/sunbird-telemetry-sdk.git#f762be5732ee552c0c70bdd540aa4e2701554c71",
"integrity": "sha512-aeA7uO77JzNQVSuUpxzUXK3I+BtYiOzDhcmYbD6R3pAETun4toP1/GR5PBfT9/iE3N5MdGJg70sQArwCIL1XaQ==",
"license": "MIT",
"dependencies": {
"axios": "^1.4.0",
@@ -30201,7 +30203,7 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/unc-path-regex": {
"node_modules/unc-path-regex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
"integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==",
@@ -30598,7 +30600,7 @@
"node": ">=10.12.0"
}
},
"node_modules/v8flags": {
"node_modules/v8flags": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
"integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==",
@@ -38294,7 +38296,7 @@
"json-parse-even-better-errors": "^2.3.0",
"lines-and-columns": "^1.1.6"
}
},
},
"typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
@@ -38361,7 +38363,7 @@
"json-parse-even-better-errors": "^2.3.0",
"lines-and-columns": "^1.1.6"
}
},
},
"typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
@@ -38747,7 +38749,7 @@
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="
},
"abbrev": {
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
@@ -38962,7 +38964,7 @@
"is-array-buffer": "^3.0.1"
}
},
"array-each": {
"array-each": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
"integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==",
@@ -40771,7 +40773,7 @@
"resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz",
"integrity": "sha512-lAJqBmFzCLcDJdI9cEnJ7loSkLTh1PbIgZUndlzvYbf6NyFEr5n9rQhOwr6CIGwZqyQ3sYeQQiP9NOVQmgmRMA=="
},
"dateformat": {
"dateformat": {
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
"integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
@@ -41028,7 +41030,7 @@
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
},
"detect-file": {
"detect-file": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
"integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==",
@@ -41209,7 +41211,7 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"encoding": {
"encoding": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
@@ -41887,7 +41889,7 @@
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"eventemitter2": {
"eventemitter2": {
"version": "0.4.14",
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
"integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==",
@@ -42080,7 +42082,7 @@
}
}
},
"expand-tilde": {
"expand-tilde": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
"integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==",
@@ -43163,7 +43165,7 @@
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.5.1.tgz",
"integrity": "sha512-yt5a1VCp2BF9CrsO689PCD5oXKP14MMhnOanQMvDn4BDpURYfzAlDVGC5fZrNQKtwn/eq3bcrxIwZ7D9QjVVRg==",
"peer": true,
"peer": true,
"requires": {
"@expo/config": "~8.1.0",
"chalk": "^4.1.0",
@@ -43177,7 +43179,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"peer": true,
"peer": true,
"requires": {
"color-convert": "^2.0.1"
}
@@ -43186,7 +43188,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"peer": true,
"peer": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -43196,7 +43198,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"peer": true,
"peer": true,
"requires": {
"color-name": "~1.1.4"
}
@@ -43217,7 +43219,7 @@
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"peer": true,
"peer": true,
"requires": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
@@ -43235,7 +43237,7 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"peer": true,
"peer": true,
"requires": {
"has-flag": "^4.0.0"
}
@@ -43817,7 +43819,7 @@
}
}
},
"findup-sync": {
"findup-sync": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz",
"integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==",
@@ -43892,7 +43894,7 @@
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
"integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ=="
},
"for-own": {
"for-own": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
"integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==",
@@ -44039,7 +44041,7 @@
"resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz",
"integrity": "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg=="
},
"getobject": {
"getobject": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz",
"integrity": "sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg==",
@@ -44088,7 +44090,7 @@
"is-glob": "^4.0.3"
}
},
"global-modules": {
"global-modules": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
"integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
@@ -44193,7 +44195,7 @@
}
}
},
"grunt": {
"grunt": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/grunt/-/grunt-1.6.1.tgz",
"integrity": "sha512-/ABUy3gYWu5iBmrUSRBP97JLpQUm0GgVveDCp6t3yRNIoltIYw7rEj3g5y1o2PGPR2vfTRGa7WC/LZHLTXnEzA==",
@@ -44307,7 +44309,7 @@
}
}
},
"grunt-known-options": {
"grunt-known-options": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz",
"integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==",
@@ -44570,7 +44572,7 @@
"react-is": "^16.7.0"
}
},
"homedir-polyfill": {
"homedir-polyfill": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
"integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
@@ -44834,7 +44836,7 @@
"side-channel": "^1.0.4"
}
},
"interpret": {
"interpret": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
"integrity": "sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA==",
@@ -44863,7 +44865,7 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"is-absolute": {
"is-absolute": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
"integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
@@ -45135,7 +45137,7 @@
"has-tostringtag": "^1.0.0"
}
},
"is-relative": {
"is-relative": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
"integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
@@ -45199,7 +45201,7 @@
"which-typed-array": "^1.1.11"
}
},
"is-unc-path": {
"is-unc-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
"integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
@@ -47942,7 +47944,7 @@
"type-check": "~0.4.0"
}
},
"liftup": {
"liftup": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz",
"integrity": "sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw==",
@@ -47994,7 +47996,7 @@
"integrity": "sha512-wIJmFtYX0rXHsXHSr4+sC5clwblEMji7HHQ4Ub1/CznVRxtCFha6JIt5JZaNf8vQrfdZnBxLLC6R8pC818jXqg==",
"optional": true
},
"lightningcss-darwin-x64": {
"lightningcss-darwin-x64": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.19.0.tgz",
"integrity": "sha512-Lif1wD6P4poaw9c/4Uh2z+gmrWhw/HtXFoeZ3bEsv6Ia4tt8rOJBdkfVaUJ6VXmpKHALve+iTyP2+50xY1wKPw==",
@@ -48398,7 +48400,7 @@
}
}
},
"make-iterator": {
"make-iterator": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
"integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
@@ -49542,11 +49544,9 @@
"minimist": "^1.2.6"
}
},
"mosip-inji-face-sdk": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/mosip-inji-face-sdk/-/mosip-inji-face-sdk-0.1.12.tgz",
"integrity": "sha512-lnoaa2lL0pS9u/9fwH4Q9OU2U56oqjXf2z4JjGxX80DQOqJGWvj49PfvPMYGFEfVucqIJyQBZUfvfpqfYO0+vw==",
"requires": {}
"mosip-mobileid-sdk": {
"version": "git+ssh://git@github.com/mosip/mosip-mobileid-sdk.git#ff4647a6c949b0acf8f36ae968dcbd407df7fb83",
"from": "mosip-mobileid-sdk@git://github.com/mosip/mosip-mobileid-sdk.git#IrisScan"
},
"ms": {
"version": "2.1.2",
@@ -49764,7 +49764,7 @@
"resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz",
"integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw=="
},
"nopt": {
"nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
"integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==",
@@ -49956,7 +49956,7 @@
"object-keys": "^1.1.1"
}
},
"object.defaults": {
"object.defaults": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
"integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==",
@@ -50008,7 +50008,7 @@
"es-abstract": "^1.22.1"
}
},
"object.map": {
"object.map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
"integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==",
@@ -50256,7 +50256,7 @@
"callsites": "^3.0.0"
}
},
"parse-filepath": {
"parse-filepath": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
"integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==",
@@ -50276,7 +50276,7 @@
"json-parse-better-errors": "^1.0.1"
}
},
"parse-passwd": {
"parse-passwd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
"integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==",
@@ -50505,7 +50505,7 @@
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"path-root": {
"path-root": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
"integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==",
@@ -51920,7 +51920,7 @@
}
}
},
"rechoir": {
"rechoir": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
"integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
@@ -52118,7 +52118,7 @@
"resolve-from": "^5.0.0"
}
},
"resolve-dir": {
"resolve-dir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
"integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==",
@@ -53473,8 +53473,9 @@
}
},
"telemetry-sdk": {
"version": "git+ssh://git@github.com/mosip/sunbird-telemetry-sdk.git#7ff643a81acd54ddb2ad8ad588314004513a3749",
"from": "telemetry-sdk@git://github.com/mosip/sunbird-telemetry-sdk.git#develop",
"version": "git+ssh://git@github.com/mosip/sunbird-telemetry-sdk.git#f762be5732ee552c0c70bdd540aa4e2701554c71",
"integrity": "sha512-aeA7uO77JzNQVSuUpxzUXK3I+BtYiOzDhcmYbD6R3pAETun4toP1/GR5PBfT9/iE3N5MdGJg70sQArwCIL1XaQ==",
"from": "telemetry-sdk@git://github.com/mosip/sunbird-telemetry-sdk.git#f762be5732ee552c0c70bdd540aa4e2701554c71",
"requires": {
"axios": "^1.4.0",
"grunt-karma": "^0.12.2",
@@ -53930,7 +53931,7 @@
"which-boxed-primitive": "^1.0.2"
}
},
"unc-path-regex": {
"unc-path-regex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
"integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==",
@@ -54232,7 +54233,7 @@
"convert-source-map": "^1.6.0"
}
},
"v8flags": {
"v8flags": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
"integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==",

View File

@@ -1,14 +1,14 @@
{
"scripts": {
"prepare": "husky install",
"jetify": "jetify",
"start": "react-native start",
"android:mosip": "react-native run-android --variant=mosipDebug",
"ios": "react-native run-ios",
"build:android:mosip": "cd android && ./gradlew :app:assembleMosipRelease && cd ..",
"i18n:compile-strings": "node scripts/compile-strings.js",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
"test": "jest",
"postinstall": "patch-package"
"postinstall": "patch-package && npm run jetify && sh tools/talisman/talisman-precommit.sh"
},
"dependencies": {
"@digitalbazaar/ed25519-signature-2018": "digitalbazaar/ed25519-signature-2018",
@@ -44,7 +44,7 @@
"i18next": "^21.6.16",
"iso-639-3": "^3.0.1",
"jwt-decode": "^3.1.2",
"mosip-inji-face-sdk": "^0.1.12",
"mosip-mobileid-sdk": "git://github.com/mosip/mosip-mobileid-sdk.git#IrisScan",
"node-forge": "^1.3.1",
"node-jose": "^2.2.0",
"patch-package": "^6.5.1",
@@ -86,7 +86,7 @@
"react-native-vector-icons": "^10.0.0",
"short-unique-id": "^4.4.4",
"simple-pem2jwk": "^0.2.4",
"telemetry-sdk": "git://github.com/mosip/sunbird-telemetry-sdk.git#develop",
"telemetry-sdk": "git://github.com/mosip/sunbird-telemetry-sdk.git#f762be5732ee552c0c70bdd540aa4e2701554c71",
"xstate": "^4.35.0"
},
"devDependencies": {
@@ -126,8 +126,7 @@
"name": "mosip-resident-app",
"version": "1.0.0",
"lint-staged": {
"*.{ts,tsx,js,css,md}": "prettier --write",
"*.strings.json": "node scripts/compile-strings.js"
"*.{ts,tsx,js,css,md}": "prettier --write"
},
"overrides": {
"react": "18.2.0",

View File

@@ -1,11 +0,0 @@
{
"header": "Would you like to use biometrics to unlock the application?",
"useBiometrics": "Use biometrics",
"usePasscode": "I'd rather use a passcode",
"errors": {
"unavailable": "Device does not support Biometrics",
"unenrolled": "To use Biometrics, please enroll your biometrics in your device settings",
"failed": "Failed to authenticate with Biometrics",
"generic": "There seems to be an error in Biometrics authentication"
}
}

View File

@@ -6,11 +6,24 @@ import {Button, Column, Text} from '../components/ui';
import {Theme} from '../components/ui/styleUtils';
import {RootRouteProps} from '../routes';
import {useAuthScreen} from './AuthScreenController';
import {
getStartEventData,
getInteractEventData,
sendInteractEvent,
sendStartEvent,
} from '../shared/telemetry/TelemetryUtils';
export const AuthScreen: React.FC<RootRouteProps> = props => {
const {t} = useTranslation('AuthScreen');
const controller = useAuthScreen(props);
const handleUsePasscodeButtonPress = () => {
sendStartEvent(getStartEventData('App Onboarding'));
sendInteractEvent(
getInteractEventData('App Onboarding', 'TOUCH', 'Use Passcode Button'),
);
controller.usePasscode();
};
return (
<Column
fill
@@ -27,12 +40,14 @@ export const AuthScreen: React.FC<RootRouteProps> = props => {
<Column margin="30 0 0 0">
<Text
testID="selectAppUnlockMethod"
style={{paddingTop: 3}}
align="center"
style={Theme.TextStyles.header}>
{t('header')}
</Text>
<Text
align="center"
style={{paddingTop: 3}}
weight="semibold"
color={Theme.Colors.GrayText}
margin="6 0">
@@ -54,7 +69,7 @@ export const AuthScreen: React.FC<RootRouteProps> = props => {
testID="usePasscode"
type="clear"
title={t('usePasscode')}
onPress={controller.usePasscode}
onPress={() => handleUsePasscodeButtonPress()}
/>
</Column>
</Column>

View File

@@ -1,14 +1,10 @@
import { useMachine, useSelector } from '@xstate/react';
import { useContext, useEffect, useState } from 'react';
import {useMachine, useSelector} from '@xstate/react';
import {useContext, useEffect, useState} from 'react';
import * as LocalAuthentication from 'expo-local-authentication';
import {
AuthEvents,
selectSettingUp,
selectAuthorized,
} from '../machines/auth';
import { RootRouteProps } from '../routes';
import { GlobalContext } from '../shared/GlobalContext';
import {AuthEvents, selectSettingUp, selectAuthorized} from '../machines/auth';
import {RootRouteProps} from '../routes';
import {GlobalContext} from '../shared/GlobalContext';
import {
biometricsMachine,
selectError,
@@ -16,12 +12,23 @@ import {
selectIsSuccess,
selectIsUnvailable,
selectUnenrolledNotice,
selectErrorResponse,
} from '../machines/biometrics';
import { SettingsEvents } from '../machines/settings';
import { useTranslation } from 'react-i18next';
import {SettingsEvents} from '../machines/settings';
import {useTranslation} from 'react-i18next';
import {
sendStartEvent,
sendImpressionEvent,
sendInteractEvent,
getStartEventData,
getInteractEventData,
getImpressionEventData,
getEndEventData,
sendEndEvent,
} from '../shared/telemetry/TelemetryUtils';
export function useAuthScreen(props: RootRouteProps) {
const { appService } = useContext(GlobalContext);
const {appService} = useContext(GlobalContext);
const authService = appService.children.get('auth');
const settingsService = appService.children.get('settings');
@@ -38,12 +45,13 @@ export function useAuthScreen(props: RootRouteProps) {
const isSuccessBio = useSelector(bioService, selectIsSuccess);
const errorMsgBio = useSelector(bioService, selectError);
const unEnrolledNoticeBio = useSelector(bioService, selectUnenrolledNotice);
const errorResponse = useSelector(bioService, selectErrorResponse);
const usePasscode = () => {
props.navigation.navigate('Passcode', { setup: isSettingUp });
props.navigation.navigate('Passcode', {setup: isSettingUp});
};
const { t } = useTranslation('AuthScreen');
const {t} = useTranslation('AuthScreen');
const fetchIsAvailable = async () => {
const result = await LocalAuthentication.hasHardwareAsync();
@@ -53,10 +61,12 @@ export function useAuthScreen(props: RootRouteProps) {
useEffect(() => {
if (isAuthorized) {
sendEndEvent(getEndEventData('App Onboarding', 'SUCCESS'));
props.navigation.reset({
index: 0,
routes: [{ name: 'Main' }],
routes: [{name: 'Main'}],
});
sendImpressionEvent(getImpressionEventData('App Onboarding', 'Home'));
return;
}
@@ -69,8 +79,17 @@ export function useAuthScreen(props: RootRouteProps) {
// handle biometric failure unknown error
} else if (errorMsgBio) {
sendEndEvent(
getEndEventData('App Onboarding', 'FAILURE', {
errorId: errorResponse.res.error,
errorMessage: errorResponse.res.warning,
stackTrace: errorResponse.stacktrace,
}),
);
// show alert message whenever biometric state gets failure
setHasAlertMsg(t(errorMsgBio));
if (errorResponse.res.error !== 'user_cancel') {
setHasAlertMsg(t(errorMsgBio));
}
// handle any unenrolled notice
} else if (unEnrolledNoticeBio) {
@@ -78,6 +97,7 @@ export function useAuthScreen(props: RootRouteProps) {
// we dont need to see this page to user once biometric is unavailable on its device
} else if (isUnavailableBio) {
sendStartEvent(getStartEventData('App Onboarding'));
usePasscode();
}
}, [isSuccessBio, isUnavailableBio, errorMsgBio, unEnrolledNoticeBio]);
@@ -85,12 +105,21 @@ export function useAuthScreen(props: RootRouteProps) {
const useBiometrics = async () => {
const isBiometricsEnrolled = await LocalAuthentication.isEnrolledAsync();
if (isBiometricsEnrolled) {
if (biometricState.matches({ failure: 'unenrolled' })) {
biometricSend({ type: 'RETRY_AUTHENTICATE' });
sendStartEvent(getStartEventData('App Onboarding'));
sendInteractEvent(
getInteractEventData(
'App Onboarding',
'TOUCH',
'Use Biometrics Button',
),
);
if (biometricState.matches({failure: 'unenrolled'})) {
biometricSend({type: 'RETRY_AUTHENTICATE'});
return;
}
biometricSend({ type: 'AUTHENTICATE' });
biometricSend({type: 'AUTHENTICATE'});
} else {
setHasAlertMsg(t('errors.unenrolled'));
}

View File

@@ -1,3 +0,0 @@
{
"unlock": "Unlock with biometrics"
}

View File

@@ -1,17 +1,26 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { TouchableOpacity } from 'react-native';
import { Icon } from 'react-native-elements';
import { Button, Centered, Column } from '../components/ui';
import { Theme } from '../components/ui/styleUtils';
import { RootRouteProps } from '../routes';
import { useBiometricScreen } from './BiometricScreenController';
import { Passcode } from '../components/Passcode';
import React, {useEffect} from 'react';
import {useTranslation} from 'react-i18next';
import {TouchableOpacity} from 'react-native';
import {Icon} from 'react-native-elements';
import {Button, Centered, Column} from '../components/ui';
import {Theme} from '../components/ui/styleUtils';
import {RootRouteProps} from '../routes';
import {useBiometricScreen} from './BiometricScreenController';
import {Passcode} from '../components/Passcode';
import {
getEventType,
incrementPasscodeRetryCount,
} from '../shared/telemetry/TelemetryUtils';
export const BiometricScreen: React.FC<RootRouteProps> = (props) => {
const { t } = useTranslation('BiometricScreen');
export const BiometricScreen: React.FC<RootRouteProps> = props => {
const {t} = useTranslation('BiometricScreen');
const controller = useBiometricScreen(props);
const handlePasscodeMismatch = (error: string) => {
incrementPasscodeRetryCount(getEventType(props.route.params?.setup));
controller.onError(error);
};
return (
<Column
fill
@@ -34,10 +43,11 @@ export const BiometricScreen: React.FC<RootRouteProps> = (props) => {
<Passcode
message="Enter your passcode to re-enable biometrics authentication."
onSuccess={() => controller.onSuccess()}
onError={(value: string) => controller.onError(value)}
onError={handlePasscodeMismatch}
storedPasscode={controller.storedPasscode}
onDismiss={() => controller.onDismiss()}
error={controller.error}
salt={controller.passcodeSalt}
/>
)}
</Column>

View File

@@ -1,20 +1,38 @@
import { useMachine, useSelector } from '@xstate/react';
import { useContext, useEffect, useState } from 'react';
import {useMachine, useSelector} from '@xstate/react';
import {useContext, useEffect, useState} from 'react';
import {Platform} from 'react-native';
import RNFingerprintChange from 'react-native-biometrics-changed';
import { AuthEvents, selectAuthorized, selectPasscode } from '../machines/auth';
import { RootRouteProps } from '../routes';
import { GlobalContext } from '../shared/GlobalContext';
import {
AuthEvents,
selectAuthorized,
selectPasscode,
selectPasscodeSalt,
} from '../machines/auth';
import {
biometricsMachine,
selectError,
selectErrorResponse,
selectIsAvailable,
selectIsSuccess,
selectIsUnenrolled,
selectIsUnvailable,
} from '../machines/biometrics';
import { Platform } from 'react-native';
import {RootRouteProps} from '../routes';
import {GlobalContext} from '../shared/GlobalContext';
import {
getStartEventData,
getEndEventData,
getImpressionEventData,
getInteractEventData,
sendEndEvent,
sendImpressionEvent,
sendInteractEvent,
sendStartEvent,
} from '../shared/telemetry/TelemetryUtils';
import {isAndroid} from '../shared/constants';
export function useBiometricScreen(props: RootRouteProps) {
const { appService } = useContext(GlobalContext);
const {appService} = useContext(GlobalContext);
const authService = appService.children.get('auth');
const [error, setError] = useState('');
@@ -27,12 +45,29 @@ export function useBiometricScreen(props: RootRouteProps) {
const isUnavailable = useSelector(bioService, selectIsUnvailable);
const isSuccessBio = useSelector(bioService, selectIsSuccess);
const isUnenrolled = useSelector(bioService, selectIsUnenrolled);
const errorMsgBio = useSelector(bioService, selectError);
const errorResponse = useSelector(bioService, selectErrorResponse);
const passcodeSalt = useSelector(authService, selectPasscodeSalt);
useEffect(() => {
if (isAvailable) {
sendStartEvent(getStartEventData('App login'));
sendInteractEvent(
getInteractEventData(
'App login',
'TOUCH',
'Unlock with Biometrics button',
),
);
}
}, [isAvailable]);
useEffect(() => {
if (isAuthorized) {
sendEndEvent(getEndEventData('App Login', 'SUCCESS'));
props.navigation.reset({
index: 0,
routes: [{ name: 'Main' }],
routes: [{name: 'Main'}],
});
return;
}
@@ -50,25 +85,46 @@ export function useBiometricScreen(props: RootRouteProps) {
return;
}
if (errorMsgBio && !isReEnabling) {
sendEndEvent(
getEndEventData('App Login', 'FAILURE', {
errorId: errorResponse.res.error,
errorMessage: errorResponse.res.warning,
stackTrace: errorResponse.stacktrace,
}),
);
}
if (isUnavailable || isUnenrolled) {
props.navigation.reset({
index: 0,
routes: [{ name: 'Passcode' }],
routes: [{name: 'Passcode'}],
});
sendStartEvent(getStartEventData('App Login'));
sendInteractEvent(
getInteractEventData('App Login', 'TOUCH', 'Unlock application button'),
);
}
}, [isAuthorized, isAvailable, isUnenrolled, isUnavailable, isSuccessBio]);
}, [
isAuthorized,
isAvailable,
isUnenrolled,
isUnavailable,
isSuccessBio,
errorMsgBio,
]);
const checkBiometricsChange = () => {
if (Platform.OS === 'android') {
if (isAndroid()) {
RNFingerprintChange.hasFingerPrintChanged().then(
async (biometricsHasChanged: boolean) => {
//if new biometrics are added, re-enable Biometrics Authentication
if (biometricsHasChanged) {
setReEnabling(true);
} else {
bioSend({ type: 'AUTHENTICATE' });
bioSend({type: 'AUTHENTICATE'});
}
}
},
);
} else {
// TODO: solution for iOS
@@ -76,11 +132,20 @@ export function useBiometricScreen(props: RootRouteProps) {
};
const useBiometrics = () => {
bioSend({ type: 'AUTHENTICATE' });
sendStartEvent(getStartEventData('App login'));
sendInteractEvent(
getInteractEventData(
'App Login',
'TOUCH',
'Unlock with biometrics button',
),
);
bioSend({type: 'AUTHENTICATE'});
};
const onSuccess = () => {
bioSend({ type: 'AUTHENTICATE' });
bioSend({type: 'AUTHENTICATE'});
setError('');
};
const onError = (value: string) => {
@@ -88,6 +153,12 @@ export function useBiometricScreen(props: RootRouteProps) {
};
const onDismiss = () => {
sendEndEvent(
getEndEventData('App Login', 'FAILURE', {
errorId: 'user_cancel',
errorMessage: 'Authentication canceled',
}),
);
setReEnabling(false);
};
@@ -95,6 +166,7 @@ export function useBiometricScreen(props: RootRouteProps) {
error,
isReEnabling,
isSuccessBio,
passcodeSalt,
storedPasscode: useSelector(authService, selectPasscode),
useBiometrics,

View File

@@ -1,7 +0,0 @@
{
"noHistory": "No history available yet",
"downloaded": "downloaded",
"shared": "shared",
"received": "received",
"deleted": "deleted"
}

View File

@@ -1,15 +1,15 @@
import React from 'react';
import { RefreshControl } from 'react-native';
import { Icon } from 'react-native-elements';
import { useTranslation } from 'react-i18next';
import { Centered, Column, Text } from '../../components/ui';
import { useHistoryTab } from './HistoryScreenController';
import { ActivityLogText } from '../../components/ActivityLogText';
import { MainRouteProps } from '../../routes/main';
import { Theme } from '../../components/ui/styleUtils';
import {RefreshControl} from 'react-native';
import {Icon} from 'react-native-elements';
import {useTranslation} from 'react-i18next';
import {Centered, Column, Text} from '../../components/ui';
import {useHistoryTab} from './HistoryScreenController';
import {ActivityLogText} from '../../components/ActivityLogText';
import {MainRouteProps} from '../../routes/main';
import {Theme} from '../../components/ui/styleUtils';
export const HistoryScreen: React.FC<MainRouteProps> = () => {
const { t } = useTranslation('HistoryScreen');
const {t} = useTranslation('HistoryScreen');
const controller = useHistoryTab();
return (
@@ -23,7 +23,7 @@ export const HistoryScreen: React.FC<MainRouteProps> = () => {
onRefresh={controller.REFRESH}
/>
}>
{controller.activities.map((activity) => (
{controller.activities.map(activity => (
<ActivityLogText
key={`${activity.timestamp}-${activity._vcKey}`}
activity={activity}
@@ -32,11 +32,15 @@ export const HistoryScreen: React.FC<MainRouteProps> = () => {
{controller.activities.length === 0 && (
<Centered fill>
<Icon
style={{ marginBottom: 20 }}
style={{marginBottom: 20}}
size={40}
name="sentiment-dissatisfied"
/>
<Text align="center" weight="semibold" margin="0 0 4 0">
<Text
align="center"
style={{paddingTop: 3}}
weight="semibold"
margin="0 0 4 0">
{t('noHistory')}
</Text>
</Centered>

View File

@@ -1,5 +0,0 @@
{
"myVcsTab": "My\nCards",
"receivedVcsTab": "Received\nCards",
"historyTab": "History"
}

View File

@@ -1,6 +1,6 @@
import React, {useEffect} from 'react';
import {Icon, Tab} from 'react-native-elements';
import {Button, Column, Text} from '../../components/ui';
import {Column, Text} from '../../components/ui';
import {Theme} from '../../components/ui/styleUtils';
import {HomeRouteProps} from '../../routes/main';
import {MyVcsTab} from './MyVcsTab';
@@ -11,10 +11,10 @@ import {TabRef} from './HomeScreenMachine';
import {useTranslation} from 'react-i18next';
import {ActorRefFrom} from 'xstate';
import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
import {isOpenId4VCIEnabled} from '../../shared/openId4VCI/Utils';
import LinearGradient from 'react-native-linear-gradient';
import {EsignetMosipVCItemMachine} from '../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
import {ErrorMessageOverlay} from '../../components/MessageOverlay';
import {Pressable} from 'react-native';
export const HomeScreen: React.FC<HomeRouteProps> = props => {
const {t} = useTranslation('HomeScreen');
@@ -44,16 +44,19 @@ export const HomeScreen: React.FC<HomeRouteProps> = props => {
return (
<LinearGradient
colors={Theme.Colors.gradientBtn}
style={Theme.Styles.downloadFabIcon}>
<Button
testID="downloadIcon"
icon={plusIcon}
style={Theme.Styles.downloadFabIconContainer}>
<Pressable
onPress={() => {
controller.GOTO_ISSUERS();
}}
type={'clearAddIdBtnBg'}
fill
/>
testID="downloadIcon"
style={({pressed}) =>
pressed
? Theme.Styles.downloadFabIconPressed
: Theme.Styles.downloadFabIconNormal
}>
{plusIcon}
</Pressable>
</LinearGradient>
);
};
@@ -76,7 +79,7 @@ export const HomeScreen: React.FC<HomeRouteProps> = props => {
</Column>
)}
</Column>
{isOpenId4VCIEnabled() && <DownloadFABIcon />}
<DownloadFABIcon />
<ErrorMessageOverlay
translationPath={'MyVcsTab'}
isVisible={controller.isMinimumStorageLimitReached}

View File

@@ -25,13 +25,13 @@ export const IntroSlidersScreen: React.FC<RootRouteProps> = props => {
key: 'two',
title: t('stepTwoTitle'),
text: t('stepTwoText'),
image: Theme.sharingIntro,
image: Theme.walletIntro,
},
{
key: 'three',
title: t('stepThreeTitle'),
text: t('stepThreeText'),
image: Theme.walletIntro,
image: Theme.sharingIntro,
},
{
key: 'four',
@@ -64,34 +64,45 @@ export const IntroSlidersScreen: React.FC<RootRouteProps> = props => {
alignItems: 'flex-end',
}}>
{controller.isPasscodeSet() ? (
<Button
testID="skip"
type="plain"
title={t('skip')}
onPress={controller.NEXT}
/>
) : (
<Button
testID="back"
type="plain"
title={t('back')}
onPress={controller.BACK}
styles={{height: 150}}
/>
) : (
<Button
testID="skip"
type="plain"
title={t('skip')}
onPress={controller.NEXT}
styles={{height: 150}}
/>
)}
</Column>
</Row>
<Image source={item.image} />
<Image
source={item.image}
resizeMode="contain"
style={{height: Dimensions.get('screen').height * 0.6}}
/>
<Column
testID="introSlide"
style={Theme.OnboardingOverlayStyles.bottomContainer}
crossAlign="center"
backgroundColor={Theme.Colors.whiteText}
width={Dimensions.get('screen').width}>
<Text testID="introTitle" weight="semibold" margin="0 0 18 0">
<Text
testID="introTitle"
style={{paddingTop: 3}}
weight="semibold"
margin="0 0 18 0">
{item.title}
</Text>
<Text
testID="introText"
style={{paddingTop: 7}}
margin="0 0 150 0"
size="large"
color={Theme.Colors.GrayText}>
@@ -111,6 +122,7 @@ export const IntroSlidersScreen: React.FC<RootRouteProps> = props => {
style={{borderRadius: 10, height: 50, marginTop: -10}}>
<Text
testID="next"
style={{paddingTop: 3}}
weight="semibold"
align="center"
color="#FFFFFF"
@@ -129,11 +141,12 @@ export const IntroSlidersScreen: React.FC<RootRouteProps> = props => {
style={{borderRadius: 10, height: 50, marginTop: -10}}>
<Text
testID="getStarted"
style={{paddingTop: 3}}
weight="semibold"
align="center"
color="#FFFFFF"
margin="15 0 0 0">
{controller.isPasscodeSet() ? t('getStarted') : t('goBack')}
{controller.isPasscodeSet() ? t('goBack') : t('getStarted')}
</Text>
</LinearGradient>
</View>
@@ -156,7 +169,7 @@ export const IntroSlidersScreen: React.FC<RootRouteProps> = props => {
dotStyle={{backgroundColor: Theme.Colors.dotColor, marginBottom: 47}}
renderItem={renderItem}
onDone={() =>
controller.isPasscodeSet() ? controller.NEXT() : controller.BACK()
controller.isPasscodeSet() ? controller.BACK() : controller.NEXT()
}
/>
</Column>

View File

@@ -1,16 +0,0 @@
{
"requestingCredential": "Requesting credential...",
"errors": {
"input": {
"empty": "The input cannot be empty",
"invalidFormat": "The input format is incorrect"
},
"backend": {
"invalidOtp": "OTP is invalid",
"invalidUin": "UIN invalid",
"invalidVid": "VID invalid",
"missingUin": "UIN not available in database",
"missingVid": "VID not available in database"
}
}
}

View File

@@ -1,27 +1,44 @@
import React from 'react';
import { MessageOverlay } from '../../../components/MessageOverlay';
import { AddVcModalProps, useAddVcModal } from './AddVcModalController';
import { OtpVerificationModal } from './OtpVerificationModal';
import { IdInputModal } from './IdInputModal';
import { useTranslation } from 'react-i18next';
import {MessageOverlay} from '../../../components/MessageOverlay';
import {AddVcModalProps, useAddVcModal} from './AddVcModalController';
import {OtpVerificationModal} from './OtpVerificationModal';
import {IdInputModal} from './IdInputModal';
import {useTranslation} from 'react-i18next';
import {GET_INDIVIDUAL_ID} from '../../../shared/constants';
import {Button, Column} from '../../../components/ui';
export const AddVcModal: React.FC<AddVcModalProps> = (props) => {
const { t } = useTranslation('AddVcModal');
export const AddVcModal: React.FC<AddVcModalProps> = props => {
const {t} = useTranslation('AddVcModal');
const controller = useAddVcModal(props);
const shouldShowAddVcModal = () => {
if (controller.isRequestingCredential) {
GET_INDIVIDUAL_ID({id: '', idType: 'UIN'});
}
return (
(!controller.isAcceptingOtpInput && !controller.isRequestingCredential) ||
!controller.isDownloadCancelled
);
};
const dismissIdInputModal = () => {
GET_INDIVIDUAL_ID({id: '', idType: 'UIN'});
controller.DISMISS();
};
return (
<React.Fragment>
<IdInputModal
service={props.service}
isVisible={
!controller.isAcceptingOtpInput && !controller.isRequestingCredential
}
onDismiss={controller.DISMISS}
isVisible={shouldShowAddVcModal()}
onDismiss={dismissIdInputModal}
onPress={props.onPress}
/>
<OtpVerificationModal
isVisible={controller.isAcceptingOtpInput}
isVisible={
controller.isAcceptingOtpInput || controller.isDownloadCancelled
}
onDismiss={controller.DISMISS}
onInputDone={controller.INPUT_OTP}
error={controller.otpError}
@@ -33,6 +50,26 @@ export const AddVcModal: React.FC<AddVcModalProps> = (props) => {
title={t('requestingCredential')}
progress
/>
<MessageOverlay
isVisible={controller.isDownloadCancelled}
title={t('confirmationDialog.title')}
message={t('confirmationDialog.message')}
customHeight={250}>
<Column>
<Button
type="gradient"
title={t('confirmationDialog.wait')}
onPress={controller.WAIT}
margin={[0, 0, 8, 0]}
/>
<Button
type="clear"
title={t('confirmationDialog.cancel')}
onPress={controller.CANCEL}
/>
</Column>
</MessageOverlay>
</React.Fragment>
);
};

View File

@@ -1,5 +1,5 @@
import { useSelector } from '@xstate/react';
import { ActorRefFrom } from 'xstate';
import {useSelector} from '@xstate/react';
import {ActorRefFrom} from 'xstate';
import {
AddVcModalEvents,
AddVcModalMachine,
@@ -7,9 +7,10 @@ import {
selectIsRequestingCredential,
selectOtpError,
selectIsAcceptingIdInput,
selectIsCancellingDownload,
} from './AddVcModalMachine';
export function useAddVcModal({ service }: AddVcModalProps) {
export function useAddVcModal({service}: AddVcModalProps) {
return {
isRequestingCredential: useSelector(service, selectIsRequestingCredential),
@@ -17,12 +18,17 @@ export function useAddVcModal({ service }: AddVcModalProps) {
isAcceptingUinInput: useSelector(service, selectIsAcceptingIdInput),
isAcceptingOtpInput: useSelector(service, selectIsAcceptingOtpInput),
isDownloadCancelled: useSelector(service, selectIsCancellingDownload),
INPUT_OTP: (otp: string) => service.send(AddVcModalEvents.INPUT_OTP(otp)),
RESEND_OTP: () => service.send(AddVcModalEvents.RESEND_OTP()),
DISMISS: () => service.send(AddVcModalEvents.DISMISS()),
WAIT: () => service.send(AddVcModalEvents.WAIT()),
CANCEL: () => service.send(AddVcModalEvents.CANCEL()),
};
}

View File

@@ -12,6 +12,15 @@ import {BackendResponseError, request} from '../../../shared/request';
import {VcIdType} from '../../../types/VC/ExistingMosipVC/vc';
import i18n from '../../../i18n';
import {VCMetadata} from '../../../shared/VCMetadata';
import {
getErrorEventData,
getImpressionEventData,
getInteractEventData,
sendErrorEvent,
sendImpressionEvent,
sendInteractEvent,
} from '../../../shared/telemetry/TelemetryUtils';
import {API_URLS} from '../../../shared/api';
const model = createModel(
{
@@ -33,6 +42,8 @@ const model = createModel(
VALIDATE_INPUT: () => ({}),
READY: (idInputRef: TextInput) => ({idInputRef}),
DISMISS: () => ({}),
CANCEL: () => ({}),
WAIT: () => ({}),
SELECT_ID_TYPE: (idType: VcIdType) => ({idType}),
},
},
@@ -57,6 +68,9 @@ export const AddVcModalMachine =
INPUT_ID: {
actions: 'setId',
},
SELECT_ID_TYPE: {
actions: ['clearIdError', 'setIdType'],
},
},
states: {
acceptingIdInput: {
@@ -146,6 +160,7 @@ export const AddVcModalMachine =
src: 'requestOtp',
onDone: [
{
actions: 'sendImpressionEvent',
target: '#AddVcModal.acceptingOtpInput',
},
],
@@ -172,8 +187,7 @@ export const AddVcModalMachine =
target: 'requestingCredential',
},
DISMISS: {
actions: 'resetIdInputRef',
target: 'acceptingIdInput',
target: 'cancelDownload',
},
RESEND_OTP: {
target: '.resendOTP',
@@ -200,6 +214,16 @@ export const AddVcModalMachine =
},
},
},
cancelDownload: {
on: {
CANCEL: {
actions: ['resetIdInputRef', 'forwardToParent'],
},
WAIT: {
target: 'acceptingOtpInput',
},
},
},
requestingCredential: {
invoke: {
src: 'requestCredential',
@@ -269,13 +293,17 @@ export const AddVcModalMachine =
context.idType === 'UIN' ? 'invalidUin' : 'invalidVid',
'VID is expired/deactivated': 'deactivatedVid',
};
return ID_ERRORS_MAP[message]
const backendError = ID_ERRORS_MAP[message]
? i18n.t(`errors.backend.${ID_ERRORS_MAP[message]}`, {
ns: 'AddVcModal',
})
: i18n.t(`errors.genericError`, {
ns: 'common',
});
sendErrorEvent(
getErrorEventData('VC Download', message, backendError),
);
return backendError;
},
}),
@@ -318,28 +346,40 @@ export const AddVcModalMachine =
clearOtp: assign({otp: ''}),
focusInput: context => context.idInputRef.focus(),
sendImpressionEvent: () => {
sendImpressionEvent(
getImpressionEventData('VC Download', 'OTP Verification'),
);
},
},
services: {
requestOtp: async context => {
return request('POST', '/residentmobileapp/req/otp', {
id: 'mosip.identity.otp.internal',
individualId: context.id,
metadata: {},
otpChannel: ['PHONE', 'EMAIL'],
requestTime: String(new Date().toISOString()),
transactionID: context.transactionId,
version: '1.0',
});
sendInteractEvent(
getInteractEventData('VC Download', 'CLICK', 'Requesting OTP'),
);
return request(
API_URLS.requestOtp.method,
API_URLS.requestOtp.buildURL(),
{
id: 'mosip.identity.otp.internal',
individualId: context.id,
metadata: {},
otpChannel: ['PHONE', 'EMAIL'],
requestTime: String(new Date().toISOString()),
transactionID: context.transactionId,
version: '1.0',
},
);
},
requestCredential: async context => {
// force wait to fix issue with hanging overlay
await new Promise(resolve => setTimeout(resolve, 1000));
const response = await request(
'POST',
'/residentmobileapp/credentialshare/request',
API_URLS.credentialRequest.method,
API_URLS.credentialRequest.buildURL(),
{
individualId: context.id,
individualIdType: context.idType,
@@ -409,3 +449,7 @@ export function selectIsRequestingOtp(state: State) {
export function selectIsRequestingCredential(state: State) {
return state.matches('requestingCredential');
}
export function selectIsCancellingDownload(state: State) {
return state.matches('cancelDownload');
}

View File

@@ -1,5 +0,0 @@
{
"header": "Downloading your Card",
"bodyText": "This may take some time, we will notify you when your Card has been downloaded and is available",
"backButton": "Back home"
}

View File

@@ -1,7 +0,0 @@
{
"header": "Enter your Application ID to get your UIN",
"getUIN": "Get UIN/VID",
"applicationId": "Application ID",
"requestingOTP": "Requesting OTP...",
"qstnMarkToolTip": "Application ID is available in the acknowledgement received after enrolment."
}

View File

@@ -1,21 +1,22 @@
import React from 'react';
import { Dimensions, I18nManager } from 'react-native';
import { Icon, Input } from 'react-native-elements';
import { Button, Centered, Column, Row, Text } from '../../../components/ui';
import { Modal } from '../../../components/ui/Modal';
import { Theme } from '../../../components/ui/styleUtils';
import {Dimensions, I18nManager} from 'react-native';
import {Icon, Input} from 'react-native-elements';
import {Button, Centered, Column, Row, Text} from '../../../components/ui';
import {Modal} from '../../../components/ui/Modal';
import {Theme} from '../../../components/ui/styleUtils';
import {
GetIdInputModalProps,
useGetIdInputModal,
} from './GetIdInputModalController';
import { KeyboardAvoidingView, Platform } from 'react-native';
import { useTranslation } from 'react-i18next';
import { MessageOverlay } from '../../../components/MessageOverlay';
import {KeyboardAvoidingView, Platform} from 'react-native';
import {useTranslation} from 'react-i18next';
import {MessageOverlay} from '../../../components/MessageOverlay';
import Tooltip from '../../../lib/react-native-elements/tooltip/Tooltip';
import { color } from 'react-native-elements/dist/helpers';
import {color} from 'react-native-elements/dist/helpers';
import {isIOS} from '../../../shared/constants';
export const GetIdInputModal: React.FC<GetIdInputModalProps> = (props) => {
const { t } = useTranslation('GetIdInputModal');
export const GetIdInputModal: React.FC<GetIdInputModalProps> = props => {
const {t} = useTranslation('GetIdInputModal');
const controller = useGetIdInputModal(props);
const inputLabel = t('enterApplicationId');
@@ -27,13 +28,13 @@ export const GetIdInputModal: React.FC<GetIdInputModalProps> = (props) => {
headerTitle={t('header')}
headerElevation={2}>
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
style={{flex: 1}}
behavior={isIOS() ? 'padding' : 'height'}>
<Column fill align="space-between" padding="32 24">
<Column>
<Text
margin="10"
style={{ color: Theme.Colors.GrayText }}
style={{color: Theme.Colors.GrayText}}
weight="regular">
{t('applicationIdLabel')}
</Text>
@@ -86,10 +87,10 @@ export const GetIdInputModal: React.FC<GetIdInputModalProps> = (props) => {
</Centered>
</Tooltip>
}
errorStyle={{ color: Theme.Colors.errorMessage }}
errorStyle={{color: Theme.Colors.errorMessage}}
errorMessage={controller.idError}
onChangeText={controller.INPUT_ID}
ref={(node) => !controller.idInputRef && controller.READY(node)}
ref={node => !controller.idInputRef && controller.READY(node)}
/>
</Row>
</Column>

View File

@@ -1,17 +1,18 @@
import { TextInput } from 'react-native';
import {TextInput} from 'react-native';
import {
assign,
DoneInvokeEvent,
ErrorPlatformEvent,
EventFrom,
DoneInvokeEvent,
sendParent,
StateFrom,
} from 'xstate';
import { createModel } from 'xstate/lib/model';
import { BackendResponseError, request } from '../../../shared/request';
import {createModel} from 'xstate/lib/model';
import {BackendResponseError, request} from '../../../shared/request';
import i18n from '../../../i18n';
import { AddVcModalMachine } from './AddVcModalMachine';
import { GET_INDIVIDUAL_ID } from '../../../shared/constants';
import {AddVcModalMachine} from './AddVcModalMachine';
import {GET_INDIVIDUAL_ID, IndividualId} from '../../../shared/constants';
import {API_URLS} from '../../../shared/api';
const model = createModel(
{
@@ -26,16 +27,16 @@ const model = createModel(
},
{
events: {
INPUT_ID: (id: string) => ({ id }),
INPUT_OTP: (otp: string) => ({ otp }),
INPUT_ID: (id: string) => ({id}),
INPUT_OTP: (otp: string) => ({otp}),
VALIDATE_INPUT: () => ({}),
ACTIVATE_ICON_COLOR: () => ({}),
DEACTIVATE_ICON_COLOR: () => ({}),
READY: (idInputRef: TextInput) => ({ idInputRef }),
READY: (idInputRef: TextInput) => ({idInputRef}),
DISMISS: () => ({}),
GOT_ID: (id: string) => ({ id }),
GOT_ID: (id: string) => ({id}),
},
}
},
);
export const GetVcModalEvents = model.events;
@@ -222,13 +223,14 @@ export const GetVcModalMachine =
transactionId: () => String(new Date().valueOf()).substring(3, 13),
}),
setIndividualId: (_context, event) =>
GET_INDIVIDUAL_ID((event as DoneInvokeEvent<string>).data),
setIndividualId: (_context, event) => {
GET_INDIVIDUAL_ID((event as DoneInvokeEvent<IndividualId>).data);
},
setIdBackendError: assign({
idError: (context, event) => {
if ((event as ErrorPlatformEvent).data == 'IDA-MLC-001') {
return i18n.t('errors.backend.timeOut', { ns: 'GetVcModal' });
return i18n.t('errors.backend.timeOut', {ns: 'GetVcModal'});
}
const message = (event as ErrorPlatformEvent).data.message;
@@ -246,15 +248,15 @@ export const GetVcModalMachine =
},
}),
clearIdError: model.assign({ idError: '' }),
clearIdError: model.assign({idError: ''}),
setIdErrorEmpty: model.assign({
idError: () => i18n.t('errors.input.empty', { ns: 'GetVcModal' }),
idError: () => i18n.t('errors.input.empty', {ns: 'GetVcModal'}),
}),
setIdErrorWrongFormat: model.assign({
idError: () =>
i18n.t('errors.input.invalidFormat', { ns: 'GetVcModal' }),
i18n.t('errors.input.invalidFormat', {ns: 'GetVcModal'}),
}),
setOtpError: assign({
@@ -282,20 +284,20 @@ export const GetVcModalMachine =
},
}),
clearOtp: assign({ otp: '' }),
clearOtp: assign({otp: ''}),
setIconColorActivate: assign({ iconColor: true }),
setIconColorActivate: assign({iconColor: true}),
setIconColorDeactivate: assign({ iconColor: false }),
setIconColorDeactivate: assign({iconColor: false}),
focusInput: (context) => context.idInputRef.focus(),
focusInput: context => context.idInputRef.focus(),
},
services: {
requestOtp: async (context) => {
requestOtp: async context => {
return await request(
'POST',
'/residentmobileapp/req/individualId/otp',
API_URLS.reqIndividualOTP.method,
API_URLS.reqIndividualOTP.buildURL(),
{
id: 'mosip.identity.otp.internal',
aid: context.id,
@@ -304,35 +306,38 @@ export const GetVcModalMachine =
requestTime: String(new Date().toISOString()),
transactionID: context.transactionId,
version: '1.0',
}
},
);
},
requestingUinVid: async (context) => {
requestingUinVid: async context => {
const response = await request(
'POST',
'/residentmobileapp/aid/get-individual-id',
API_URLS.getIndividualId.method,
API_URLS.getIndividualId.buildURL(),
{
aid: context.id,
otp: context.otp,
transactionID: context.transactionId,
}
},
);
return response.response.individualId;
return {
id: response.response.individualId,
idType: response.response.individualIdType || 'UIN',
};
},
},
guards: {
isEmptyId: ({ id }) => !id || !id.length,
isEmptyId: ({id}) => !id || !id.length,
isWrongIdFormat: ({ id }) => !/^\d{14,29}$/.test(id),
isWrongIdFormat: ({id}) => !/^\d{14,29}$/.test(id),
isIdInvalid: (_context, event: unknown) =>
['RES-SER-449', 'IDA-MLC-001'].includes(
(event as BackendResponseError).name
(event as BackendResponseError).name,
),
},
}
},
);
type State = StateFrom<typeof GetVcModalMachine>;

View File

@@ -50,6 +50,7 @@ export const HistoryTab: React.FC<HistoryTabProps> = props => {
/>
<Text
testID="noHistory"
style={{paddingTop: 3}}
align="center"
weight="semibold"
margin="0 0 4 0">

Some files were not shown because too many files have changed in this diff Show More