Sprint 9 - 31 August 2023 Build (#788)

* Removed async  at downloadModal in auth.ts

* Removed debug.keystore file

* fix(INJI-187) : [Vijay|Alka] Fixed Purple Theme Related Bugs and Duplicate Permission popup issue.

* fix(INJI-187) : [Vijay] fix about inji documentation page redirection

* Fixes in ui-revamp-release

* fix:vcsharing and wallet binding

* Removed key names version, tuvali-version in locals of AboutInji obj.

* feat(INJI-172): display error popup to restrict vc share when audit entry is not possible

audit entry cannot be made when the device remaining storage has reached the minimumStorageRequiredForAuditEntryInMB

Issue Link https://mosip.atlassian.net/browse/INJI-172

* Reverted lst commit and removed version number in locals

* fix:vcsharing and wallet binding

* removing logger

* fix(INJI-187) : [Vijay] fix welcome screen auth issue, hide the inji tour guide option and fix the tuvali version in about inji

* fix(INJI-92): [Bhargavi] show proper error message in the UI for the deactivated UIN & VID

Issue Link https://mosip.atlassian.net/browse/INJI-92

* fix(INJI-187) : [Vijay] add missing literals in hindi

* feat[#190]:[Pooja] fix back button on incoming VC page to show QR when clicked

* refator(INJI-172): [Kiruthika] rename the config key for storage limit to minimumStorageRequiredInMB

Issue Link https://mosip.atlassian.net/browse/INJI-172

* refator(INJI-172): [Kiruthika] rename translation path of storage limit reached error shown while adding / receiving VC

Issue Link https://mosip.atlassian.net/browse/INJI-172

* refator(INJI-172): [Kiruthika] rename storage limit reached for making audit entry variable

Issue Link https://mosip.atlassian.net/browse/INJI-172

* refator(INJI-172): [Kiruthika] remove unused context storeError in vcItem machine

Issue Link https://mosip.atlassian.net/browse/INJI-172

* fix:logout crash

* feat(#162): [Tilak|Pooja] Create Short UUID using npm lib and display it in about page

* [Release 03-07-2023]fix:app crash on logout and remove checkbox and kebab on vc while sharing

* feat(#162): [Tilak|Pooja] Update app id ux and use alphanum_upper of length 12 for uuid

* feat(#162): [Tilak|Pooja] Add copy button for app id

* feat(#162): [Tilak|Pooja] Update app id when settings context is present in db but not app id

* feat[#162]:[Pooja] add translations for app ID

* feat[#162]:[Pooja] add translations for copy button

* [Release 03-07-2023]add:loader while saving credential registry

* [Release 03-07-2023]fix:saving credentialRegistry

* fix(INJI-206): [Vijay] fix mmkv storage issue, environment url change persistance and literal change

* fix(INJI-206): [Vijay] add comment on the getFileName method

* Adjusted splash-screen & updated locals

* Cleared existing develop changes in PR

* Cleared comments mentioned in locals

* refator(INJI-172): [Kiruthika] modify storage limit reached error message while adding or receiving card

Issue Link https://mosip.atlassian.net/browse/INJI-172

* refator(INJI-172): [Kiruthika] add storage limit reached error message in spanish locale

Issue Link https://mosip.atlassian.net/browse/INJI-172

* refator(INJI-172): [Kiruthika] move storage limit reached check function to storage class

Issue Link https://mosip.atlassian.net/browse/INJI-172

* refator(INJI-172): [Kiruthika] add missing storage limit reached error message in strings.json file

Issue Link https://mosip.atlassian.net/browse/INJI-172

* fix(INJI-206): [Vijay] remove vc from RNFS when vc metadata is removed from MMKV

* refator(INJI-172): [Kiruthika] extract service and action

Issue Link https://mosip.atlassian.net/browse/INJI-172

* refator(INJI-172): [Kiruthika] remove MB from min storage limit property name

Issue Link https://mosip.atlassian.net/browse/INJI-172

* refator(INJI-172): [Kiruthika] extract function for displayng storage limit reached error

Issue Link https://mosip.atlassian.net/browse/INJI-172

* fix(INJI-172): [Kiruthika] show storage limit popup on minimum storage limit reached criteria when bluetooth is off

Issue Link https://mosip.atlassian.net/browse/INJI-172

* Test check

* refator(INJI-172): [Kiruthika] rename the config key of storage limit

Issue Link https://mosip.atlassian.net/browse/INJI-172

* Calculate HMAC and store Encrypted HMAC in Database

* Revert Test Check

* fix(INJI-212): [Vijay] fix qrlogin success popup redirection to history issue and vc sharing in progress screen issue

* Change error pop up text in my vc's tab for tampered data

* Revert english json changes

* refator(INJI-172): [Kiruthika] extract service and action

Issue Link https://mosip.atlassian.net/browse/INJI-172

* refator(INJI-172): [Kiruthika] replace equality check of boolean with type conversion

Issue Link https://mosip.atlassian.net/browse/INJI-172

* Add encoding for decryption of HMAC

* [Release 03-07-2023]fix:sharing timeout message displaying always

* feat(#162): [Tilak|Pooja] remove unnecessary style from purple

* [Release 03-07-2023]fix:sharing timeout message displaying always

* [Release 03-07-2023]fix:navigation to history tab after successfully receiving VC

* [Release 03-07-2023]fix:navigation to history tab after successfully receiving VC

* [Release 03-07-2023]fix:navigation to history tab after successfully receiving VC

* refator(INJI-182): [Kiruthika] send app id as X-AppId in header in the API requests

Issue Link https://mosip.atlassian.net/browse/INJI-182

* fix(INJI-210): [Bhargavi] show the home screen after closing the success status overlay

Issue Link https://mosip.atlassian.net/browse/INJI-210

* feat(#162): [Tilak|Pooja] extract app id constants.

* refator(INJI-182): [Kiruthika] send app id in header only for the residentmobileapp API requests

Issue Link https://mosip.atlassian.net/browse/INJI-182

* feat(#INJI-178) [Adityan] calculate and store encrypted HMAC hash of vc's metadata in database

* feat(#INJI-178) [Adityan] calculate and store encrypted HMAC hash of vc's metadata in database and fix

* feat(#INJI-173) [Adityan] Added a beta release workflow when tagged

* Refactor the code and remove unused params

* Refactor the code and remove unused parameters

* feat(#162): [Tilak|Pooja] fix merge issue in hindi and format app id dictionary

* Refactor the code and remove debug logs

* feat(#162): [Tilak|Pooja] remove unused parameters in settings.ts

* feat(INJI-149): add a popup when keychain fails to get password

Reason: when keychain.getGenericPassword errors due to a false/falsy response,
  the state machine goes ahead and clears the MMKV store and the VC
  directory as without the key the encrypted data is not meaningful,
  it may so happen that the keychain/keystore is not unlocked yet as
  most modern mobile OSes can choose to lock access to it when the phone
  is locked

Fix: earlier the state machine would go and clear the datastores without
  user intervention, this fix will give the user a prompt if encrypted
  VCs exist on app-data and allow user to try reading the keychain once
  again before silently deleting everything

Co-authored-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com>
Co-authored-by: Alka <prasadalka1998@gmail.com>

* feat(INJI-149): add DualMessageOverlay component

details about the component:

- has two buttons tryAgain & cancel each occupying half the space in the
  row
- kludge: uses similar styling for both themes
    - TODO: override the width styles in-place instead
- this component should not be used for production as this is strictly
  temporary for narrowing down in on bugs

Other changes:

- removed additional button from original MessageOverlay component

* fix(INJI-149): remove unavailable action from store.ts

* chore: add missed out Podfile.lock changes

* feat(INJI-149): remove extra button component of half width

* feat(INJI-149): make DualMessageOverlay button gradient type

Co-authored-by: <vijay@Vijays-MacBook-Pro.local>

* chore: add check for Verifiable document store init

other changes:
- add Podfile.lock
- remove debug logs

* chore: bump up the tuvali version to v0.4.2 for release 18-07-2023

* fix: remove double quotes to the margin top property in send vc screen

* feat(#162): [Tilak] fix ios logout crash by navigating on timeout

* feat(#162): [Tilak] fix ios logout crash by navigating on timeout

* fix(INJI-178): check for tampered VC without considering pinned state

Issue Link https://mosip.atlassian.net/browse/INJI-178

* refactor: [Kiruthika | Dhivya] fix type warnings

* fix(INJI-223): show the camera in scan screen while sharing the VC and click on scan icon

Issue Link https://mosip.atlassian.net/browse/INJI-223

* fix version code error for internal build workflow (#761)

* chore: update pod deps' to match development team of 1st project

* feat(inji-214): [Dhivya|Tilak] integrate the secure store

* feat[#211]:[Pooja] fix turncated passcode and confirm password button

* feat[#211]:[Pooja] fix truncated histroy title text in iOS

* feat(inji-214): [Dhivya|Tilak] conditional check added for platform

* feat(inji-214): [Dhivya] modified state machine for generating keypair

* feat[#211]:[Pooja] fix multiple error being displayed when the vc binding fails

* fix(INJI-223): disconnect the device while sharing the VC and click on tab icons except scan icon

Issue Link https://mosip.atlassian.net/browse/INJI-223

* feat[#211]:[Pooja] fix download card button styles

* feat(inji-214): [Dhivya|Tilak] add logstate for vcitem state machine

(cherry picked from commit 7001679cdd6f46dfc5a60474f30284292e69b888)

* version code for beta build in pipeline

* feat(inji-214): [Dhivya|Tilak] use Secure key store to sign jwt for auth

* feat[#211]:[Pooja] add error message when VC activation fails in kebab pop up

* chore(INJI-216): bump up tuvali version to v0.4.3

* feat[#225]:[Pooja] fix about inji url in about page

* chore(INJI-216): bump up tuvali version to v0.4.3

* chore(INJI-216): update podlock file with the correct tuvali version

* feat(inji-214): [Dhivya|Tilak] use sdk hmac method to create hmac for vc key

* feat(inji-214): [Tilak|Dhivya] refactor hmac functionality

* feat(inji-214): [Dhivya|Tilak] modify generatehmac function definition

* refactor(INJI-223): get the scan string from route config to keep it consistent

Issue Link https://mosip.atlassian.net/browse/INJI-223

* feat[mosip#211]:[Pooja] refactor onCancel

Co-authored-by: Harsh Vardhan <harsh59v@gmail.com>

* feat(inji-175): show warning message to user on android hardware keystore

* feat(inji-175): refactor the files

* feat(inji-214): [Dhivya|Tilak] use sdk to create signature

* feat:(inji-175) show warning message when device keystore not supports

* fix(inji-175): resolve conflicts

* feat(inji-175): locales changes for hardware keystore not exists warning text

* feat(INJI-222): add popup for error in decryption

* feat(INJI-222): wrap app init component & popups

* feat(INJI-222): propagate event from _store to app state machine

Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com>

* feat(inji-175): modify arabic title locale

* chore(INJI-222): move error msg text to errors obj

* feat(inji-175): update mosip package name

* Implemented Receive Card & Received Cards options in Settings

* Fixed all the locals

* fix(inji-175): modify variable name and refactor existing code

* [INJI-69]fix:The mobile camera is not opening on the scan page when the flight mode is turned

* [INJI-69]fix:The mobile camera is not opening on the scan page when the flight mode is turned

* [INJI-69]fix:The mobile camera is not opening on the scan page when the flight mode is turned

* [Inji-248]fix:The audit for deleting the VC is not logged in the history

* fix:naming conventions, removed log and values in locals

* Update .project

* Initial commit 07-08-2023

* [INJI-249]fix:Back button from the ellipsis page not working on the mobile

* Inplemented INJI Tour Guide in settings

* Set navigation from auth to home screen

* feat(inji-214): [Dhivya|Tilak] Update encryption and decryption to call SecureKeystore sdk with biometric auth enabled

* Resolved the comments

* Updated 'Get Started' button text in hindi locals

* feat(INJI-238): handling errors when invalidate keys of biometric

* feat(INJI-238): add reset keys action

* fix(INJI-149): skip showing read-error popup on launch

* feat(inji-238): fetch enabled device biometric from sdk

* chore: disallow expansion of received cards

* feat(inji-214): [Dhivya|Tilak] fix app events RESET_KEY_INVALIDATE_ERROR_DISMISS not accessible issue

* feat(inji-214): [Dhivya|Tilak] fix generate key pair failing due to wrong count of arguments

* fix(INJI-192): add error message translations for HMAC calculation

* feat(inji-214): [Dhivya|Tilak] Update biometric popup text

* feat(inji-238): translations added for biometric text

* feat(inji-214): [Dhivya|Tilak] fix language not getting updated for auth popup

* fix(INJI-286):Show VC detailed view in the Received Vc screen

* fix(INJI-286):Show VC detailed view in the Received Vc screen

* fix(INJI-285): Added the flip camera buton in the scan screen

* fix(INJI-262):pinned VC's audit logs are missing

* fix(INJI-262):pinned VC's audit logs are missing

* fix(INJI-214): secure-keystore version changes

* fix(INJI-214): fixed Podfile.lock merge issue

* refactor(INJI-214): refactor the code

* fix(INJI-262):pinned VC's audit logs are missing

* fix(INJI-211[1]): improve isPinned evaluation of (un)pinned VCs (#102)

* fix(INJI-211[1]): Pin/unpin card not working intermittently

* fix(INJI-263):camera is not opening when vc is pinned

* fix(INJI-262):pinned VC's audit logs are missing

* fix(INJI-262):pinned VC's audit logs are missing

* [INJI-69]fix:The mobile camera is not opening on the scan page when the flight mode is turned

* feat(inji-238): handle key not found error after key invalidation

* feat(inji-238): reinit app machine on dismiss of invalidation popup

* fix(INJI-262):pinned VC's audit logs are missing

* fix(INJI-263):camera is not opening when vc is pinned

* feat(INJI-255): hash the passcode and store in mmkv

* feat(inji-238): use a dummy key to request biometric auth everytime user opens the app

* feat(inji-238): revert tamperedErrorMessageString value

* feat(inji-293): use jetify to fix react-native/location bundle issue

* feat(inji-293): fix jetify running location

* Inji 282 disable camera error (#775)

* fix(INJI-282): Correct error message will be displayed when camera is disabled

* fix(INJI-260):app not aligned properly with the smaller display screen (#778)

* Fixed tab elements which are still in english (#779)

* feat(INJI-202): hash uin/vid to store in mmkv and file storage (#780)

* feat(INJI-202): hash uin/vid to store in mmkv and file storage

* feat(INJI-202): refactor to store salt in constants

* feat(INJI-202): refactor argon2i salt and add function to get MMKV data

* feat(INJI-202): refactor to get mkkv data method

---------

Co-authored-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com>

* fix(inji-253): set thumbprint on wallet binding for e-signet login

* fix(INJI-296): label mismatch in language files (#772)

* fix(INJI-262):pinned VC's audit logs are missing

* fix(INJI-286): expand received card popup on click (#783)

Revert "chore: disallow expansion of received cards"

This reverts commit 038b6e4259.

* chore(INJI-304): bump up secure keystore version to v0.1.1

* chore(INJI-304): bump up tuvali version to v0.4.4

* Inji 304 update tuvali and secure keystore version (#784)

* chore(INJI-304): bump up secure keystore version to v0.1.1

* chore(INJI-304): bump up tuvali version to v0.4.4

---------

Co-authored-by: dhivya0413 <120356578+dhivya0413@users.noreply.github.com>

* fix(INJI-304): tuvali & secure-keystore version update in package-lock

Co-authored-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com>

* fix(INJI-282) Cross button of pop-up will navigate to home (#786)

* fix(INJI-282) Cross button of pop-up will navigate to home

* Icon made as pressble part

* fix(INJI-253): set thumbprint for custom secure keystore for qr login (#787)

---------

Co-authored-by: anil_majji <majjianilkumar050@gmail.com>
Co-authored-by: Monobikash Das <43202165+MonobikashDas@users.noreply.github.com>
Co-authored-by: Vijay <vijay@Vijays-MacBook-Pro.local>
Co-authored-by: Alka Prasad <Alka1703@users.noreply.github.com>
Co-authored-by: Sri Kanth Kola <srikanthsri7447@gmail.com>
Co-authored-by: vijay151096 <94220135+vijay151096@users.noreply.github.com>
Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com>
Co-authored-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com>
Co-authored-by: Tilak Puli <tilakpuli15@gmail.com>
Co-authored-by: adityankannan-tw <adityan.kannan@thoughtworks.com>
Co-authored-by: adityankannan-tw <109274996+adityankannan-tw@users.noreply.github.com>
Co-authored-by: PoojaBabusing <115976560+PoojaBabusing@users.noreply.github.com>
Co-authored-by: Harsh Vardhan <harsh59v@gmail.com>
Co-authored-by: Alka <prasadalka1998@gmail.com>
Co-authored-by: Dhivya <dhivya.v@thoughtworks.com>
Co-authored-by: srikanth716 <97477121+srikanth716@users.noreply.github.com>
Co-authored-by: Anil kumar M <106086523+Anil-kumar-Majji@users.noreply.github.com>
Co-authored-by: Swati Goel <meet2swati@gmail.com>
Co-authored-by: dhivya0413 <120356578+dhivya0413@users.noreply.github.com>
This commit is contained in:
KiruthikaJeyashankar
2023-08-31 15:01:49 +05:30
committed by GitHub
parent a8878c6aca
commit fddc16de6a
330 changed files with 18469 additions and 8181 deletions

13
.env
View File

@@ -1,3 +1,14 @@
MIMOTO_HOST=https://api.qa-121.mosip.net/residentmobileapp
# 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
GOOGLE_NEARBY_MESSAGES_API_KEY=
#Application Theme can be ( orange | purple )
APPLICATION_THEME=orange
#environment can be changed if it is toggled
CREDENTIAL_REGISTRY_EDIT=true
#supported languages( en, fil, ar, hi, kn, ta)
APPLICATION_LANGUAGE=en

View File

@@ -1,10 +1,10 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug, triage
assignees:
assignees:
---
**Describe the bug**
A clear and concise description of what the bug is.
@@ -20,18 +20,33 @@ Steps to reproduce the behavior:
**Expected behavior**
A clear and concise description of what you expected to happen.
**Actual behavior**
A clear and concise description of what factually occurred.
**Screenshots**
If applicable, add screenshots to help explain your problem.
If applicable, add screenshots to help explain your problem.
**Add the screenshot of the profile page with commit id**
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Wallet Device: [e.g. iPhone6]
- Phone make/model: [e.g. :Vivo Y73]
- OS: [e.g. iOS8.1]
- BLE version : [e.g. 4.2]
- Verifier Device: [e.g. iPhone6]
- Phone make/model: [e.g. :Vivo Y73]
- OS: [e.g. iOS8.1]
- BLE version : [e.g. 4.2]
- Inji app version: [e.g 0.3.0]
- Mimoto version: [e.g 1.2.x]
- MOSIP Version: [e.g. 1.2.1]
- Mimoto server: [e.g. https://.....com]
- MOSIP server: [e.g. https://...mosip.com]
**Where does the issue occur: Wallet/Verifier?**
**Logs of wallet and verifier:**
**Additional context**
Add any other context about the problem here.

142
.github/workflows/android-beta-build.yml vendored Normal file
View File

@@ -0,0 +1,142 @@
name: Android Beta Build
env:
backendServiceDefaultUrl: https://api.sandbox.mosip.net
on:
workflow_dispatch:
inputs:
tag:
description: 'Tag to be published'
required: true
default: 'v1.2.3'
type: string
body:
description: 'Release body message'
required: true
default: 'Changes in this Release'
type: string
pre-release:
description: 'Pre-release? True/False'
required: true
default: False
type: string
backendServiceUrl:
description: '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
buildDescription:
description: 'What to test'
required: true
default: 'QA-Triple environment'
type: string
registry_edit:
description: 'Edit Registry'
required: true
default: 'true'
type: choice
options:
- false
- true
jobs:
build-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/setup-node@v3
with:
node-version: '16.x'
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ github.event.inputs.tag }}
release_name: ${{ github.event.inputs.tag }}
body: ${{ github.event.inputs.body }}
draft: false
prerelease: ${{fromJSON(github.event.inputs.pre-release)}}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check for git tag
run: |
cd android/scripts
./git-tag.sh
- name: Install npm dependencies
run: |
npm ci
- 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: Export variables for keystore
run: |
cd android/app
export RELEASE_KEYSTORE_ALIAS=androidreleasekey
export RELEASE_KEYSTORE_PASSWORD=$RELEASE_KEYSTORE_PASSWORD
env:
RELEASE_KEYSTORE_PASSWORD: '${{secrets.INJI_ANDROID_RELEASE_STOREPASS}}'
- 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
run: |
cd android/scripts
./beta-build.sh
env:
MIMOTO_HOST: ${{ github.event.inputs.backendServiceUrl }}
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 }}

View File

@@ -1,4 +1,7 @@
name: ID PASS - MOSIP Resident Application
name: PR - Android build verification
env:
backendServiceDefaultUrl: https://api.sandbox.mosip.net
on:
push:
@@ -7,16 +10,26 @@ on:
- develop
- demobranch
- qa-develop
- 0.9
tags:
- '*'
pull_request:
types: [ opened, synchronize ]
branches:
- develop
- 'release-**'
tags:
- '*'
jobs:
build-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
# Cache stuff for faster build
- uses: actions/setup-node@v3
with:
node-version: '16.x'
- name: Cache local npm repository
uses: actions/cache@v3.0.11
with:
@@ -30,7 +43,7 @@ jobs:
path: |
~/.gradle/caches
~/.gradle/wrapper
- name: Install npm dependencies
run: |
npm ci
@@ -39,33 +52,63 @@ jobs:
run: |
echo "${{ secrets.ENV_FILE }}" > .env.local > android/local.properties
- name: Setup branch and env
- 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
- 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
gpg2 --quiet --batch --passphrase=${{secrets.gpg_secret}} --allow-secret-key-import --import ./.github/keys/mosipgpgkey_sec.gpg
- name: Build App Newlogic Release
- 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
./gradlew :app:assembleNewlogicRelease
env:
FIREBASE_SECRET: ${{ secrets.GPG_SECRET }}
- name: Generate keystore
run: |
keytool \
-genkey -v \
-storetype PKCS12 \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-storepass $DEBUG_KEYSTORE_PASSWORD \
-keypass $DEBUG_KEYSTORE_PASSWORD \
-alias androiddebugkey \
-keystore android/app/debug.keystore \
-dname "CN=io.mosip.residentapp,OU=,O=,L=,S=,C=US"
env:
DEBUG_KEYSTORE_PASSWORD: '${{secrets.INJI_ANDROID_DEBUG_STOREPASS}}'
- name: Upload Artifact
uses: actions/upload-artifact@v3.1.1
- name: Export variables for keystore
run: |
cd android/app
export DEBUG_KEYSTORE_ALIAS=androiddebugkey
export DEBUG_KEYSTORE_PASSWORD=$DEBUG_KEYSTORE_PASSWORD
env:
DEBUG_KEYSTORE_PASSWORD: '${{secrets.INJI_ANDROID_DEBUG_STOREPASS}}'
- name: Bump version code
uses: chkfung/android-version-actions@v1.2.1
with:
name: output
path: android/app/build/outputs/apk/newlogic/release/
retention-days: 1
gradlePath: android/app/build.gradle
versionCode: ${{github.run_number}}
- name: Run Build using Fastlane
run: |
cd android/scripts
./verify-build.sh
env:
DEBUG_KEYSTORE_ALIAS: androiddebugkey
DEBUG_KEYSTORE_PASSWORD: '${{secrets.INJI_ANDROID_DEBUG_STOREPASS}}'

View File

@@ -1,74 +0,0 @@
name: ID PASS - MOSIP Resident Application Custom build
env:
backendServiceDefaultUrl: https://api.qa-121.mosip.net/residentmobileapp
on:
workflow_dispatch:
inputs:
backendServiceUrl:
description: 'Backend service URL'
required: true
default: 'https://api.qa-121.mosip.net/residentmobileapp'
type: string
jobs:
build-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
# Cache stuff for faster build
- 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 install
- 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: Build App Newlogic Release
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
./gradlew :app:assembleNewlogicRelease
env:
MIMOTO_HOST: ${{ github.event.inputs.backendServiceUrl }}
FIREBASE_SECRET: ${{ secrets.GPG_SECRET }}
- name: Upload Artifact
uses: actions/upload-artifact@v3.1.1
with:
name: apk-output
path: android/app/build/outputs/apk/newlogic/release/
retention-days: 10

View File

@@ -0,0 +1,124 @@
name: Android Internal Build
env:
backendServiceDefaultUrl: https://api.sandbox.mosip.net
on:
workflow_dispatch:
inputs:
backendServiceUrl:
description: '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
buildDescription:
description: 'What to test'
required: true
default: 'QA-Triple environment'
type: string
registry_edit:
description: 'Edit Registry'
required: true
default: 'true'
type: choice
options:
- false
- true
jobs:
build-android:
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 ci
- 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: Export variables for keystore
run: |
cd android/app
export RELEASE_KEYSTORE_ALIAS=androidreleasekey
export RELEASE_KEYSTORE_PASSWORD=$RELEASE_KEYSTORE_PASSWORD
env:
RELEASE_KEYSTORE_PASSWORD: '${{secrets.INJI_ANDROID_RELEASE_STOREPASS}}'
- 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
run: |
cd android/scripts
./internal-build.sh
env:
MIMOTO_HOST: ${{ github.event.inputs.backendServiceUrl }}
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 }}

View File

@@ -1,4 +1,7 @@
name: 'Delete old artifacts'
on:
workflow_dispatch:
name: 'Delete old artifacts'
on:
workflow_dispatch:
@@ -8,5 +11,5 @@ jobs:
steps:
- uses: kolpav/purge-artifacts-action@v1
with:
token: ${{ secrets. access_token }}
token: ${{ secrets.ACTION_PAT }}
expire-in: 2days # Setting this to 0 will delete all artifacts

59
.github/workflows/ios-build-verify.yml vendored Normal file
View File

@@ -0,0 +1,59 @@
name: PR - IOS build verification
on:
push:
branches:
- main
- develop
- demobranch
- qa-develop
- 0.9
tags:
- '*'
pull_request:
types: [ opened, synchronize ]
branches:
- develop
- 'release-**'
tags:
- '*'
jobs:
build_ios:
name: Building the IPA
runs-on: macos-13
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/setup-node@v3
with:
node-version: '16.x'
- name: Install npm dependencies
run: |
npm install
- name: Install Pod
run: |
cd ios
pod install
- name: Building the IPA
run: |
cd ios
fastlane build_verify
env:
APP_STORE_CONNECT_TEAM_ID: '${{ secrets.APP_STORE_CONNECT_TEAM_ID }}'
DEVELOPER_APP_ID: '${{ secrets.IOS_INJI_DEVELOPER_APP_ID }}'
INJI_IOS_DEVELOPER_APP_IDENTIFIER: '${{ secrets.INJI_IOS_DEVELOPER_APP_IDENTIFIER }}'
INJI_IOS_DEVELOPER_PORTAL_TEAM_ID: '${{ secrets.INJI_IOS_DEVELOPER_PORTAL_TEAM_ID }}'
INJI_IOS_FASTLANE_APPLE_ID: '${{ secrets.INJI_IOS_FASTLANE_APPLE_ID }}'
INJI_IOS_FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: '${{ secrets.INJI_IOS_FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}'
GIT_AUTHORIZATION: '${{ secrets.GIT_AUTHORIZATION }}'
INJI_IOS_PROVISIONING_PROFILE_SPECIFIER: '${{ secrets.INJI_IOS_PROVISIONING_PROFILE_SPECIFIER }}'
INJI_IOS_TEMP_KEYCHAIN_PASSWORD: '${{ secrets.INJI_IOS_TEMP_KEYCHAIN_PASSWORD }}'
INJI_IOS_TEMP_KEYCHAIN_USER: '${{ secrets.INJI_IOS_TEMP_KEYCHAIN_USER }}'
APPLE_KEY_ID: '${{ secrets.APPLE_KEY_ID }}'
APPLE_ISSUER_ID: '${{ secrets.APPLE_ISSUER_ID }}'
APPLE_KEY_CONTENT: '${{ secrets.APPLE_KEY_CONTENT }}'
MATCH_PASSWORD: '${{ secrets.INJI_IOS_MATCH_PASSWORD }}'

88
.github/workflows/ios-build.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: Inji iOS build
on:
workflow_dispatch:
inputs:
backendServiceUrl:
description: '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
internal-testers:
description: 'Internal Testers Group'
required: true
default: 'QA-Triple'
type: choice
options:
- Dev-testing
- MEC
- QA
- MOSIP-Collab
- MOSIP-Dev-testing
buildDescription:
description: 'What to test'
required: true
default: 'QA-Triple environment build'
type: string
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'
- name: Install npm dependencies
run: |
npm install
- name: Install Pod
run: |
cd ios
pod install
- name: Deploy iOS Beta to TestFlight
run: |
cd ios
fastlane beta
env:
APP_STORE_CONNECT_TEAM_ID: '${{ secrets.APP_STORE_CONNECT_TEAM_ID }}'
DEVELOPER_APP_ID: '${{ secrets.IOS_INJI_DEVELOPER_APP_ID }}'
INJI_IOS_DEVELOPER_APP_IDENTIFIER: '${{ secrets.INJI_IOS_DEVELOPER_APP_IDENTIFIER }}'
INJI_IOS_DEVELOPER_PORTAL_TEAM_ID: '${{ secrets.INJI_IOS_DEVELOPER_PORTAL_TEAM_ID }}'
INJI_IOS_FASTLANE_APPLE_ID: '${{ secrets.INJI_IOS_FASTLANE_APPLE_ID }}'
INJI_IOS_FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: '${{ secrets.INJI_IOS_FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}'
GIT_AUTHORIZATION: '${{ secrets.GIT_AUTHORIZATION }}'
INJI_IOS_PROVISIONING_PROFILE_SPECIFIER: '${{ secrets.INJI_IOS_PROVISIONING_PROFILE_SPECIFIER }}'
INJI_IOS_TEMP_KEYCHAIN_PASSWORD: '${{ secrets.INJI_IOS_TEMP_KEYCHAIN_PASSWORD }}'
INJI_IOS_TEMP_KEYCHAIN_USER: '${{ secrets.INJI_IOS_TEMP_KEYCHAIN_USER }}'
APPLE_KEY_ID: '${{ secrets.APPLE_KEY_ID }}'
APPLE_ISSUER_ID: '${{ secrets.APPLE_ISSUER_ID }}'
APPLE_KEY_CONTENT: '${{ secrets.APPLE_KEY_CONTENT }}'
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 }}

View File

@@ -1,43 +0,0 @@
name: Tagging of repos
env:
tag: v1.2.3
on:
workflow_dispatch:
inputs:
tag:
description: 'Tag to be published'
required: true
default: 'v1.2.3'
type: string
body:
description: 'Release body message'
required: true
default: 'Changes in this Release'
type: string
pre-release:
description: 'Pre-release? True/False'
required: true
default: False
type: string
jobs:
build:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3.1.0
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: ${{ github.event.inputs.tag }}
release_name: ${{ github.event.inputs.tag }}
body: |
${{ github.event.inputs.body }}
draft: false
prerelease: ${{fromJSON(github.event.inputs.pre-release)}}

9
.gitignore vendored
View File

@@ -60,8 +60,6 @@ yarn-error.log
# BUCK
buck-out/
\.buckd/
*.keystore
!debug.keystore
# Bundle artifacts
*.jsbundle
@@ -90,4 +88,9 @@ dist/
.vscode/
temp/
.eslintcache
.env.local
.env.local
.yalc/
yalc.lock
android/app/debug.keystore

95
App.tsx
View File

@@ -1,20 +1,105 @@
import React, { useContext } 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 { selectIsReady } from './machines/app';
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 SecureKeystore from 'react-native-secure-keystore';
import { isCustomSecureKeystore } from './shared/cryptoutil/cryptoUtil';
import i18n from './i18n';
import './i18n';
// kludge: this is a bad practice but has been done temporarily to surface
// an occurance of a bug with minimal residual code changes, this should
// be removed once the bug cause is determined & fixed, ref: INJI-222
const DecryptErrorAlert = (controller, t) => {
const heading = t('errors.decryptionFailed');
const desc = t('errors.decryptionFailed');
const ignoreBtnTxt = t('ignore');
Alert.alert(heading, desc, [
{
text: ignoreBtnTxt,
onPress: () => controller.ignoreDecrypt(),
style: 'cancel',
},
]);
};
const AppLayoutWrapper: React.FC = () => {
const { appService } = useContext(GlobalContext);
const isDecryptError = useSelector(appService, selectIsDecryptError);
const controller = useApp();
const { t } = useTranslation('WelcomeScreen');
if (isDecryptError) {
DecryptErrorAlert(controller, t);
}
return <AppLayout />;
};
const AppLoadingWrapper: React.FC = () => {
const { appService } = useContext(GlobalContext);
const isReadError = useSelector(appService, selectIsReadError);
const isKeyInvalidateError = useSelector(
appService,
selectIsKeyInvalidateError
);
const controller = useApp();
const { t } = useTranslation('WelcomeScreen');
return (
<>
<AppLoading />
<ErrorMessageOverlay
translationPath={'WelcomeScreen'}
isVisible={isKeyInvalidateError}
error={'errors.invalidateKeyError'}
onDismiss={controller.RESET}
/>
{isReadError ? (
<DualMessageOverlay
isVisible={isReadError}
title={t('failedToReadKeys')}
message={t('retryRead')}
onTryAgain={controller.TRY_AGAIN}
onIgnore={controller.IGNORE}
/>
) : null}
</>
);
};
const AppInitialization: React.FC = () => {
const { appService } = useContext(GlobalContext);
const hasFontsLoaded = useFont();
const isReady = useSelector(appService, selectIsReady);
const hasFontsLoaded = useFont();
const { t } = useTranslation('common');
return isReady && hasFontsLoaded ? <AppLayout /> : <AppLoading />;
useEffect(() => {
if (isCustomSecureKeystore()) {
SecureKeystore.updatePopup(
t('biometricPopup.title'),
t('biometricPopup.description')
);
}
}, [i18n.language]);
return isReady && hasFontsLoaded ? (
<AppLayoutWrapper />
) : (
<AppLoadingWrapper />
);
};
export default function App() {

1
AppMetaData.md Normal file
View File

@@ -0,0 +1 @@
Inji is a mobile app that can be used as a digital wallet to store credentials. It also allows verification of identity in both offline and online modes, at any place and time.

View File

@@ -15,6 +15,7 @@ Be sure to have the following build tools installed before proceeding:
## Generate keystore for APK signing
```shell
# Generate and use Debug keystore for development and testing purposes
keytool \
-genkey -v \
-storetype PKCS12 \
@@ -23,6 +24,21 @@ keytool \
-validity 10000 \
-storepass 'android' \
-keypass 'android' \
-alias androiddebugkey \
-keystore android/app/debug.keystore \
-dname "CN=io.mosip.residentapp,OU=,O=,L=,S=,C=US"
```
```shell
# Generate and use Release keystore for Publishing to Play store
keytool \
-genkey -v \
-storetype PKCS12 \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-storepass '<USE-YOUR-RELEASE-PASSWORD-HERE>' \
-keypass '<USE-YOUR-RELEASE-PASSWORD-HERE>' \
-alias androidreleasekey \
-keystore android/app/release.keystore \
-dname "CN=io.mosip.residentapp,OU=,O=,L=,S=,C=US"
@@ -93,15 +109,19 @@ You need Android SDK CLI to build APK.
# 1. Install dependencies
npm install
# Setup the environment variable for keystore
export RELEASE_KEYSTORE=release.keystore
# 2. Setup the environment variables for the keystore
# Debug keystore
export DEBUG_KEYSTORE_ALIAS=androiddebugkey
export DEBUG_KEYSTORE_PASSWORD=android
# Release keystore
export RELEASE_KEYSTORE_ALIAS=androidreleasekey
export RELEASE_KEYSTORE_PASSWORD=android
export RELEASE_KEYSTORE_PASSWORD=<USE-YOUR-RELEASE-PASSWORD-HERE>
# https://hostname/residentmobileapp is the Mimoto service url
export BACKEND_SERVICE_URL=https://hostname/residentmobileapp
# Use DEBUG_KEYSTORE, DEBUG_KEYSTORE_ALIAS, DEBUG_KEYSTORE_PASSWORD for debug build
# Use one of following command to build the flavor you need.
# Build for Mosip Philippines test
npm run build:android:ph
@@ -143,6 +163,14 @@ More info here:
- [React Native - Publishing to the App Store](https://reactnative.dev/docs/publishing-to-app-store)
- [Apple Developer - Distributing Your App for Beta Testing and Releases](https://developer.apple.com/documentation/xcode/distributing-your-app-for-beta-testing-and-releases)
## View the complete DB :
1. Connect your phone to the laptop and open Android Studio.
2. On the bottom right vertical tab you will find a `Device File Explorer` button. Click on it and select you phone.
3. Navigate to `data -> data -> io.mosip.residentapp ->databases`. You will find a file named `RKStorage` in it. Download it.
4. Download [DB Browser for SQLite](https://sqlitebrowser.org/dl/) .
5. Open the file in this application. Click on `Browse Data` button and select `catalystLocalStorage` table. Now you should be able to view the entire DB of Inji.
## Credits
Credits listed [here](/Credits.md)

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>MOSIP Resident App</name>
<name>Inji</name>
<comment>Project android created by Buildship.</comment>
<projects>
</projects>

6
android/Gemfile Normal file
View File

@@ -0,0 +1,6 @@
source "https://rubygems.org"
gem "fastlane"
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)

220
android/Gemfile.lock Normal file
View File

@@ -0,0 +1,220 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.6)
rexml
addressable (2.8.4)
public_suffix (>= 2.0.2, < 6.0)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.765.0)
aws-sdk-core (3.172.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.64.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.122.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.2)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
declarative (0.0.20)
digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.8.1)
emoji_regex (3.2.3)
excon (0.99.0)
faraday (1.10.3)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.6)
fastlane (2.212.2)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0)
naturally (~> 2.2)
optparse (~> 0.1.1)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-increment_version_code (0.4.3)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.42.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (0.11.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
webrick
google-apis-iamcredentials_v1 (0.17.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-playcustomapp_v1 (0.13.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.19.0)
google-apis-core (>= 0.9.0, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.3.1)
google-cloud-storage (1.44.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.19.0)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.5.2)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.5)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.6.2)
json (2.6.3)
jwt (2.7.0)
memoist (0.16.2)
mini_magick (4.12.0)
mini_mime (1.1.2)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
naturally (2.2.1)
optparse (0.1.1)
os (1.1.4)
plist (3.7.0)
public_suffix (5.0.1)
rake (13.0.6)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.2.5)
rouge (2.0.7)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
signet (0.17.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.10)
CFPropertyList
naturally
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (1.8.0)
webrick (1.8.1)
word_wrap (1.0.0)
xcodeproj (1.22.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
rexml (~> 3.2.4)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
arm64-darwin-21
DEPENDENCIES
fastlane
fastlane-plugin-increment_version_code
BUNDLED WITH
2.4.10

View File

@@ -1,4 +1,4 @@
plugins {
plugins {
id 'com.gladed.androidgitversion' version '0.4.14'
}
@@ -132,7 +132,9 @@ def jscFlavor = 'org.webkit:android-jsc:+'
def enableHermes = project.ext.react.get("enableHermes", false);
androidGitVersion {
baseCode 1
baseCode 0
codeFormat 'MXXNXXPXX'
format '% tag %%commit_%%branch%'
}
android {
@@ -142,7 +144,7 @@ android {
APP_NAME_RELEASE = "@string/app_name"
APP_NAME_PH = "@string/app_name_ph"
APP_NAME_MOSIP = "@string/app_name_mosip"
APP_NAME_NEWLOGIC = "@string/app_name_newlogic"
APP_NAME_BETA = "@string/app_name_beta"
}
compileOptions {
@@ -154,11 +156,8 @@ android {
applicationId 'io.mosip.residentapp'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
// Update versionName and/or versionCode via git tag <XX.xx.xx>
// More info here:
// https://github.com/gladed/gradle-android-git-version#3-use-a-git-tag-to-specify-your-version-number-see-semantic-versioning
versionName androidGitVersion.name()
versionCode androidGitVersion.code()
versionCode 1
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
@@ -178,9 +177,9 @@ android {
}
signingConfigs {
release {
def keystore = System.getenv("RELEASE_KEYSTORE") ?: "debug.keystore"
def keystoreAlias = System.getenv("RELEASE_KEYSTORE_ALIAS") ?: "androiddebugkey"
def keystorePass = System.getenv("RELEASE_KEYSTORE_PASSWORD") ?: "android"
def keystore = file('release.keystore')
def keystoreAlias = System.getenv("RELEASE_KEYSTORE_ALIAS")
def keystorePass = System.getenv("RELEASE_KEYSTORE_PASSWORD")
storeFile file("$keystore")
storePassword "$keystorePass"
keyAlias "$keystoreAlias"
@@ -189,9 +188,9 @@ android {
v1SigningEnabled false
}
debug {
def keystore = System.getenv("DEBUG_KEYSTORE") ?: "debug.keystore"
def keystoreAlias = System.getenv("DEBUG_KEYSTORE_ALIAS") ?: "androiddebugkey"
def keystorePass = System.getenv("DEBUG_KEYSTORE_PASSWORD") ?: "android"
def keystore = file('debug.keystore')
def keystoreAlias = System.getenv("DEBUG_KEYSTORE_ALIAS")
def keystorePass = System.getenv("DEBUG_KEYSTORE_PASSWORD")
storeFile file("$keystore")
storePassword "$keystorePass"
keyAlias "$keystoreAlias"
@@ -205,6 +204,10 @@ android {
signingConfig signingConfigs.debug
}
release {
lintOptions {
checkReleaseBuilds false
abortOnError false
}
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.release
@@ -217,16 +220,16 @@ android {
productFlavors {
mosip {
versionName defaultConfig.versionName + "-mosip"
versionName defaultConfig.versionName
manifestPlaceholders = [
APP_NAME: APP_NAME_MOSIP
]
dimension "inji"
}
newlogic {
versionName defaultConfig.versionName + "-newlogic"
beta {
versionName defaultConfig.versionName
manifestPlaceholders = [
APP_NAME: APP_NAME_NEWLOGIC
APP_NAME: APP_NAME_BETA
]
dimension "inji"
}
@@ -252,6 +255,7 @@ dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+"// From node_modules
implementation 'com.facebook.soloader:soloader:0.10.1+'
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
@@ -320,4 +324,4 @@ try {
}
} catch(Exception e) {
logger.warn("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
}
}

Binary file not shown.

View File

@@ -2,6 +2,15 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<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.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<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 -->
<application tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" android:usesCleartextTraffic="true" />
</manifest>

View File

@@ -1,10 +1,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.mosip.residentapp">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<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" />
@@ -31,7 +36,8 @@
<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: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">
<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">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -45,4 +51,4 @@
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>
</manifest>

View File

@@ -29,9 +29,7 @@ public class MainActivity extends ReactActivity {
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.ACCESS_WIFI_STATE,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.CHANGE_WIFI_MULTICAST_STATE
};
private static final int REQUEST_CODE_REQUIRED_PERMISSIONS = 1;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 140 KiB

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources>
<color name="splashscreen_background">#ffffff</color>
<color name="splashscreen_background">#FFFFFF</color>
<color name="iconBackground">#FFFFFF</color>
<color name="colorPrimary">#023c69</color>
<color name="colorPrimaryDark">#ffffff</color>

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources>
<string name="app_name">MOSIP Resident App</string>
<string name="app_name">Inji</string>
<string name="app_name_mosip">MOSIP Resident App - Mosip/Inji</string>
<string name="app_name_newlogic">MOSIP Resident App - Newlogic</string>
<string name="app_name_ph">MOSIP Resident App - PH</string>
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
<string name="expo_splash_screen_resize_mode" translatable="false">cover</string>
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
</resources>

View File

@@ -4,8 +4,8 @@ buildscript {
ext {
buildToolsVersion = "29.0.3"
minSdkVersion = 23
compileSdkVersion = 30
targetSdkVersion = 30
compileSdkVersion = 31
targetSdkVersion = 31
}
repositories {
google()
@@ -34,6 +34,11 @@ allprojects {
// 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()

2
android/fastlane/Appfile Normal file
View File

@@ -0,0 +1,2 @@
json_key_file("play_config.json")
package_name("io.mosip.residentapp")

86
android/fastlane/Fastfile Normal file
View File

@@ -0,0 +1,86 @@
default_platform(:android)
MIMOTO_HOST = ENV["MIMOTO_HOST"]
APPLICATION_THEME = ENV["APPLICATION_THEME"]
RELEASE_KEYSTORE_ALIAS = ENV["RELEASE_KEYSTORE_ALIAS"]
RELEASE_KEYSTORE_PASSWORD = ENV["RELEASE_KEYSTORE_PASSWORD"]
PLAY_CONSOLE_RELEASE_DESCRIPTION = ENV["PLAY_CONSOLE_RELEASE_DESCRIPTION"]
SLACK_URL = ENV["SLACK_URL"]
CREDENTIAL_REGISTRY_EDIT = ENV["CREDENTIAL_REGISTRY_EDIT"]
desc "Verify Build for Android"
lane :android_build_verify do
gradle(
task: "assembleMosipDebug",
)
end
desc "Deploy an Internal testing version to the Google Play"
lane :android_build_internal do
previous_build_number = google_play_track_version_codes(
package_name: "io.mosip.residentapp",
track: "internal",
json_key: "play_config.json",
)[0]
current_build_number = previous_build_number + 1
increment_version_code(
gradle_file_path: "app/build.gradle",
version_code: current_build_number
)
git_commit = sh('git rev-parse --short HEAD').strip
git_branch = sh('git rev-parse --abbrev-ref HEAD').strip
versionName = "#{git_commit}-#{git_branch}"
gradle(task: "clean bundleMosipRelease")
upload_to_play_store(
track: 'internal',
release_status: 'completed',
version_name: versionName,
)
slack(
message: "Inji - #{versionName} (#{current_build_number}) is uploaded to Play store. Description : #{PLAY_CONSOLE_RELEASE_DESCRIPTION}.",
success: true,
slack_url: "#{SLACK_URL}",
default_payloads: [:git_branch, :last_git_commit]
)
end
desc "Deploy an Beta version to the Google Play"
lane :android_build_beta do
git_tag = sh('git describe --abbrev=0 --tags --exact-match HEAD').strip
def convert_tag_to_code(version)
parts = version.split('.')
version_code = parts[0].to_i * 1000000 + parts[1].to_i * 1000 + parts[2].to_i
return version_code
end
versionCode = convert_tag_to_code(git_tag)
increment_version_code(
gradle_file_path: "app/build.gradle",
version_code: versionCode
)
versionName = "Inji #{git_tag}"
gradle(task: "clean bundleBetaRelease")
upload_to_play_store(
track: 'alpha',
release_status: 'completed',
version_name: versionName,
)
slack(
message: "Inji [Beta]- #{versionName} is uploaded to Play store. Description : #{PLAY_CONSOLE_RELEASE_DESCRIPTION}.",
success: true,
slack_url: "#{SLACK_URL}",
default_payloads: [:git_branch, :last_git_commit]
)
end

View File

@@ -0,0 +1,5 @@
# Autogenerated by fastlane
#
# Ensure this file is checked in to source control!
gem 'fastlane-plugin-increment_version_code'

View File

@@ -0,0 +1,45 @@
## fastlane documentation
# Installation
Make sure you have the latest version of the Xcode command line tools installed:
```sh
xcode-select --install
```
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
# Available Actions
### android_build_verify
```sh
[bundle exec] fastlane android_build_verify
```
Verify Build for Android
### android_build_internal
```sh
[bundle exec] fastlane android_build_internal
```
Deploy an Internal testing version to the Google Play
### android_build_beta
```sh
[bundle exec] fastlane android_build_beta
```
Deploy an Beta version to the Google Play
---
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).

View File

@@ -38,4 +38,7 @@ expo.gif.enabled=true
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
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

10
android/scripts/beta-build.sh Executable file
View File

@@ -0,0 +1,10 @@
#As react-native/location npm package is old, We need to run this to map androidX source code
(cd ../../ && npx jetify)
cd ..
yes | sudo gem install bundler
yes | sudo fastlane install_plugins
bundle exec fastlane android_build_beta

10
android/scripts/git-tag.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
TAG=$(git describe --abbrev=0 --tags --exact-match HEAD 2>/dev/null)
if [[ -z "$TAG" ]]; then
echo "No tag found for the current commit. Please provide a Tag to proceed with the Build."
exit 1
else
echo "Tag for the current commit: $TAG"
fi

View File

@@ -0,0 +1,10 @@
#As react-native/location npm package is old, We need to run this to map androidX source code
(cd ../../ && npx jetify)
cd ..
yes | sudo gem install bundler
yes | sudo fastlane install_plugins
bundle exec fastlane android_build_internal

10
android/scripts/verify-build.sh Executable file
View File

@@ -0,0 +1,10 @@
#As react-native/location npm package is old, We need to run this to map androidX source code
(cd ../../ && npx jetify)
cd ..
yes | sudo gem install bundler
yes | sudo fastlane install_plugins
bundle exec fastlane android_build_verify

View File

@@ -1,4 +1,4 @@
rootProject.name = 'MOSIP Resident App'
rootProject.name = 'Inji'
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
useExpoModules()

View File

@@ -1,12 +1,12 @@
export default {
name: 'MOSIP Resident App',
slug: 'mosip-resident-app',
name: 'Inji',
slug: 'inji',
version: '1.0.0',
orientation: 'portrait',
icon: './assets/icon.png',
splash: {
image: './assets/splash.png',
resizeMode: 'contain',
resizeMode: 'cover',
backgroundColor: '#ffffff',
},
updates: {

BIN
assets/Secure-Sharing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
assets/Secure-Sharing2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
assets/camera-flip-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

BIN
assets/help-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 27 KiB

BIN
assets/inji-home-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
assets/inji-logo-white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
assets/inji_small_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
assets/intro-scanner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

BIN
assets/lock-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
assets/magnifier-zoom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 607 B

BIN
assets/phone_mockup_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/progressing-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -10,5 +10,8 @@
"VC_RECEIVED_BUT_PRESENCE_VERIFICATION_FAILED": "received. Presence verification failed",
"PRESENCE_VERIFIED_AND_VC_SHARED": "verified and shared",
"PRESENCE_VERIFICATION_FAILED": "verification failed",
"QRLOGIN_SUCCESFULL": "QRLogin sucessfull"
"QRLOGIN_SUCCESFULL": "QRLogin successful",
"WALLET_BINDING_SUCCESSFULL": "Activation successful",
"WALLET_BINDING_FAILURE": "Activation failed",
"VC_REMOVED":"Removed from wallet"
}

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { formatDistanceToNow } from 'date-fns';
import { useTranslation } from 'react-i18next';
import * as DateFnsLocale from '../lib/date-fns/locale';
import * as DateFnsLocale from 'date-fns/locale';
import { ActivityLog } from '../machines/activityLog';
import { TextItem } from './ui/TextItem';
@@ -14,6 +14,7 @@ export const ActivityLogText: React.FC<{ activity: ActivityLog }> = (props) => {
<TextItem
label={getActionLabel(activity, i18n.language)}
text={`${activity.vcLabel} ${t(activity.type)}`}
topDivider
/>
);
};
@@ -26,6 +27,6 @@ function getActionLabel(activity: ActivityLog, language: string) {
locale: DateFnsLocale[language],
}),
]
.filter((label) => label.trim() !== '')
.filter((label) => label?.trim() !== '')
.join(' · ');
}

35
components/CopyButton.tsx Normal file
View File

@@ -0,0 +1,35 @@
import React, { useState } from 'react';
import { Pressable } from 'react-native';
import { Theme } from './ui/styleUtils';
import Clipboard from '@react-native-clipboard/clipboard';
import { Icon } from 'react-native-elements';
import { Text } from './ui';
import { useTranslation } from 'react-i18next';
export const CopyButton: React.FC<CopyButtonProps> = ({ content }) => {
const { t } = useTranslation('common');
const [buttonText, setButtonText] = useState(t('clipboard.copy'));
return (
<Pressable
style={Theme.Styles.iconContainer}
onPress={() => {
setButtonText(t('clipboard.copied'));
setTimeout(() => setButtonText(t('clipboard.copy')), 3000);
Clipboard.setString(content);
}}>
<Icon
type={'material'}
name={'file-copy'}
color={Theme.Colors.Icon}
style={{ marginRight: 2 }}
size={19}
/>
<Text style={Theme.TextStyles.semibold}>{buttonText}</Text>
</Pressable>
);
};
interface CopyButtonProps {
content: string;
}

View File

@@ -10,15 +10,15 @@ export const DeviceInfoList: React.FC<DeviceInfoProps> = (props) => {
<TextItem
divider
label={props.of === 'receiver' ? t('requestedBy') : t('sentBy')}
text={props.deviceInfo.deviceName}
text={t(props.deviceInfo.deviceName)}
/>
</React.Fragment>
);
};
interface DeviceInfoProps {
of: 'sender' | 'receiver';
deviceInfo: DeviceInfo;
of?: string;
}
export interface DeviceInfo {

View File

@@ -0,0 +1,106 @@
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';
/**
* DualMessageOverlay is like MessageOverlay but with two buttons
*
* 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');
return (
<Overlay
isVisible={props.isVisible}
overlayStyle={Theme.MessageOverlayStyles.overlay}
onShow={props.onShow}
onBackdropPress={props.onBackdropPress}>
<Column
width={Dimensions.get('screen').width * 0.8}
style={
!props.progress
? Theme.MessageOverlayStyles.popupOverLay
: { height: 230 }
}>
<Column padding="21" crossAlign="center">
{props.title && (
<Text
align="center"
weight="bold"
margin="0 0 10 0"
color={Theme.Colors.Details}>
{props.title}
</Text>
)}
{props.message && (
<Text
align="center"
weight="semibold"
size="small"
margin="10 0 0 0"
color={Theme.Colors.Details}>
{props.message}
</Text>
)}
{props.hint && (
<Text
size="smaller"
color={Theme.Colors.textLabel}
margin={[2, 0, 0, 0]}>
{props.hint}
</Text>
)}
{props.children}
</Column>
<Column style={{ marginBottom: 10 }}>
<Row style={Theme.MessageOverlayStyles.buttonContainer}>
{!props.children && props.onTryAgain ? (
<Button
title={t('tryAgain')}
type="gradient"
onPress={props.onTryAgain}
styles={{
...Theme.MessageOverlayStyles.halfButton,
...Theme.ButtonStyles.gradient,
width: Dimensions.get('screen').width * 0.36,
}}
/>
) : null}
{!props.children && props.onIgnore ? (
<Button
type="gradient"
title={t('ignore')}
onPress={props.onIgnore}
styles={{
...Theme.MessageOverlayStyles.halfButton,
...Theme.ButtonStyles.gradient,
width: Dimensions.get('screen').width * 0.36,
}}
/>
) : null}
</Row>
</Column>
</Column>
</Overlay>
);
};
export interface DualMessageOverlayProps {
isVisible: boolean;
title?: string;
message?: string;
progress?: boolean | number;
requester?: boolean;
hint?: string;
onIgnore?: () => void;
onBackdropPress?: () => void;
onShow?: () => void;
onTryAgain?: () => void;
}

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react';
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';
@@ -9,22 +9,36 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
const { t } = useTranslation('common');
const [isEditing, setIsEditing] = useState(false);
const [newValue, setNewValue] = useState(props.value);
const [overlayOpened, setOverlayOpened] = useState(true);
useEffect(() => {
if (props.credentialRegistryResponse === 'success') {
closePopup();
}
}, [props.credentialRegistryResponse]);
return (
<ListItem bottomDivider onPress={() => setIsEditing(true)}>
<ListItem bottomDivider topDivider onPress={() => setIsEditing(true)}>
<Icon
name={props.Icon}
type="antdesign"
size={20}
style={Theme.Styles.profileIconBg}
containerStyle={Theme.Styles.settingsIconBg}
type={props.IconType}
size={25}
color={Theme.Colors.Icon}
/>
<ListItem.Content>
<ListItem.Title>
<Text color={Theme.Colors.profileLabel}>{props.label}</Text>
<Text weight="semibold" color={Theme.Colors.profileLabel}>
{props.label}
</Text>
</ListItem.Title>
<Text color={Theme.Colors.profileValue}>{props.value}</Text>
</ListItem.Content>
<Text color={Theme.Colors.profileValue}>{props.value}</Text>
<Icon
name="chevron-right"
size={21}
color={Theme.Colors.profileLanguageValue}
/>
<Overlay
overlayStyle={{ padding: 24, elevation: 6 }}
isVisible={isEditing}
@@ -35,13 +49,25 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
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()}
<Row>
<Button fill type="clear" title={t('cancel')} onPress={dismiss} />
<Button fill title={t('save')} onPress={edit} />
<Button
fill
title={t('save')}
onPress={edit}
loading={props.progress}
/>
</Row>
</Column>
</Overlay>
@@ -50,12 +76,20 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
function edit() {
props.onEdit(newValue);
setIsEditing(false);
if (props.credentialRegistryResponse === undefined) {
setIsEditing(false);
}
}
function dismiss() {
setNewValue(props.value);
setIsEditing(false);
props.onCancel();
}
function closePopup() {
setIsEditing(false);
setOverlayOpened(false);
}
};
@@ -63,5 +97,11 @@ interface EditableListItemProps {
label: string;
value: string;
Icon: string;
IconType?: string;
onEdit: (newValue: string) => void;
display?: 'none' | 'flex';
credentialRegistryResponse: string;
onCancel: () => void;
progress?: boolean;
errorMessage?: string;
}

79
components/HelpScreen.tsx Normal file
View File

@@ -0,0 +1,79 @@
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');
const [showHelpPage, setShowHelpPage] = useState(false);
return (
<React.Fragment>
<Pressable
onPress={() => {
setShowHelpPage(!showHelpPage);
}}>
{props.triggerComponent}
</Pressable>
<Modal
isVisible={showHelpPage}
headerTitle={t('header')}
headerElevation={2}
onDismiss={() => {
setShowHelpPage(!showHelpPage);
}}>
<ScrollView>
<Column fill padding="10" align="space-between">
<Text margin="7" style={Theme.TextStyles.header}>
{t('whatIsDigitalCredential?')}
</Text>
<Text style={Theme.TextStyles.helpDetailes}>{t('detail-1')}</Text>
<Text margin="7" style={Theme.TextStyles.header}>
{t('whatCanDoWithDigitalCredential?')}
</Text>
<Text style={Theme.TextStyles.helpDetailes}>{t('detail-2')}</Text>
<Text margin="7" style={Theme.TextStyles.header}>
{t('howToAddCard?')}
</Text>
<Text style={Theme.TextStyles.helpDetailes}>{t('detail-3')}</Text>
<Text margin="7" style={Theme.TextStyles.header}>
{t('howToRemoveCardFromWallet?')}
</Text>
<Text style={Theme.TextStyles.helpDetailes}>{t('detail-4')}</Text>
<Text margin="7" style={Theme.TextStyles.header}>
{t('canWeAddMultipleCards?')}
</Text>
<Text style={Theme.TextStyles.helpDetailes}>{t('detail-5')}</Text>
<Text margin="7" style={Theme.TextStyles.header}>
{t('howToShareCard?')}
</Text>
<Text style={Theme.TextStyles.helpDetailes}>{t('detail-6')}</Text>
<Text margin="7" style={Theme.TextStyles.header}>
{t('howToActivateCardForOnlineLogin?')}
</Text>
<Text style={Theme.TextStyles.helpDetailes}>{t('detail-7')}</Text>
<Text margin="7" style={Theme.TextStyles.header}>
{t('howToViewActivity?')}
</Text>
<Text style={Theme.TextStyles.helpDetailes}>{t('detail-8')}</Text>
<Text margin="7" style={Theme.TextStyles.header}>
{t('whatCanDoBiometricsChanged?')}
</Text>
<Text style={Theme.TextStyles.helpDetailes}>{t('detail-9')}</Text>
</Column>
</ScrollView>
</Modal>
</React.Fragment>
);
};
interface HelpScreenProps {
triggerComponent: React.ReactElement;
}

94
components/KebabPopUp.tsx Normal file
View File

@@ -0,0 +1,94 @@
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';
export const KebabPopUp: React.FC<KebabPopUpProps> = (props) => {
const controller = useKebabPopUp(props);
const { t } = useTranslation('HomeScreenKebabPopUp');
return (
<Column>
<Icon
name={props.iconName}
type={props.iconType}
color={Theme.Colors.GrayIcon}
/>
<Overlay
isVisible={props.isVisible}
onBackdropPress={props.onDismiss}
overlayStyle={Theme.KebabPopUpStyles.kebabPopUp}>
<Row style={Theme.KebabPopUpStyles.kebabHeaderStyle}>
<Text weight="bold">{t('title')}</Text>
<Icon
name="close"
onPress={props.onDismiss}
color={Theme.Colors.Details}
size={25}
/>
</Row>
<Column>
<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>
</ListItem.Content>
</ListItem>
<WalletBinding
label={t('offlineAuthenticationDisabled!')}
content={t('offlineAuthDisabledMessage')}
service={props.service}
/>
<HistoryTab
service={props.service}
label={t('viewActivityLog')}
vcKey={props.vcKey}
/>
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Title>
<Pressable onPress={() => controller.REMOVE(props.vcKey)}>
<Text size="small" weight="bold">
{t('removeFromWallet')}
</Text>
</Pressable>
</ListItem.Title>
</ListItem.Content>
</ListItem>
<RemoveVcWarningOverlay
isVisible={controller.isRemoveWalletWarning}
onConfirm={controller.CONFIRM}
onCancel={controller.CANCEL}
/>
</Column>
</Overlay>
</Column>
);
};
export interface KebabPopUpProps {
iconName: string;
iconType?: string;
vcKey: string;
isVisible: boolean;
onDismiss: () => void;
service: ActorRefFrom<typeof vcItemMachine>;
}

View File

@@ -0,0 +1,80 @@
import { useSelector } from '@xstate/react';
import { ActorRefFrom } from 'xstate';
import {
selectKebabPopUpWalletBindingInProgress,
selectKebabPopUp,
selectKebabPopUpAcceptingBindingOtp,
selectKebabPopUpBindingWarning,
selectRemoveWalletWarning,
selectEmptyWalletBindingId,
selectIsPinned,
selectOtpError,
selectShowWalletBindingError,
selectWalletBindingError,
VcItemEvents,
vcItemMachine,
selectShowActivities,
} from '../machines/vcItem';
import { selectActivities } from '../machines/activityLog';
import { GlobalContext } from '../shared/GlobalContext';
import { useContext } from 'react';
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 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));
const isPinned = useSelector(service, selectIsPinned);
const isBindingWarning = useSelector(service, selectKebabPopUpBindingWarning);
const isRemoveWalletWarning = useSelector(service, selectRemoveWalletWarning);
const isAcceptingOtpInput = useSelector(
service,
selectKebabPopUpAcceptingBindingOtp
);
const isWalletBindingError = useSelector(
service,
selectShowWalletBindingError
);
const otpError = useSelector(service, selectOtpError);
const walletBindingError = useSelector(service, selectWalletBindingError);
const WalletBindingInProgress = useSelector(
service,
selectKebabPopUpWalletBindingInProgress
);
const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
const isKebabPopUp = useSelector(service, selectKebabPopUp);
const isShowActivities = useSelector(service, selectShowActivities);
const { appService } = useContext(GlobalContext);
const activityLogService = appService.children.get('activityLog');
return {
isPinned,
PIN_CARD,
KEBAB_POPUP,
ADD_WALLET_BINDING_ID,
CONFIRM,
DISMISS,
REMOVE,
CANCEL,
INPUT_OTP,
SHOW_ACTIVITY,
isBindingWarning,
isAcceptingOtpInput,
isWalletBindingError,
walletBindingError,
otpError,
WalletBindingInProgress,
emptyWalletBindingId,
isKebabPopUp,
isShowActivities,
isRemoveWalletWarning,
activities: useSelector(activityLogService, selectActivities),
};
}

View File

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

View File

@@ -7,7 +7,7 @@ export const Logo: React.FC<LogoProps> = (props) => {
<View>
<Image
style={{ resizeMode: 'contain', ...props }}
source={Theme.MosipLogo}
source={Theme.MosipSplashLogo}
/>
</View>
);

View File

@@ -14,14 +14,33 @@ export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
overlayStyle={Theme.MessageOverlayStyles.overlay}
onShow={props.onShow}
onBackdropPress={props.onBackdropPress}>
<Column width={Dimensions.get('screen').width * 0.8}>
<Column padding="24">
<Column
width={Dimensions.get('screen').width * 0.8}
style={
!props.progress
? Theme.MessageOverlayStyles.popupOverLay
: { height: 100 }
}>
<Column padding="21" crossAlign="center">
{props.title && (
<Text weight="semibold" margin="0 0 12 0">
<Text
align="center"
weight="bold"
margin="0 0 10 0"
color={Theme.Colors.Details}>
{props.title}
</Text>
)}
{props.message && <Text margin="0 0 12 0">{props.message}</Text>}
{props.message && (
<Text
align="center"
weight="semibold"
size="small"
margin="10 0 12 0"
color={Theme.Colors.Details}>
{props.message}
</Text>
)}
{props.progress && <Progress progress={props.progress} />}
{props.hint && (
<Text
@@ -35,6 +54,7 @@ export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
</Column>
{!props.children && props.onCancel ? (
<Button
type="gradient"
title={t('cancel')}
onPress={props.onCancel}
styles={Theme.MessageOverlayStyles.button}
@@ -45,6 +65,31 @@ export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
);
};
export const ErrorMessageOverlay: React.FC<ErrorMessageOverlayProps> = ({
isVisible,
error,
onDismiss,
translationPath,
}) => {
const { t } = useTranslation(translationPath);
return (
<MessageOverlay
isVisible={isVisible}
title={t(error + '.title')}
message={t(error + '.message')}
onBackdropPress={onDismiss}
/>
);
};
export interface ErrorMessageOverlayProps {
isVisible: boolean;
error?: string;
onDismiss?: () => void;
translationPath: string;
}
const Progress: React.FC<Pick<MessageOverlayProps, 'progress'>> = (props) => {
return typeof props.progress === 'boolean' ? (
props.progress && (
@@ -60,6 +105,7 @@ export interface MessageOverlayProps {
title?: string;
message?: string;
progress?: boolean | number;
requester?: boolean;
hint?: string;
onCancel?: () => void;
onBackdropPress?: () => void;

View File

@@ -23,6 +23,7 @@ export const Passcode: React.FC<PasscodeProps> = (props) => {
onSuccess={props.onSuccess}
onError={props.onError}
passcode={props.storedPasscode}
salt={props.salt}
/>
</Column>
<Column fill>
@@ -39,6 +40,7 @@ interface PasscodeProps {
message?: string;
error: string;
storedPasscode: string;
salt: string;
onSuccess: () => void;
onError: (value: string) => void;
onDismiss: () => void;

View File

@@ -1,6 +1,8 @@
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;
@@ -16,8 +18,9 @@ export const PasscodeVerify: React.FC<PasscodeVerifyProps> = (props) => {
return <PinInput length={MAX_PIN} onDone={verify} />;
function verify(value: string) {
if (props.passcode === value) {
async function verify(value: string) {
const hashedPasscode = await hashData(value, props.salt, argon2iConfig);
if (props.passcode === hashedPasscode) {
setIsVerified(true);
} else {
props.onError(t('passcodeMismatchError'));
@@ -29,4 +32,5 @@ interface PasscodeVerifyProps {
passcode: string;
onSuccess: () => void;
onError?: (error: string) => void;
salt: string;
}

View File

@@ -22,6 +22,7 @@ export const PinInput: React.FC<PinInputProps> = (props) => {
selectTextOnFocus
keyboardType="numeric"
maxLength={1}
secureTextEntry
selectionColor={Theme.Colors.inputSelection}
style={Theme.PinInputStyle.input}
key={index}

View File

@@ -0,0 +1,75 @@
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';
export const ProgressingModal: React.FC<ProgressingModalProps> = (props) => {
const { t } = useTranslation('ScanScreen');
let n = 0;
const [curPage, setCurPage] = useState(n);
const highLightDot = () => setCurPage(n + 1);
return (
<React.Fragment>
<Modal
isVisible={props.isVisible}
headerLeft={t(props.title)}
onDismiss={props.onCancel}
headerLabel={props.label}
headerElevation={3}
requester={props.requester}>
<Centered crossAlign="center" fill>
<Column margin="24 0" align="space-around">
<Image
source={Theme.InjiProgressingLogo}
height={2}
width={2}
style={{ marginBottom: 15, marginLeft: -6 }}
/>
{props.progress && (
<PaginationDot
activeDotColor={'black'}
curPage={curPage}
maxPage={3}
/>
)}
</Column>
<Column style={{ display: props.hint ? 'flex' : 'none' }}>
<Column style={Theme.SelectVcOverlayStyles.timeoutHintContainer}>
<Text
align="center"
color={Theme.Colors.TimoutText}
style={Theme.TextStyles.bold}>
{props.hint}
</Text>
{props.onCancel && (
<Button
type="clear"
title={t('common:cancel')}
onPress={props.onCancel}
/>
)}
</Column>
</Column>
</Centered>
</Modal>
</React.Fragment>
);
};
export interface ProgressingModalProps {
isVisible: boolean;
title?: string;
label?: string;
hint?: string;
onCancel?: () => void;
requester?: boolean;
progress?: boolean | number;
onBackdropPress?: () => void;
}

View File

@@ -0,0 +1,64 @@
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 QRCode from 'react-native-qrcode-svg';
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');
const [isQrOverlayVisible, setIsQrOverlayVisible] = useState(false);
const toggleQrOverlay = () => setIsQrOverlayVisible(!isQrOverlayVisible);
return (
<React.Fragment>
<Pressable onPress={toggleQrOverlay}>
<Row margin="20 0 0 0">
<QRCode
size={90}
value={props.qrCodeDetailes}
backgroundColor={Theme.Colors.QRCodeBackgroundColor}
/>
</Row>
<Row
align="flex-end"
margin="-30 0 0 60"
style={Theme.QrCodeStyles.magnifierZoom}>
<Image source={Theme.MagnifierZoom} />
</Row>
</Pressable>
<Overlay
isVisible={isQrOverlayVisible}
onBackdropPress={toggleQrOverlay}
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">
{t('qrCodeHeader')}
</Text>
<Icon
name="close"
onPress={toggleQrOverlay}
color={Theme.Colors.Details}
size={32}
/>
</Row>
<Centered pY={30}>
<QRCode
size={300}
value={props.qrCodeDetailes}
backgroundColor={Theme.Colors.QRCodeBackgroundColor}
/>
</Centered>
</Column>
</Overlay>
</React.Fragment>
);
};
interface QrCodeOverlayProps {
qrCodeDetailes: string;
}

View File

@@ -2,20 +2,28 @@ 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 } from 'react-native';
import {
Linking,
TouchableOpacity,
View,
Image,
Pressable,
} from 'react-native';
import { Theme } from './ui/styleUtils';
import { Column, Button, Text } from './ui';
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);
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
const [type, setType] = useState(Camera.Constants.Type.back);
const [cameraType, setCameraType] = useState(Camera.Constants.Type.back);
const controller = useScanLayout();
const isActive = useSelector(appService, selectIsActive);
@@ -43,24 +51,36 @@ export const QrScanner: React.FC<QrScannerProps> = (props) => {
return <View />;
}
if (hasPermission === false) {
const CameraDisabledPopUp: React.FC = () => {
return (
<Column fill align="space-between">
<Text align="center" color={Theme.Colors.errorMessage}>
{t('missingPermissionText')}
</Text>
<Button title={t('allowCameraButton')} onPress={openSettings} />
</Column>
<View>
<Row style={Theme.Styles.cameraDisabledPopUp}>
<Column>
<Text color={Theme.Colors.whiteText} weight="bold">
{t('cameraAccessDisabled')}
</Text>
<Text
color={Theme.Colors.whiteText}
weight="semibold"
size="smaller">
{t('cameraPermissionGuideLabel')}
</Text>
</Column>
<Pressable>
<Icon
name="close"
onPress={controller.DISMISS}
color={Theme.Colors.whiteText}
size={19}
/>
</Pressable>
</Row>
</View>
);
}
};
return (
<View>
{props.title && (
<Text align="center" margin="16 0" color={Theme.Colors.Details}>
{props.title}
</Text>
)}
{hasPermission == false && <CameraDisabledPopUp />}
<View style={Theme.Styles.scannerContainer}>
<Camera
style={Theme.Styles.scanner}
@@ -68,25 +88,32 @@ export const QrScanner: React.FC<QrScannerProps> = (props) => {
barcodeTypes: [BarCodeScanner.Constants.BarCodeType.qr],
}}
onBarCodeScanned={scanned ? undefined : onBarcodeScanned}
type={type}
type={cameraType}
/>
</View>
<Column margin="24 0">
{props.title && (
<Text
align="center"
weight="semibold"
style={Theme.TextStyles.base}
margin="20 57 0 57">
{props.title}
</Text>
)}
<Column margin="18 0" crossAlign="center">
<TouchableOpacity
style={Theme.Styles.flipIconButton}
onPress={() => {
setType(
type === Camera.Constants.Type.back
setCameraType(
cameraType === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
}}>
<Icon
name="flip-camera-ios"
color={Theme.Colors.flipCameraIcon}
size={64}
/>
<Image source={Theme.CameraFlipIcon} />
</TouchableOpacity>
<Text align="center" weight="semibold" margin="9 0">
{t('flipCamera')}
</Text>
</Column>
</View>
);

View File

@@ -1,208 +0,0 @@
import React, { useContext, useRef } from 'react';
import { useInterpret, useSelector } from '@xstate/react';
import { Image, ImageBackground, Pressable } from 'react-native';
import { CheckBox, Icon } from 'react-native-elements';
import { ActorRefFrom } from 'xstate';
import {
createVcItemMachine,
selectVerifiableCredential,
selectGeneratedOn,
selectId,
vcItemMachine,
selectContext,
} from '../machines/vcItem';
import { Column, Row, Text } from './ui';
import { Theme } from './ui/styleUtils';
import { GlobalContext } from '../shared/GlobalContext';
import { RotatingIcon } from './RotatingIcon';
import { useTranslation } from 'react-i18next';
const VerifiedIcon: React.FC = () => {
return (
<Icon
name="check-circle"
color={Theme.Colors.VerifiedIcon}
size={14}
containerStyle={{ marginStart: 4, bottom: 1 }}
/>
);
};
const getDetails = (arg1, arg2, verifiableCredential) => {
if (arg1 === 'Full Name') {
return (
<Column>
<Text color={Theme.Colors.DetailsLabel} size="smaller">
{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>
);
}
if (arg1 === 'Status') {
return (
<Column>
<Text size="smaller" color={Theme.Colors.DetailsLabel}>
{arg1}
</Text>
<Row>
<Text
weight="bold"
color={Theme.Colors.Details}
size="smaller"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}>
{!verifiableCredential ? '' : arg2}
</Text>
{!verifiableCredential ? null : <VerifiedIcon />}
</Row>
</Column>
);
}
return (
<Column>
<Text color={Theme.Colors.DetailsLabel} size="smaller">
{arg1}
</Text>
<Text
numLines={1}
color={Theme.Colors.Details}
weight="bold"
size="smaller"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}>
{!verifiableCredential ? '' : arg2}
</Text>
</Column>
);
};
export const SingleVcItem: React.FC<VcItemProps> = (props) => {
const { appService } = useContext(GlobalContext);
const { t } = useTranslation('VcDetails');
const machine = useRef(
createVcItemMachine(
appService.getSnapshot().context.serviceRefs,
props.vcKey
)
);
const service = useInterpret(machine.current);
const context = useSelector(service, selectContext);
const verifiableCredential = useSelector(service, selectVerifiableCredential);
const uin = useSelector(service, selectId);
const generatedOn = useSelector(service, selectGeneratedOn);
const fullName = !verifiableCredential
? ''
: getLocalizedField(verifiableCredential.credentialSubject.fullName);
const selectableOrCheck = props.selectable ? (
<CheckBox
checked={props.selected}
checkedIcon={<Icon name="radio-button-checked" />}
uncheckedIcon={<Icon name="radio-button-unchecked" />}
onPress={() => props.onPress(service)}
/>
) : null;
return (
<Pressable
style={Theme.Styles.closeCardBgContainer}
onPress={() => props.onPress(service)}>
<ImageBackground
source={!verifiableCredential ? null : Theme.CloseCard}
resizeMode="stretch"
borderRadius={4}
style={
!verifiableCredential
? Theme.Styles.vertloadingContainer
: Theme.Styles.backgroundImageContainer
}>
<Row style={Theme.Styles.homeCloseCardDetailsHeader}>
<Image
source={Theme.MosipLogo}
style={Theme.Styles.logo}
resizeMethod="auto"
/>
</Row>
<Row
crossAlign="center"
margin="5 0 0 0"
style={!verifiableCredential ? Theme.Styles.loadingContainer : null}>
<Column
style={
!verifiableCredential
? Theme.Styles.loadingContainer
: Theme.Styles.closeDetails
}>
<Image
source={
!verifiableCredential
? Theme.ProfileIcon
: { uri: context.credential.biometrics.face }
}
style={Theme.Styles.closeCardImage}
/>
<Column margin="0 0 0 10" style={{ alignItems: 'flex-start' }}>
{getDetails(t('fullName'), fullName, verifiableCredential)}
{getDetails(t('uin'), uin, verifiableCredential)}
{getDetails(t('generatedOn'), generatedOn, verifiableCredential)}
{getDetails(t('status'), t('valid'), verifiableCredential)}
</Column>
</Column>
{verifiableCredential ? (
selectableOrCheck
) : (
<RotatingIcon name="sync" color={Theme.Colors.rotatingIcon} />
)}
</Row>
</ImageBackground>
</Pressable>
);
};
interface VcItemProps {
vcKey: string;
margin?: string;
selectable?: boolean;
selected?: boolean;
onPress?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
onShow?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
}
interface LocalizedField {
language: string;
value: string;
}
function getLocalizedField(rawField: string | LocalizedField) {
if (typeof rawField === 'string') {
return rawField;
}
try {
const locales: LocalizedField[] = JSON.parse(JSON.stringify(rawField));
return locales[0].value;
} catch (e) {
return '';
}
}

View File

@@ -18,7 +18,13 @@ export const TextEditOverlay: React.FC<EditOverlayProps> = (props) => {
<Text weight="semibold" margin="0 0 16 0">
{props.label}
</Text>
<Input autoFocus value={value} onChangeText={setValue} />
<Input
autoFocus
value={value}
maxLength={props.maxLength}
selectionColor={Theme.Colors.Cursor}
onChangeText={setValue}
/>
<Row>
<Button
fill
@@ -50,4 +56,5 @@ interface EditOverlayProps {
value: string;
onSave: (value: string) => void;
onDismiss: () => void;
maxLength?: number | undefined;
}

View File

@@ -1,25 +1,18 @@
import { formatDistanceToNow } from 'date-fns';
import React from 'react';
import * as DateFnsLocale from '../lib/date-fns/locale';
import * as DateFnsLocale from 'date-fns/locale';
import { useTranslation } from 'react-i18next';
import { Image, ImageBackground } from 'react-native';
import { Image, ImageBackground, View } from 'react-native';
import { Icon } from 'react-native-elements';
import { VC, CredentialSubject, LocalizedField } from '../types/vc';
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';
const VerifiedIcon: React.FC = () => {
return (
<Icon
name="check-circle"
color={Theme.Colors.VerifiedIcon}
size={14}
containerStyle={{ marginStart: 4, bottom: 1 }}
/>
);
};
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');
@@ -33,38 +26,29 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
}
return (
<Column>
<Column margin="10">
<ImageBackground
borderRadius={10}
style={Theme.Styles.openCardBgContainer}
source={Theme.OpenCard}>
<Row style={Theme.Styles.openDetailsHeader}>
<Column margin={'0 0 0 10'}>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('idType')}
</Text>
<Text weight="bold" size="smaller" color={Theme.Colors.Details}>
{t('nationalCard')}
</Text>
<Row align="space-between">
<Column align="space-evenly" crossAlign="center">
<Image
source={
props.vc?.credential.biometrics?.face
? { uri: props.vc?.credential.biometrics.face }
: Theme.ProfileIcon
}
style={Theme.Styles.openCardImage}
/>
<QrCodeOverlay qrCodeDetailes={String(props.vc.credential)} />
<Column margin="20 0 0 0">
<Image source={Theme.MosipLogo} style={Theme.Styles.logo} />
</Column>
</Column>
<Image source={Theme.MosipLogo} style={Theme.Styles.logo} />
</Row>
<Row style={Theme.Styles.openDetailsContainer}>
<Image
source={
props.vc?.credential.biometrics?.face
? { uri: props.vc?.credential.biometrics.face }
: Theme.ProfileIcon
}
style={Theme.Styles.openCardImage}
/>
<Column style={Theme.Styles.labelPartContainer}>
<Column fill>
<Column align="space-evenly">
<Column>
<Text
weight="bold"
size="smaller"
@@ -80,173 +64,206 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
)}
</Text>
</Column>
<Row>
<Column>
<Column>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('idType')}
</Text>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.Details}>
{t('nationalCard')}
</Text>
</Column>
{uin ? (
<Column margin="20 0 0 0">
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('uin')}
</Text>
<Text
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{uin}
</Text>
</Column>
) : null}
{uin ? (
<Column fill style={Theme.Styles.labelPart}>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('uin')}
</Text>
<Text
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{uin}
</Text>
{vid ? (
<Column margin="20 0 0 0">
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('vid')}
</Text>
<Text
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{vid}
</Text>
</Column>
) : null}
<Column margin="20 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>
) : null}
{vid ? (
<Column fill style={Theme.Styles.labelPart}>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('vid')}
</Text>
<Text
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{vid}
</Text>
<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"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('generatedOn')}
</Text>
<Text
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{new Date(props.vc?.generatedOn).toLocaleDateString()}
</Text>
</Column>
<Column margin="20 0 0 0">
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('status')}
</Text>
<Row>
<Text
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{t('valid')}
</Text>
{props.vc?.isVerified && <VerifiedIcon />}
</Row>
</Column>
<Column margin="20 0 0 0">
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('phoneNumber')}
</Text>
<Text
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{getLocalizedField(
props.vc?.verifiableCredential.credentialSubject.phone
)}
</Text>
</Column>
</Column>
) : null}
<Column fill style={Theme.Styles.labelPart}>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('generatedOn')}
</Text>
<Text
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{new Date(props.vc?.generatedOn).toLocaleDateString()}
</Text>
</Column>
<Column fill style={Theme.Styles.labelPart}>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('status')}
</Text>
<Row>
<Text
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{t('valid')}
</Text>
{props.vc?.isVerified && <VerifiedIcon />}
</Row>
</Column>
<Column fill style={Theme.Styles.labelPart}>
<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 fill style={Theme.Styles.labelPart}>
<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 fill style={Theme.Styles.labelPart}>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('phoneNumber')}
</Text>
<Text
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{getLocalizedField(
props.vc?.verifiableCredential.credentialSubject.phone
)}
</Text>
</Column>
<Column fill style={Theme.Styles.labelPart}>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('email')}
</Text>
<Row>
<Text
style={
props.vc?.verifiableCredential.credentialSubject.email
.length > 25
? { flex: 1 }
: { flex: 0 }
}
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{getLocalizedField(
props.vc?.verifiableCredential.credentialSubject.email
)}
</Text>
</Row>
</Column>
<Column fill style={Theme.Styles.labelPart}>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('address')}
</Text>
<Row>
<Text
style={{ flex: 1 }}
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{getFullAddress(
props.vc?.verifiableCredential.credentialSubject
)}
</Text>
</Row>
</Column>
</Row>
</Column>
</Row>
<View style={Theme.Styles.hrLine}></View>
<Column>
<Column fill style={Theme.Styles.labelPart}>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('email')}
</Text>
<Row>
<Text
style={
props.vc?.verifiableCredential.credentialSubject.email
.length > 25
? { flex: 1 }
: { flex: 0 }
}
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{getLocalizedField(
props.vc?.verifiableCredential.credentialSubject.email
)}
</Text>
</Row>
</Column>
<Column style={Theme.Styles.labelPart}>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('address')}
</Text>
<Row>
<Text
style={{ flex: 1 }}
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{getFullAddress(
props.vc?.verifiableCredential.credentialSubject
)}
</Text>
</Row>
</Column>
{CREDENTIAL_REGISTRY_EDIT === 'true' && (
<Column fill style={Theme.Styles.labelPart}>
<Text
weight="bold"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('credentialRegistry')}
</Text>
<Text
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{props.vc?.credentialRegistry}
</Text>
</Column>
)}
</Column>
<VcItemTags tag={props.vc?.tag} />
</ImageBackground>
@@ -268,58 +285,61 @@ export const VcDetails: React.FC<VcDetailsProps> = (props) => {
/>
))}
{props.isBindingPending ? (
<Column style={Theme.Styles.openCardBgContainer}>
<Row margin={'0 0 5 0'}>
<Icon
name="shield-alert"
color={Theme.Colors.Icon}
size={30}
type="material-community"
/>
</Row>
<Text
style={{ flex: 1 }}
weight="semibold"
size="small"
margin={'0 0 5 0'}
color={Theme.Colors.Details}>
{t('offlineAuthDisabledHeader')}
</Text>
<Text
style={{ flex: 1 }}
weight="regular"
size="small"
margin={'0 0 5 0'}
color={Theme.Colors.Details}>
{t('offlineAuthDisabledMessage')}
</Text>
<Button
title={t('enableVerification')}
onPress={props.onBinding}
type="radius"
/>
</Column>
) : (
<Column style={Theme.Styles.openCardBgContainer}>
<Row crossAlign="center">
<Icon
name="verified-user"
color={Theme.Colors.VerifiedIcon}
size={28}
containerStyle={{ marginStart: 4, bottom: 1 }}
/>
{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"
/>
<Text
style={{ flex: 1 }}
weight="semibold"
size="small"
margin={'0 0 5 0'}
color={Theme.Colors.statusLabel}>
{t('offlineAuthDisabledHeader')}
</Text>
</Row>
<Text
numLines={1}
color={Theme.Colors.Details}
weight="bold"
size="smaller"
margin="10 10 10 10"
children={t('profileAuthenticated')}></Text>
</Row>
</Column>
style={{ flex: 1 }}
weight="regular"
size="small"
margin={'0 0 5 0'}
color={Theme.Colors.statusLabel}>
{t('offlineAuthDisabledMessage')}
</Text>
<Button
title={t('enableVerification')}
onPress={props.onBinding}
type="radius"
/>
</Column>
) : (
<Column style={Theme.Styles.openCardBgContainer}>
<Row crossAlign="center">
<Icon
name="verified-user"
color={Theme.Colors.VerifiedIcon}
size={28}
containerStyle={{ marginStart: 4, bottom: 1 }}
/>
<Text
numLines={1}
color={Theme.Colors.statusLabel}
weight="bold"
size="smaller"
margin="10 10 10 10"
children={t('profileAuthenticated')}></Text>
</Row>
</Column>
)
) : (
<></>
)}
</Column>
);
@@ -329,6 +349,7 @@ interface VcDetailsProps {
vc: VC;
isBindingPending: boolean;
onBinding?: () => void;
activeTab?: Number;
}
function getFullAddress(credential: CredentialSubject) {
@@ -351,15 +372,3 @@ function getFullAddress(credential: CredentialSubject) {
.filter(Boolean)
.join(', ');
}
function getLocalizedField(rawField: string | LocalizedField[]) {
if (typeof rawField === 'string') {
return rawField;
}
try {
const locales: LocalizedField[] = JSON.parse(JSON.stringify(rawField));
return locales[0].value;
} catch (e) {
return '';
}
}

View File

@@ -1,7 +1,6 @@
import React, { useContext, useRef } from 'react';
import { useInterpret, useSelector } from '@xstate/react';
import { Pressable, Image, ImageBackground, Dimensions } from 'react-native';
import { CheckBox, Icon } from 'react-native-elements';
import { Pressable } from 'react-native';
import { ActorRefFrom } from 'xstate';
import {
createVcItemMachine,
@@ -11,113 +10,21 @@ import {
selectContext,
selectTag,
selectEmptyWalletBindingId,
selectIsSavingFailedInIdle,
selectKebabPopUp,
} from '../machines/vcItem';
import { Column, Row, Text } from './ui';
import { VcItemEvents } from '../machines/vcItem';
import { ErrorMessageOverlay } from './MessageOverlay';
import { Theme } from './ui/styleUtils';
import { RotatingIcon } from './RotatingIcon';
import { GlobalContext } from '../shared/GlobalContext';
import { useTranslation } from 'react-i18next';
const VerifiedIcon: React.FC = () => {
return (
<Icon
name="check-circle"
color={Theme.Colors.VerifiedIcon}
size={14}
containerStyle={{ marginStart: 4, bottom: 1 }}
/>
);
};
import { LocalizedField } from '../types/vc';
import { VcItemTags } from './VcItemTags';
const getDetails = (arg1, arg2, verifiableCredential) => {
if (arg1 === 'Status') {
return (
<Column>
<Text
weight="bold"
size="smaller"
color={
!verifiableCredential
? Theme.Colors.LoadingDetailsLabel
: Theme.Colors.DetailsLabel
}>
{arg1}
</Text>
<Row>
<Text
numLines={1}
color={Theme.Colors.Details}
weight="bold"
size="smaller"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}>
{!verifiableCredential ? '' : arg2}
</Text>
{!verifiableCredential ? null : <VerifiedIcon />}
</Row>
</Column>
);
} else {
return (
<Column>
<Text
color={
!verifiableCredential
? Theme.Colors.LoadingDetailsLabel
: Theme.Colors.DetailsLabel
}
weight="bold"
size="smaller">
{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>
);
}
};
const WalletVerified: React.FC = () => {
return (
<Icon
name="verified-user"
color={Theme.Colors.VerifiedIcon}
size={28}
containerStyle={{ marginStart: 4, bottom: 1 }}
/>
);
};
const WalletUnverified: React.FC = () => {
return (
<Icon
name="shield-alert"
color={Theme.Colors.Icon}
size={28}
type="material-community"
containerStyle={{ marginStart: 4, bottom: 1 }}
/>
);
};
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 { t } = useTranslation('VcDetails');
const machine = useRef(
createVcItemMachine(
appService.getSnapshot().context.serviceRefs,
@@ -126,179 +33,73 @@ export const VcItem: React.FC<VcItemProps> = (props) => {
);
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);
//Assigning the UIN and VID from the VC details to display the idtype label
const uin = verifiableCredential?.credentialSubject.UIN;
const vid = verifiableCredential?.credentialSubject.VID;
const storeErrorTranslationPath = 'errors.savingFailed';
const generatedOn = useSelector(service, selectGeneratedOn);
const fullName = !verifiableCredential
? ''
: getLocalizedField(verifiableCredential.credentialSubject.fullName);
const selectableOrCheck = props.selectable ? (
<CheckBox
checked={props.selected}
checkedIcon={<Icon name="radio-button-checked" />}
uncheckedIcon={<Icon name="radio-button-unchecked" />}
onPress={() => props.onPress(service)}
/>
) : null;
const tag = useSelector(service, selectTag);
return (
<Pressable
onPress={() => props.onPress(service)}
disabled={!verifiableCredential}
style={
props.selected
? Theme.Styles.selectedBindedVc
: Theme.Styles.closeCardBgContainer
}>
<ImageBackground
source={!verifiableCredential ? null : Theme.CloseCard}
resizeMode="stretch"
borderRadius={4}
<React.Fragment>
<Pressable
onPress={() => props.onPress(service)}
disabled={!verifiableCredential}
style={
!verifiableCredential
? Theme.Styles.vertloadingContainer
: Theme.Styles.backgroundImageContainer
props.selected
? Theme.Styles.selectedBindedVc
: Theme.Styles.closeCardBgContainer
}>
<Row style={Theme.Styles.homeCloseCardDetailsHeader}>
<Column>
<Text
color={
!verifiableCredential
? Theme.Colors.LoadingDetailsLabel
: Theme.Colors.DetailsLabel
}
weight="bold"
size="smaller">
{t('idType')}
</Text>
<Text
weight="bold"
color={Theme.Colors.Details}
size="smaller"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}>
{t('nationalCard')}
</Text>
</Column>
<Image
source={Theme.MosipLogo}
style={Theme.Styles.logo}
resizeMethod="auto"
/>
</Row>
<Row
crossAlign="center"
margin="5 0 0 0"
style={!verifiableCredential ? Theme.Styles.loadingContainer : null}>
<Column
style={
!verifiableCredential
? Theme.Styles.loadingContainer
: Theme.Styles.closeDetails
}>
<Image
source={
!verifiableCredential
? Theme.ProfileIcon
: { uri: context.credential.biometrics.face }
}
style={Theme.Styles.closeCardImage}
/>
<Column margin="0 0 0 25" style={{ alignItems: 'flex-start' }}>
{getDetails(t('fullName'), fullName, verifiableCredential)}
{!verifiableCredential
? getDetails(t('id'), uin || vid, verifiableCredential)
: null}
{uin ? getDetails(t('uin'), uin, verifiableCredential) : null}
{vid ? getDetails(t('vid'), vid, verifiableCredential) : null}
{getDetails(t('generatedOn'), generatedOn, verifiableCredential)}
{getDetails(t('status'), t('valid'), verifiableCredential)}
</Column>
</Column>
{verifiableCredential ? (
selectableOrCheck
) : (
<RotatingIcon name="sync" color={Theme.Colors.rotatingIcon} />
)}
</Row>
<VcItemTags tag={tag} />
</ImageBackground>
<Row>
{emptyWalletBindingId ? (
<Row
width={Dimensions.get('screen').width * 0.8}
align="space-between"
crossAlign="center">
<Row crossAlign="center" style={{ flex: 1 }}>
{verifiableCredential && <WalletUnverified />}
<Text
color={Theme.Colors.Details}
weight="semibold"
size="small"
margin="10 33 10 10"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}
children={t('offlineAuthDisabledHeader')}></Text>
</Row>
<Pressable>
<Icon
name="dots-three-horizontal"
type="entypo"
color={Theme.Colors.GrayIcon}
<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>
) : (
<Row
width={Dimensions.get('screen').width * 0.8}
align="space-between"
crossAlign="center">
<Row crossAlign="center" style={{ flex: 1 }}>
<WalletVerified />
<Text
color={Theme.Colors.Details}
weight="semibold"
size="smaller"
margin="10 10 10 10"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}
children={t('profileAuthenticated')}></Text>
</Row>
{props.showOnlyBindedVc ? null : (
<Pressable>
<Icon
name="dots-three-horizontal"
type="entypo"
color={Theme.Colors.GrayIcon}
/>
</Pressable>
)}
</Row>
)}
</Row>
</Pressable>
</Pressable>
<ErrorMessageOverlay
isVisible={isSavingFailedInIdle}
error={storeErrorTranslationPath}
onDismiss={DISMISS}
translationPath={'VcDetails'}
/>
</React.Fragment>
);
};
@@ -310,16 +111,8 @@ interface VcItemProps {
showOnlyBindedVc?: boolean;
onPress?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
onShow?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
}
function getLocalizedField(rawField: string | LocalizedField) {
if (typeof rawField === 'string') {
return rawField;
}
try {
const locales: LocalizedField[] = JSON.parse(JSON.stringify(rawField));
return locales[0].value;
} catch (e) {
return '';
}
activeTab?: string;
iconName?: string;
iconType?: string;
isSharingVc?: boolean;
}

View File

@@ -0,0 +1,133 @@
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 { vcItemMachine } from '../machines/vcItem';
import { VerifiableCredential } from '../types/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={28}
type="material-community"
containerStyle={{ marginStart: 4, bottom: 1 }}
/>
);
};
const WalletVerifiedIcon: React.FC = () => {
return (
<Icon
name="verified-user"
color={Theme.Colors.VerifiedIcon}
size={28}
containerStyle={{ marginStart: 4, bottom: 1 }}
/>
);
};
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}
weight="semibold"
size="small"
margin="10 33 10 10"
style={
!props.verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.statusLabel
}
children={t('offlineAuthDisabledHeader')}></Text>
</Row>
</Row>
);
};
const WalletVerifiedActivationDetails: React.FC<WalletVerifiedDetailsProps> = (
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,
}}>
<WalletVerifiedIcon />
<Text
color={Theme.Colors.statusLabel}
weight="semibold"
size="smaller"
margin="10 10 10 10"
style={
!props.verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}
children={t('profileAuthenticated')}></Text>
</Row>
</Row>
);
};
export const VcItemActivationStatus: React.FC<VcItemActivationStatusProps> = (
props
) => {
return (
<Row>
{props.emptyWalletBindingId ? (
<WalletUnverifiedActivationDetails
verifiableCredential={props.verifiableCredential}
onPress={props.onPress}
/>
) : (
<WalletVerifiedActivationDetails
verifiableCredential={props.verifiableCredential}
showOnlyBindedVc={props.showOnlyBindedVc}
onPress={props.onPress}
/>
)}
</Row>
);
};
interface VcItemActivationStatusProps {
showOnlyBindedVc: boolean;
onPress: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
verifiableCredential: VerifiableCredential;
emptyWalletBindingId: boolean;
}
interface WalletVerifiedDetailsProps {
showOnlyBindedVc: boolean;
onPress: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
verifiableCredential: VerifiableCredential;
}
interface WalletUnVerifiedDetailsProps {
onPress: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
verifiableCredential: VerifiableCredential;
}

View File

@@ -0,0 +1,205 @@
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';
import { VcItemTags } from './VcItemTags';
import VerifiedIcon from './VerifiedIcon';
import { Column, Row, Text } from './ui';
import { Theme } from './ui/styleUtils';
import { CheckBox, Icon } from 'react-native-elements';
const getDetails = (arg1, arg2, verifiableCredential) => {
if (arg1 === 'Status') {
return (
<Column>
<Text
weight="bold"
size="smaller"
color={
!verifiableCredential
? Theme.Colors.LoadingDetailsLabel
: Theme.Colors.DetailsLabel
}>
{arg1}
</Text>
<Row>
<Text
numLines={1}
color={Theme.Colors.Details}
weight="bold"
size="smaller"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}>
{!verifiableCredential ? '' : arg2}
</Text>
{!verifiableCredential ? null : <VerifiedIcon />}
</Row>
</Column>
);
} else {
return (
<Column>
<Text
color={
!verifiableCredential
? Theme.Colors.LoadingDetailsLabel
: Theme.Colors.DetailsLabel
}
size="smaller"
weight={'bold'}
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>
);
}
};
export const VcItemContent: React.FC<VcItemContentProps> = (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"
borderRadius={4}
style={
!props.verifiableCredential
? Theme.Styles.vertloadingContainer
: Theme.Styles.backgroundImageContainer
}>
<Column>
<Row align="space-between">
<Row>
<ImageBackground
source={
!props.verifiableCredential
? Theme.ProfileIcon
: { uri: props.context.credential.biometrics.face }
}
style={Theme.Styles.closeCardImage}>
{props.iconName && (
<Icon
name={props.iconName}
type={props.iconType}
color={Theme.Colors.Icon}
style={{ marginLeft: -80 }}
/>
)}
</ImageBackground>
<Column margin="0 0 0 10">
{getDetails(t('fullName'), fullName, props.verifiableCredential)}
<Column margin="10 0 0 0">
<Text
color={
!props.verifiableCredential
? Theme.Colors.LoadingDetailsLabel
: Theme.Colors.DetailsLabel
}
weight="semibold"
size="smaller"
align="left">
{t('idType')}
</Text>
<Text
weight="semibold"
color={Theme.Colors.Details}
size="smaller"
style={
!props.verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}>
{t('nationalCard')}
</Text>
</Column>
</Column>
</Row>
<Column>
{props.verifiableCredential ? selectableOrCheck : null}
</Column>
</Row>
<Row
align="space-between"
margin="5 0 0 0"
style={
!props.verifiableCredential ? Theme.Styles.loadingContainer : null
}>
<Column>
{uin ? getDetails(t('uin'), uin, props.verifiableCredential) : null}
{vid ? getDetails(t('vid'), vid, props.verifiableCredential) : null}
{!props.verifiableCredential
? getDetails(t('id'), uin || vid, props.verifiableCredential)
: null}
{getDetails(
t('generatedOn'),
props.generatedOn,
props.verifiableCredential
)}
</Column>
<Column>
{props.verifiableCredential
? getDetails(t('status'), isvalid, props.verifiableCredential)
: null}
</Column>
<Column
style={{ display: props.verifiableCredential ? 'flex' : 'none' }}>
<Image
source={Theme.MosipLogo}
style={Theme.Styles.logo}
resizeMethod="auto"
/>
</Column>
</Row>
</Column>
<VcItemTags tag={props.tag} />
</ImageBackground>
);
};
interface VcItemContentProps {
context: any;
verifiableCredential: VerifiableCredential;
generatedOn: string;
tag: string;
selectable: boolean;
selected: boolean;
iconName?: string;
iconType?: string;
service: any;
onPress?: () => void;
}

View File

@@ -0,0 +1,16 @@
import React from 'react';
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} />
</View>
</View>
);
};
export default VerifiedIcon;

View File

@@ -15,7 +15,7 @@ import { Column, Row, Text } from './ui';
import { Theme } from './ui/styleUtils';
import { RotatingIcon } from './RotatingIcon';
import { GlobalContext } from '../shared/GlobalContext';
import { LocalizedField } from '../types/vc';
import { getLocalizedField } from '../i18n';
export const VidItem: React.FC<VcItemProps> = (props) => {
const { appService } = useContext(GlobalContext);
@@ -105,15 +105,3 @@ interface VcItemProps {
selected?: boolean;
onPress?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
}
function getLocalizedField(rawField: string | LocalizedField) {
if (typeof rawField === 'string') {
return rawField;
}
try {
const locales: LocalizedField[] = JSON.parse(JSON.stringify(rawField));
return locales[0].value;
} catch (e) {
return '';
}
}

View File

@@ -8,27 +8,30 @@ import { Text } from './Text';
import { Theme, Spacing } from './styleUtils';
export const Button: React.FC<ButtonProps> = (props) => {
const type = props.type || 'solid';
const type = props.type || 'solid' || 'radius' || 'gradient';
const buttonStyle: StyleProp<ViewStyle> = [
props.fill ? Theme.ButtonStyles.fill : null,
Theme.ButtonStyles[type],
{ width: '100%' },
{ width: props.width ?? '100%' },
];
const containerStyle: StyleProp<ViewStyle> = [
Theme.ButtonStyles.container,
!(type === 'gradient') ? Theme.ButtonStyles.container : null,
props.disabled ? Theme.ButtonStyles.disabled : null,
props.margin ? Theme.spacing('margin', props.margin) : null,
type === 'gradient'
? props.isVcThere
? Theme.ButtonStyles.float
: Theme.ButtonStyles.gradient
: null,
props.styles,
];
const handleOnPress = (event: GestureResponderEvent) => {
if (!props.disabled && props.onPress) {
props.onPress(event);
}
};
return (
return !(type === 'gradient') ? (
<RNEButton
buttonStyle={buttonStyle}
containerStyle={[
@@ -44,6 +47,8 @@ export const Button: React.FC<ButtonProps> = (props) => {
color={
type === 'solid' || type === 'addId' || type === 'radius'
? Theme.Colors.whiteText
: type === 'plain'
? Theme.Colors.plainText
: Theme.Colors.AddIdBtnTxt
}>
{props.title}
@@ -54,6 +59,34 @@ export const Button: React.FC<ButtonProps> = (props) => {
onPress={handleOnPress}
loading={props.loading}
/>
) : (
<RNEButton
buttonStyle={buttonStyle}
ViewComponent={require('react-native-linear-gradient').default}
linearGradientProps={{
colors: !props.disabled
? Theme.Colors.GradientColors
: Theme.Colors.DisabledColors,
}}
containerStyle={containerStyle}
type={props.type}
raised={props.raised}
title={
<Text
style={Theme.TextStyles.bold}
color={
type === 'solid' || type === 'gradient' || type === 'radius'
? Theme.Colors.whiteText
: Theme.Colors.DownloadIdBtnTxt
}>
{props.title}
</Text>
}
style={[buttonStyle]}
icon={props.icon}
onPress={handleOnPress}
loading={props.loading}
/>
);
};
@@ -61,11 +94,14 @@ interface ButtonProps {
title: string;
disabled?: boolean;
margin?: Spacing;
type?: RNEButtonProps['type'];
type?: RNEButtonProps['type'] | 'gradient';
isVcThere?: boolean;
onPress?: RNEButtonProps['onPress'];
fill?: boolean;
raised?: boolean;
loading?: boolean;
icon?: RNEButtonProps['icon'];
styles?: StyleProp<ViewStyle>;
colors?: (string | number)[];
width?: number;
}

View File

@@ -66,6 +66,12 @@ export const Column = createLayout('column');
export const Centered = createLayout('column', 'center', 'center');
export const HorizontallyCentered = createLayout(
'column',
'flex-start',
'center'
);
interface LayoutProps {
fill?: boolean;
align?: FlexStyle['justifyContent'];

View File

@@ -2,9 +2,13 @@ import React from 'react';
import { I18nManager, Modal as RNModal, View } from 'react-native';
import { Icon } from 'react-native-elements';
import { Column, Row, Text } from '.';
import { useSendVcScreen } from '../../screens/Scan/SendVcScreenController';
import { DeviceInfoList } from '../DeviceInfoList';
import { ElevationLevel, Theme } from './styleUtils';
export const Modal: React.FC<ModalProps> = (props) => {
const controller = useSendVcScreen();
return (
<RNModal
animationType="slide"
@@ -19,8 +23,8 @@ export const Modal: React.FC<ModalProps> = (props) => {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
marginHorizontal: 21,
marginVertical: 16,
marginHorizontal: 18,
marginVertical: 8,
}}>
{props.headerRight ? (
<Icon
@@ -34,17 +38,45 @@ export const Modal: React.FC<ModalProps> = (props) => {
name="arrow-left"
type="material-community"
onPress={props.onDismiss}
color={Theme.Colors.Details}
containerStyle={Theme.Styles.backArrowContainer}
color={Theme.Colors.Icon}
/>
) : null}
<Row fill align="center" margin={'5 30 0 0'}>
<Text weight="semibold">{props.headerTitle}</Text>
<Row
fill
align={props.headerLeft ? 'flex-start' : 'center'}
margin={'16 0 0 0'}>
<Column>
<Text style={Theme.TextStyles.header}>
{props.headerTitle || props.headerLeft}
</Text>
{!props.requester ? (
<Text
weight="semibold"
style={Theme.TextStyles.small}
color={
props.headerLabelColor
? props.headerLabelColor
: Theme.Colors.profileLanguageValue
}>
{props.headerLabel}
</Text>
) : (
<Text
weight="semibold"
style={Theme.TextStyles.small}
color={Theme.Colors.IconBg}>
<DeviceInfoList deviceInfo={controller.receiverInfo} />
</Text>
)}
</Column>
</Row>
{props.headerRight || props.arrowLeft || (
<Icon
name="close"
onPress={props.onDismiss}
color={Theme.Colors.Icon}
color={Theme.Colors.Details}
size={27}
/>
)}
</View>
@@ -57,10 +89,14 @@ export const Modal: React.FC<ModalProps> = (props) => {
export interface ModalProps {
isVisible: boolean;
onDismiss: () => void;
requester?: boolean;
onDismiss?: () => void;
headerTitle?: string;
headerElevation?: ElevationLevel;
headerLabel?: string;
headerLabelColor?: string;
headerRight?: React.ReactElement;
headerLeft?: React.ReactElement;
arrowLeft?: React.ReactElement;
onShow?: () => void;
}

View File

@@ -0,0 +1,69 @@
import React, { useEffect, useState } from 'react';
import { Dimensions } from 'react-native';
import { Icon, ListItem } from 'react-native-elements';
import { Column } from './Layout';
import { Text } from './Text';
import { Theme } from './styleUtils';
interface Picker extends React.VFC<PickerProps<unknown>> {
<T>(props: PickerProps<T>): ReturnType<React.FC>;
}
export const SetupPicker: Picker = (props: PickerProps<unknown>) => {
const [isContentVisible, setIsContentVisible] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(-1);
useEffect(() => {
setSelectedIndex(
props.items.findIndex(({ value }) => value === props.selectedValue)
);
}, [props.selectedValue]);
const toggleContent = () => setIsContentVisible(!isContentVisible);
const selectItem = (index: number) => {
setSelectedIndex(index);
props.onValueChange(props.items[index].value, index);
toggleContent();
};
return (
<Column
width={Dimensions.get('window').width * 0.8}
backgroundColor={Theme.Colors.whiteBackgroundColor}>
{props.items.map((item, index) => (
<ListItem
bottomDivider
topDivider={index !== 0}
onPress={() => selectItem(index)}
key={index}>
<ListItem.Content>
<ListItem.Title>
<Text
color={selectedIndex === index ? Theme.Colors.Icon : null}
weight={selectedIndex === index ? 'semibold' : 'regular'}>
{item.label}
</Text>
</ListItem.Title>
</ListItem.Content>
{selectedIndex === index ? (
<Icon name="radio-button-checked" color={Theme.Colors.Icon} />
) : (
<Icon name="radio-button-unchecked" color={Theme.Colors.GrayIcon} />
)}
</ListItem>
))}
</Column>
);
};
interface PickerProps<T> {
items: PickerItem<T>[];
selectedValue: T;
onValueChange: (value: T, index: number) => void;
}
interface PickerItem<T> {
label: string;
value: T;
}

View File

@@ -28,7 +28,7 @@ interface TextProps {
weight?: 'regular' | 'semibold' | 'bold';
align?: TextStyle['textAlign'];
margin?: Spacing;
size?: 'small' | 'smaller' | 'regular';
size?: 'small' | 'smaller' | 'regular' | 'large';
lineHeight?: number;
numLines?: number;
style?: StyleProp<TextStyle>;

View File

@@ -10,10 +10,17 @@ export const TextItem: React.FC<TextItemProps> = (props) => {
pX={24}
pY={props.label ? 16 : 12}
style={{
borderBottomColor: Theme.Colors.borderBottomColor,
borderBottomWidth: props.divider ? 1 : 0,
borderColor: Theme.Colors.borderBottomColor,
borderBottomWidth: props.divider ? 2 : 0,
borderTopWidth: props.topDivider ? 2 : 0,
alignItems: 'flex-start',
}}>
<Text
color={Theme.Colors.textValue}
weight={props.label ? 'semibold' : 'regular'}
style={{ textAlign: 'left' }}>
{props.text}
</Text>
{props.label && (
<Text
size="smaller"
@@ -23,12 +30,6 @@ export const TextItem: React.FC<TextItemProps> = (props) => {
{props.label}
</Text>
)}
<Text
color={Theme.Colors.textValue}
weight={props.label ? 'semibold' : 'regular'}
style={{ textAlign: 'left' }}>
{props.text}
</Text>
</Column>
);
};
@@ -37,5 +38,6 @@ interface TextItemProps {
text: string;
label?: string;
divider?: boolean;
topDivider?: boolean;
margin?: string;
}

View File

@@ -1,3 +1,3 @@
export { Text } from './Text';
export { Button } from './Button';
export { Row, Column, Centered } from './Layout';
export { Row, Column, Centered, HorizontallyCentered } from './Layout';

View File

@@ -1,8 +1,10 @@
import { DefaultTheme } from './themes/DefaultTheme';
import { PurpleTheme } from './themes/PurpleTheme';
import { APPLICATION_THEME } from 'react-native-dotenv';
// To change the theme, CSS theme file has to import and assign it to Theme in line no 6
export const Theme = DefaultTheme;
export const Theme =
APPLICATION_THEME.toLowerCase() === 'purple' ? PurpleTheme : DefaultTheme;
type SpacingXY = [number, number];
type SpacingFull = [number, number, number, number];

View File

@@ -3,21 +3,36 @@ import { Dimensions, StyleSheet, ViewStyle } from 'react-native';
import { Spacing } from '../styleUtils';
const Colors = {
Black: '#231F20',
Grey: '#B0B0B0',
Black: '#000000',
Zambezi: '#5F5F5F',
Grey: '#C7C7C7',
Grey5: '#E0E0E0',
Grey6: '#F2F2F2',
Gray40: '#666666',
Gray44: '#707070',
Gray9: '#171717',
DimGray: '#737373',
Orange: '#F2811D',
LightGrey: '#f7f5f0',
LightGrey: '#F5F5F5',
ShadeOfGrey: '#6F6F6F',
White: '#FFFFFF',
Red: '#EB5757',
Green: '#219653',
Red: '#D52929',
Green: '#4B9D20',
Transparent: 'transparent',
Warning: '#f0ad4e',
LightOrange: '#fce7e3',
GrayText: '#6F6F6F',
dorColor: '#CBCBCB',
plainText: '#FFD6A7',
walletbindingLabel: '#000000',
LightOrange: '#FDF1E6',
GradientColors: ['#F59B4B', '#E86E04'],
DisabledColors: ['#C7C7C7', '#C7C7C7'],
TimeoutHintBoxColor: '#FFF7E5',
TimoutText: '#8B6105',
resendCodeTimer: '#555555',
};
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5;
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6;
export const DefaultTheme = {
Colors: {
@@ -27,25 +42,32 @@ export const DefaultTheme = {
LoadingDetailsLabel: Colors.Orange,
AddIdBtnBg: Colors.Orange,
AddIdBtnTxt: Colors.Orange,
ClearAddIdBtnBg: Colors.Transparent,
DownloadIdBtnTxt: Colors.White,
Loading: Colors.Orange,
Cursor: Colors.Orange,
noUinText: Colors.Orange,
IconBg: Colors.Orange,
popUp: Colors.Green,
Icon: Colors.Orange,
GrayIcon: Colors.Grey,
helpText: Colors.Gray44,
borderBottomColor: Colors.Grey6,
whiteBackgroundColor: Colors.White,
lightGreyBackgroundColor: Colors.LightGrey,
profileLanguageValue: Colors.Grey,
profileVersion: Colors.Grey,
aboutVersion: Colors.Gray40,
profileAuthFactorUnlock: Colors.Grey,
profileLabel: Colors.Black,
profileValue: Colors.Grey,
switchHead: Colors.Orange,
switchTrackTrue: Colors.LightOrange,
switchTrackFalse: Colors.Grey,
overlayBackgroundColor: Colors.White,
rotatingIcon: Colors.Grey5,
loadingLabel: Colors.Grey6,
textLabel: Colors.Grey,
textValue: Colors.Black,
requesterName: Colors.Red,
errorMessage: Colors.Red,
QRCodeBackgroundColor: Colors.LightGrey,
ReceiveVcModalBackgroundColor: Colors.LightGrey,
@@ -54,12 +76,28 @@ export const DefaultTheme = {
whiteText: Colors.White,
flipCameraIcon: Colors.Black,
IdInputModalBorder: Colors.Grey,
RetrieveIdLabel: Colors.ShadeOfGrey,
inputSelection: Colors.Orange,
checkCircleIcon: Colors.White,
OnboardingCircleIcon: Colors.White,
OnboardingCloseIcon: Colors.White,
WarningIcon: Colors.Warning,
DefaultToggle: Colors.LightOrange,
ProfileIconBg: Colors.LightOrange,
GrayText: Colors.GrayText,
gradientBtn: ['#F59B4B', '#E86E04'],
dotColor: Colors.dorColor,
plainText: Colors.plainText,
IconBackground: Colors.LightOrange,
GradientColors: Colors.GradientColors,
DisabledColors: Colors.DisabledColors,
getVidColor: Colors.Zambezi,
TimeoutHintBoxColor: Colors.TimeoutHintBoxColor,
TimoutText: Colors.TimoutText,
walletbindingLabel: Colors.Black,
walletbindingContent: Colors.Gray40,
resendCodeTimer: Colors.resendCodeTimer,
statusLabel: Colors.Black,
},
Styles: StyleSheet.create({
title: {
@@ -78,6 +116,19 @@ export const DefaultTheme = {
backgroundColor: Colors.Grey,
borderRadius: 4,
},
statusLabel: {
color: Colors.Black,
},
verifiedIconContainer: {
marginLeft: 5,
},
verifiedIconInner: {
backgroundColor: 'white',
borderRadius: 10,
},
vcItemLabelHeader: {
color: Colors.Orange,
},
closeDetails: {
flex: 1,
flexDirection: 'row',
@@ -89,6 +140,38 @@ export const DefaultTheme = {
backgroundColor: Colors.Grey6,
borderRadius: 4,
},
loadingCardDetailsContainer: {
flex: 1,
flexDirection: 'row',
backgroundColor: Colors.Grey6,
borderRadius: 4,
},
cardDetailsContainer: {},
bottomTabIconStyle: {
padding: 4,
width: 36,
height: 36,
borderRadius: 6,
backgroundColor: Colors.LightOrange,
},
downloadingVcPopUp: {
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: Colors.Green,
height: 39,
position: 'relative',
paddingHorizontal: 12,
},
homeScreenContainer: {
alignItems: 'center',
justifyContent: 'center',
borderRadius: 10,
backgroundColor: '#fff',
shadowColor: '#000',
shadowOpacity: 0.4,
elevation: 5,
padding: 10,
},
vertloadingContainer: {
flex: 1,
backgroundColor: Colors.Grey6,
@@ -99,10 +182,11 @@ export const DefaultTheme = {
flex: 1,
justifyContent: 'flex-start',
},
logoContainer: {
closecardMosipLogo: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
alignSelf: 'flex-end',
marginLeft: 300,
},
closeCardBgContainer: {
@@ -116,6 +200,12 @@ export const DefaultTheme = {
elevation: 4,
},
selectedBindedVc: {
borderRadius: 15,
margin: 5,
borderWidth: 3,
borderColor: Colors.Green,
},
selectedVc: {
borderRadius: 10,
margin: 5,
borderWidth: 2,
@@ -138,7 +228,7 @@ export const DefaultTheme = {
width: 100,
},
bottomButtonsContainer: {
height: 120,
height: 'auto',
borderTopLeftRadius: 27,
borderTopRightRadius: 27,
padding: 6,
@@ -175,8 +265,6 @@ export const DefaultTheme = {
backgroundImageContainer: {
flex: 1,
padding: 10,
borderBottomColor: Colors.Grey,
borderBottomWidth: 1,
},
successTag: {
backgroundColor: Colors.Green,
@@ -198,13 +286,17 @@ export const DefaultTheme = {
justifyContent: 'space-between',
},
logo: {
height: 36,
width: 30,
height: 35,
width: 90,
},
homeCloseCardDetailsHeader: {
flex: 1,
},
cardDetailsHeader: {
flex: 1,
justifyContent: 'space-between',
},
mosipLogoContainer: {},
details: {
width: 290,
marginLeft: 110,
@@ -221,6 +313,38 @@ export const DefaultTheme = {
borderRadius: 6,
backgroundColor: Colors.LightOrange,
},
IconContainer: {
padding: 6,
width: 36,
marginRight: 4,
marginLeft: 10,
height: 36,
borderRadius: 10,
backgroundColor: Colors.LightOrange,
},
settingsIconBg: {
padding: 6,
width: 36,
marginRight: 4,
height: 36,
backgroundColor: Colors.Transparent,
},
backArrowContainer: {
padding: 6,
width: 36,
height: 36,
borderRadius: 10,
backgroundColor: Colors.LightOrange,
},
receiveCardsContainer: {
height: Dimensions.get('window').height * 0.12,
width: Dimensions.get('window').width * 0.45,
alignItems: 'center',
borderBottomRightRadius: 0,
padding: 15,
marginVertical: 18,
elevation: 1,
},
domainVerifiyIcon: {
padding: 20,
marginLeft: 120,
@@ -241,24 +365,46 @@ export const DefaultTheme = {
height: 135,
borderRadius: 5,
},
versionContainer: {
backgroundColor: Colors.Grey6,
margin: 4,
borderRadius: 14,
},
primaryRow: {
backgroundColor: Colors.LightOrange,
paddingHorizontal: 18,
paddingVertical: 9,
justifyContent: 'space-between',
},
iconContainer: {
flexDirection: 'row',
alignItems: 'flex-end',
},
scannerContainer: {
borderWidth: 4,
borderColor: Colors.Black,
borderRadius: 32,
justifyContent: 'center',
height: 300,
width: 300,
borderRadius: 24,
alignSelf: 'center',
height: 350,
width: 320,
overflow: 'hidden',
marginLeft: 18,
},
scanner: {
height: 400,
width: '100%',
margin: 'auto',
},
flipIconButton: {
alignSelf: 'center',
cameraDisabledPopUp: {
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: Colors.Red,
height: 75,
position: 'relative',
paddingHorizontal: 15,
marginTop: -36,
},
photoConsentLabel: {
backgroundColor: Colors.White,
padding: 0,
borderWidth: 0,
},
tabIndicator: {
backgroundColor: Colors.Orange,
@@ -273,55 +419,134 @@ export const DefaultTheme = {
detailsText: {
fontWeight: 'bold',
fontSize: 15,
fontFamily: 'Poppins_700Bold',
fontFamily: 'Inter_700Bold',
},
getId: {
justifyContent: 'center',
alignItems: 'center',
marginTop: 10,
marginVertical: 6,
},
placeholder: {
fontFamily: 'Poppins_400Regular',
fontFamily: 'Inter_600SemiBold',
},
hrLine: {
borderBottomColor: 'black',
borderBottomWidth: 1,
marginTop: 10,
},
}),
QrCodeStyles: StyleSheet.create({
magnifierZoom: {
backgroundColor: Colors.White,
width: 30,
height: 30,
alignItems: 'center',
padding: 5,
borderTopLeftRadius: 11,
elevation: 4,
},
expandedQrCode: {
backgroundColor: Colors.White,
width: 350,
borderRadius: 21,
},
QrCodeHeader: {
backgroundColor: Colors.White,
borderTopLeftRadius: 21,
borderTopRightRadius: 21,
justifyContent: 'space-between',
fontFamily: 'Inter_700Bold',
paddingBottom: 10,
paddingRight: 15,
paddingLeft: 130,
elevation: 2,
},
warningText: {
color: Colors.Red,
fontSize: 18,
},
}),
PinInputStyle: StyleSheet.create({
input: {
borderBottomWidth: 1,
borderBottomWidth: 3,
borderColor: Colors.Grey,
color: Colors.Black,
flex: 1,
fontFamily: 'Poppins_600SemiBold',
fontSize: 18,
fontWeight: '600',
height: 40,
fontSize: 33,
fontFamily: 'Inter_600SemiBold',
height: 60,
lineHeight: 28,
margin: 8,
textAlign: 'center',
},
onEnteringPin: {
borderBottomWidth: 3,
borderColor: Colors.Orange,
color: Colors.Black,
flex: 1,
fontFamily: 'Inter_700Bold',
fontSize: 29,
height: 40,
margin: 8,
textAlign: 'center',
},
}),
TextStyles: StyleSheet.create({
base: {
header: {
color: Colors.Black,
fontFamily: 'Inter_700Bold',
fontSize: 18,
lineHeight: 22,
paddingTop: 4,
},
retrieveIdLabel: {
color: Colors.ShadeOfGrey,
fontFamily: 'Inter_600SemiBold',
lineHeight: 18,
},
helpDetailes: {
margin: 5,
color: Colors.Gray44,
fontFamily: 'Inter_600SemiBold',
},
aboutDetailes: {
color: Colors.Black,
fontSize: 18,
lineHeight: 28,
margin: 7,
lineHeight: 18,
},
error: {
color: Colors.Red,
fontFamily: 'Inter_600SemiBold',
fontSize: 12,
},
base: {
color: Colors.Black,
fontSize: 16,
lineHeight: 18,
},
regular: {
fontFamily: 'Poppins_400Regular',
fontFamily: 'Inter_400Regular',
fontSize: 14,
},
semibold: {
fontFamily: 'Poppins_600SemiBold',
fontFamily: 'Inter_600SemiBold',
fontSize: 15,
},
bold: {
fontFamily: 'Poppins_700Bold',
fontFamily: 'Inter_700Bold',
},
small: {
fontSize: 14,
fontSize: 13,
lineHeight: 21,
},
smaller: {
fontSize: 12,
fontSize: 11,
lineHeight: 18,
},
large: {
fontSize: 18,
},
}),
VcItemStyles: StyleSheet.create({
title: {
@@ -376,15 +601,29 @@ export const DefaultTheme = {
borderColor: Colors.Orange,
},
container: {
minHeight: 48,
height: 45,
flexDirection: 'row',
},
disabled: {
opacity: 0.5,
backgroundColor: Colors.Grey,
},
addId: {
backgroundColor: Colors.Orange,
},
gradient: {
borderRadius: 9,
width: Dimensions.get('window').width * 0.72,
alignSelf: 'center',
margin: 4,
},
float: {
borderRadius: 9,
alignSelf: 'center',
fontSize: 10,
elevation: 5,
position: 'absolute',
bottom: 24,
},
clearAddIdBtnBg: {
backgroundColor: Colors.Transparent,
},
@@ -404,14 +643,35 @@ export const DefaultTheme = {
padding: 32,
},
}),
QRCodeOverlay: StyleSheet.create({
header: {},
}),
SelectVcOverlayStyles: StyleSheet.create({
overlay: {
elevation: 5,
backgroundColor: Colors.White,
padding: 0,
},
consentCheckContainer: {
backgroundColor: Colors.White,
borderWidth: 0,
marginTop: -15,
fontFamily: 'Inter_600SemiBold',
padding: 0,
},
timeoutHintContainer: {
backgroundColor: Colors.TimeoutHintBoxColor,
margin: 21,
paddingHorizontal: 14,
paddingVertical: 12,
borderRadius: 12,
},
sharedSuccessfully: {
flex: 1,
backgroundColor: Colors.White,
},
}),
CreditsStyles: StyleSheet.create({
AppMetaDataStyles: StyleSheet.create({
buttonContainer: {
position: 'absolute',
left: 0,
@@ -421,9 +681,23 @@ export const DefaultTheme = {
flex: 1,
width: Dimensions.get('screen').width,
},
markdownView: {
contentView: {
flex: 1,
padding: 20,
},
header: {
fontSize: 20,
fontWeight: 'normal',
color: 'rgb(28,28,30)',
},
}),
FooterStyles: StyleSheet.create({
bottom: {
justifyContent: 'flex-end',
backgroundColor: Colors.Grey6,
borderRadius: 15,
padding: 10,
},
}),
ModalStyles: StyleSheet.create({
modal: {
@@ -464,16 +738,44 @@ export const DefaultTheme = {
height: Dimensions.get('screen').height,
},
}),
KebabPopUpStyles: StyleSheet.create({
kebabPopUp: {
borderTopLeftRadius: 15,
borderTopRightRadius: 15,
width: Dimensions.get('screen').width,
marginTop: Dimensions.get('screen').height * 0.55,
},
kebabHeaderStyle: {
justifyContent: 'space-between',
fontFamily: 'Inter_700Bold',
paddingRight: 15,
paddingLeft: 130,
paddingTop: 18,
},
}),
MessageOverlayStyles: StyleSheet.create({
overlay: {
elevation: 5,
backgroundColor: Colors.White,
padding: 0,
padding: 5,
borderRadius: 10,
},
buttonContainer: {
justifyContent: 'center',
marginBottom: 75,
},
popupOverLay: {
height: 150,
backgroundColor: Colors.White,
},
button: {
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
},
halfButton: {
borderRadius: 8,
margin: '0.5%',
},
}),
BindingVcWarningOverlay: StyleSheet.create({
overlay: {
@@ -631,7 +933,7 @@ export const DefaultTheme = {
sliderTitle: {
color: Colors.White,
marginBottom: 20,
fontFamily: 'Poppins_700Bold',
fontFamily: 'Inter_700Bold',
},
text: {
color: Colors.White,
@@ -651,20 +953,43 @@ export const DefaultTheme = {
top: 40,
zIndex: 1,
},
bottomContainer: {
padding: 20,
borderTopLeftRadius: 30,
borderTopRightRadius: 30,
marginTop: -185,
paddingBottom: 100,
},
}),
claimsContainer: StyleSheet.create({
container: {
backgroundColor: Colors.Transparent,
},
}),
OpenCard: require('../../../assets/ID-open.png'),
CloseCard: require('../../../assets/ID-closed.png'),
OpenCard: '',
CloseCard: '',
ProfileIcon: require('../../../assets/placeholder-photo.png'),
MosipSplashLogo: require('../../../assets/icon.png'),
MosipLogo: require('../../../assets/mosip-logo.png'),
CameraFlipIcon: require('../../../assets/camera-flip-icon.png'),
DomainWarningLogo: require('../../../assets/domain-warning.png'),
WarningLogo: require('../../../assets/warningLogo.png'),
OtpLogo: require('../../../assets/otp-mobile-logo.png'),
SuccessLogo: require('../../../assets/success-logo.png'),
ReceiveCardIcon: require('../../../assets/receive-card-icon.png'),
ReceivedCardsIcon: require('../../../assets/received-cards-icon.png'),
DigitalIdentityLogo: require('../../../assets/digital-identity-icon.png'),
InjiLogoWhite: require('../../../assets/inji-logo-white.png'),
InjiProgressingLogo: require('../../../assets/progressing-logo.png'),
LockIcon: require('../../../assets/lock-icon.png'),
InjiHomeLogo: require('../../../assets/inji-home-logo.png'),
MagnifierZoom: require('../../../assets/magnifier-zoom.png'),
HelpIcon: require('../../../assets/help-icon.png'),
sharingIntro: require('../../../assets/Secure-Sharing.png'),
walletIntro: require('../../../assets/intro-wallet-binding.png'),
IntroScanner: require('../../../assets/intro-scanner.png'),
injiSmallLogo: require('../../../assets/inji_small_logo.png'),
protectPrivacy: require('../../../assets/phone_mockup_1.png'),
elevation(level: ElevationLevel): ViewStyle {
// https://ethercreative.github.io/react-native-shadow-generator/

View File

@@ -4,48 +4,72 @@ import { Spacing } from '../styleUtils';
const Colors = {
Black: '#231F20',
Zambezi: '#5F5F5F',
Grey: '#B0B0B0',
Grey5: '#E0E0E0',
Grey6: '#F2F2F2',
Gray40: '#666666',
Gray44: '#707070',
Gray9: '#171717',
DimGray: '#737373',
Orange: '#F2811D',
LightOrange: '#FDF1E6',
LightGrey: '#FAF9FF',
ShadeOfGrey: '#6F6F6F',
White: '#FFFFFF',
Red: '#EB5757',
Green: '#219653',
Purple: '#70308C',
Transparent: 'transparent',
Warning: '#f0ad4e',
GrayText: '#6F6F6F',
dorColor: '#CBCBCB',
plainText: '#FFD6A7',
walletbindingLabel: '#000000',
GradientColors: ['#373086', '#70308C'],
DisabledColors: ['#C7C7C7', '#C7C7C7'],
Purple: '#70308C',
LightPurple: '#AEA7FF',
TimeoutHintBoxColor: '#FFF7E5',
TimoutText: '#8B6105',
resendCodeTimer: '#555555',
};
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5;
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6;
export const PurpleTheme = {
Colors: {
TabItemText: Colors.Purple,
Details: Colors.White,
DetailsLabel: Colors.White,
LoadingDetailsLabel: Colors.Black,
Details: Colors.Black,
DetailsLabel: Colors.Purple,
LoadingDetailsLabel: Colors.Purple,
AddIdBtnBg: Colors.Purple,
AddIdBtnTxt: Colors.Purple,
ClearAddIdBtnBg: 'transparent',
DownloadIdBtnTxt: Colors.White,
Loading: Colors.Purple,
Cursor: Colors.Purple,
noUinText: Colors.Purple,
IconBg: Colors.Purple,
popUp: Colors.Green,
Icon: Colors.Purple,
GrayIcon: Colors.Grey,
Loading: Colors.Purple,
helpText: Colors.Gray44,
borderBottomColor: Colors.Grey6,
whiteBackgroundColor: Colors.White,
lightGreyBackgroundColor: Colors.LightGrey,
profileLanguageValue: Colors.Grey,
profileVersion: Colors.Grey,
aboutVersion: Colors.Gray40,
profileAuthFactorUnlock: Colors.Grey,
profileLabel: Colors.Black,
profileValue: Colors.Grey,
switchHead: Colors.Purple,
switchTrackTrue: Colors.LightPurple,
switchTrackFalse: Colors.Grey,
overlayBackgroundColor: Colors.White,
rotatingIcon: Colors.Grey5,
loadingLabel: Colors.Grey6,
textLabel: Colors.Grey,
textValue: Colors.Black,
requesterName: Colors.Red,
errorMessage: Colors.Red,
QRCodeBackgroundColor: Colors.LightGrey,
ReceiveVcModalBackgroundColor: Colors.LightGrey,
@@ -54,11 +78,28 @@ export const PurpleTheme = {
whiteText: Colors.White,
flipCameraIcon: Colors.Black,
IdInputModalBorder: Colors.Grey,
inputSelection: Colors.Orange,
RetrieveIdLabel: Colors.ShadeOfGrey,
inputSelection: Colors.Purple,
checkCircleIcon: Colors.White,
OnboardingCircleIcon: Colors.White,
OnboardingCloseIcon: Colors.White,
WarningIcon: Colors.Warning,
DefaultToggle: Colors.LightPurple,
ProfileIconBg: Colors.LightPurple,
GrayText: Colors.GrayText,
gradientBtn: Colors.GradientColors,
dotColor: Colors.dorColor,
plainText: Colors.plainText,
IconBackground: Colors.LightPurple,
GradientColors: Colors.GradientColors,
DisabledColors: Colors.DisabledColors,
getVidColor: Colors.Zambezi,
TimeoutHintBoxColor: Colors.TimeoutHintBoxColor,
TimoutText: Colors.TimoutText,
walletbindingLabel: Colors.Black,
walletbindingContent: Colors.Gray40,
resendCodeTimer: Colors.resendCodeTimer,
statusLabel: Colors.Black,
},
Styles: StyleSheet.create({
title: {
@@ -77,6 +118,19 @@ export const PurpleTheme = {
backgroundColor: Colors.Grey,
borderRadius: 4,
},
statusLabel: {
color: Colors.Black,
},
verifiedIconContainer: {
marginLeft: 5,
},
verifiedIconInner: {
backgroundColor: 'white',
borderRadius: 10,
},
vcItemLabelHeader: {
color: Colors.Purple,
},
closeDetails: {
flex: 1,
flexDirection: 'row',
@@ -88,6 +142,38 @@ export const PurpleTheme = {
backgroundColor: Colors.Grey6,
borderRadius: 4,
},
loadingCardDetailsContainer: {
flex: 1,
flexDirection: 'row',
backgroundColor: Colors.Grey6,
borderRadius: 4,
},
cardDetailsContainer: {},
bottomTabIconStyle: {
padding: 4,
width: 36,
height: 36,
borderRadius: 6,
backgroundColor: Colors.LightPurple,
},
downloadingVcPopUp: {
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: Colors.Green,
height: 39,
position: 'relative',
paddingHorizontal: 12,
},
homeScreenContainer: {
alignItems: 'center',
justifyContent: 'center',
borderRadius: 10,
backgroundColor: '#fff',
shadowColor: '#000',
shadowOpacity: 0.4,
elevation: 5,
padding: 10,
},
vertloadingContainer: {
flex: 1,
backgroundColor: Colors.Grey6,
@@ -98,10 +184,11 @@ export const PurpleTheme = {
flex: 1,
justifyContent: 'flex-start',
},
logoContainer: {
closecardMosipLogo: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
alignSelf: 'flex-end',
marginLeft: 300,
},
closeCardBgContainer: {
@@ -114,10 +201,54 @@ export const PurpleTheme = {
shadowRadius: 3,
elevation: 4,
},
selectedBindedVc: {
borderRadius: 15,
margin: 5,
borderWidth: 3,
borderColor: Colors.Green,
},
selectedVc: {
borderRadius: 10,
margin: 5,
borderWidth: 2,
borderColor: Colors.Purple,
},
labelPartContainer: {
marginLeft: 16,
flex: 1,
},
urlContainer: {
backgroundColor: Colors.White,
padding: 10,
borderRadius: 12,
fontSize: 12,
},
lockDomainContainer: {
backgroundColor: Colors.White,
alignSelf: 'center',
borderRadius: 15,
width: 100,
},
bottomButtonsContainer: {
height: 'auto',
borderTopLeftRadius: 27,
borderTopRightRadius: 27,
padding: 6,
backgroundColor: Colors.White,
},
consentPageTop: {
backgroundColor: Colors.White,
height: 160,
borderRadius: 6,
},
consentDottedLine: {
width: 182,
borderWidth: 2,
margin: 5,
borderStyle: 'dashed',
borderRadius: 1,
borderColor: 'grey',
},
labelPart: {
marginTop: 10,
alignItems: 'flex-start',
@@ -136,8 +267,6 @@ export const PurpleTheme = {
backgroundImageContainer: {
flex: 1,
padding: 10,
borderBottomColor: Colors.Grey,
borderBottomWidth: 1,
},
successTag: {
backgroundColor: Colors.Green,
@@ -159,13 +288,17 @@ export const PurpleTheme = {
justifyContent: 'space-between',
},
logo: {
height: 36,
width: 36,
height: 35,
width: 90,
},
homeCloseCardDetailsHeader: {
flex: 1,
},
cardDetailsHeader: {
flex: 1,
justifyContent: 'space-between',
},
mosipLogoContainer: {},
details: {
width: 290,
marginLeft: 110,
@@ -175,6 +308,55 @@ export const PurpleTheme = {
flex: 1,
padding: 10,
},
profileIconBg: {
padding: 8,
width: 40,
height: 40,
borderRadius: 6,
backgroundColor: Colors.LightPurple,
},
IconContainer: {
padding: 6,
width: 36,
marginRight: 4,
marginLeft: 10,
height: 36,
borderRadius: 10,
backgroundColor: Colors.LightPurple,
},
settingsIconBg: {
padding: 6,
width: 36,
marginRight: 4,
height: 36,
backgroundColor: Colors.Transparent,
},
backArrowContainer: {
padding: 6,
width: 36,
height: 36,
borderRadius: 10,
backgroundColor: Colors.LightPurple,
},
receiveCardsContainer: {
height: Dimensions.get('window').height * 0.12,
width: Dimensions.get('window').width * 0.45,
alignItems: 'center',
borderBottomRightRadius: 0,
padding: 15,
marginVertical: 18,
elevation: 1,
},
domainVerifiyIcon: {
padding: 20,
marginLeft: 120,
width: 130,
height: 130,
borderRadius: 60,
borderWidth: 10,
borderColor: Colors.White,
backgroundColor: Colors.LightPurple,
},
closeCardImage: {
width: 105,
height: 135,
@@ -185,13 +367,26 @@ export const PurpleTheme = {
height: 135,
borderRadius: 5,
},
versionContainer: {
backgroundColor: Colors.Grey6,
margin: 4,
borderRadius: 14,
},
primaryRow: {
backgroundColor: Colors.LightPurple,
paddingHorizontal: 18,
paddingVertical: 9,
justifyContent: 'space-between',
},
iconContainer: {
flexDirection: 'row',
alignItems: 'flex-end',
},
scannerContainer: {
borderWidth: 4,
borderColor: Colors.Black,
borderRadius: 32,
justifyContent: 'center',
height: 300,
width: 300,
borderRadius: 24,
alignSelf: 'center',
height: 350,
width: 320,
overflow: 'hidden',
},
scanner: {
@@ -199,9 +394,19 @@ export const PurpleTheme = {
width: '100%',
margin: 'auto',
},
flipIconButton: {
alignSelf: 'center',
cameraDisabledPopUp: {
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: Colors.Red,
height: 75,
position: 'relative',
paddingHorizontal: 15,
marginTop: -36,
},
photoConsentLabel: {
backgroundColor: Colors.White,
padding: 0,
borderWidth: 0,
},
tabIndicator: {
backgroundColor: Colors.Purple,
@@ -216,55 +421,134 @@ export const PurpleTheme = {
detailsText: {
fontWeight: 'bold',
fontSize: 15,
fontFamily: 'Poppins_700Bold',
fontFamily: 'Inter_700Bold',
},
getId: {
justifyContent: 'center',
alignItems: 'center',
marginTop: 10,
marginVertical: 6,
},
placeholder: {
fontFamily: 'Poppins_400Regular',
fontFamily: 'Inter_600SemiBold',
},
hrLine: {
borderBottomColor: 'black',
borderBottomWidth: 1,
marginTop: 10,
},
}),
QrCodeStyles: StyleSheet.create({
magnifierZoom: {
backgroundColor: Colors.White,
width: 30,
height: 30,
alignItems: 'center',
padding: 5,
borderTopLeftRadius: 11,
elevation: 4,
},
expandedQrCode: {
backgroundColor: Colors.White,
width: 350,
borderRadius: 21,
},
QrCodeHeader: {
backgroundColor: Colors.White,
borderTopLeftRadius: 21,
borderTopRightRadius: 21,
justifyContent: 'space-between',
fontFamily: 'Inter_700Bold',
paddingBottom: 10,
paddingRight: 15,
paddingLeft: 130,
elevation: 2,
},
warningText: {
color: Colors.Red,
fontSize: 18,
},
}),
PinInputStyle: StyleSheet.create({
input: {
borderBottomWidth: 1,
borderBottomWidth: 3,
borderColor: Colors.Grey,
color: Colors.Black,
flex: 1,
fontFamily: 'Poppins_600SemiBold',
fontSize: 18,
fontWeight: '600',
fontSize: 33,
fontFamily: 'Inter_600SemiBold',
height: 40,
lineHeight: 28,
margin: 8,
textAlign: 'center',
},
onEnteringPin: {
borderBottomWidth: 3,
borderColor: Colors.Purple,
color: Colors.Black,
flex: 1,
fontFamily: 'Inter_700Bold',
fontSize: 29,
height: 60,
margin: 8,
textAlign: 'center',
},
}),
TextStyles: StyleSheet.create({
base: {
header: {
color: Colors.Black,
fontFamily: 'Inter_700Bold',
fontSize: 18,
lineHeight: 22,
paddingTop: 4,
},
retrieveIdLabel: {
color: Colors.ShadeOfGrey,
fontFamily: 'Inter_600SemiBold',
lineHeight: 18,
},
helpDetailes: {
margin: 5,
color: Colors.Gray44,
fontFamily: 'Inter_600SemiBold',
},
aboutDetailes: {
color: Colors.Black,
fontSize: 18,
lineHeight: 28,
margin: 7,
lineHeight: 18,
},
error: {
color: Colors.Red,
fontFamily: 'Inter_600SemiBold',
fontSize: 12,
},
base: {
color: Colors.Black,
fontSize: 16,
lineHeight: 18,
},
regular: {
fontFamily: 'Poppins_400Regular',
fontFamily: 'Inter_400Regular',
fontSize: 14,
},
semibold: {
fontFamily: 'Poppins_600SemiBold',
fontFamily: 'Inter_600SemiBold',
fontSize: 15,
},
bold: {
fontFamily: 'Poppins_700Bold',
fontFamily: 'Inter_700Bold',
},
small: {
fontSize: 14,
fontSize: 13,
lineHeight: 21,
},
smaller: {
fontSize: 12,
fontSize: 11,
lineHeight: 18,
},
large: {
fontSize: 18,
},
}),
VcItemStyles: StyleSheet.create({
title: {
@@ -294,7 +578,7 @@ export const PurpleTheme = {
}),
ToastItemStyles: StyleSheet.create({
toastContainer: {
backgroundColor: Colors.Orange,
backgroundColor: Colors.Purple,
position: 'absolute',
alignSelf: 'center',
top: 80,
@@ -319,22 +603,35 @@ export const PurpleTheme = {
borderColor: Colors.Purple,
},
container: {
minHeight: 48,
height: 45,
flexDirection: 'row',
},
disabled: {
opacity: 0.5,
backgroundColor: Colors.Grey,
},
addId: {
backgroundColor: Colors.Purple,
},
gradient: {
borderRadius: 9,
width: Dimensions.get('window').width * 0.72,
alignSelf: 'center',
margin: 4,
},
float: {
borderRadius: 9,
alignSelf: 'center',
fontSize: 10,
elevation: 5,
position: 'absolute',
bottom: 24,
},
clearAddIdBtnBg: {
backgroundColor: Colors.Transparent,
},
radius: {
flex: 1,
borderRadius: 10,
backgroundColor: Colors.Orange,
backgroundColor: Colors.Purple,
},
}),
OIDCAuthStyles: StyleSheet.create({
@@ -348,14 +645,35 @@ export const PurpleTheme = {
padding: 32,
},
}),
QRCodeOverlay: StyleSheet.create({
header: {},
}),
SelectVcOverlayStyles: StyleSheet.create({
overlay: {
elevation: 5,
backgroundColor: Colors.White,
padding: 0,
},
consentCheckContainer: {
backgroundColor: Colors.White,
borderWidth: 0,
marginTop: -15,
fontFamily: 'Inter_600SemiBold',
padding: 0,
},
timeoutHintContainer: {
backgroundColor: Colors.TimeoutHintBoxColor,
margin: 21,
paddingHorizontal: 14,
paddingVertical: 12,
borderRadius: 12,
},
sharedSuccessfully: {
flex: 1,
backgroundColor: Colors.White,
},
}),
CreditsStyles: StyleSheet.create({
AppMetaDataStyles: StyleSheet.create({
buttonContainer: {
position: 'absolute',
left: 0,
@@ -365,9 +683,23 @@ export const PurpleTheme = {
flex: 1,
width: Dimensions.get('screen').width,
},
markdownView: {
contentView: {
flex: 1,
padding: 20,
},
header: {
fontSize: 20,
fontWeight: 'normal',
color: 'rgb(28,28,30)',
},
}),
FooterStyles: StyleSheet.create({
bottom: {
justifyContent: 'flex-end',
backgroundColor: Colors.Grey6,
borderRadius: 15,
padding: 10,
},
}),
ModalStyles: StyleSheet.create({
modal: {
@@ -408,11 +740,51 @@ export const PurpleTheme = {
height: Dimensions.get('screen').height,
},
}),
KebabPopUpStyles: StyleSheet.create({
kebabPopUp: {
borderTopLeftRadius: 15,
borderTopRightRadius: 15,
width: Dimensions.get('screen').width,
marginTop: Dimensions.get('screen').height * 0.55,
},
kebabHeaderStyle: {
justifyContent: 'space-between',
fontFamily: 'Inter_700Bold',
paddingRight: 15,
paddingLeft: 130,
paddingTop: 18,
},
}),
MessageOverlayStyles: StyleSheet.create({
overlay: {
elevation: 5,
backgroundColor: Colors.White,
padding: 5,
borderRadius: 10,
},
buttonContainer: {
justifyContent: 'center',
marginBottom: 75,
},
popupOverLay: {
height: 150,
backgroundColor: Colors.White,
},
button: {
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
},
halfButton: {
borderRadius: 8,
margin: '0.5%',
},
}),
BindingVcWarningOverlay: StyleSheet.create({
overlay: {
elevation: 5,
backgroundColor: Colors.White,
padding: 0,
borderRadius: 15,
},
button: {
borderTopLeftRadius: 0,
@@ -488,7 +860,7 @@ export const PurpleTheme = {
position: 'absolute',
top: 32,
right: 0,
color: Colors.Orange,
color: Colors.Purple,
},
}),
MessageStyles: StyleSheet.create({
@@ -545,8 +917,8 @@ export const PurpleTheme = {
overlay: {
padding: 24,
bottom: 86,
backgroundColor: Colors.Transparent,
shadowColor: Colors.Transparent,
backgroundColor: 'transparent',
shadowColor: 'transparent',
},
slide: {
width: '100%',
@@ -563,7 +935,7 @@ export const PurpleTheme = {
sliderTitle: {
color: Colors.White,
marginBottom: 20,
fontFamily: 'Poppins_700Bold',
fontFamily: 'Inter_700Bold',
},
text: {
color: Colors.White,
@@ -583,17 +955,12 @@ export const PurpleTheme = {
top: 40,
zIndex: 1,
},
}),
BindingVcWarningOverlay: StyleSheet.create({
overlay: {
elevation: 5,
backgroundColor: Colors.White,
padding: 0,
borderRadius: 15,
},
button: {
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
bottomContainer: {
padding: 20,
borderTopLeftRadius: 30,
borderTopRightRadius: 30,
marginTop: -185,
paddingBottom: 100,
},
}),
claimsContainer: StyleSheet.create({
@@ -601,13 +968,31 @@ export const PurpleTheme = {
backgroundColor: Colors.Transparent,
},
}),
OpenCard: require('../../../purpleAassets/bg_cart_one.png'),
CloseCard: require('../../../purpleAassets/cart_unsel.png'),
ProfileIcon: require('../../../purpleAassets/profile_icon_unsel.png'),
MosipLogo: require('../../../purpleAassets/logo.png'),
OpenCard: '',
CloseCard: '',
ProfileIcon: require('../../../purpleAssets/profile_icon.png'),
MosipSplashLogo: require('../../../assets/icon.png'),
MosipLogo: require('../../../assets/mosip-logo.png'),
CameraFlipIcon: require('../../../assets/camera-flip-icon.png'),
DomainWarningLogo: require('../../../assets/domain-warning.png'),
WarningLogo: require('../../../assets/warningLogo.png'),
OtpLogo: require('../../../assets/otp-mobile-logo.png'),
OtpLogo: require('../../../purpleAssets/otp-mobile-logo.png'),
SuccessLogo: require('../../../assets/success-logo.png'),
ReceiveCardIcon: require('../../../assets/receive-card-icon.png'),
ReceivedCardsIcon: require('../../../assets/received-cards-icon.png'),
DigitalIdentityLogo: require('../../../assets/digital-identity-icon.png'),
InjiLogoWhite: require('../../../assets/inji-logo-white.png'),
InjiProgressingLogo: require('../../../assets/progressing-logo.png'),
LockIcon: require('../../../assets/lock-icon.png'),
InjiHomeLogo: require('../../../assets/inji-home-logo.png'),
MagnifierZoom: require('../../../assets/magnifier-zoom.png'),
HelpIcon: require('../../../assets/help-icon.png'),
sharingIntro: require('../../../assets/Secure-Sharing.png'),
walletIntro: require('../../../assets/intro-wallet-binding.png'),
IntroScanner: require('../../../assets/intro-scanner.png'),
injiSmallLogo: require('../../../assets/inji_small_logo.png'),
protectPrivacy: require('../../../assets/phone_mockup_1.png'),
elevation(level: ElevationLevel): ViewStyle {
// https://ethercreative.github.io/react-native-shadow-generator/

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