Merge remote-tracking branch 'upstream/develop' into inji-415-release
2
.bundle/config
Normal file
@@ -0,0 +1,2 @@
|
||||
BUNDLE_PATH: "vendor/bundle"
|
||||
BUNDLE_FORCE_RUBY_PLATFORM: 1
|
||||
11
.env
@@ -1,9 +1,15 @@
|
||||
# after making changes to the env file, ensure to start the bundler (or the project) with a --reset-cache
|
||||
# eg . npm build android:newlogic --reset-cache
|
||||
|
||||
MIMOTO_HOST=https://api.qa-inji.mosip.net
|
||||
#MIMOTO_HOST=http://mock.mimoto.newlogic.dev
|
||||
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 )
|
||||
APPLICATION_THEME=orange
|
||||
|
||||
@@ -12,3 +18,6 @@ CREDENTIAL_REGISTRY_EDIT=true
|
||||
|
||||
#supported languages( en, fil, ar, hi, kn, ta)
|
||||
APPLICATION_LANGUAGE=en
|
||||
|
||||
#Toggle for openID for VC
|
||||
ENABLE_OPENID_FOR_VC=false
|
||||
|
||||
4
.eslintrc.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: '@react-native',
|
||||
};
|
||||
15
.github/workflows/android-beta-build.yml
vendored
@@ -1,7 +1,8 @@
|
||||
name: Android Beta Build
|
||||
|
||||
env:
|
||||
backendServiceDefaultUrl: https://api.sandbox.mosip.net
|
||||
mimotoBackendServiceDefaultUrl: https://api.sandbox.mosip.net
|
||||
esignetBackendServiceDefaultUrl: https://api.sandbox.mosip.net
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -21,8 +22,13 @@ on:
|
||||
required: true
|
||||
default: False
|
||||
type: string
|
||||
backendServiceUrl:
|
||||
description: 'Backend service URL'
|
||||
mimotoBackendServiceUrl:
|
||||
description: 'Mimoto backend service URL'
|
||||
required: true
|
||||
default: 'https://api.sandbox.mosip.net'
|
||||
type: string
|
||||
esignetBackendServiceUrl:
|
||||
description: 'Esignet backend service URL'
|
||||
required: true
|
||||
default: 'https://api.sandbox.mosip.net'
|
||||
type: string
|
||||
@@ -133,7 +139,8 @@ jobs:
|
||||
cd android/scripts
|
||||
./beta-build.sh
|
||||
env:
|
||||
MIMOTO_HOST: ${{ github.event.inputs.backendServiceUrl }}
|
||||
MIMOTO_HOST: ${{ github.event.inputs.mimotoBackendServiceUrl }}
|
||||
ESIGNET_HOST: ${{ github.event.inputs.esignetBackendServiceUrl }}
|
||||
APPLICATION_THEME: ${{ github.event.inputs.theme }}
|
||||
RELEASE_KEYSTORE_ALIAS: androidreleasekey
|
||||
RELEASE_KEYSTORE_PASSWORD: '${{secrets.INJI_ANDROID_RELEASE_STOREPASS}}'
|
||||
|
||||
123
.github/workflows/android-internal-build.yml
vendored
@@ -1,13 +1,25 @@
|
||||
name: Android Internal Build
|
||||
run-name: ${{ github.event.inputs.buildname }}
|
||||
|
||||
env:
|
||||
backendServiceDefaultUrl: https://api.sandbox.mosip.net
|
||||
mimotoBackendServiceDefaultUrl: https://api.sandbox.mosip.net
|
||||
esignetBackendServiceDefaultUrl: https://api.sandbox.mosip.net
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
backendServiceUrl:
|
||||
description: 'Backend service URL'
|
||||
buildname:
|
||||
description: 'Build App For'
|
||||
required: true
|
||||
default: 'Sprint-x/Collab/release-x.x.x'
|
||||
type: string
|
||||
mimotoBackendServiceUrl:
|
||||
description: 'Mimoto backend service URL'
|
||||
required: true
|
||||
default: 'https://api.sandbox.mosip.net'
|
||||
type: string
|
||||
esignetBackendServiceUrl:
|
||||
description: 'Esignet backend service URL'
|
||||
required: true
|
||||
default: 'https://api.sandbox.mosip.net'
|
||||
type: string
|
||||
@@ -41,7 +53,7 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.x'
|
||||
|
||||
|
||||
- name: Cache local npm repository
|
||||
uses: actions/cache@v3.0.11
|
||||
with:
|
||||
@@ -68,7 +80,7 @@ jobs:
|
||||
run: |
|
||||
# Strip git ref prefix from version
|
||||
echo "BRANCH_NAME=$(echo ${{ github.ref }} | sed -e 's,.*/\(.*\),\1,')" >> $GITHUB_ENV
|
||||
echo "GPG_TTY=$(tty)" >> $GITHUB_ENV
|
||||
echo "GPG_TTY=$(tty)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup branch and GPG public key
|
||||
run: |
|
||||
@@ -99,11 +111,11 @@ jobs:
|
||||
cd android/app
|
||||
export RELEASE_KEYSTORE_ALIAS=androidreleasekey
|
||||
export RELEASE_KEYSTORE_PASSWORD=$RELEASE_KEYSTORE_PASSWORD
|
||||
env:
|
||||
env:
|
||||
RELEASE_KEYSTORE_PASSWORD: '${{secrets.INJI_ANDROID_RELEASE_STOREPASS}}'
|
||||
|
||||
- name: Create Google Play Config file
|
||||
run : |
|
||||
run: |
|
||||
cd android
|
||||
echo "$INJI_ANDROID_PLAY_STORE_CONFIG_JSON" > play_config.json.b64
|
||||
base64 -d -i play_config.json.b64 > play_config.json
|
||||
@@ -111,14 +123,103 @@ jobs:
|
||||
INJI_ANDROID_PLAY_STORE_CONFIG_JSON: ${{ secrets.INJI_ANDROID_PLAY_STORE_CONFIG_JSON }}
|
||||
|
||||
- name: Run Build
|
||||
run: |
|
||||
run: |
|
||||
cd android/scripts
|
||||
./internal-build.sh
|
||||
env:
|
||||
MIMOTO_HOST: ${{ github.event.inputs.backendServiceUrl }}
|
||||
APPLICATION_THEME: ${{ github.event.inputs.theme }}
|
||||
MIMOTO_HOST: ${{ github.event.inputs.mimotoBackendServiceUrl }}
|
||||
ESIGNET_HOST: ${{ github.event.inputs.esignetBackendServiceUrl }}
|
||||
APPLICATION_THEME: ${{ github.event.inputs.theme }}
|
||||
RELEASE_KEYSTORE_ALIAS: androidreleasekey
|
||||
RELEASE_KEYSTORE_PASSWORD: '${{secrets.INJI_ANDROID_RELEASE_STOREPASS}}'
|
||||
SLACK_URL: '${{ secrets.SLACK_WEBHOOK_DEVOPS }}'
|
||||
PLAY_CONSOLE_RELEASE_DESCRIPTION: ${{ github.event.inputs.buildDescription }}
|
||||
CREDENTIAL_REGISTRY_EDIT: ${{ github.event.inputs.registry_edit }}
|
||||
CREDENTIAL_REGISTRY_EDIT: ${{ github.event.inputs.registry_edit }}
|
||||
|
||||
upload-to-actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.x'
|
||||
|
||||
- name: Cache local npm repository
|
||||
uses: actions/cache@v3.0.11
|
||||
with:
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
path: ~/.npm
|
||||
|
||||
- name: Cache local gradle repository
|
||||
uses: actions/cache@v3.0.11
|
||||
with:
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: |
|
||||
npm i
|
||||
|
||||
- name: Create .env.local file
|
||||
run: |
|
||||
echo "${{ secrets.ENV_FILE }}" > .env.local > android/local.properties
|
||||
|
||||
- name: Setup branch and env
|
||||
run: |
|
||||
# Strip git ref prefix from version
|
||||
echo "BRANCH_NAME=$(echo ${{ github.ref }} | sed -e 's,.*/\(.*\),\1,')" >> $GITHUB_ENV
|
||||
echo "GPG_TTY=$(tty)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup branch and GPG public key
|
||||
run: |
|
||||
# Strip git ref prefix from version
|
||||
echo ${{ env.BRANCH_NAME }}
|
||||
echo ${{ env.GPG_TTY }}
|
||||
|
||||
sudo apt-get --yes install gnupg2
|
||||
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
|
||||
base64 -d -i release.keystore.b64 > android/app/release.keystore
|
||||
env:
|
||||
ANDROID_KEYSTORE_FILE: ${{ secrets.INJI_ANDROID_RELEASE_KEYSTORE }}
|
||||
|
||||
- name: Create Google Play Config file
|
||||
run: |
|
||||
cd android
|
||||
echo "$INJI_ANDROID_PLAY_STORE_CONFIG_JSON" > play_config.json.b64
|
||||
base64 -d -i play_config.json.b64 > play_config.json
|
||||
env:
|
||||
INJI_ANDROID_PLAY_STORE_CONFIG_JSON: ${{ secrets.INJI_ANDROID_PLAY_STORE_CONFIG_JSON }}
|
||||
|
||||
- name: Run Build for upload
|
||||
run: |
|
||||
npx jetify
|
||||
cd android
|
||||
./gradlew :app:assembleMosipRelease
|
||||
env:
|
||||
MIMOTO_HOST: ${{ github.event.inputs.mimotoBackendServiceUrl }}
|
||||
ESIGNET_HOST: ${{ github.event.inputs.esignetBackendServiceUrl }}
|
||||
APPLICATION_THEME: ${{ github.event.inputs.theme }}
|
||||
RELEASE_KEYSTORE_ALIAS: androidreleasekey
|
||||
RELEASE_KEYSTORE_PASSWORD: '${{secrets.INJI_ANDROID_RELEASE_STOREPASS}}'
|
||||
CREDENTIAL_REGISTRY_EDIT: ${{ github.event.inputs.registry_edit }}
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
with:
|
||||
name: ${{ github.event.inputs.buildname }}
|
||||
path: android/app/build/outputs/apk/mosip/release/
|
||||
retention-days: 10
|
||||
|
||||
72
.github/workflows/ios-automation-build.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
name: Inji iOS Automation Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
mimotoBackendServiceUrl:
|
||||
description: 'Mimoto backend service URL'
|
||||
required: true
|
||||
default: 'https://api.sandbox.mosip.net'
|
||||
type: string
|
||||
esignetBackendServiceUrl:
|
||||
description: 'Esignet backend service URL'
|
||||
required: true
|
||||
default: 'https://api.sandbox.mosip.net'
|
||||
type: string
|
||||
theme:
|
||||
description: 'Application Theme'
|
||||
required: true
|
||||
default: 'orange'
|
||||
type: choice
|
||||
options:
|
||||
- orange
|
||||
- purple
|
||||
registry_edit:
|
||||
description: 'Edit Registry'
|
||||
required: true
|
||||
default: 'true'
|
||||
type: choice
|
||||
options:
|
||||
- false
|
||||
- true
|
||||
|
||||
jobs:
|
||||
build_ios:
|
||||
name: Uploading app to Actions
|
||||
runs-on: macos-13
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.x'
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: |
|
||||
npm install
|
||||
|
||||
- name: Install Pod
|
||||
run: |
|
||||
cd ios
|
||||
pod install
|
||||
pod install
|
||||
|
||||
- name: Build iOS for automation on simulator
|
||||
run: |
|
||||
cd ios
|
||||
fastlane build_app_for_automation_on_simulator
|
||||
|
||||
- name: Upload Inji.app artifact
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
with:
|
||||
name: Inji
|
||||
path: ios/fastlane/Inji_app_file/Products/Applications/
|
||||
retention-days: 1
|
||||
|
||||
- name: Upload Runner Log on Failure
|
||||
if: failure() # Only run this step if the previous steps failed
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
with:
|
||||
name: runner-log
|
||||
path: /Users/runner/Library/Logs/gym/
|
||||
retention-days: 1
|
||||
32
.github/workflows/ios-build.yml
vendored
@@ -1,10 +1,15 @@
|
||||
name: Inji iOS build
|
||||
|
||||
on:
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
backendServiceUrl:
|
||||
description: 'Backend service URL'
|
||||
mimotoBackendServiceUrl:
|
||||
description: 'Mimoto backend service URL'
|
||||
required: true
|
||||
default: 'https://api.sandbox.mosip.net'
|
||||
type: string
|
||||
esignetBackendServiceUrl:
|
||||
description: 'Esignet backend service URL'
|
||||
required: true
|
||||
default: 'https://api.sandbox.mosip.net'
|
||||
type: string
|
||||
@@ -45,22 +50,23 @@ jobs:
|
||||
build_ios:
|
||||
name: Deploying to Testflight
|
||||
runs-on: macos-13
|
||||
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.x'
|
||||
|
||||
node-version: '18.x'
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: |
|
||||
npm install
|
||||
|
||||
|
||||
- name: Install Pod
|
||||
run: |
|
||||
cd ios
|
||||
pod install
|
||||
|
||||
pod install
|
||||
|
||||
- name: Deploy iOS Beta to TestFlight
|
||||
run: |
|
||||
cd ios
|
||||
@@ -82,7 +88,13 @@ 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.backendServiceUrl }}
|
||||
TESTFLIGHT_INTERNAL_TESTERS_GROUP: ${{ github.event.inputs.internal-testers }}
|
||||
TESTFLIGHT_BETA_APP_DESCRIPTION: ${{ github.event.inputs.buildDescription }}
|
||||
|
||||
- name: Upload Runner Log on Failure
|
||||
if: failure() # Only run this step if the previous steps failed
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
with:
|
||||
name: runner-log
|
||||
path: /Users/runner/Library/Logs/gym/
|
||||
retention-days: 1
|
||||
|
||||
32
.gitignore
vendored
@@ -41,6 +41,7 @@ DerivedData
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
ios/.xcode.env.local
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
@@ -50,6 +51,10 @@ build/
|
||||
local.properties
|
||||
*.iml
|
||||
*.hprof
|
||||
*.hprof
|
||||
.cxx/
|
||||
*.keystore
|
||||
!debug.keystore
|
||||
|
||||
# node.js
|
||||
#
|
||||
@@ -57,6 +62,18 @@ node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/
|
||||
**/fastlane/report.xml
|
||||
**/fastlane/Preview.html
|
||||
**/fastlane/screenshots
|
||||
**/fastlane/test_output
|
||||
|
||||
# BUCK
|
||||
buck-out/
|
||||
\.buckd/
|
||||
@@ -64,9 +81,15 @@ buck-out/
|
||||
# Bundle artifacts
|
||||
*.jsbundle
|
||||
|
||||
# CocoaPods
|
||||
ios/Pods/
|
||||
|
||||
# Ruby / CocoaPods
|
||||
/ios/Pods/
|
||||
/vendor/bundle/
|
||||
# Temporary files created by Metro to check the health of the file watcher
|
||||
.metro-health-check*
|
||||
# testing
|
||||
/coverage
|
||||
c
|
||||
# OS X temporary files that should never be committed
|
||||
.DS_Store
|
||||
src/components/.DS_Store
|
||||
@@ -94,3 +117,8 @@ temp/
|
||||
yalc.lock
|
||||
|
||||
android/app/debug.keystore
|
||||
|
||||
# Expo
|
||||
.expo
|
||||
dist/
|
||||
web-build/
|
||||
7
.prettierrc.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
arrowParens: 'avoid',
|
||||
bracketSameLine: true,
|
||||
bracketSpacing: false,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": true,
|
||||
"jsxSingleQuote": false,
|
||||
"quoteProps": "consistent",
|
||||
"printWidth": 80,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false
|
||||
}
|
||||
5
.watchmanconfig
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ignore_dirs": [],
|
||||
"fsevents_latency": 0.5,
|
||||
"fsevents_try_resync": true
|
||||
}
|
||||
63
App.tsx
@@ -1,23 +1,29 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import React, {useContext, useEffect} from 'react';
|
||||
import AppLoading from 'expo-app-loading';
|
||||
import { AppLayout } from './screens/AppLayout';
|
||||
import { useFont } from './shared/hooks/useFont';
|
||||
import { GlobalContextProvider } from './components/GlobalContextProvider';
|
||||
import { GlobalContext } from './shared/GlobalContext';
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {AppLayout} from './screens/AppLayout';
|
||||
import {useFont} from './shared/hooks/useFont';
|
||||
import {GlobalContextProvider} from './components/GlobalContextProvider';
|
||||
import {GlobalContext} from './shared/GlobalContext';
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {
|
||||
selectIsDecryptError,
|
||||
selectIsKeyInvalidateError,
|
||||
selectIsReadError,
|
||||
selectIsReady,
|
||||
} from './machines/app';
|
||||
import { DualMessageOverlay } from './components/DualMessageOverlay';
|
||||
import { useApp } from './screens/AppController';
|
||||
import { Alert } from 'react-native';
|
||||
import { ErrorMessageOverlay } from './components/MessageOverlay';
|
||||
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 {MessageOverlay} from './components/MessageOverlay';
|
||||
import SecureKeystore from 'react-native-secure-keystore';
|
||||
import { isCustomSecureKeystore } from './shared/cryptoutil/cryptoUtil';
|
||||
import {isCustomSecureKeystore} from './shared/cryptoutil/cryptoUtil';
|
||||
import i18n from './i18n';
|
||||
|
||||
// kludge: this is a bad practice but has been done temporarily to surface
|
||||
@@ -35,36 +41,43 @@ const DecryptErrorAlert = (controller, t) => {
|
||||
},
|
||||
]);
|
||||
};
|
||||
function configureTelemetry() {
|
||||
const config = getTelemetryConfigData();
|
||||
initializeTelemetry(config);
|
||||
sendAppInfoEvent(getAppInfoData());
|
||||
}
|
||||
|
||||
const AppLayoutWrapper: React.FC = () => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const isDecryptError = useSelector(appService, selectIsDecryptError);
|
||||
const controller = useApp();
|
||||
const { t } = useTranslation('WelcomeScreen');
|
||||
const {t} = useTranslation('WelcomeScreen');
|
||||
if (isDecryptError) {
|
||||
DecryptErrorAlert(controller, t);
|
||||
}
|
||||
configureTelemetry();
|
||||
return <AppLayout />;
|
||||
};
|
||||
|
||||
const AppLoadingWrapper: React.FC = () => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const isReadError = useSelector(appService, selectIsReadError);
|
||||
const isKeyInvalidateError = useSelector(
|
||||
appService,
|
||||
selectIsKeyInvalidateError
|
||||
selectIsKeyInvalidateError,
|
||||
);
|
||||
const controller = useApp();
|
||||
const { t } = useTranslation('WelcomeScreen');
|
||||
const {t} = useTranslation('WelcomeScreen');
|
||||
return (
|
||||
<>
|
||||
<AppLoading />
|
||||
|
||||
<ErrorMessageOverlay
|
||||
translationPath={'WelcomeScreen'}
|
||||
<MessageOverlay
|
||||
isVisible={isKeyInvalidateError}
|
||||
error={'errors.invalidateKeyError'}
|
||||
onDismiss={controller.RESET}
|
||||
title={t('errors.invalidateKeyError.title')}
|
||||
message={t('errors.invalidateKeyError.message')}
|
||||
onButtonPress={controller.RESET}
|
||||
buttonText={t('common:ok')}
|
||||
customHeight={'auto'}
|
||||
/>
|
||||
|
||||
{isReadError ? (
|
||||
@@ -81,16 +94,16 @@ const AppLoadingWrapper: React.FC = () => {
|
||||
};
|
||||
|
||||
const AppInitialization: React.FC = () => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const isReady = useSelector(appService, selectIsReady);
|
||||
const hasFontsLoaded = useFont();
|
||||
const { t } = useTranslation('common');
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
useEffect(() => {
|
||||
if (isCustomSecureKeystore()) {
|
||||
SecureKeystore.updatePopup(
|
||||
t('biometricPopup.title'),
|
||||
t('biometricPopup.description')
|
||||
t('biometricPopup.description'),
|
||||
);
|
||||
}
|
||||
}, [i18n.language]);
|
||||
|
||||
23
README.md
@@ -174,3 +174,26 @@ More info here:
|
||||
## Credits
|
||||
|
||||
Credits listed [here](/Credits.md)
|
||||
|
||||
## Congratulations! :tada:
|
||||
|
||||
You've successfully run and modified your React Native App. :partying_face:
|
||||
|
||||
### Now what?
|
||||
|
||||
- If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps).
|
||||
- If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started).
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page.
|
||||
|
||||
# Learn More
|
||||
|
||||
To learn more about React Native, take a look at the following resources:
|
||||
|
||||
- [React Native Website](https://reactnative.dev) - learn more about React Native.
|
||||
- [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment.
|
||||
- [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**.
|
||||
- [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts.
|
||||
- [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native.
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
arguments=
|
||||
auto.sync=false
|
||||
build.scans.enabled=false
|
||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
||||
connection.project.dir=
|
||||
eclipse.preferences.version=1
|
||||
gradle.user.home=
|
||||
java.home=C\:/Program Files/OpenJDK/openjdk-11.0.12_7
|
||||
jvm.arguments=
|
||||
offline.mode=false
|
||||
override.workspace.settings=true
|
||||
show.console.view=true
|
||||
show.executions.view=true
|
||||
@@ -1,55 +0,0 @@
|
||||
# To learn about Buck see [Docs](https://buckbuild.com/).
|
||||
# To run your application with Buck:
|
||||
# - install Buck
|
||||
# - `npm start` - to start the packager
|
||||
# - `cd android`
|
||||
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
|
||||
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
|
||||
# - `buck install -r android/app` - compile, install and run application
|
||||
#
|
||||
|
||||
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
|
||||
|
||||
lib_deps = []
|
||||
|
||||
create_aar_targets(glob(["libs/*.aar"]))
|
||||
|
||||
create_jar_targets(glob(["libs/*.jar"]))
|
||||
|
||||
android_library(
|
||||
name = "all-libs",
|
||||
exported_deps = lib_deps,
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = "app-code",
|
||||
srcs = glob([
|
||||
"src/main/java/**/*.java",
|
||||
]),
|
||||
deps = [
|
||||
":all-libs",
|
||||
":build_config",
|
||||
":res",
|
||||
],
|
||||
)
|
||||
|
||||
android_build_config(
|
||||
name = "build_config",
|
||||
package = "io.mosip.residentapp",
|
||||
)
|
||||
|
||||
android_resource(
|
||||
name = "res",
|
||||
package = "io.mosip.residentapp",
|
||||
res = "src/main/res",
|
||||
)
|
||||
|
||||
android_binary(
|
||||
name = "app",
|
||||
keystore = "//android/keystores:debug",
|
||||
manifest = "src/main/AndroidManifest.xml",
|
||||
package_type = "debug",
|
||||
deps = [
|
||||
":app-code",
|
||||
],
|
||||
)
|
||||
@@ -5,152 +5,109 @@ 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
|
||||
|
||||
/**
|
||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||
* and bundleReleaseJsAndAssets).
|
||||
* These basically call `react-native bundle` with the correct arguments during the Android build
|
||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||
* bundle directly from the development server. Below you can see all the possible configurations
|
||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
||||
*
|
||||
* project.ext.react = [
|
||||
* // the name of the generated asset file containing your JS bundle
|
||||
* bundleAssetName: "index.android.bundle",
|
||||
*
|
||||
* // the entry file for bundle generation. If none specified and
|
||||
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
|
||||
* // default. Can be overridden with ENTRY_FILE environment variable.
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // https://reactnative.dev/docs/performance#enable-the-ram-format
|
||||
* bundleCommand: "ram-bundle",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
* // whether to bundle JS and assets in release mode
|
||||
* bundleInRelease: true,
|
||||
*
|
||||
* // whether to bundle JS and assets in another build variant (if configured).
|
||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'bundleIn${productFlavor}${buildType}'
|
||||
* // 'bundleIn${buildType}'
|
||||
* // bundleInFreeDebug: true,
|
||||
* // bundleInPaidRelease: true,
|
||||
* // bundleInBeta: true,
|
||||
*
|
||||
* // whether to disable dev mode in custom build variants (by default only disabled in release)
|
||||
* // for example: to disable dev mode in the staging build type (if configured)
|
||||
* devDisabledInStaging: true,
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'devDisabledIn${productFlavor}${buildType}'
|
||||
* // 'devDisabledIn${buildType}'
|
||||
*
|
||||
* // the root of your project, i.e. where "package.json" lives
|
||||
* root: "../../",
|
||||
*
|
||||
* // where to put the JS bundle asset in debug mode
|
||||
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
||||
*
|
||||
* // where to put the JS bundle asset in release mode
|
||||
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in debug mode
|
||||
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in release mode
|
||||
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
||||
*
|
||||
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
||||
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
||||
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
||||
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
||||
* // for example, you might want to remove it from here.
|
||||
* inputExcludes: ["android/**", "ios/**"],
|
||||
*
|
||||
* // override which node gets called and with what additional arguments
|
||||
* nodeExecutableAndArgs: ["node"],
|
||||
*
|
||||
* // supply additional arguments to the packager
|
||||
* extraPackagerArgs: []
|
||||
* ]
|
||||
*/
|
||||
|
||||
project.ext.react = [
|
||||
enableHermes: (findProperty('expo.jsEngine') ?: "jsc") == "hermes",
|
||||
bundleInDebug: true,
|
||||
bundleInRelease: true,
|
||||
devDisabledInRelease: true,
|
||||
cliPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/cli.js",
|
||||
hermesCommand: new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/%OS-BIN%/hermesc",
|
||||
composeSourceMapsPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/compose-source-maps.js",
|
||||
]
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../react.gradle")
|
||||
react {
|
||||
/* Folders */
|
||||
// The root of your project, i.e. where "package.json" lives. Default is '..'
|
||||
// root = file("../")
|
||||
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
|
||||
// reactNativeDir = file("../node_modules/react-native")
|
||||
// The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
|
||||
// codegenDir = file("../node_modules/@react-native/codegen")
|
||||
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
|
||||
// cliFile = file("../node_modules/react-native/cli.js")
|
||||
/* Variants */
|
||||
// The list of variants to that are debuggable. For those we're going to
|
||||
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
|
||||
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
|
||||
// debuggableVariants = ["liteDebug", "prodDebug"]
|
||||
/* Bundling */
|
||||
// A list containing the node command and its flags. Default is just 'node'.
|
||||
// nodeExecutableAndArgs = ["node"]
|
||||
//
|
||||
// The command to run when bundling. By default is 'bundle'
|
||||
// bundleCommand = "ram-bundle"
|
||||
//
|
||||
// The path to the CLI configuration file. Default is empty.
|
||||
// bundleConfig = file(../rn-cli.config.js)
|
||||
//
|
||||
// The name of the generated asset file containing your JS bundle
|
||||
// bundleAssetName = "MyApplication.android.bundle"
|
||||
//
|
||||
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
|
||||
// entryFile = file("../js/MyApplication.android.js")
|
||||
//
|
||||
// A list of extra flags to pass to the 'bundle' commands.
|
||||
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
|
||||
// extraPackagerArgs = []
|
||||
/* Hermes Commands */
|
||||
// The hermes compiler command to run. By default it is 'hermesc'
|
||||
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
|
||||
//
|
||||
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
|
||||
// hermesFlags = ["-O", "-output-source-map"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true to create two separate APKs instead of one:
|
||||
* - An APK that only works on ARM devices
|
||||
* - An APK that only works on x86 devices
|
||||
* The advantage is the size of the APK is reduced by about 4MB.
|
||||
* Upload all the APKs to the Play Store and people will download
|
||||
* the correct one based on the CPU architecture of their device.
|
||||
* Set this to true to create four separate APKs instead of one,
|
||||
* one for each native architecture. This is useful if you don't
|
||||
* use App Bundles (https://developer.android.com/guide/app-bundle/)
|
||||
* and want to have separate APKs to upload to the Play Store.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = true
|
||||
|
||||
/**
|
||||
* Run Proguard to shrink the Java bytecode in release builds.
|
||||
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore.
|
||||
* The preferred build flavor of JavaScriptCore (JSC)
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||
*
|
||||
* The international variant includes ICU i18n library and necessary data
|
||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* this variant is about 6MiB larger per architecture than default.
|
||||
*/
|
||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||
|
||||
/**
|
||||
* Whether to enable the Hermes VM.
|
||||
*
|
||||
* This should be set on project.ext.react and mirrored here. If it is not set
|
||||
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
||||
* and the benefits of using Hermes will therefore be sharply reduced.
|
||||
* Private function to get the list of Native Architectures you want to build.
|
||||
* This reads the value from reactNativeArchitectures in your gradle.properties
|
||||
* file and works together with the --active-arch-only flag of react-native run-android.
|
||||
*/
|
||||
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||
def reactNativeArchitectures() {
|
||||
def value = project.getProperties().get("reactNativeArchitectures")
|
||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||
}
|
||||
|
||||
androidGitVersion {
|
||||
baseCode 0
|
||||
codeFormat 'MXXNXXPXX'
|
||||
format '% tag %%commit_%%branch%'
|
||||
format '%tag_%%commit_%%branch%'
|
||||
}
|
||||
|
||||
android {
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
ext {
|
||||
APP_NAME_RELEASE = "@string/app_name"
|
||||
APP_NAME_PH = "@string/app_name_ph"
|
||||
APP_NAME_MOSIP = "@string/app_name_mosip"
|
||||
APP_NAME_BETA = "@string/app_name_beta"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
namespace 'io.mosip.residentapp'
|
||||
|
||||
|
||||
namespace 'io.mosip.residentapp'
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'io.mosip.residentapp'
|
||||
@@ -164,7 +121,8 @@ android {
|
||||
|
||||
manifestPlaceholders = [
|
||||
APP_NAME: APP_NAME_RELEASE,
|
||||
GOOGLE_NEARBY_MESSAGES_API_KEY: "${properties.getProperty('GOOGLE_NEARBY_MESSAGES_API_KEY')}"
|
||||
GOOGLE_NEARBY_MESSAGES_API_KEY: "${properties.getProperty('GOOGLE_NEARBY_MESSAGES_API_KEY')}",
|
||||
appAuthRedirectScheme: 'io.mosip.residentapp.inji'
|
||||
]
|
||||
}
|
||||
splits {
|
||||
@@ -175,6 +133,7 @@ android {
|
||||
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
def keystore = file('release.keystore')
|
||||
@@ -233,28 +192,22 @@ android {
|
||||
]
|
||||
dimension "inji"
|
||||
}
|
||||
ph {
|
||||
versionName defaultConfig.versionName + "-ph"
|
||||
manifestPlaceholders = [
|
||||
APP_NAME: APP_NAME_PH
|
||||
]
|
||||
dimension "inji"
|
||||
}
|
||||
}
|
||||
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.all { output ->
|
||||
def datetime = new Date().format('yyyyMMdd_HHmm')
|
||||
def architecture = output.getFilter(com.android.build.OutputFile.ABI) ?: "universal"
|
||||
outputFileName = "${defaultConfig.applicationId}-${variant.versionName}_${datetime}_${architecture}.apk"
|
||||
outputFileName = "Inji_${variant.versionName}_${datetime}_${architecture}.apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation "com.facebook.react:react-native:+"// From node_modules
|
||||
|
||||
// The version of react-native is set by the React Native Gradle Plugin
|
||||
implementation("com.facebook.react:react-android")
|
||||
|
||||
implementation 'com.facebook.soloader:soloader:0.10.1+'
|
||||
|
||||
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
|
||||
@@ -283,9 +236,7 @@ dependencies {
|
||||
}
|
||||
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.fbjni'
|
||||
}
|
||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
|
||||
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||
@@ -294,9 +245,8 @@ dependencies {
|
||||
exclude group:'com.facebook.flipper'
|
||||
}
|
||||
|
||||
if (enableHermes) {
|
||||
debugImplementation files(new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim(), "../android/hermes-debug.aar"))
|
||||
releaseImplementation files(new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim(), "../android/hermes-release.aar"))
|
||||
if (hermesEnabled.toBoolean()) {
|
||||
implementation("com.facebook.react:hermes-android")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
@@ -306,15 +256,7 @@ dependencies {
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
// puts all compile dependencies into folder libs for BUCK to use
|
||||
task copyDownloadableDepsToLibs(type: Copy) {
|
||||
from configurations.compile
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
|
||||
applyNativeModulesAppBuildGradle(project)
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
apply from: "./eas-build.gradle"
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
"""Helper definitions to glob .aar and .jar targets"""
|
||||
|
||||
def create_aar_targets(aarfiles):
|
||||
for aarfile in aarfiles:
|
||||
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
|
||||
lib_deps.append(":" + name)
|
||||
android_prebuilt_aar(
|
||||
name = name,
|
||||
aar = aarfile,
|
||||
)
|
||||
|
||||
def create_jar_targets(jarfiles):
|
||||
for jarfile in jarfiles:
|
||||
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
|
||||
lib_deps.append(":" + name)
|
||||
prebuilt_jar(
|
||||
name = name,
|
||||
binary_jar = jarfile,
|
||||
)
|
||||
@@ -12,5 +12,8 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/>
|
||||
<!-- from Android 12, Location permission isn't required as we aren't using it to determine location -->
|
||||
|
||||
<application tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" android:usesCleartextTraffic="true" />
|
||||
<application tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" android:usesCleartextTraffic="true" >
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
@@ -17,22 +17,27 @@ import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||
import com.facebook.react.ReactInstanceEventListener;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.modules.network.NetworkingModule;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
/**
|
||||
* Class responsible of loading Flipper inside your React Native application. This is the debug
|
||||
* flavor of it. Here you can add your own plugins and customize the Flipper setup.
|
||||
*/
|
||||
public class ReactNativeFlipper {
|
||||
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (FlipperUtils.shouldEnableFlipper(context)) {
|
||||
final FlipperClient client = AndroidFlipperClient.getInstance(context);
|
||||
|
||||
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
|
||||
client.addPlugin(new ReactFlipperPlugin());
|
||||
client.addPlugin(new DatabasesFlipperPlugin(context));
|
||||
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
|
||||
client.addPlugin(CrashReporterPlugin.getInstance());
|
||||
|
||||
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
|
||||
NetworkingModule.setCustomClientBuilder(
|
||||
new NetworkingModule.CustomClientBuilder() {
|
||||
@@ -43,12 +48,13 @@ public class ReactNativeFlipper {
|
||||
});
|
||||
client.addPlugin(networkFlipperPlugin);
|
||||
client.start();
|
||||
|
||||
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
|
||||
// Hence we run if after all native modules have been initialized
|
||||
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext == null) {
|
||||
reactInstanceManager.addReactInstanceEventListener(
|
||||
new ReactInstanceManager.ReactInstanceEventListener() {
|
||||
new ReactInstanceEventListener() {
|
||||
@Override
|
||||
public void onReactContextInitialized(ReactContext reactContext) {
|
||||
reactInstanceManager.removeReactInstanceEventListener(this);
|
||||
|
||||
@@ -1,54 +1,51 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.mosip.residentapp">
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/>
|
||||
<!-- from Android 12, Location permission isn't required as we aren't using it to determine location -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
|
||||
android:usesPermissionFlags="neverForLocation" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.NFC"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="https" />
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="https"/>
|
||||
</intent>
|
||||
</queries>
|
||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme">
|
||||
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true" />
|
||||
<meta-data android:name="expo.modules.updates.EXPO_SDK_VERSION" android:value="43.0.0" />
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS" />
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0" />
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://exp.host/@nlpaolo/mosip-resident-app" />
|
||||
<meta-data android:name="com.google.android.nearby.messages.API_KEY" android:value="${GOOGLE_NEARBY_MESSAGES_API_KEY}" />
|
||||
<activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustPan" android:theme="@style/Theme.App.SplashScreen" android:screenOrientation="portrait">
|
||||
|
||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme">
|
||||
<meta-data android:name="com.google.android.nearby.messages.API_KEY" android:value="${GOOGLE_NEARBY_MESSAGES_API_KEY}"/>
|
||||
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_SDK_VERSION" android:value="48.0.0"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://exp.host/@anonymous/inji"/>
|
||||
<activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|locale|layoutDirection" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="io.mosip.residentapp" />
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="io.mosip.residentapp"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
|
||||
</application>
|
||||
</manifest>
|
||||
</manifest>
|
||||
@@ -1,4 +1,5 @@
|
||||
package io.mosip.residentapp;
|
||||
import expo.modules.ReactActivityDelegateWrapper;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
@@ -13,6 +14,9 @@ import androidx.core.content.ContextCompat;
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate;
|
||||
import expo.modules.ReactActivityDelegateWrapper;
|
||||
|
||||
/**
|
||||
@@ -108,4 +112,20 @@ public class MainActivity extends ReactActivity {
|
||||
}
|
||||
recreate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
|
||||
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
|
||||
* (aka React 18) with two boolean flags.
|
||||
*/
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new ReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, new DefaultReactActivityDelegate( this,
|
||||
getMainComponentName(),
|
||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||
DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled
|
||||
// If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18).
|
||||
DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package io.mosip.residentapp;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
|
||||
|
||||
import expo.modules.ReactActivityDelegateWrapper;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
// Set the theme to AppTheme BEFORE onCreate to support
|
||||
// coloring the background, status bar, and navigation bar.
|
||||
// This is required for expo-splash-screen.
|
||||
setTheme(R.style.AppTheme);
|
||||
super.onCreate(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "main";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new ReactActivityDelegateWrapper(
|
||||
this,
|
||||
new ReactActivityDelegate(this, getMainComponentName()) {
|
||||
@Override
|
||||
protected ReactRootView createRootView() {
|
||||
return new RNGestureHandlerEnabledRootView(MainActivity.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
package io.mosip.residentapp;
|
||||
import expo.modules.ApplicationLifecycleDispatcher;
|
||||
import expo.modules.ReactNativeHostWrapper;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
@@ -12,10 +14,10 @@ import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.defaults.DefaultReactNativeHost;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import expo.modules.ApplicationLifecycleDispatcher;
|
||||
import expo.modules.ReactNativeHostWrapper;
|
||||
import timber.log.Timber;
|
||||
|
||||
import com.facebook.react.bridge.JSIModulePackage;
|
||||
@@ -26,9 +28,8 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
private final ReactNativeHost mReactNativeHost = new ReactNativeHostWrapper(
|
||||
this,
|
||||
new ReactNativeHost(this) {
|
||||
private final ReactNativeHost mReactNativeHost =
|
||||
new ReactNativeHostWrapper(this, new DefaultReactNativeHost(this) {
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
@@ -47,6 +48,14 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
@Override
|
||||
protected boolean isNewArchEnabled() {
|
||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
}
|
||||
@Override
|
||||
protected Boolean isHermesEnabled() {
|
||||
return BuildConfig.IS_HERMES_ENABLED;
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
@@ -59,8 +68,11 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
super.onCreate();
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
|
||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
ApplicationLifecycleDispatcher.onApplicationCreate(this);
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||
DefaultNewArchitectureEntryPoint.load();
|
||||
}
|
||||
ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.plant(new Timber.DebugTree());
|
||||
@@ -68,44 +80,10 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
// Setup Firebase
|
||||
FirebaseAnalytics.getInstance(this);
|
||||
Timber.plant(new CrashReportingTree());
|
||||
ApplicationLifecycleDispatcher.onApplicationCreate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
|
||||
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
*
|
||||
* @param context
|
||||
* @param reactInstanceManager
|
||||
*/
|
||||
private static void initializeFlipper(
|
||||
Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
try {
|
||||
/*
|
||||
We use reflection here to pick up the class that initializes Flipper,
|
||||
since Flipper library is not available in release mode
|
||||
*/
|
||||
Class<?> aClass = Class.forName("io.mosip.residentapp.ReactNativeFlipper");
|
||||
aClass
|
||||
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
|
||||
.invoke(null, context, reactInstanceManager);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A tree which logs important information for crash reporting.
|
||||
*/
|
||||
@@ -128,4 +106,10 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB |
BIN
android/app/src/main/res/drawable/splash_image.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
@@ -1,5 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:drawable="@color/splashscreen_background"/>
|
||||
</layer-list>
|
||||
|
||||
<!-- Splash screen image -->
|
||||
<item>
|
||||
<bitmap
|
||||
android:src="@drawable/splash_image"
|
||||
android:gravity="center" />
|
||||
</item>
|
||||
</layer-list>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_mosip"/>
|
||||
</adaptive-icon>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_mosip"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 17 KiB |
@@ -1,2 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<resources/>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<resources>
|
||||
<color name="splashscreen_background">#FFFFFF</color>
|
||||
<color name="iconBackground">#FFFFFF</color>
|
||||
<color name="colorPrimary">#023c69</color>
|
||||
<color name="colorPrimaryDark">#ffffff</color>
|
||||
<color name="ic_launcher_background">#ffffff</color>
|
||||
</resources>
|
||||
@@ -1,4 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<resources>
|
||||
<string name="app_name">Inji</string>
|
||||
<string name="app_name_mosip">MOSIP Resident App - Mosip/Inji</string>
|
||||
@@ -6,4 +5,5 @@
|
||||
<string name="app_name_ph">MOSIP Resident App - PH</string>
|
||||
<string name="expo_splash_screen_resize_mode" translatable="false">cover</string>
|
||||
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
|
||||
<string name="ExpoLocalization_supportsRTL" translatable="false">undefined</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*/
|
||||
package io.mosip.residentapp;
|
||||
|
||||
import android.content.Context;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
|
||||
/**
|
||||
* Class responsible of loading Flipper inside your React Native application. This is the release
|
||||
* flavor of it so it's empty as we don't want to load Flipper.
|
||||
*/
|
||||
public class ReactNativeFlipper {
|
||||
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||
// Do nothing as we don't want to initialize Flipper on Release.
|
||||
}
|
||||
}
|
||||
@@ -2,53 +2,29 @@
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "29.0.3"
|
||||
buildToolsVersion = "33.0.0"
|
||||
minSdkVersion = 23
|
||||
compileSdkVersion = 33
|
||||
targetSdkVersion = 33
|
||||
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
|
||||
ndkVersion = "21.4.7075529"
|
||||
kotlinVersion = "1.9.0"
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
gradlePluginPortal()
|
||||
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:4.2.2")
|
||||
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'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../android"))
|
||||
}
|
||||
maven {
|
||||
// Android JSC is installed from npm
|
||||
url(new File(["node", "--print", "require.resolve('jsc-android/package.json')"].execute(null, rootDir).text.trim(), "../dist"))
|
||||
}
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
force "com.facebook.soloader:soloader:0.10.1"
|
||||
}
|
||||
}
|
||||
|
||||
google()
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
jcenter()
|
||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
allprojects { repositories { maven { url "$rootDir/../node_modules/expo-camera/android/maven" } } }
|
||||
|
||||
|
||||
// @generated begin expo-camera-import - expo prebuild (DO NOT MODIFY) sync-f244f4f3d8bf7229102e8f992b525b8602c74770
|
||||
def expoCameraMavenPath = new File(["node", "--print", "require.resolve('expo-camera/package.json')"].execute(null, rootDir).text.trim(), "../android/maven")
|
||||
allprojects { repositories { maven { url(expoCameraMavenPath) } } }
|
||||
// @generated end expo-camera-import
|
||||
@@ -1,6 +1,7 @@
|
||||
default_platform(:android)
|
||||
|
||||
MIMOTO_HOST = ENV["MIMOTO_HOST"]
|
||||
ESIGNET_HOST = ENV["ESIGNET_HOST"]
|
||||
APPLICATION_THEME = ENV["APPLICATION_THEME"]
|
||||
RELEASE_KEYSTORE_ALIAS = ENV["RELEASE_KEYSTORE_ALIAS"]
|
||||
RELEASE_KEYSTORE_PASSWORD = ENV["RELEASE_KEYSTORE_PASSWORD"]
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
@@ -26,19 +26,35 @@ android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
# Version of flipper SDK to use with React Native
|
||||
FLIPPER_VERSION=0.99.0
|
||||
FLIPPER_VERSION=0.125.0
|
||||
|
||||
# The hosted JavaScript engine
|
||||
# Supported values: expo.jsEngine = "hermes" | "jsc"
|
||||
expo.jsEngine=jsc
|
||||
# # The hosted JavaScript engine
|
||||
# # Supported values: expo.jsEngine = "hermes" | "jsc"
|
||||
# expo.jsEngine=jsc
|
||||
|
||||
# Enable GIF support in React Native images (~200 B increase)
|
||||
expo.gif.enabled=true
|
||||
# Enable webp support in React Native images (~85 KB increase)
|
||||
expo.webp.enabled=true
|
||||
# Enable animated webp support (~3.4 MB increase)
|
||||
# Disabled by default because iOS doesn't support animated webp
|
||||
expo.webp.animated=false
|
||||
# # Enable GIF support in React Native images (~200 B increase)
|
||||
# expo.gif.enabled=true
|
||||
# # Enable webp support in React Native images (~85 KB increase)
|
||||
# expo.webp.enabled=true
|
||||
# # Enable animated webp support (~3.4 MB increase)
|
||||
# # Disabled by default because iOS doesn't support animated webp
|
||||
# expo.webp.animated=false
|
||||
|
||||
# If the AsyncStorage_db_size_in_MB is not configured, Default DB_MAX_SIZE is 6MB in Android
|
||||
AsyncStorage_db_size_in_MB=30
|
||||
# # If the AsyncStorage_db_size_in_MB is not configured, Default DB_MAX_SIZE is 6MB in Android
|
||||
# AsyncStorage_db_size_in_MB=30
|
||||
|
||||
# Use this property to specify which architecture you want to build.
|
||||
# You can also override it from the CLI using
|
||||
# ./gradlew <task> -PreactNativeArchitectures=x86_64
|
||||
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
# Use this property to enable support to the new architecture.
|
||||
# This will allow you to use TurboModules and the Fabric render in
|
||||
# your application. You should enable this flag either if you want
|
||||
# to write custom TurboModules/Fabric components OR use libraries that
|
||||
# are providing them.
|
||||
newArchEnabled=false
|
||||
# Use this property to enable or disable the Hermes JS engine.
|
||||
# If set to false, you will be using JSC instead.
|
||||
hermesEnabled=true
|
||||
|
||||
expo.jsEngine=hermes
|
||||
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
@@ -1,5 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
269
android/gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -17,78 +17,113 @@
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
@@ -105,79 +140,95 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
37
android/gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -54,7 +55,7 @@ goto fail
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
@@ -64,40 +65,28 @@ echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
:omega
|
||||
@@ -1,9 +1,8 @@
|
||||
rootProject.name = 'Inji'
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
|
||||
useExpoModules()
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
|
||||
applyNativeModulesSettingsGradle(settings)
|
||||
|
||||
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
||||
include ':app'
|
||||
includeBuild('../node_modules/react-native-gradle-plugin')
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle")
|
||||
useExpoModules()
|
||||
@@ -14,7 +14,7 @@ export default {
|
||||
},
|
||||
assetBundlePatterns: ['**/*'],
|
||||
ios: {
|
||||
bundleIdentifier: 'io.mosip.residentapp',
|
||||
bundleIdentifier: 'io.mosip.inji.mobileid',
|
||||
buildNumber: '1.0.0',
|
||||
supportsTablet: true,
|
||||
},
|
||||
@@ -28,4 +28,5 @@ export default {
|
||||
},
|
||||
platforms: ['android', 'ios'],
|
||||
privacy: 'hidden',
|
||||
plugins: ['expo-localization'],
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
assets/capture-button.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/card_bg.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
assets/digit-icon.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
assets/no-internet-connection.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/pending_activation.png
Normal file
|
After Width: | Height: | Size: 673 B |
BIN
assets/pin_icon.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/something-went-wrong.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 140 KiB |
31
components/BannerNotification.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {Row, Text} from './ui';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import testIDProps from '../shared/commonUtil';
|
||||
|
||||
export const BannerNotification: React.FC<BannerNotificationProps> = props => {
|
||||
return (
|
||||
<View {...testIDProps(props.testId)}>
|
||||
<Row style={Theme.Styles.downloadingVcPopUp}>
|
||||
<Text color={Theme.Colors.whiteText} weight="semibold" size="smaller">
|
||||
{props.message}
|
||||
</Text>
|
||||
<Icon
|
||||
testID="close"
|
||||
name="close"
|
||||
onPress={props.onClosePress}
|
||||
color={Theme.Colors.whiteText}
|
||||
size={19}
|
||||
/>
|
||||
</Row>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export interface BannerNotificationProps {
|
||||
message: string;
|
||||
onClosePress: () => void;
|
||||
testId: string;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Dimensions, View } from 'react-native';
|
||||
import { Overlay } from 'react-native-elements';
|
||||
import { Column, Row, Text } from './ui';
|
||||
import { Button } from './ui/Button';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Dimensions, StyleSheet} from 'react-native';
|
||||
import {Overlay} from 'react-native-elements';
|
||||
import {Column, Row, Text} from './ui';
|
||||
import {Button} from './ui/Button';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
|
||||
/**
|
||||
* DualMessageOverlay is like MessageOverlay but with two buttons
|
||||
@@ -12,10 +12,18 @@ import { Theme } from './ui/styleUtils';
|
||||
* NOTE: This has been added for surfacing bugs and needs to be refactored
|
||||
* before use.
|
||||
*/
|
||||
export const DualMessageOverlay: React.FC<DualMessageOverlayProps> = (
|
||||
props
|
||||
) => {
|
||||
const { t } = useTranslation('common');
|
||||
export const DualMessageOverlay: React.FC<DualMessageOverlayProps> = props => {
|
||||
const {t} = useTranslation('common');
|
||||
const style = StyleSheet.create({
|
||||
customHeight: {
|
||||
height: props.customHeight
|
||||
? props.customHeight
|
||||
: props.progress
|
||||
? 100
|
||||
: 150,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Overlay
|
||||
isVisible={props.isVisible}
|
||||
@@ -24,11 +32,7 @@ export const DualMessageOverlay: React.FC<DualMessageOverlayProps> = (
|
||||
onBackdropPress={props.onBackdropPress}>
|
||||
<Column
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
style={
|
||||
!props.progress
|
||||
? Theme.MessageOverlayStyles.popupOverLay
|
||||
: { height: 230 }
|
||||
}>
|
||||
style={[Theme.MessageOverlayStyles.popupOverLay, style.customHeight]}>
|
||||
<Column padding="21" crossAlign="center">
|
||||
{props.title && (
|
||||
<Text
|
||||
@@ -59,7 +63,7 @@ export const DualMessageOverlay: React.FC<DualMessageOverlayProps> = (
|
||||
)}
|
||||
{props.children}
|
||||
</Column>
|
||||
<Column style={{ marginBottom: 10 }}>
|
||||
<Column style={{marginBottom: 10}}>
|
||||
<Row style={Theme.MessageOverlayStyles.buttonContainer}>
|
||||
{!props.children && props.onTryAgain ? (
|
||||
<Button
|
||||
@@ -103,4 +107,5 @@ export interface DualMessageOverlayProps {
|
||||
onBackdropPress?: () => void;
|
||||
onShow?: () => void;
|
||||
onTryAgain?: () => void;
|
||||
customHeight?: number | string | undefined;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,31 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Dimensions, I18nManager } from 'react-native';
|
||||
import { Icon, ListItem, Overlay, Input } from 'react-native-elements';
|
||||
import { Text, Column, Row, Button } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Dimensions, I18nManager} from 'react-native';
|
||||
import {Icon, ListItem, Overlay, Input} from 'react-native-elements';
|
||||
import {Text, Column, Row, Button} from './ui';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
const { t } = useTranslation('common');
|
||||
export const EditableListItem: React.FC<EditableListItemProps> = props => {
|
||||
const {t} = useTranslation('common');
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [newValue, setNewValue] = useState(props.value);
|
||||
const [items, setItems] = useState(props.items);
|
||||
const [overlayOpened, setOverlayOpened] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.credentialRegistryResponse === 'success') {
|
||||
if (props.response === 'success') {
|
||||
closePopup();
|
||||
}
|
||||
}, [props.credentialRegistryResponse]);
|
||||
}, [props.response]);
|
||||
|
||||
function updateItems(label: string, value: string) {
|
||||
const updatedItems = items.map(item => {
|
||||
if (item.label === label) {
|
||||
return {...item, value: value};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
setItems(updatedItems);
|
||||
}
|
||||
|
||||
return (
|
||||
<ListItem bottomDivider topDivider onPress={() => setIsEditing(true)}>
|
||||
@@ -29,10 +39,10 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Text weight="semibold" color={Theme.Colors.profileLabel}>
|
||||
{props.label}
|
||||
{props.title}
|
||||
</Text>
|
||||
</ListItem.Title>
|
||||
<Text color={Theme.Colors.profileValue}>{props.value}</Text>
|
||||
<Text color={Theme.Colors.profileValue}>{props.content}</Text>
|
||||
</ListItem.Content>
|
||||
<Icon
|
||||
name="chevron-right"
|
||||
@@ -40,26 +50,32 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
color={Theme.Colors.profileLanguageValue}
|
||||
/>
|
||||
<Overlay
|
||||
overlayStyle={{ padding: 24, elevation: 6 }}
|
||||
overlayStyle={{padding: 24, elevation: 6}}
|
||||
isVisible={isEditing}
|
||||
onBackdropPress={dismiss}>
|
||||
<Column width={Dimensions.get('screen').width * 0.8}>
|
||||
<Text>{t('editLabel', { label: props.label })}</Text>
|
||||
<Input
|
||||
autoFocus
|
||||
value={newValue}
|
||||
onChangeText={setNewValue}
|
||||
selectionColor={Theme.Colors.Cursor}
|
||||
inputStyle={{
|
||||
textAlign: I18nManager.isRTL ? 'right' : 'left',
|
||||
}}
|
||||
/>
|
||||
{props.credentialRegistryResponse === 'error' && (
|
||||
<Text style={Theme.TextStyles.error}>{props.errorMessage}</Text>
|
||||
)}
|
||||
{props.credentialRegistryResponse === 'success' &&
|
||||
overlayOpened &&
|
||||
closePopup()}
|
||||
{props.items.map((item: ListItemProps, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<Text>{t('editLabel', {label: item.label})}</Text>
|
||||
<Input
|
||||
autoFocus
|
||||
value={items[index].value}
|
||||
onChangeText={value => updateItems(item.label, value)}
|
||||
selectionColor={Theme.Colors.Cursor}
|
||||
inputStyle={{
|
||||
textAlign: I18nManager.isRTL ? 'right' : 'left',
|
||||
}}
|
||||
/>
|
||||
{index === 0 && props.response === 'error' && (
|
||||
<Text style={Theme.TextStyles.error}>
|
||||
{props.errorMessage}
|
||||
</Text>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
{props.response === 'success' && overlayOpened && closePopup()}
|
||||
<Row>
|
||||
<Button fill type="clear" title={t('cancel')} onPress={dismiss} />
|
||||
<Button
|
||||
@@ -75,14 +91,13 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
);
|
||||
|
||||
function edit() {
|
||||
props.onEdit(newValue);
|
||||
if (props.credentialRegistryResponse === undefined) {
|
||||
props.onEdit(items);
|
||||
if (props.response === undefined) {
|
||||
setIsEditing(false);
|
||||
}
|
||||
}
|
||||
|
||||
function dismiss() {
|
||||
setNewValue(props.value);
|
||||
setIsEditing(false);
|
||||
props.onCancel();
|
||||
}
|
||||
@@ -94,14 +109,20 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
};
|
||||
|
||||
interface EditableListItemProps {
|
||||
label: string;
|
||||
value: string;
|
||||
title: string;
|
||||
content: string;
|
||||
items: ListItemProps[];
|
||||
Icon: string;
|
||||
IconType?: string;
|
||||
onEdit: (newValue: string) => void;
|
||||
onEdit: (values: ListItemProps[]) => void;
|
||||
display?: 'none' | 'flex';
|
||||
credentialRegistryResponse: string;
|
||||
response?: string;
|
||||
onCancel: () => void;
|
||||
progress?: boolean;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
interface ListItemProps {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import React, { useCallback, useContext, useEffect, useRef } from 'react';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
import { Camera } from 'expo-camera';
|
||||
import { Platform, StyleSheet } from 'react-native';
|
||||
import { Button, Centered, Column, Row, Text } from './ui';
|
||||
import { useInterpret, useSelector } from '@xstate/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import React, {useCallback, useContext, useEffect, useRef} from 'react';
|
||||
import {Camera} from 'expo-camera';
|
||||
import {
|
||||
Platform,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
Image,
|
||||
} from 'react-native';
|
||||
import {Button, Centered, Column, Row, Text} from './ui';
|
||||
import {useInterpret, useSelector} from '@xstate/react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {
|
||||
FaceScannerEvents,
|
||||
selectIsCheckingPermission,
|
||||
@@ -17,14 +22,14 @@ import {
|
||||
selectIsCapturing,
|
||||
selectIsVerifying,
|
||||
} from '../machines/faceScanner';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
import { selectIsActive } from '../machines/app';
|
||||
import { RotatingIcon } from './RotatingIcon';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import {GlobalContext} from '../shared/GlobalContext';
|
||||
import {selectIsActive} from '../machines/app';
|
||||
import {RotatingIcon} from './RotatingIcon';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
|
||||
export const FaceScanner: React.FC<FaceScannerProps> = (props) => {
|
||||
const { t } = useTranslation('FaceScanner');
|
||||
const { appService } = useContext(GlobalContext);
|
||||
export const FaceScanner: React.FC<FaceScannerProps> = props => {
|
||||
const {t} = useTranslation('FaceScanner');
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const isActive = useSelector(appService, selectIsActive);
|
||||
|
||||
const machine = useRef(createFaceScannerMachine(props.vcImage));
|
||||
@@ -46,7 +51,7 @@ export const FaceScanner: React.FC<FaceScannerProps> = (props) => {
|
||||
service.send(FaceScannerEvents.READY(node));
|
||||
}
|
||||
},
|
||||
[isScanning]
|
||||
[isScanning],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -80,34 +85,48 @@ export const FaceScanner: React.FC<FaceScannerProps> = (props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Column crossAlign="center">
|
||||
<Column style={[styles.scannerContainer]}>
|
||||
<View>
|
||||
<View style={Theme.Styles.scannerContainer}>
|
||||
<Camera
|
||||
ratio="4:3"
|
||||
style={styles.scanner}
|
||||
style={Theme.Styles.scanner}
|
||||
type={whichCamera}
|
||||
ref={setCameraRef}
|
||||
/>
|
||||
</Column>
|
||||
</View>
|
||||
<Text
|
||||
align="center"
|
||||
weight="semibold"
|
||||
style={Theme.TextStyles.base}
|
||||
margin="50 57 0 57">
|
||||
{t('imageCaptureGuide')}
|
||||
</Text>
|
||||
<Centered margin="24 0">
|
||||
{isCapturing || isVerifying ? (
|
||||
<RotatingIcon name="sync" size={64} />
|
||||
) : (
|
||||
<Row crossAlign="center">
|
||||
<Icon
|
||||
name="flip-camera-ios"
|
||||
color={Theme.Colors.flipCameraIcon}
|
||||
size={64}
|
||||
onPress={() => service.send(FaceScannerEvents.FLIP_CAMERA())}
|
||||
style={{ margin: 8, marginEnd: 32 }}
|
||||
/>
|
||||
<Icon
|
||||
name="photo-camera"
|
||||
color={Theme.Colors.flipCameraIcon}
|
||||
size={64}
|
||||
onPress={() => service.send(FaceScannerEvents.CAPTURE())}
|
||||
style={{ margin: 8, marginTop: 12, marginStart: 32 }}
|
||||
/>
|
||||
<Row align="center" style={{marginTop: 42}}>
|
||||
<Centered style={Theme.Styles.imageCaptureButton}>
|
||||
<TouchableOpacity
|
||||
onPress={() => service.send(FaceScannerEvents.CAPTURE())}>
|
||||
<Image source={Theme.ImageCaptureButton} />
|
||||
</TouchableOpacity>
|
||||
<Text size="small" weight="semibold" margin="8">
|
||||
{t('capture')}
|
||||
</Text>
|
||||
</Centered>
|
||||
|
||||
<Centered>
|
||||
<TouchableOpacity
|
||||
onPress={() => service.send(FaceScannerEvents.FLIP_CAMERA())}>
|
||||
<Image
|
||||
source={Theme.CameraFlipIcon}
|
||||
style={Theme.Styles.cameraFlipIcon}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<Text size="smaller" weight="semibold" margin="8">
|
||||
{t('flipCamera')}
|
||||
</Text>
|
||||
</Centered>
|
||||
</Row>
|
||||
)}
|
||||
{/* TODO: remove warning when iOS SDK is ready */}
|
||||
@@ -117,7 +136,7 @@ export const FaceScanner: React.FC<FaceScannerProps> = (props) => {
|
||||
</Text>
|
||||
)}
|
||||
</Centered>
|
||||
</Column>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -126,21 +145,3 @@ interface FaceScannerProps {
|
||||
onValid: () => void;
|
||||
onInvalid: () => void;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scannerContainer: {
|
||||
borderWidth: 4,
|
||||
borderColor: Theme.Colors.textValue,
|
||||
borderRadius: 32,
|
||||
justifyContent: 'center',
|
||||
height: 400,
|
||||
width: 300,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
|
||||
scanner: {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
margin: 'auto',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Pressable } from 'react-native';
|
||||
import { Modal } from './ui/Modal';
|
||||
import { ScrollView } from 'react-native-gesture-handler';
|
||||
import { MainRouteProps } from '../routes/main';
|
||||
import { Column, Text } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import React, {useState} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Pressable} from 'react-native';
|
||||
import {Modal} from './ui/Modal';
|
||||
import {ScrollView} from 'react-native-gesture-handler';
|
||||
import {MainRouteProps} from '../routes/main';
|
||||
import {Column, Text} from './ui';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
|
||||
export const HelpScreen: React.FC<HelpScreenProps & MainRouteProps> = (
|
||||
props
|
||||
) => {
|
||||
const { t } = useTranslation('HelpScreen');
|
||||
export const HelpScreen: React.FC<HelpScreenProps & MainRouteProps> = props => {
|
||||
const {t} = useTranslation('HelpScreen');
|
||||
|
||||
const [showHelpPage, setShowHelpPage] = useState(false);
|
||||
|
||||
@@ -23,6 +21,7 @@ export const HelpScreen: React.FC<HelpScreenProps & MainRouteProps> = (
|
||||
{props.triggerComponent}
|
||||
</Pressable>
|
||||
<Modal
|
||||
testID="helpScreen"
|
||||
isVisible={showHelpPage}
|
||||
headerTitle={t('header')}
|
||||
headerElevation={2}
|
||||
@@ -75,5 +74,6 @@ export const HelpScreen: React.FC<HelpScreenProps & MainRouteProps> = (
|
||||
};
|
||||
|
||||
interface HelpScreenProps {
|
||||
testID?: string;
|
||||
triggerComponent: React.ReactElement;
|
||||
}
|
||||
|
||||
@@ -1,32 +1,40 @@
|
||||
import React from 'react';
|
||||
import { Icon, ListItem, Overlay } from 'react-native-elements';
|
||||
import { Theme } from '../components/ui/styleUtils';
|
||||
import { Column, Row, Text } from '../components/ui';
|
||||
import { WalletBinding } from '../screens/Home/MyVcs/WalletBinding';
|
||||
import { Pressable } from 'react-native';
|
||||
import { useKebabPopUp } from './KebabPopUpController';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { vcItemMachine } from '../machines/vcItem';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { HistoryTab } from '../screens/Home/MyVcs/HistoryTab';
|
||||
import { RemoveVcWarningOverlay } from '../screens/Home/MyVcs/RemoveVcWarningOverlay';
|
||||
import {Icon, ListItem, Overlay} from 'react-native-elements';
|
||||
import {Theme} from '../components/ui/styleUtils';
|
||||
import {Column, Row, Text} from '../components/ui';
|
||||
import {WalletBinding} from '../screens/Home/MyVcs/WalletBinding';
|
||||
import {Pressable, View} from 'react-native';
|
||||
import {useKebabPopUp} from './KebabPopUpController';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {ExistingMosipVCItemMachine} from '../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {HistoryTab} from '../screens/Home/MyVcs/HistoryTab';
|
||||
import {RemoveVcWarningOverlay} from '../screens/Home/MyVcs/RemoveVcWarningOverlay';
|
||||
import {ScrollView} from 'react-native-gesture-handler';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
import testIDProps from '../shared/commonUtil';
|
||||
|
||||
export const KebabPopUp: React.FC<KebabPopUpProps> = (props) => {
|
||||
export const KebabPopUp: React.FC<KebabPopUpProps> = props => {
|
||||
const controller = useKebabPopUp(props);
|
||||
const { t } = useTranslation('HomeScreenKebabPopUp');
|
||||
const {t} = useTranslation('HomeScreenKebabPopUp');
|
||||
return (
|
||||
<Column>
|
||||
<Icon
|
||||
{...testIDProps('ellipsis')}
|
||||
accessible={true}
|
||||
name={props.iconName}
|
||||
type={props.iconType}
|
||||
color={Theme.Colors.GrayIcon}
|
||||
size={Theme.ICON_SMALL_SIZE}
|
||||
/>
|
||||
<Overlay
|
||||
isVisible={props.isVisible}
|
||||
onBackdropPress={props.onDismiss}
|
||||
overlayStyle={Theme.KebabPopUpStyles.kebabPopUp}>
|
||||
<Row style={Theme.KebabPopUpStyles.kebabHeaderStyle}>
|
||||
<Text weight="bold">{t('title')}</Text>
|
||||
<View></View>
|
||||
<Text testID="kebabTitle" weight="bold">
|
||||
{t('title')}
|
||||
</Text>
|
||||
<Icon
|
||||
name="close"
|
||||
onPress={props.onDismiss}
|
||||
@@ -34,17 +42,15 @@ export const KebabPopUp: React.FC<KebabPopUpProps> = (props) => {
|
||||
size={25}
|
||||
/>
|
||||
</Row>
|
||||
<Column>
|
||||
<ScrollView>
|
||||
<ListItem bottomDivider>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Pressable onPress={controller.PIN_CARD}>
|
||||
<Text size="small" weight="bold">
|
||||
{props.vcKey.split(':')[4] == 'true'
|
||||
? t('unPinCard')
|
||||
: t('pinCard')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
<ListItem.Title
|
||||
onPress={controller.PIN_CARD}
|
||||
{...testIDProps('pinOrUnPinCard')}>
|
||||
<Text size="small" weight="bold">
|
||||
{props.vcMetadata.isPinned ? t('unPinCard') : t('pinCard')}
|
||||
</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
</ListItem>
|
||||
@@ -56,19 +62,20 @@ export const KebabPopUp: React.FC<KebabPopUpProps> = (props) => {
|
||||
/>
|
||||
|
||||
<HistoryTab
|
||||
testID="viewActivityLog"
|
||||
service={props.service}
|
||||
label={t('viewActivityLog')}
|
||||
vcKey={props.vcKey}
|
||||
vcMetadata={props.vcMetadata}
|
||||
/>
|
||||
|
||||
<ListItem bottomDivider>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Pressable onPress={() => controller.REMOVE(props.vcKey)}>
|
||||
<Text size="small" weight="bold">
|
||||
{t('removeFromWallet')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
<ListItem.Title
|
||||
onPress={() => controller.REMOVE(props.vcMetadata)}
|
||||
{...testIDProps('removeFromWallet')}>
|
||||
<Text size="small" weight="bold">
|
||||
{t('removeFromWallet')}
|
||||
</Text>
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
</ListItem>
|
||||
@@ -78,7 +85,7 @@ export const KebabPopUp: React.FC<KebabPopUpProps> = (props) => {
|
||||
onConfirm={controller.CONFIRM}
|
||||
onCancel={controller.CANCEL}
|
||||
/>
|
||||
</Column>
|
||||
</ScrollView>
|
||||
</Overlay>
|
||||
</Column>
|
||||
);
|
||||
@@ -87,8 +94,8 @@ export const KebabPopUp: React.FC<KebabPopUpProps> = (props) => {
|
||||
export interface KebabPopUpProps {
|
||||
iconName: string;
|
||||
iconType?: string;
|
||||
vcKey: string;
|
||||
vcMetadata: VCMetadata;
|
||||
isVisible: boolean;
|
||||
onDismiss: () => void;
|
||||
service: ActorRefFrom<typeof vcItemMachine>;
|
||||
service: ActorRefFrom<typeof ExistingMosipVCItemMachine>;
|
||||
}
|
||||
|
||||
@@ -1,57 +1,70 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {
|
||||
selectKebabPopUpWalletBindingInProgress,
|
||||
ExistingMosipVCItemEvents,
|
||||
ExistingMosipVCItemMachine,
|
||||
selectEmptyWalletBindingId,
|
||||
selectIsPinned,
|
||||
selectKebabPopUp,
|
||||
selectKebabPopUpAcceptingBindingOtp,
|
||||
selectKebabPopUpBindingWarning,
|
||||
selectRemoveWalletWarning,
|
||||
selectEmptyWalletBindingId,
|
||||
selectIsPinned,
|
||||
selectKebabPopUpWalletBindingInProgress,
|
||||
selectOtpError,
|
||||
selectRemoveWalletWarning,
|
||||
selectShowActivities,
|
||||
selectShowWalletBindingError,
|
||||
selectWalletBindingError,
|
||||
VcItemEvents,
|
||||
vcItemMachine,
|
||||
selectShowActivities,
|
||||
} from '../machines/vcItem';
|
||||
import { selectActivities } from '../machines/activityLog';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
import { useContext } from 'react';
|
||||
} from '../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {selectActivities} from '../machines/activityLog';
|
||||
import {GlobalContext} from '../shared/GlobalContext';
|
||||
import {useContext} from 'react';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
import {
|
||||
EsignetMosipVCItemEvents,
|
||||
EsignetMosipVCItemMachine,
|
||||
} from '../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
|
||||
|
||||
export function useKebabPopUp(props) {
|
||||
const service = props.service as ActorRefFrom<typeof vcItemMachine>;
|
||||
const PIN_CARD = () => service.send(VcItemEvents.PIN_CARD());
|
||||
const KEBAB_POPUP = () => service.send(VcItemEvents.KEBAB_POPUP());
|
||||
const service = props.service as
|
||||
| ActorRefFrom<typeof ExistingMosipVCItemMachine>
|
||||
| ActorRefFrom<typeof EsignetMosipVCItemMachine>;
|
||||
const vcEvents =
|
||||
props.vcKey !== undefined && props.vcMetadata.isFromOpenId4VCI()
|
||||
? EsignetMosipVCItemEvents
|
||||
: ExistingMosipVCItemEvents;
|
||||
const PIN_CARD = () => service.send(vcEvents.PIN_CARD());
|
||||
const KEBAB_POPUP = () => service.send(vcEvents.KEBAB_POPUP());
|
||||
const ADD_WALLET_BINDING_ID = () =>
|
||||
service.send(VcItemEvents.ADD_WALLET_BINDING_ID());
|
||||
const CONFIRM = () => service.send(VcItemEvents.CONFIRM());
|
||||
const REMOVE = (vcKey: string) => service.send(VcItemEvents.REMOVE(vcKey));
|
||||
const DISMISS = () => service.send(VcItemEvents.DISMISS());
|
||||
const CANCEL = () => service.send(VcItemEvents.CANCEL());
|
||||
const SHOW_ACTIVITY = () => service.send(VcItemEvents.SHOW_ACTIVITY());
|
||||
const INPUT_OTP = (otp: string) => service.send(VcItemEvents.INPUT_OTP(otp));
|
||||
service.send(vcEvents.ADD_WALLET_BINDING_ID());
|
||||
const CONFIRM = () => service.send(vcEvents.CONFIRM());
|
||||
const REMOVE = (vcMetadata: VCMetadata) =>
|
||||
service.send(vcEvents.REMOVE(vcMetadata));
|
||||
const DISMISS = () => service.send(vcEvents.DISMISS());
|
||||
const CANCEL = () => service.send(vcEvents.CANCEL());
|
||||
const SHOW_ACTIVITY = () => service.send(vcEvents.SHOW_ACTIVITY());
|
||||
const INPUT_OTP = (otp: string) => service.send(vcEvents.INPUT_OTP(otp));
|
||||
const RESEND_OTP = () => service.send(vcEvents.RESEND_OTP());
|
||||
const isPinned = useSelector(service, selectIsPinned);
|
||||
const isBindingWarning = useSelector(service, selectKebabPopUpBindingWarning);
|
||||
const isRemoveWalletWarning = useSelector(service, selectRemoveWalletWarning);
|
||||
const isAcceptingOtpInput = useSelector(
|
||||
service,
|
||||
selectKebabPopUpAcceptingBindingOtp
|
||||
selectKebabPopUpAcceptingBindingOtp,
|
||||
);
|
||||
const isWalletBindingError = useSelector(
|
||||
service,
|
||||
selectShowWalletBindingError
|
||||
selectShowWalletBindingError,
|
||||
);
|
||||
const otpError = useSelector(service, selectOtpError);
|
||||
const walletBindingError = useSelector(service, selectWalletBindingError);
|
||||
const WalletBindingInProgress = useSelector(
|
||||
service,
|
||||
selectKebabPopUpWalletBindingInProgress
|
||||
selectKebabPopUpWalletBindingInProgress,
|
||||
);
|
||||
const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
|
||||
const isKebabPopUp = useSelector(service, selectKebabPopUp);
|
||||
const isShowActivities = useSelector(service, selectShowActivities);
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const activityLogService = appService.children.get('activityLog');
|
||||
|
||||
return {
|
||||
@@ -64,6 +77,7 @@ export function useKebabPopUp(props) {
|
||||
REMOVE,
|
||||
CANCEL,
|
||||
INPUT_OTP,
|
||||
RESEND_OTP,
|
||||
SHOW_ACTIVITY,
|
||||
isBindingWarning,
|
||||
isAcceptingOtpInput,
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import React from 'react';
|
||||
import { SUPPORTED_LANGUAGES } from '../i18n';
|
||||
import { I18nManager, View } from 'react-native';
|
||||
import { Picker } from './ui/Picker';
|
||||
import {SUPPORTED_LANGUAGES} from '../i18n';
|
||||
import {I18nManager, View} from 'react-native';
|
||||
import {Picker} from './ui/Picker';
|
||||
import Storage from '../shared/storage';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
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();
|
||||
export const LanguageSelector: React.FC<LanguageSelectorProps> = props => {
|
||||
const {i18n} = useTranslation();
|
||||
const languages = Object.entries(SUPPORTED_LANGUAGES).map(
|
||||
([value, label]) => ({ label, value })
|
||||
([value, label]) => ({label, value}),
|
||||
);
|
||||
|
||||
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) {
|
||||
@@ -35,6 +37,7 @@ export const LanguageSelector: React.FC<LanguageSelectorProps> = (props) => {
|
||||
return (
|
||||
<View>
|
||||
<Picker
|
||||
testID="language"
|
||||
items={languages}
|
||||
selectedValue={i18n.language}
|
||||
onValueChange={changeLanguage}
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
import { Dimensions } from 'react-native';
|
||||
import { Overlay, LinearProgress } from 'react-native-elements';
|
||||
import { Button, Column, Text } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import {Dimensions, StyleSheet} from 'react-native';
|
||||
import {Overlay, LinearProgress} from 'react-native-elements';
|
||||
import {Button, Column, Text} from './ui';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
|
||||
export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
|
||||
const { t } = useTranslation('common');
|
||||
export const MessageOverlay: React.FC<MessageOverlayProps> = props => {
|
||||
const {t} = useTranslation('common');
|
||||
const style = StyleSheet.create({
|
||||
customHeight: {
|
||||
height: props.customHeight
|
||||
? props.customHeight
|
||||
: props.progress
|
||||
? 100
|
||||
: 150,
|
||||
},
|
||||
});
|
||||
return (
|
||||
<Overlay
|
||||
isVisible={props.isVisible}
|
||||
@@ -16,11 +25,7 @@ export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
|
||||
onBackdropPress={props.onBackdropPress}>
|
||||
<Column
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
style={
|
||||
!props.progress
|
||||
? Theme.MessageOverlayStyles.popupOverLay
|
||||
: { height: 100 }
|
||||
}>
|
||||
style={[Theme.MessageOverlayStyles.popupOverLay, style.customHeight]}>
|
||||
<Column padding="21" crossAlign="center">
|
||||
{props.title && (
|
||||
<Text
|
||||
@@ -52,11 +57,11 @@ export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
|
||||
)}
|
||||
{props.children}
|
||||
</Column>
|
||||
{!props.children && props.onCancel ? (
|
||||
{!props.children && props.onButtonPress ? (
|
||||
<Button
|
||||
type="gradient"
|
||||
title={t('cancel')}
|
||||
onPress={props.onCancel}
|
||||
title={props.buttonText ? t(props.buttonText) : t('cancel')}
|
||||
onPress={props.onButtonPress}
|
||||
styles={Theme.MessageOverlayStyles.button}
|
||||
/>
|
||||
) : null}
|
||||
@@ -71,7 +76,7 @@ export const ErrorMessageOverlay: React.FC<ErrorMessageOverlayProps> = ({
|
||||
onDismiss,
|
||||
translationPath,
|
||||
}) => {
|
||||
const { t } = useTranslation(translationPath);
|
||||
const {t} = useTranslation(translationPath);
|
||||
|
||||
return (
|
||||
<MessageOverlay
|
||||
@@ -90,7 +95,7 @@ export interface ErrorMessageOverlayProps {
|
||||
translationPath: string;
|
||||
}
|
||||
|
||||
const Progress: React.FC<Pick<MessageOverlayProps, 'progress'>> = (props) => {
|
||||
const Progress: React.FC<Pick<MessageOverlayProps, 'progress'>> = props => {
|
||||
return typeof props.progress === 'boolean' ? (
|
||||
props.progress && (
|
||||
<LinearProgress variant="indeterminate" color={Theme.Colors.Loading} />
|
||||
@@ -103,11 +108,15 @@ const Progress: React.FC<Pick<MessageOverlayProps, 'progress'>> = (props) => {
|
||||
export interface MessageOverlayProps {
|
||||
isVisible: boolean;
|
||||
title?: string;
|
||||
buttonText?: string;
|
||||
message?: string;
|
||||
progress?: boolean | number;
|
||||
requester?: boolean;
|
||||
hint?: string;
|
||||
onCancel?: () => void;
|
||||
onButtonPress?: () => void;
|
||||
onStayInProgress?: () => void;
|
||||
onRetry?: () => void;
|
||||
onBackdropPress?: () => void;
|
||||
onShow?: () => void;
|
||||
customHeight?: number | string | undefined;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PinInput } from './PinInput';
|
||||
import { hashData } from '../shared/commonUtil';
|
||||
import { argon2iConfig } from '../shared/constants';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {PinInput} from './PinInput';
|
||||
import {hashData} from '../shared/commonUtil';
|
||||
import {argon2iConfig} from '../shared/constants';
|
||||
|
||||
export const MAX_PIN = 6;
|
||||
|
||||
export const PasscodeVerify: React.FC<PasscodeVerifyProps> = (props) => {
|
||||
const { t } = useTranslation('PasscodeVerify');
|
||||
export const PasscodeVerify: React.FC<PasscodeVerifyProps> = props => {
|
||||
const {t} = useTranslation('PasscodeVerify');
|
||||
const [isVerified, setIsVerified] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -16,7 +16,9 @@ export const PasscodeVerify: React.FC<PasscodeVerifyProps> = (props) => {
|
||||
}
|
||||
}, [isVerified]);
|
||||
|
||||
return <PinInput length={MAX_PIN} onDone={verify} />;
|
||||
return (
|
||||
<PinInput testID="confirmPasscodePin" length={MAX_PIN} onDone={verify} />
|
||||
);
|
||||
|
||||
async function verify(value: string) {
|
||||
const hashedPasscode = await hashData(value, props.salt, argon2iConfig);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { TextInput } from 'react-native';
|
||||
import { usePinInput } from '../machines/pinInput';
|
||||
import { Row } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import React, {useEffect} from 'react';
|
||||
import {TextInput} from 'react-native';
|
||||
import {usePinInput} from '../machines/pinInput';
|
||||
import {Row} from './ui';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
|
||||
export const PinInput: React.FC<PinInputProps> = (props) => {
|
||||
const { state, send, events } = usePinInput(props.length);
|
||||
const { inputRefs, values } = state.context;
|
||||
const { UPDATE_INPUT, FOCUS_INPUT, KEY_PRESS } = events;
|
||||
export const PinInput: React.FC<PinInputProps> = props => {
|
||||
const {state, send, events} = usePinInput(props.length);
|
||||
const {inputRefs, values} = state.context;
|
||||
const {UPDATE_INPUT, FOCUS_INPUT, KEY_PRESS} = events;
|
||||
|
||||
useEffect(() => {
|
||||
if (props.onDone && values.filter(Boolean).length === inputRefs.length) {
|
||||
@@ -16,7 +16,7 @@ export const PinInput: React.FC<PinInputProps> = (props) => {
|
||||
}, [state]);
|
||||
|
||||
return (
|
||||
<Row width="100%">
|
||||
<Row width="100%" testID={props.testID}>
|
||||
{inputRefs.map((input, index) => (
|
||||
<TextInput
|
||||
selectTextOnFocus
|
||||
@@ -29,7 +29,7 @@ export const PinInput: React.FC<PinInputProps> = (props) => {
|
||||
ref={input}
|
||||
value={values[index]}
|
||||
// KNOWN ISSUE: https://github.com/facebook/react-native/issues/19507
|
||||
onKeyPress={({ nativeEvent }) => send(KEY_PRESS(nativeEvent.key))}
|
||||
onKeyPress={({nativeEvent}) => send(KEY_PRESS(nativeEvent.key))}
|
||||
onChangeText={(value: string) =>
|
||||
send(UPDATE_INPUT(value.replace(/\D/g, ''), index))
|
||||
}
|
||||
@@ -41,6 +41,7 @@ export const PinInput: React.FC<PinInputProps> = (props) => {
|
||||
};
|
||||
|
||||
interface PinInputProps {
|
||||
testID?: string;
|
||||
length: number;
|
||||
onDone?: (value: string) => void;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,22 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Centered, Column, Text } from './ui';
|
||||
import { Modal } from './ui/Modal';
|
||||
import { Image } from 'react-native';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import PaginationDot from 'react-native-animated-pagination-dot';
|
||||
import React, {useState} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Button, Centered, Column, Text} from './ui';
|
||||
import {Modal} from './ui/Modal';
|
||||
import {Image} from 'react-native';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
import Spinner from 'react-native-spinkit';
|
||||
|
||||
export const ProgressingModal: React.FC<ProgressingModalProps> = (props) => {
|
||||
const { t } = useTranslation('ScanScreen');
|
||||
|
||||
let n = 0;
|
||||
const [curPage, setCurPage] = useState(n);
|
||||
|
||||
const highLightDot = () => setCurPage(n + 1);
|
||||
export const ProgressingModal: React.FC<ProgressingModalProps> = props => {
|
||||
const {t} = useTranslation('ScanScreen');
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Modal
|
||||
isVisible={props.isVisible}
|
||||
headerLeft={t(props.title)}
|
||||
headerTitle={t(props.title)}
|
||||
onDismiss={props.onCancel}
|
||||
headerLabel={props.label}
|
||||
headerElevation={3}
|
||||
modalStyle={Theme.ModalStyles.progressingModal}
|
||||
requester={props.requester}>
|
||||
<Centered crossAlign="center" fill>
|
||||
<Column margin="24 0" align="space-around">
|
||||
@@ -29,34 +24,43 @@ export const ProgressingModal: React.FC<ProgressingModalProps> = (props) => {
|
||||
source={Theme.InjiProgressingLogo}
|
||||
height={2}
|
||||
width={2}
|
||||
style={{ marginBottom: 15, marginLeft: -6 }}
|
||||
style={{marginBottom: 15, marginLeft: -6}}
|
||||
/>
|
||||
{props.progress && (
|
||||
<PaginationDot
|
||||
activeDotColor={'black'}
|
||||
curPage={curPage}
|
||||
maxPage={3}
|
||||
<Spinner
|
||||
type="ThreeBounce"
|
||||
color={Theme.Colors.Loading}
|
||||
style={{marginLeft: 6}}
|
||||
/>
|
||||
)}
|
||||
</Column>
|
||||
|
||||
<Column style={{ display: props.hint ? 'flex' : 'none' }}>
|
||||
{(props.isHintVisible || props.isBleErrorVisible) && (
|
||||
<Column style={Theme.SelectVcOverlayStyles.timeoutHintContainer}>
|
||||
<Text
|
||||
align="center"
|
||||
color={Theme.Colors.TimoutText}
|
||||
margin="10"
|
||||
color={Theme.Colors.TimoutHintText}
|
||||
size="small"
|
||||
style={Theme.TextStyles.bold}>
|
||||
{props.hint}
|
||||
</Text>
|
||||
{props.onCancel && (
|
||||
{props.onStayInProgress && (
|
||||
<Button
|
||||
type="clear"
|
||||
title={t('common:cancel')}
|
||||
onPress={props.onCancel}
|
||||
title={t('status.stayOnTheScreen')}
|
||||
onPress={props.onStayInProgress}
|
||||
/>
|
||||
)}
|
||||
|
||||
{props.onRetry && (
|
||||
<Button
|
||||
type="clear"
|
||||
title={t('status.retry')}
|
||||
onPress={props.onRetry}
|
||||
/>
|
||||
)}
|
||||
</Column>
|
||||
</Column>
|
||||
)}
|
||||
</Centered>
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
@@ -65,11 +69,13 @@ export const ProgressingModal: React.FC<ProgressingModalProps> = (props) => {
|
||||
|
||||
export interface ProgressingModalProps {
|
||||
isVisible: boolean;
|
||||
isHintVisible: boolean;
|
||||
isBleErrorVisible?: boolean;
|
||||
title?: string;
|
||||
label?: string;
|
||||
hint?: string;
|
||||
onCancel?: () => void;
|
||||
onStayInProgress?: () => void;
|
||||
onRetry?: () => void;
|
||||
requester?: boolean;
|
||||
progress?: boolean | number;
|
||||
onBackdropPress?: () => void;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Dimensions, Pressable } from 'react-native';
|
||||
import { Icon, Overlay } from 'react-native-elements';
|
||||
import { Centered, Column, Row, Text } from './ui';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Dimensions, Pressable, View} from 'react-native';
|
||||
import {Icon, Overlay} from 'react-native-elements';
|
||||
import {Centered, Column, Row, Text} from './ui';
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { Image } from 'react-native';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
import {Image} from 'react-native';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
export const QrCodeOverlay: React.FC<QrCodeOverlayProps> = (props) => {
|
||||
const { t } = useTranslation('VcDetails');
|
||||
export const QrCodeOverlay: React.FC<QrCodeOverlayProps> = props => {
|
||||
const {t} = useTranslation('VcDetails');
|
||||
|
||||
const [isQrOverlayVisible, setIsQrOverlayVisible] = useState(false);
|
||||
|
||||
@@ -16,16 +16,18 @@ export const QrCodeOverlay: React.FC<QrCodeOverlayProps> = (props) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Pressable onPress={toggleQrOverlay}>
|
||||
<Row margin="20 0 0 0">
|
||||
<QRCode
|
||||
size={90}
|
||||
value={props.qrCodeDetailes}
|
||||
backgroundColor={Theme.Colors.QRCodeBackgroundColor}
|
||||
/>
|
||||
</Row>
|
||||
<View style={Theme.QrCodeStyles.QrView}>
|
||||
<Row>
|
||||
<QRCode
|
||||
size={90}
|
||||
value={props.qrCodeDetailes}
|
||||
backgroundColor={Theme.Colors.QRCodeBackgroundColor}
|
||||
/>
|
||||
</Row>
|
||||
</View>
|
||||
<Row
|
||||
align="flex-end"
|
||||
margin="-30 0 0 60"
|
||||
margin="-35 0 0 67"
|
||||
style={Theme.QrCodeStyles.magnifierZoom}>
|
||||
<Image source={Theme.MagnifierZoom} />
|
||||
</Row>
|
||||
@@ -33,10 +35,14 @@ export const QrCodeOverlay: React.FC<QrCodeOverlayProps> = (props) => {
|
||||
<Overlay
|
||||
isVisible={isQrOverlayVisible}
|
||||
onBackdropPress={toggleQrOverlay}
|
||||
overlayStyle={{ padding: 1, borderRadius: 21 }}>
|
||||
overlayStyle={{padding: 1, borderRadius: 21}}>
|
||||
<Column style={Theme.QrCodeStyles.expandedQrCode}>
|
||||
<Row pY={20} style={Theme.QrCodeStyles.QrCodeHeader}>
|
||||
<Text align="center" style={Theme.TextStyles.header} weight="bold">
|
||||
<Text
|
||||
testID="qrCodeHeader"
|
||||
align="center"
|
||||
style={Theme.TextStyles.header}
|
||||
weight="bold">
|
||||
{t('qrCodeHeader')}
|
||||
</Text>
|
||||
<Icon
|
||||
@@ -46,7 +52,7 @@ export const QrCodeOverlay: React.FC<QrCodeOverlayProps> = (props) => {
|
||||
size={32}
|
||||
/>
|
||||
</Row>
|
||||
<Centered pY={30}>
|
||||
<Centered testID="qrCodeDetailes" pY={30}>
|
||||
<QRCode
|
||||
size={300}
|
||||
value={props.qrCodeDetailes}
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import React, {useContext, useEffect, useState} from 'react';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
import { Camera } from 'expo-camera';
|
||||
import { BarCodeEvent, BarCodeScanner } from 'expo-barcode-scanner';
|
||||
import {
|
||||
Linking,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
Image,
|
||||
Pressable,
|
||||
} from 'react-native';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { Column, Button, Text, Centered, Row } from './ui';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { selectIsActive } from '../machines/app';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useScanLayout } from '../screens/Scan/ScanLayoutController';
|
||||
import {Camera} from 'expo-camera';
|
||||
import {BarCodeEvent, BarCodeScanner} from 'expo-barcode-scanner';
|
||||
import {Linking, TouchableOpacity, View, Image, Pressable} from 'react-native';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
import {Column, Button, Text, Centered, Row} from './ui';
|
||||
import {GlobalContext} from '../shared/GlobalContext';
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {selectIsActive} from '../machines/app';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {useScanLayout} from '../screens/Scan/ScanLayoutController';
|
||||
|
||||
export const QrScanner: React.FC<QrScannerProps> = (props) => {
|
||||
const { t } = useTranslation('QrScanner');
|
||||
const { appService } = useContext(GlobalContext);
|
||||
export const QrScanner: React.FC<QrScannerProps> = props => {
|
||||
const {t} = useTranslation('QrScanner');
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const [hasPermission, setHasPermission] = useState(null);
|
||||
const [scanned, setScanned] = useState(false);
|
||||
const [cameraType, setCameraType] = useState(Camera.Constants.Type.back);
|
||||
@@ -106,12 +100,15 @@ export const QrScanner: React.FC<QrScannerProps> = (props) => {
|
||||
setCameraType(
|
||||
cameraType === Camera.Constants.Type.back
|
||||
? Camera.Constants.Type.front
|
||||
: Camera.Constants.Type.back
|
||||
: Camera.Constants.Type.back,
|
||||
);
|
||||
}}>
|
||||
<Image source={Theme.CameraFlipIcon} />
|
||||
<Image
|
||||
source={Theme.CameraFlipIcon}
|
||||
style={Theme.Styles.cameraFlipIcon}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<Text align="center" weight="semibold" margin="9 0">
|
||||
<Text size="small" weight="semibold" margin="8">
|
||||
{t('flipCamera')}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
113
components/VC/EsignetMosipVCItem/EsignetMosipVCItem.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import React, {useContext, useEffect, useRef} from 'react';
|
||||
import {useInterpret, useSelector} from '@xstate/react';
|
||||
import {Pressable, View} from 'react-native';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {GlobalContext} from '../../../shared/GlobalContext';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import {Row} from '../../ui';
|
||||
import {KebabPopUp} from '../../KebabPopUp';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import {EsignetMosipVCItemContent} from './EsignetMosipVCItemContent';
|
||||
import {EsignetMosipVCActivationStatus} from './EsignetMosipVCItemActivationStatus';
|
||||
import {
|
||||
createEsignetMosipVCItemMachine,
|
||||
EsignetMosipVCItemEvents,
|
||||
EsignetMosipVCItemMachine,
|
||||
selectContext,
|
||||
selectGeneratedOn,
|
||||
selectKebabPopUp,
|
||||
selectVerifiableCredentials,
|
||||
} from '../../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
|
||||
|
||||
export const EsignetMosipVCItem: React.FC<EsignetMosipVCItemProps> = props => {
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const machine = useRef(
|
||||
createEsignetMosipVCItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcMetadata,
|
||||
),
|
||||
);
|
||||
|
||||
const service = useInterpret(machine.current, {devTools: __DEV__});
|
||||
|
||||
useEffect(() => {
|
||||
service.send(EsignetMosipVCItemEvents.UPDATE_VC_METADATA(props.vcMetadata));
|
||||
}, [props.vcMetadata]);
|
||||
|
||||
const context = useSelector(service, selectContext);
|
||||
|
||||
const isKebabPopUp = useSelector(service, selectKebabPopUp);
|
||||
const DISMISS = () => service.send(EsignetMosipVCItemEvents.DISMISS());
|
||||
const KEBAB_POPUP = () =>
|
||||
service.send(EsignetMosipVCItemEvents.KEBAB_POPUP());
|
||||
|
||||
const credentials = useSelector(service, selectVerifiableCredentials);
|
||||
const generatedOn = useSelector(service, selectGeneratedOn);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Pressable
|
||||
accessible={false}
|
||||
onPress={() => props.onPress(service)}
|
||||
disabled={!credentials}
|
||||
style={
|
||||
props.selected
|
||||
? Theme.Styles.selectedBindedVc
|
||||
: Theme.Styles.closeCardBgContainer
|
||||
}>
|
||||
<EsignetMosipVCItemContent
|
||||
context={context}
|
||||
credential={credentials}
|
||||
generatedOn={generatedOn}
|
||||
selectable={props.selectable}
|
||||
selected={props.selected}
|
||||
service={service}
|
||||
iconName={props.iconName}
|
||||
iconType={props.iconType}
|
||||
onPress={() => props.onPress(service)}
|
||||
/>
|
||||
<View style={Theme.Styles.horizontalLine} />
|
||||
{props.isSharingVc ? null : (
|
||||
<Row style={Theme.Styles.activationTab}>
|
||||
{props.activeTab !== 'receivedVcsTab' &&
|
||||
props.activeTab != 'sharingVcScreen' && (
|
||||
<EsignetMosipVCActivationStatus
|
||||
verifiableCredential={credentials}
|
||||
showOnlyBindedVc={props.showOnlyBindedVc}
|
||||
emptyWalletBindingId
|
||||
/>
|
||||
)}
|
||||
<View style={Theme.Styles.verticalLine} />
|
||||
<Row style={Theme.Styles.kebabIcon}>
|
||||
<Pressable onPress={KEBAB_POPUP} accessible={false}>
|
||||
<KebabPopUp
|
||||
testID="ellipsis"
|
||||
vcMetadata={props.vcMetadata}
|
||||
iconName="dots-three-horizontal"
|
||||
iconType="entypo"
|
||||
isVisible={isKebabPopUp}
|
||||
onDismiss={DISMISS}
|
||||
service={service}
|
||||
/>
|
||||
</Pressable>
|
||||
</Row>
|
||||
</Row>
|
||||
)}
|
||||
</Pressable>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export interface EsignetMosipVCItemProps {
|
||||
vcMetadata: VCMetadata;
|
||||
margin?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
showOnlyBindedVc?: boolean;
|
||||
onPress?: (vcRef?: ActorRefFrom<typeof EsignetMosipVCItemMachine>) => void;
|
||||
onShow?: (vcRef?: ActorRefFrom<typeof EsignetMosipVCItemMachine>) => void;
|
||||
activeTab?: string;
|
||||
iconName?: string;
|
||||
iconType?: string;
|
||||
isSharingVc?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import React from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Dimensions} from 'react-native';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import {Row, Text} from '../../ui';
|
||||
import {VerifiableCredential} from '../../../types/VC/EsignetMosipVC/vc';
|
||||
|
||||
const WalletUnverifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="shield-alert"
|
||||
color={Theme.Colors.Icon}
|
||||
size={Theme.ICON_MID_SIZE}
|
||||
type="material-community"
|
||||
containerStyle={{marginStart: 10, bottom: 1, marginLeft: 10}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const WalletVerifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="verified-user"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={Theme.ICON_MID_SIZE}
|
||||
containerStyle={{marginStart: 10, bottom: 1, marginLeft: 10}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const WalletUnverifiedActivationDetails: React.FC<
|
||||
WalletUnVerifiedDetailsProps
|
||||
> = props => {
|
||||
const {t} = useTranslation('VcDetails');
|
||||
return (
|
||||
<Row
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
align="space-between"
|
||||
crossAlign="center">
|
||||
<Row
|
||||
crossAlign="center"
|
||||
style={{
|
||||
flex: 1,
|
||||
}}>
|
||||
{props.verifiableCredential && <WalletUnverifiedIcon />}
|
||||
<Text
|
||||
color={Theme.Colors.Details}
|
||||
testID="activationPending"
|
||||
weight="regular"
|
||||
margin="8 10 10 5"
|
||||
style={
|
||||
!props.verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.statusLabel
|
||||
}
|
||||
children={t('offlineAuthDisabledHeader')}></Text>
|
||||
</Row>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
const WalletVerifiedActivationDetails: React.FC<
|
||||
WalletVerifiedDetailsProps
|
||||
> = props => {
|
||||
const {t} = useTranslation('WalletBinding');
|
||||
return (
|
||||
<Row
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
align="space-between"
|
||||
crossAlign="center">
|
||||
<Row
|
||||
crossAlign="center"
|
||||
style={{
|
||||
flex: 1,
|
||||
}}>
|
||||
<WalletVerifiedIcon />
|
||||
<Text
|
||||
color={Theme.Colors.statusLabel}
|
||||
testID="activated"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
margin="8 10 10 5"
|
||||
style={
|
||||
!props.verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.statusLabel
|
||||
}
|
||||
children={t('profileAuthenticated')}></Text>
|
||||
</Row>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export const EsignetMosipVCActivationStatus: React.FC<
|
||||
EsignetMosipVCActivationStatusProps
|
||||
> = props => {
|
||||
return (
|
||||
<Row margin="0 0 0 -6">
|
||||
{props.emptyWalletBindingId ? (
|
||||
<WalletUnverifiedActivationDetails
|
||||
verifiableCredential={props.verifiableCredential}
|
||||
/>
|
||||
) : (
|
||||
<WalletVerifiedActivationDetails
|
||||
verifiableCredential={props.verifiableCredential}
|
||||
showOnlyBindedVc={props.showOnlyBindedVc}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export interface EsignetMosipVCActivationStatusProps {
|
||||
showOnlyBindedVc: boolean;
|
||||
verifiableCredential: VerifiableCredential;
|
||||
emptyWalletBindingId: boolean;
|
||||
}
|
||||
|
||||
interface WalletVerifiedDetailsProps {
|
||||
showOnlyBindedVc: boolean;
|
||||
verifiableCredential: VerifiableCredential;
|
||||
}
|
||||
|
||||
interface WalletUnVerifiedDetailsProps {
|
||||
verifiableCredential: VerifiableCredential;
|
||||
}
|
||||
309
components/VC/EsignetMosipVCItem/EsignetMosipVCItemContent.tsx
Normal file
@@ -0,0 +1,309 @@
|
||||
import React from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Image, ImageBackground} from 'react-native';
|
||||
import {CheckBox, Icon} from 'react-native-elements';
|
||||
import {Column, Row, Text} from '../../ui';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import VerifiedIcon from '../../VerifiedIcon';
|
||||
import {getLocalizedField} from '../../../i18n';
|
||||
import {
|
||||
Credential,
|
||||
VerifiableCredential,
|
||||
} from '../../../types/VC/EsignetMosipVC/vc';
|
||||
import testIDProps from '../../../shared/commonUtil';
|
||||
|
||||
const getDetails = (arg1: string, arg2: string, credential: Credential) => {
|
||||
if (arg1 === 'Status') {
|
||||
return (
|
||||
<Column>
|
||||
<Text
|
||||
testID="status"
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={
|
||||
!credential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}>
|
||||
{arg1}
|
||||
</Text>
|
||||
<Row
|
||||
style={{
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginLeft: 9,
|
||||
}}>
|
||||
{!credential ? null : <VerifiedIcon />}
|
||||
<Text
|
||||
testID="valid"
|
||||
numLines={1}
|
||||
color={Theme.Colors.Details}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
style={
|
||||
!credential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.detailsValue
|
||||
}>
|
||||
{!credential ? '' : arg2}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Column>
|
||||
<Text
|
||||
color={
|
||||
!credential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}
|
||||
size="smaller"
|
||||
weight="regular"
|
||||
style={Theme.Styles.vcItemLabelHeader}>
|
||||
{arg1}
|
||||
</Text>
|
||||
<Text
|
||||
numLines={4}
|
||||
color={Theme.Colors.Details}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
style={
|
||||
!credential ? Theme.Styles.loadingTitle : Theme.Styles.subtitle
|
||||
}>
|
||||
{!credential ? '' : arg2}
|
||||
</Text>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function getIdNumber(id: string) {
|
||||
if (id) {
|
||||
return '*'.repeat(id.length - 4) + id.slice(-4);
|
||||
}
|
||||
}
|
||||
|
||||
export const EsignetMosipVCItemContent: React.FC<
|
||||
EsignetMosipVCItemContentProps
|
||||
> = props => {
|
||||
//Assigning the UIN and VID from the VC details to display the idtype label
|
||||
const uin = props.credential?.credential?.credentialSubject.UIN;
|
||||
const vid = props.credential?.credential?.credentialSubject.VID;
|
||||
const fullName = !props.credential?.credential
|
||||
? ''
|
||||
: getLocalizedField(
|
||||
props.credential?.credential.credentialSubject.fullName,
|
||||
);
|
||||
const {t} = useTranslation('VcDetails');
|
||||
const isvalid = !props.credential?.credential ? '' : t('valid');
|
||||
const selectableOrCheck = props.selectable ? (
|
||||
<CheckBox
|
||||
checked={props.selected}
|
||||
checkedIcon={<Icon name="radio-button-checked" />}
|
||||
uncheckedIcon={<Icon name="radio-button-unchecked" />}
|
||||
onPress={() => props.onPress()}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<ImageBackground
|
||||
source={!props.credential?.credential ? null : Theme.CloseCard}
|
||||
resizeMode="stretch"
|
||||
style={
|
||||
!props.credential?.credential
|
||||
? Theme.Styles.vertloadingContainer
|
||||
: Theme.Styles.backgroundImageContainer
|
||||
}>
|
||||
<Column>
|
||||
<Row align="space-between">
|
||||
<Row margin="5 0 0 5">
|
||||
<ImageBackground
|
||||
imageStyle={Theme.Styles.faceImage}
|
||||
source={
|
||||
!props.credential?.credential
|
||||
? Theme.ProfileIcon
|
||||
: {uri: props.credential.credential.credentialSubject.face}
|
||||
}
|
||||
style={Theme.Styles.closeCardImage}>
|
||||
{props.iconName && (
|
||||
<Image
|
||||
source={Theme.PinIcon}
|
||||
style={Theme.Styles.pinIcon}
|
||||
{...testIDProps('pinIcon')}
|
||||
/>
|
||||
)}
|
||||
</ImageBackground>
|
||||
|
||||
<Column margin="0 0 10 20" height={96} align="space-between">
|
||||
<Column>
|
||||
<Text
|
||||
testID="fullNameTitle"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={
|
||||
!props.credential?.credential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}>
|
||||
{t('fullName')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="fullNameValue"
|
||||
weight="semibold"
|
||||
style={
|
||||
!props.credential?.credential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.detailsValue
|
||||
}>
|
||||
{fullName}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column>
|
||||
<Text
|
||||
testID="idType"
|
||||
color={
|
||||
!props.credential?.credential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
align="left">
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="nationalCard"
|
||||
weight="semibold"
|
||||
color={Theme.Colors.Details}
|
||||
style={
|
||||
!props.credential?.credential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.detailsValue
|
||||
}>
|
||||
{t('nationalCard')}
|
||||
</Text>
|
||||
</Column>
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
<Column>
|
||||
{props.credential?.credential ? selectableOrCheck : null}
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
<Row
|
||||
align="space-between"
|
||||
margin="9 10 0 7"
|
||||
style={
|
||||
!props.credential?.credential ? Theme.Styles.loadingContainer : null
|
||||
}>
|
||||
<Column>
|
||||
{uin ? (
|
||||
<Column margin="0 0 9 0">
|
||||
<Text
|
||||
testID="uin"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('uin')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="uinNumber"
|
||||
weight="semibold"
|
||||
size="extraSmall"
|
||||
color={Theme.Colors.statusLabel}>
|
||||
{getIdNumber(uin)}
|
||||
</Text>
|
||||
</Column>
|
||||
) : null}
|
||||
|
||||
{vid ? (
|
||||
<Column margin="0 0 9 0">
|
||||
<Text
|
||||
testID="vid"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('vid')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="vidNumber"
|
||||
weight="semibold"
|
||||
size="extraSmall"
|
||||
color={Theme.Colors.Details}>
|
||||
{getIdNumber(vid)}
|
||||
</Text>
|
||||
</Column>
|
||||
) : null}
|
||||
{!props.credential?.credential
|
||||
? getDetails(t('id'), uin | vid, props.credential?.credential)
|
||||
: null}
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
<Row
|
||||
style={{flexDirection: 'row', flex: 1}}
|
||||
align="space-between"
|
||||
margin="0 8 5 8">
|
||||
<Column margin="9 0 0 0">
|
||||
<Text
|
||||
testID="generatedOnTitle"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={
|
||||
!props.credential?.credential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}>
|
||||
{t('generatedOn')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="generatedOnValue"
|
||||
weight="semibold"
|
||||
style={
|
||||
!props.credential?.credential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{props.generatedOn}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column margin="0 35 0 0">
|
||||
{props.credential?.credential
|
||||
? getDetails(t('status'), isvalid, props.credential?.credential)
|
||||
: null}
|
||||
</Column>
|
||||
<Column
|
||||
testID="logo"
|
||||
style={{
|
||||
display: props.credential?.credential ? 'flex' : 'none',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Image
|
||||
src={props.credential?.issuerLogo}
|
||||
style={Theme.Styles.issuerLogo}
|
||||
resizeMethod="scale"
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</Column>
|
||||
</Row>
|
||||
</Column>
|
||||
</ImageBackground>
|
||||
);
|
||||
};
|
||||
|
||||
interface EsignetMosipVCItemContentProps {
|
||||
context: any;
|
||||
credential: VerifiableCredential;
|
||||
generatedOn: string;
|
||||
selectable: boolean;
|
||||
selected: boolean;
|
||||
iconName?: string;
|
||||
iconType?: string;
|
||||
service: any;
|
||||
onPress?: () => void;
|
||||
}
|
||||
452
components/VC/EsignetMosipVCItem/EsignetMosipVCItemDetails.tsx
Normal file
@@ -0,0 +1,452 @@
|
||||
import React from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Button, Column, Row, Text} from '../../ui';
|
||||
import {Image, ImageBackground, View} from 'react-native';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import {QrCodeOverlay} from '../../QrCodeOverlay';
|
||||
import {getLocalizedField} from '../../../i18n';
|
||||
import VerifiedIcon from '../../VerifiedIcon';
|
||||
import {CREDENTIAL_REGISTRY_EDIT} from 'react-native-dotenv';
|
||||
import {TextItem} from '../../ui/TextItem';
|
||||
import {format, formatDistanceToNow, parse} from 'date-fns';
|
||||
import DateFnsLocale from 'date-fns/locale';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {WalletBindingResponse} from '../../../shared/cryptoutil/cryptoUtil';
|
||||
import {
|
||||
CredentialSubject,
|
||||
VcIdType,
|
||||
VCSharingReason,
|
||||
VerifiableCredential,
|
||||
VerifiablePresentation,
|
||||
} from '../../../types/VC/EsignetMosipVC/vc';
|
||||
|
||||
export const EsignetMosipVCItemDetails: React.FC<
|
||||
EsignetMosipVCItemDetailsProps
|
||||
> = props => {
|
||||
const {t, i18n} = useTranslation('VcDetails');
|
||||
|
||||
const uin = props.vc?.verifiableCredential.credential.credentialSubject.UIN;
|
||||
const vid = props.vc?.verifiableCredential.credential.credentialSubject.VID;
|
||||
if (props.vc?.verifiableCredential == null) {
|
||||
return <Text align="center">Loading details...</Text>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Column margin="10">
|
||||
<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">
|
||||
<Column align="space-evenly" crossAlign="center">
|
||||
<Image
|
||||
source={
|
||||
props.vc?.verifiableCredential.credential.credentialSubject
|
||||
?.face
|
||||
? {
|
||||
uri: props.vc?.verifiableCredential.credential
|
||||
.credentialSubject.face,
|
||||
}
|
||||
: Theme.ProfileIcon
|
||||
}
|
||||
style={Theme.Styles.openCardImage}
|
||||
/>
|
||||
|
||||
<QrCodeOverlay
|
||||
qrCodeDetailes={String(props.vc.verifiableCredential)}
|
||||
/>
|
||||
<Column margin="20 0 0 0">
|
||||
<Image
|
||||
src={props.vc.verifiableCredential.issuerLogo}
|
||||
style={Theme.Styles.issuerLogo}
|
||||
/>
|
||||
</Column>
|
||||
</Column>
|
||||
<Column align="space-evenly" padding="10">
|
||||
<Column>
|
||||
<Text
|
||||
testID="fullNameTitle"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('fullName')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="fullNameValue"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credential.credentialSubject
|
||||
.fullName,
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
<Row>
|
||||
<Column>
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
testID="gender"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('gender')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="genderValue"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credential
|
||||
.credentialSubject.gender,
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column margin="25 0 0 0">
|
||||
<Text
|
||||
testID="idType"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="nationalCard"
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{t('nationalCard')}
|
||||
</Text>
|
||||
</Column>
|
||||
{uin ? (
|
||||
<Column margin="25 0 0 0">
|
||||
<Text
|
||||
testID="uin"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('uin')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="uinNumber"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{uin}
|
||||
</Text>
|
||||
</Column>
|
||||
) : null}
|
||||
|
||||
{vid ? (
|
||||
<Column margin="25 0 0 0">
|
||||
<Text
|
||||
testID="vid"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('vid')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="vidNumber"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{vid}
|
||||
</Text>
|
||||
</Column>
|
||||
) : null}
|
||||
<Column margin="30 0 0 0">
|
||||
<Text
|
||||
testID="generatedOnTitle"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('generatedOn')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="generatedOnValue"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{new Date(props.vc?.generatedOn).toLocaleDateString()}
|
||||
</Text>
|
||||
</Column>
|
||||
</Column>
|
||||
<Column margin="0 0 0 40">
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
testID="dateOfBirth"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('dateOfBirth')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="dateOfBirthValue"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getFormattedDateOfBirth()}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column margin="25 0 0 0">
|
||||
<Text
|
||||
testID="status"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('status')}
|
||||
</Text>
|
||||
<Row
|
||||
style={{
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<VerifiedIcon />
|
||||
<Text
|
||||
testID="valid"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{t('valid')}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
<Column margin="92 0 0 0">
|
||||
<Text
|
||||
testID="phoneNumber"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('phoneNumber')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="phoneNumberValue"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credential
|
||||
.credentialSubject.phone,
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
</Column>
|
||||
</Row>
|
||||
</Column>
|
||||
</Row>
|
||||
<View style={Theme.Styles.hrLine}></View>
|
||||
<Column padding="10">
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
testID="emailId"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('email')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
testID="emailIdValue"
|
||||
style={
|
||||
props.vc?.verifiableCredential.credential.credentialSubject
|
||||
.email.length > 25
|
||||
? {flex: 1}
|
||||
: {flex: 0}
|
||||
}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credential.credentialSubject
|
||||
.email,
|
||||
)}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
|
||||
<Column style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
testID="address"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('address')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
testID="addressValue"
|
||||
style={{flex: 1}}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getFullAddress(
|
||||
props.vc?.verifiableCredential.credential.credentialSubject,
|
||||
)}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
{CREDENTIAL_REGISTRY_EDIT === 'true' && (
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('credentialRegistry')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{props.vc?.credentialRegistry}
|
||||
</Text>
|
||||
</Column>
|
||||
)}
|
||||
</Column>
|
||||
</ImageBackground>
|
||||
|
||||
{props.vc?.reason?.length > 0 && (
|
||||
<Text
|
||||
testID="reasonForSharingTitle"
|
||||
margin="24 24 16 24"
|
||||
weight="semibold">
|
||||
{t('reasonForSharing')}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{props.vc?.reason?.map((reason, index) => (
|
||||
<TextItem
|
||||
testID="reason"
|
||||
key={index}
|
||||
divider
|
||||
label={formatDistanceToNow(reason.timestamp, {
|
||||
addSuffix: true,
|
||||
locale: DateFnsLocale[i18n.language],
|
||||
})}
|
||||
text={reason.message}
|
||||
/>
|
||||
))}
|
||||
|
||||
{props.activeTab !== 1 ? (
|
||||
props.isBindingPending ? (
|
||||
<Column style={Theme.Styles.openCardBgContainer} padding="10">
|
||||
<Column margin={'0 0 4 0'} crossAlign={'flex-start'}>
|
||||
<Image source={Theme.activationPending}></Image>
|
||||
<Text
|
||||
testID="offlineAuthDisabledHeader"
|
||||
style={{flex: 1}}
|
||||
weight="semibold"
|
||||
size="small"
|
||||
margin={'5 0 0 0'}
|
||||
color={Theme.Colors.statusLabel}>
|
||||
{t('offlineAuthDisabledHeader')}
|
||||
</Text>
|
||||
</Column>
|
||||
<Text
|
||||
testID="offlineAuthDisabledMessage"
|
||||
style={{flex: 1, lineHeight: 17}}
|
||||
weight="regular"
|
||||
size="small"
|
||||
margin={'3 0 0 0'}
|
||||
color={Theme.Colors.statusMessage}>
|
||||
{t('offlineAuthDisabledMessage')}
|
||||
</Text>
|
||||
|
||||
<Button
|
||||
testID="enableVerification"
|
||||
title={t('enableVerification')}
|
||||
onPress={props.onBinding}
|
||||
type="gradient"
|
||||
styles={{width: '100%'}}
|
||||
/>
|
||||
</Column>
|
||||
) : (
|
||||
<Column style={Theme.Styles.openCardBgContainer} padding="10">
|
||||
<Row crossAlign="center">
|
||||
<Icon
|
||||
name="verified-user"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={28}
|
||||
containerStyle={{marginStart: 4, bottom: 1}}
|
||||
/>
|
||||
<Text
|
||||
testID="profileAuthenticated"
|
||||
numLines={1}
|
||||
color={Theme.Colors.statusLabel}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
margin="10 10 10 10"
|
||||
children={t('profileAuthenticated')}></Text>
|
||||
</Row>
|
||||
</Column>
|
||||
)
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
|
||||
function getFormattedDateOfBirth(): React.ReactNode {
|
||||
return format(
|
||||
parse(
|
||||
getLocalizedField(
|
||||
props.vc?.verifiableCredential.credential.credentialSubject
|
||||
.dateOfBirth,
|
||||
),
|
||||
'yyyy/MM/dd',
|
||||
new Date(),
|
||||
),
|
||||
'MM/dd/yyyy',
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export interface EsignetMosipVCItemDetailsProps {
|
||||
vc: VC;
|
||||
isBindingPending: boolean;
|
||||
onBinding?: () => void;
|
||||
activeTab?: number;
|
||||
}
|
||||
|
||||
export interface VC {
|
||||
id: string;
|
||||
idType: VcIdType;
|
||||
tag: string;
|
||||
verifiableCredential: VerifiableCredential;
|
||||
verifiablePresentation?: VerifiablePresentation;
|
||||
generatedOn: Date;
|
||||
requestId: string;
|
||||
isVerified: boolean;
|
||||
lastVerifiedOn: number;
|
||||
locked: boolean;
|
||||
reason?: VCSharingReason[];
|
||||
shouldVerifyPresence?: boolean;
|
||||
walletBindingResponse?: WalletBindingResponse;
|
||||
credentialRegistry: string;
|
||||
isPinned?: boolean;
|
||||
hashedId: string;
|
||||
}
|
||||
|
||||
function getFullAddress(credential: CredentialSubject) {
|
||||
if (!credential) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const fields = [
|
||||
'addressLine1',
|
||||
'addressLine2',
|
||||
'addressLine3',
|
||||
'city',
|
||||
'province',
|
||||
'region',
|
||||
];
|
||||
|
||||
return fields
|
||||
.map(field => getLocalizedField(credential[field]))
|
||||
.concat(credential.postalCode)
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
}
|
||||
134
components/VC/ExistingMosipVCItem/ExistingMosipVCItem.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import React, {useContext, useEffect, useMemo, useRef} from 'react';
|
||||
import {useInterpret, useSelector} from '@xstate/react';
|
||||
import {View, Pressable} from 'react-native';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {
|
||||
createExistingMosipVCItemMachine,
|
||||
selectVerifiableCredential,
|
||||
selectGeneratedOn,
|
||||
ExistingMosipVCItemMachine,
|
||||
selectContext,
|
||||
selectTag,
|
||||
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 {ExistingMosipVCItemContent} from './ExistingMosipVCItemContent';
|
||||
import {ExistingMosipVCItemActivationStatus} from './ExistingMosipVCItemActivationStatus';
|
||||
import {Row} from '../../ui';
|
||||
import {KebabPopUp} from '../../KebabPopUp';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import {format} from 'date-fns';
|
||||
|
||||
export const ExistingMosipVCItem: React.FC<
|
||||
ExistingMosipVCItemProps
|
||||
> = props => {
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const machine = useRef(
|
||||
createExistingMosipVCItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcMetadata,
|
||||
),
|
||||
);
|
||||
|
||||
const service = useInterpret(machine.current, {devTools: __DEV__});
|
||||
|
||||
useEffect(() => {
|
||||
service.send(
|
||||
ExistingMosipVCItemEvents.UPDATE_VC_METADATA(props.vcMetadata),
|
||||
);
|
||||
}, [props.vcMetadata]);
|
||||
|
||||
const context = useSelector(service, selectContext);
|
||||
const verifiableCredential = useSelector(service, selectVerifiableCredential);
|
||||
const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
|
||||
const isKebabPopUp = useSelector(service, selectKebabPopUp);
|
||||
const DISMISS = () => service.send(ExistingMosipVCItemEvents.DISMISS());
|
||||
const KEBAB_POPUP = () =>
|
||||
service.send(ExistingMosipVCItemEvents.KEBAB_POPUP());
|
||||
const isSavingFailedInIdle = useSelector(service, selectIsSavingFailedInIdle);
|
||||
|
||||
const storeErrorTranslationPath = 'errors.savingFailed';
|
||||
|
||||
const generatedOn = useSelector(service, selectGeneratedOn);
|
||||
const formattedDate = format(new Date(generatedOn), 'MM/dd/yyyy');
|
||||
const tag = useSelector(service, selectTag);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Pressable
|
||||
accessible={false}
|
||||
onPress={() => props.onPress(service)}
|
||||
disabled={!verifiableCredential}
|
||||
style={
|
||||
props.selected
|
||||
? Theme.Styles.selectedBindedVc
|
||||
: Theme.Styles.closeCardBgContainer
|
||||
}>
|
||||
<ExistingMosipVCItemContent
|
||||
context={context}
|
||||
verifiableCredential={verifiableCredential}
|
||||
generatedOn={formattedDate}
|
||||
tag={tag}
|
||||
selectable={props.selectable}
|
||||
selected={props.selected}
|
||||
service={service}
|
||||
iconName={props.iconName}
|
||||
iconType={props.iconType}
|
||||
onPress={() => props.onPress(service)}
|
||||
/>
|
||||
<View style={Theme.Styles.horizontalLine} />
|
||||
{props.isSharingVc ? null : (
|
||||
<Row style={Theme.Styles.activationTab}>
|
||||
{props.activeTab !== 'receivedVcsTab' &&
|
||||
props.activeTab != 'sharingVcScreen' && (
|
||||
<ExistingMosipVCItemActivationStatus
|
||||
verifiableCredential={verifiableCredential}
|
||||
emptyWalletBindingId={emptyWalletBindingId}
|
||||
onPress={() => props.onPress(service)}
|
||||
showOnlyBindedVc={props.showOnlyBindedVc}
|
||||
/>
|
||||
)}
|
||||
<View style={Theme.Styles.verticalLine} />
|
||||
<Row style={Theme.Styles.kebabIcon}>
|
||||
<Pressable onPress={KEBAB_POPUP} accessible={false}>
|
||||
<KebabPopUp
|
||||
vcMetadata={props.vcMetadata}
|
||||
iconName="dots-three-horizontal"
|
||||
iconType="entypo"
|
||||
isVisible={isKebabPopUp}
|
||||
onDismiss={DISMISS}
|
||||
service={service}
|
||||
/>
|
||||
</Pressable>
|
||||
</Row>
|
||||
</Row>
|
||||
)}
|
||||
</Pressable>
|
||||
<ErrorMessageOverlay
|
||||
isVisible={isSavingFailedInIdle}
|
||||
error={storeErrorTranslationPath}
|
||||
onDismiss={DISMISS}
|
||||
translationPath={'VcDetails'}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export interface ExistingMosipVCItemProps {
|
||||
vcMetadata: VCMetadata;
|
||||
margin?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
showOnlyBindedVc?: boolean;
|
||||
onPress?: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
|
||||
onShow?: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
|
||||
activeTab?: string;
|
||||
iconName?: string;
|
||||
iconType?: string;
|
||||
isSharingVc?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
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';
|
||||
|
||||
const WalletUnverifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="shield-alert"
|
||||
color={Theme.Colors.Icon}
|
||||
size={Theme.ICON_MID_SIZE}
|
||||
type="material-community"
|
||||
containerStyle={{marginStart: 10, bottom: 1, marginLeft: 10}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const WalletVerifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="verified-user"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={Theme.ICON_MID_SIZE}
|
||||
containerStyle={{marginStart: 10, bottom: 1, marginLeft: 10}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const WalletUnverifiedActivationDetails: React.FC<
|
||||
WalletUnVerifiedDetailsProps
|
||||
> = props => {
|
||||
const {t} = useTranslation('VcDetails');
|
||||
return (
|
||||
<Row
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
align="space-between"
|
||||
crossAlign="center">
|
||||
<Row
|
||||
crossAlign="center"
|
||||
style={{
|
||||
flex: 1,
|
||||
}}>
|
||||
{props.verifiableCredential && <WalletUnverifiedIcon />}
|
||||
<Text
|
||||
color={Theme.Colors.Details}
|
||||
testID="activationPending"
|
||||
weight="regular"
|
||||
margin="8 10 10 5"
|
||||
style={
|
||||
!props.verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.statusLabel
|
||||
}
|
||||
children={t('offlineAuthDisabledHeader')}></Text>
|
||||
</Row>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
const WalletVerifiedActivationDetails: React.FC<
|
||||
WalletVerifiedDetailsProps
|
||||
> = props => {
|
||||
const {t} = useTranslation('WalletBinding');
|
||||
return (
|
||||
<Row
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
align="space-between"
|
||||
crossAlign="center">
|
||||
<Row
|
||||
crossAlign="center"
|
||||
style={{
|
||||
flex: 1,
|
||||
}}>
|
||||
<WalletVerifiedIcon />
|
||||
<Text
|
||||
color={Theme.Colors.statusLabel}
|
||||
testID="activated"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
margin="8 10 10 5"
|
||||
style={
|
||||
!props.verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.statusLabel
|
||||
}
|
||||
children={t('profileAuthenticated')}></Text>
|
||||
</Row>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export const ExistingMosipVCItemActivationStatus: React.FC<
|
||||
ExistingMosipVCItemActivationStatusProps
|
||||
> = props => {
|
||||
return (
|
||||
<Row margin="0 0 0 -6">
|
||||
{props.emptyWalletBindingId ? (
|
||||
<WalletUnverifiedActivationDetails
|
||||
verifiableCredential={props.verifiableCredential}
|
||||
onPress={props.onPress}
|
||||
/>
|
||||
) : (
|
||||
<WalletVerifiedActivationDetails
|
||||
verifiableCredential={props.verifiableCredential}
|
||||
showOnlyBindedVc={props.showOnlyBindedVc}
|
||||
onPress={props.onPress}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
313
components/VC/ExistingMosipVCItem/ExistingMosipVCItemContent.tsx
Normal file
@@ -0,0 +1,313 @@
|
||||
import React from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Image, ImageBackground, View} from 'react-native';
|
||||
import {getLocalizedField} from '../../../i18n';
|
||||
import {VerifiableCredential} from '../../../types/VC/ExistingMosipVC/vc';
|
||||
import {VcItemTags} from '../common/VcItemTags';
|
||||
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';
|
||||
|
||||
const getDetails = (arg1, arg2, verifiableCredential) => {
|
||||
if (arg1 === 'Status') {
|
||||
return (
|
||||
<Column
|
||||
style={{
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginTop: 7,
|
||||
}}>
|
||||
<Text
|
||||
testID="status"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={
|
||||
!verifiableCredential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}>
|
||||
{arg1}
|
||||
</Text>
|
||||
<Row
|
||||
style={{
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginLeft: 9,
|
||||
}}>
|
||||
{!verifiableCredential ? null : <VerifiedIcon />}
|
||||
<Text
|
||||
testID="valid"
|
||||
numLines={1}
|
||||
color={Theme.Colors.Details}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.detailsValue
|
||||
}>
|
||||
{!verifiableCredential ? '' : arg2}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Column>
|
||||
<Text
|
||||
color={
|
||||
!verifiableCredential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}
|
||||
size="smaller"
|
||||
weight="regular"
|
||||
style={Theme.Styles.vcItemLabelHeader}>
|
||||
{arg1}
|
||||
</Text>
|
||||
<Text
|
||||
numLines={4}
|
||||
color={Theme.Colors.Details}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
style={
|
||||
!verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{!verifiableCredential ? '' : arg2}
|
||||
</Text>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function getIdNumber(id: string) {
|
||||
return '*'.repeat(id.length - 4) + id.slice(-4);
|
||||
}
|
||||
|
||||
export const ExistingMosipVCItemContent: React.FC<
|
||||
ExistingMosipVCItemContentProps
|
||||
> = props => {
|
||||
//Assigning the UIN and VID from the VC details to display the idtype label
|
||||
const uin = props.verifiableCredential?.credentialSubject.UIN;
|
||||
const vid = props.verifiableCredential?.credentialSubject.VID;
|
||||
const fullName = !props.verifiableCredential
|
||||
? ''
|
||||
: getLocalizedField(props.verifiableCredential.credentialSubject.fullName);
|
||||
const {t} = useTranslation('VcDetails');
|
||||
const isvalid = !props.verifiableCredential ? '' : t('valid');
|
||||
const selectableOrCheck = props.selectable ? (
|
||||
<CheckBox
|
||||
checked={props.selected}
|
||||
checkedIcon={<Icon name="radio-button-checked" />}
|
||||
uncheckedIcon={<Icon name="radio-button-unchecked" />}
|
||||
onPress={() => props.onPress()}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<ImageBackground
|
||||
source={!props.verifiableCredential ? null : Theme.CloseCard}
|
||||
resizeMode="stretch"
|
||||
style={
|
||||
!props.verifiableCredential
|
||||
? Theme.Styles.vertloadingContainer
|
||||
: Theme.Styles.backgroundImageContainer
|
||||
}>
|
||||
<Column>
|
||||
<Row align="space-between">
|
||||
<Row margin="5 0 0 5">
|
||||
<ImageBackground
|
||||
imageStyle={Theme.Styles.faceImage}
|
||||
source={
|
||||
!props.verifiableCredential
|
||||
? Theme.ProfileIcon
|
||||
: {uri: props.context.credential.biometrics.face}
|
||||
}
|
||||
style={Theme.Styles.closeCardImage}>
|
||||
{props.iconName && (
|
||||
<Image
|
||||
source={Theme.PinIcon}
|
||||
style={Theme.Styles.pinIcon}
|
||||
{...testIDProps('pinIcon')}
|
||||
/>
|
||||
)}
|
||||
</ImageBackground>
|
||||
|
||||
<Column margin="0 0 10 20" height={96} align="space-between">
|
||||
<Column>
|
||||
<Text
|
||||
testID="fullNameTitle"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={
|
||||
!props.verifiableCredential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}>
|
||||
{t('fullName')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="fullNameValue"
|
||||
weight="semibold"
|
||||
style={
|
||||
!props.verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.detailsValue
|
||||
}>
|
||||
{fullName}
|
||||
</Text>
|
||||
</Column>
|
||||
|
||||
<Column>
|
||||
<Text
|
||||
testID="idType"
|
||||
color={
|
||||
!props.verifiableCredential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
align="left">
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="nationalCard"
|
||||
weight="semibold"
|
||||
color={Theme.Colors.Details}
|
||||
style={
|
||||
!props.verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.detailsValue
|
||||
}>
|
||||
{t('nationalCard')}
|
||||
</Text>
|
||||
</Column>
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
<Column>
|
||||
{props.verifiableCredential ? selectableOrCheck : null}
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
<Row
|
||||
align="space-between"
|
||||
margin="9 10 0 7"
|
||||
style={
|
||||
!props.verifiableCredential ? Theme.Styles.loadingContainer : null
|
||||
}>
|
||||
<Column>
|
||||
{uin ? (
|
||||
<Column margin="0 0 9 0">
|
||||
<Text
|
||||
testID="uin"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('uin')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="uinNumber"
|
||||
weight="semibold"
|
||||
size="extraSmall"
|
||||
color={Theme.Colors.statusLabel}>
|
||||
{getIdNumber(uin)}
|
||||
</Text>
|
||||
</Column>
|
||||
) : null}
|
||||
|
||||
{vid ? (
|
||||
<Column margin="0 0 9 0">
|
||||
<Text
|
||||
testID="vid"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('vid')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="vidNumber"
|
||||
weight="semibold"
|
||||
size="extraSmall"
|
||||
color={Theme.Colors.Details}>
|
||||
{getIdNumber(vid)}
|
||||
</Text>
|
||||
</Column>
|
||||
) : null}
|
||||
{!props.verifiableCredential
|
||||
? getDetails(t('id'), uin || vid, props.verifiableCredential)
|
||||
: null}
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
<Row
|
||||
style={{flexDirection: 'row', flex: 1}}
|
||||
align="space-between"
|
||||
margin="0 8 5 8">
|
||||
<Column margin="9 0 0 0">
|
||||
<Text
|
||||
testID="generatedOnTitle"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={
|
||||
!props.verifiableCredential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}>
|
||||
{t('generatedOn')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="generatedOnValue"
|
||||
weight="semibold"
|
||||
style={
|
||||
!props.verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{props.generatedOn}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column margin="0 35 0 0">
|
||||
{props.verifiableCredential
|
||||
? getDetails(t('status'), isvalid, props.verifiableCredential)
|
||||
: null}
|
||||
</Column>
|
||||
<Column
|
||||
testID="logo"
|
||||
style={{
|
||||
display: props.verifiableCredential ? 'flex' : 'none',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Image
|
||||
source={Theme.MosipSplashLogo}
|
||||
style={Theme.Styles.logo}
|
||||
resizeMethod="scale"
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</Column>
|
||||
</Row>
|
||||
</Column>
|
||||
|
||||
<VcItemTags tag={props.tag} />
|
||||
</ImageBackground>
|
||||
);
|
||||
};
|
||||
|
||||
interface ExistingMosipVCItemContentProps {
|
||||
context: any;
|
||||
verifiableCredential: VerifiableCredential;
|
||||
generatedOn: string;
|
||||
tag: string;
|
||||
selectable: boolean;
|
||||
selected: boolean;
|
||||
iconName?: string;
|
||||
iconType?: string;
|
||||
service: any;
|
||||
onPress?: () => void;
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import {formatDistanceToNow} 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 { Icon } from 'react-native-elements';
|
||||
import { VC, CredentialSubject } from '../types/vc';
|
||||
import { Button, Column, Row, Text } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { TextItem } from './ui/TextItem';
|
||||
import { VcItemTags } from './VcItemTags';
|
||||
import VerifiedIcon from './VerifiedIcon';
|
||||
import { getLocalizedField } from '../i18n';
|
||||
import { CREDENTIAL_REGISTRY_EDIT } from 'react-native-dotenv';
|
||||
import { QrCodeOverlay } from './QrCodeOverlay';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Image, ImageBackground, View} from 'react-native';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {VC, CredentialSubject} from '../../../types/VC/ExistingMosipVC/vc';
|
||||
import {Button, Column, Row, Text} from '../../ui';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import {TextItem} from '../../ui/TextItem';
|
||||
import {VcItemTags} from '../common/VcItemTags';
|
||||
import VerifiedIcon from '../../VerifiedIcon';
|
||||
import {getLocalizedField} from '../../../i18n';
|
||||
import {CREDENTIAL_REGISTRY_EDIT} from 'react-native-dotenv';
|
||||
import {QrCodeOverlay} from '../../QrCodeOverlay';
|
||||
|
||||
export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
const { t, i18n } = useTranslation('VcDetails');
|
||||
export const ExistingMosipVCItemDetails: React.FC<
|
||||
ExistingMosipVCItemDetailsProps
|
||||
> = props => {
|
||||
const {t, i18n} = useTranslation('VcDetails');
|
||||
|
||||
//Assigning the UIN and VID from the VC details to display the idtype label
|
||||
const uin = props.vc?.verifiableCredential.credentialSubject.UIN;
|
||||
@@ -28,15 +30,17 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
return (
|
||||
<Column margin="10">
|
||||
<ImageBackground
|
||||
borderRadius={10}
|
||||
imageStyle={{width: '100%'}}
|
||||
resizeMethod="scale"
|
||||
resizeMode="stretch"
|
||||
style={Theme.Styles.openCardBgContainer}
|
||||
source={Theme.OpenCard}>
|
||||
<Row align="space-between">
|
||||
<Row align="space-between" padding="10" margin="0 10 0 10">
|
||||
<Column align="space-evenly" crossAlign="center">
|
||||
<Image
|
||||
source={
|
||||
props.vc?.credential.biometrics?.face
|
||||
? { uri: props.vc?.credential.biometrics.face }
|
||||
? {uri: props.vc?.credential.biometrics.face}
|
||||
: Theme.ProfileIcon
|
||||
}
|
||||
style={Theme.Styles.openCardImage}
|
||||
@@ -44,36 +48,61 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
|
||||
<QrCodeOverlay qrCodeDetailes={String(props.vc.credential)} />
|
||||
<Column margin="20 0 0 0">
|
||||
<Image source={Theme.MosipLogo} style={Theme.Styles.logo} />
|
||||
<Image
|
||||
source={Theme.MosipLogo}
|
||||
style={Theme.Styles.vcDetailsLogo}
|
||||
/>
|
||||
</Column>
|
||||
</Column>
|
||||
<Column align="space-evenly">
|
||||
<Column align="space-evenly" padding="10">
|
||||
<Column>
|
||||
<Text
|
||||
weight="bold"
|
||||
testID="fullNameTitle"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('fullName')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="fullNameValue"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.fullName
|
||||
props.vc?.verifiableCredential.credentialSubject.fullName,
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
<Row>
|
||||
<Column>
|
||||
<Column>
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
testID="gender"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('gender')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="genderValue"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.gender,
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column margin="25 0 0 0">
|
||||
<Text
|
||||
testID="idType"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="nationalCard"
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
@@ -81,14 +110,16 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
</Text>
|
||||
</Column>
|
||||
{uin ? (
|
||||
<Column margin="20 0 0 0">
|
||||
<Column margin="25 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
testID="uin"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('uin')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="uinNumber"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
@@ -98,14 +129,16 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
) : null}
|
||||
|
||||
{vid ? (
|
||||
<Column margin="20 0 0 0">
|
||||
<Column margin="25 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
testID="vid"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('vid')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="vidNumber"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
@@ -113,87 +146,83 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
</Text>
|
||||
</Column>
|
||||
) : null}
|
||||
<Column margin="20 0 0 0">
|
||||
<Column margin="30 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('dateOfBirth')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{new Date(
|
||||
getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject
|
||||
.dateOfBirth
|
||||
)
|
||||
).toLocaleDateString()}
|
||||
</Text>
|
||||
</Column>
|
||||
</Column>
|
||||
<Column margin="0 0 0 40">
|
||||
<Column>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('gender')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.gender
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
testID="generatedOnTitle"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('generatedOn')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="generatedOnValue"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{new Date(props.vc?.generatedOn).toLocaleDateString()}
|
||||
</Text>
|
||||
</Column>
|
||||
</Column>
|
||||
<Column margin="0 0 0 40">
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
testID="dateOfBirth"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('dateOfBirth')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="dateOfBirthValue"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{new Date(
|
||||
getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject
|
||||
.dateOfBirth,
|
||||
),
|
||||
).toLocaleDateString()}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column margin="25 0 0 0">
|
||||
<Text
|
||||
testID="status"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('status')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Row
|
||||
style={{
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
{props.vc?.isVerified && <VerifiedIcon />}
|
||||
<Text
|
||||
testID="valid"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{t('valid')}
|
||||
</Text>
|
||||
{props.vc?.isVerified && <VerifiedIcon />}
|
||||
</Row>
|
||||
</Column>
|
||||
<Column margin="20 0 0 0">
|
||||
<Column margin="92 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
testID="phoneNumber"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('phoneNumber')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="phoneNumberValue"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.phone
|
||||
props.vc?.verifiableCredential.credentialSubject.phone,
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
@@ -202,27 +231,29 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
</Column>
|
||||
</Row>
|
||||
<View style={Theme.Styles.hrLine}></View>
|
||||
<Column>
|
||||
<Column padding="10">
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
testID="emailId"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('email')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
testID="emailIdValue"
|
||||
style={
|
||||
props.vc?.verifiableCredential.credentialSubject.email
|
||||
.length > 25
|
||||
? { flex: 1 }
|
||||
: { flex: 0 }
|
||||
? {flex: 1}
|
||||
: {flex: 0}
|
||||
}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credentialSubject.email
|
||||
props.vc?.verifiableCredential.credentialSubject.email,
|
||||
)}
|
||||
</Text>
|
||||
</Row>
|
||||
@@ -230,19 +261,21 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
|
||||
<Column style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
testID="address"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('address')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
style={{ flex: 1 }}
|
||||
testID="addressValue"
|
||||
style={{flex: 1}}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getFullAddress(
|
||||
props.vc?.verifiableCredential.credentialSubject
|
||||
props.vc?.verifiableCredential.credentialSubject,
|
||||
)}
|
||||
</Text>
|
||||
</Row>
|
||||
@@ -250,12 +283,14 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
{CREDENTIAL_REGISTRY_EDIT === 'true' && (
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
testID="credentialRegistry"
|
||||
weight="regular"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('credentialRegistry')}
|
||||
</Text>
|
||||
<Text
|
||||
testID="credentialRegistryValue"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
@@ -268,13 +303,17 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
</ImageBackground>
|
||||
|
||||
{props.vc?.reason?.length > 0 && (
|
||||
<Text margin="24 24 16 24" weight="semibold">
|
||||
<Text
|
||||
testID="reasonForSharingTitle"
|
||||
margin="24 24 16 24"
|
||||
weight="semibold">
|
||||
{t('reasonForSharing')}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{props.vc?.reason?.map((reason, index) => (
|
||||
<TextItem
|
||||
testID="reason"
|
||||
key={index}
|
||||
divider
|
||||
label={formatDistanceToNow(reason.timestamp, {
|
||||
@@ -287,48 +326,48 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
|
||||
{props.activeTab !== 1 ? (
|
||||
props.isBindingPending ? (
|
||||
<Column style={Theme.Styles.openCardBgContainer}>
|
||||
<Row margin={'0 0 5 0'} crossAlign={'center'}>
|
||||
<Icon
|
||||
name="shield-alert"
|
||||
color={Theme.Colors.Icon}
|
||||
size={30}
|
||||
type="material-community"
|
||||
/>
|
||||
<Column style={Theme.Styles.openCardBgContainer} padding="10">
|
||||
<Column margin={'0 0 4 0'} crossAlign={'flex-start'}>
|
||||
<Image source={Theme.activationPending}></Image>
|
||||
<Text
|
||||
style={{ flex: 1 }}
|
||||
testID="offlineAuthDisabledHeader"
|
||||
style={{flex: 1}}
|
||||
weight="semibold"
|
||||
size="small"
|
||||
margin={'0 0 5 0'}
|
||||
margin={'5 0 0 0'}
|
||||
color={Theme.Colors.statusLabel}>
|
||||
{t('offlineAuthDisabledHeader')}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
<Text
|
||||
style={{ flex: 1 }}
|
||||
testID="offlineAuthDisabledMessage"
|
||||
style={{flex: 1, lineHeight: 17}}
|
||||
weight="regular"
|
||||
size="small"
|
||||
margin={'0 0 5 0'}
|
||||
color={Theme.Colors.statusLabel}>
|
||||
margin={'3 0 0 0'}
|
||||
color={Theme.Colors.statusMessage}>
|
||||
{t('offlineAuthDisabledMessage')}
|
||||
</Text>
|
||||
|
||||
<Button
|
||||
testID="enableVerification"
|
||||
title={t('enableVerification')}
|
||||
onPress={props.onBinding}
|
||||
type="radius"
|
||||
type="gradient"
|
||||
styles={{width: '100%'}}
|
||||
/>
|
||||
</Column>
|
||||
) : (
|
||||
<Column style={Theme.Styles.openCardBgContainer}>
|
||||
<Column style={Theme.Styles.openCardBgContainer} padding="10">
|
||||
<Row crossAlign="center">
|
||||
<Icon
|
||||
name="verified-user"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={28}
|
||||
containerStyle={{ marginStart: 4, bottom: 1 }}
|
||||
containerStyle={{marginStart: 4, bottom: 1}}
|
||||
/>
|
||||
<Text
|
||||
testID="profileAuthenticated"
|
||||
numLines={1}
|
||||
color={Theme.Colors.statusLabel}
|
||||
weight="bold"
|
||||
@@ -345,7 +384,7 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
interface VcDetailsProps {
|
||||
export interface ExistingMosipVCItemDetailsProps {
|
||||
vc: VC;
|
||||
isBindingPending: boolean;
|
||||
onBinding?: () => void;
|
||||
@@ -367,7 +406,7 @@ function getFullAddress(credential: CredentialSubject) {
|
||||
];
|
||||
|
||||
return fields
|
||||
.map((field) => getLocalizedField(credential[field]))
|
||||
.map(field => getLocalizedField(credential[field]))
|
||||
.concat(credential.postalCode)
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
19
components/VC/VcDetailsContainer.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
EsignetMosipVCItemDetails,
|
||||
EsignetMosipVCItemDetailsProps,
|
||||
} from './EsignetMosipVCItem/EsignetMosipVCItemDetails';
|
||||
import {VCMetadata} from '../../shared/VCMetadata';
|
||||
import {
|
||||
ExistingMosipVCItemDetails,
|
||||
ExistingMosipVCItemDetailsProps,
|
||||
} from './ExistingMosipVCItem/ExistingMosipVCItemDetails';
|
||||
|
||||
export const VcDetailsContainer: React.FC<
|
||||
EsignetMosipVCItemDetailsProps | ExistingMosipVCItemDetailsProps
|
||||
> = props => {
|
||||
if (VCMetadata.fromVC(props.vc.vcMetadata).isFromOpenId4VCI()) {
|
||||
return <EsignetMosipVCItemDetails {...props} />;
|
||||
}
|
||||
return <ExistingMosipVCItemDetails {...props} />;
|
||||
};
|
||||
18
components/VC/VcItemContainer.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
EsignetMosipVCItem,
|
||||
EsignetMosipVCItemProps,
|
||||
} from './EsignetMosipVCItem/EsignetMosipVCItem';
|
||||
import React from 'react';
|
||||
import {
|
||||
ExistingMosipVCItem,
|
||||
ExistingMosipVCItemProps,
|
||||
} from './ExistingMosipVCItem/ExistingMosipVCItem';
|
||||
|
||||
export const VcItemContainer: React.FC<
|
||||
ExistingMosipVCItemProps | EsignetMosipVCItemProps
|
||||
> = props => {
|
||||
if (props.vcMetadata.isFromOpenId4VCI()) {
|
||||
return <EsignetMosipVCItem {...props} />;
|
||||
}
|
||||
return <ExistingMosipVCItem {...props} />;
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Chip } from 'react-native-elements';
|
||||
import { Row, Text } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import {Chip} from 'react-native-elements';
|
||||
import {Row, Text} from '../../ui';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
|
||||
export const VcItemTags: React.FC<{ tag: string }> = (props) => {
|
||||
export const VcItemTags: React.FC<{tag: string}> = props => {
|
||||
return (
|
||||
props.tag !== '' && (
|
||||
<Row align="flex-end" margin={[8, 0, 0, 0]}>
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"generatedOn": "Generated on",
|
||||
"generatedOn": "Generated On",
|
||||
"status": "Status",
|
||||
"valid": "Valid",
|
||||
"photo": "Photo",
|
||||
"fullName": "Full name",
|
||||
"fullName": "Full Name",
|
||||
"gender": "Gender",
|
||||
"dateOfBirth": "Date of birth",
|
||||
"phoneNumber": "Phone number",
|
||||
"dateOfBirth": "Date of Birth",
|
||||
"phoneNumber": "Phone Number",
|
||||
"email": "Email",
|
||||
"address": "Address",
|
||||
"reasonForSharing": "Reason for sharing",
|
||||
"idType": "ID type",
|
||||
"idType": "ID Type",
|
||||
"id": "Id",
|
||||
"nationalCard": "National Card",
|
||||
"uin": "UIN",
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
import React, { useContext, useRef } from 'react';
|
||||
import { useInterpret, useSelector } from '@xstate/react';
|
||||
import { Pressable } from 'react-native';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {
|
||||
createVcItemMachine,
|
||||
selectVerifiableCredential,
|
||||
selectGeneratedOn,
|
||||
vcItemMachine,
|
||||
selectContext,
|
||||
selectTag,
|
||||
selectEmptyWalletBindingId,
|
||||
selectIsSavingFailedInIdle,
|
||||
selectKebabPopUp,
|
||||
} from '../machines/vcItem';
|
||||
import { VcItemEvents } from '../machines/vcItem';
|
||||
import { ErrorMessageOverlay } from './MessageOverlay';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
import { VcItemContent } from './VcItemContent';
|
||||
import { VcItemActivationStatus } from './VcItemActivationStatus';
|
||||
import { Row } from './ui';
|
||||
import { KebabPopUp } from './KebabPopUp';
|
||||
import { logState } from '../machines/app';
|
||||
|
||||
export const VcItem: React.FC<VcItemProps> = (props) => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const machine = useRef(
|
||||
createVcItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcKey
|
||||
)
|
||||
);
|
||||
|
||||
const service = useInterpret(machine.current, { devTools: __DEV__ });
|
||||
service.subscribe(logState);
|
||||
const context = useSelector(service, selectContext);
|
||||
const verifiableCredential = useSelector(service, selectVerifiableCredential);
|
||||
const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
|
||||
const isKebabPopUp = useSelector(service, selectKebabPopUp);
|
||||
const DISMISS = () => service.send(VcItemEvents.DISMISS());
|
||||
const KEBAB_POPUP = () => service.send(VcItemEvents.KEBAB_POPUP());
|
||||
const isSavingFailedInIdle = useSelector(service, selectIsSavingFailedInIdle);
|
||||
|
||||
const storeErrorTranslationPath = 'errors.savingFailed';
|
||||
|
||||
const generatedOn = useSelector(service, selectGeneratedOn);
|
||||
const tag = useSelector(service, selectTag);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Pressable
|
||||
onPress={() => props.onPress(service)}
|
||||
disabled={!verifiableCredential}
|
||||
style={
|
||||
props.selected
|
||||
? Theme.Styles.selectedBindedVc
|
||||
: Theme.Styles.closeCardBgContainer
|
||||
}>
|
||||
<VcItemContent
|
||||
context={context}
|
||||
verifiableCredential={verifiableCredential}
|
||||
generatedOn={generatedOn}
|
||||
tag={tag}
|
||||
selectable={props.selectable}
|
||||
selected={props.selected}
|
||||
service={service}
|
||||
iconName={props.iconName}
|
||||
iconType={props.iconType}
|
||||
onPress={() => props.onPress(service)}
|
||||
/>
|
||||
{props.isSharingVc ? null : (
|
||||
<Row crossAlign="center">
|
||||
{props.activeTab !== 'receivedVcsTab' &&
|
||||
props.activeTab != 'sharingVcScreen' && (
|
||||
<VcItemActivationStatus
|
||||
verifiableCredential={verifiableCredential}
|
||||
emptyWalletBindingId={emptyWalletBindingId}
|
||||
onPress={() => props.onPress(service)}
|
||||
showOnlyBindedVc={props.showOnlyBindedVc}
|
||||
/>
|
||||
)}
|
||||
<Pressable onPress={KEBAB_POPUP}>
|
||||
<KebabPopUp
|
||||
vcKey={props.vcKey}
|
||||
iconName="dots-three-horizontal"
|
||||
iconType="entypo"
|
||||
isVisible={isKebabPopUp}
|
||||
onDismiss={DISMISS}
|
||||
service={service}
|
||||
/>
|
||||
</Pressable>
|
||||
</Row>
|
||||
)}
|
||||
</Pressable>
|
||||
<ErrorMessageOverlay
|
||||
isVisible={isSavingFailedInIdle}
|
||||
error={storeErrorTranslationPath}
|
||||
onDismiss={DISMISS}
|
||||
translationPath={'VcDetails'}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
interface VcItemProps {
|
||||
vcKey: string;
|
||||
margin?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
showOnlyBindedVc?: boolean;
|
||||
onPress?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
onShow?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
activeTab?: string;
|
||||
iconName?: string;
|
||||
iconType?: string;
|
||||
isSharingVc?: boolean;
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import {View} from 'react-native';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
|
||||
const VerifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<View style={Theme.Styles.verifiedIconContainer}>
|
||||
<View style={Theme.Styles.verifiedIconInner}>
|
||||
<Icon name="check-circle" color={Theme.Colors.VerifiedIcon} size={14} />
|
||||
<Icon name="check-circle" color={Theme.Colors.VerifiedIcon} size={12} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||