Files
inji-wallet/machines/bleShare/scan/scanMachine.ts
dhivya0413 769ed54bcf [INJI-195] Upgrade React native version to 0.71.8 (#824)
* chore(INJI-195): upgrade react native version and dependencies

* chore(INJI-195): upgrade expo version and dependencies

* chore(INJI-195): modify associated files on version upgrade

* chore(INJI-295): fixed react native flipper and patch packages

* chore(INJI-195): fix for expo prebuild

* chore(INJI-195): expo linked to android/ios projects

* chore(INJI-195): update metro config

* chore(INJI-195): fix ios build with mmkv storage patch

* chore(INJI-195): gradle version modified

* chore(INJI-195): fixed rn version 0.71.8 due to mmkv library issue

* chore(INJI-195): removed files in android

* chore(INJI-195): fix 0.71.8 for iOS project through pods with expo linking

* chore(INJI-195): fix for custom fonts added through pods due to rn linkage

* chore(INJI-195): fix for removing assets.car generated from pods

* Modify Node version in pipeline (#806)

* chore(INJI-195): fix for android splash screen not shown up

* chore(INJI-195): upgraded to node 18 in pipeline

* chore(INJI-195): add the pod install twice to remove duplicates via script workaround (#807)

Signed-off-by: dhivya0413 <120356578+dhivya0413@users.noreply.github.com>

* INJI-195 Set the signing team for iOS  (#810)

* chore(INJI-195): add the pod install twice to remove duplicates via script workaround

* chore(INJI-195): set the signing team for ios build

---------

Signed-off-by: dhivya0413 <120356578+dhivya0413@users.noreply.github.com>

* Modify github runner to self hosted runner (#811)

* chore(INJI-195): add the pod install twice to remove duplicates via script workaround

* chore(INJI-195): set the signing team for ios build

* chore(INJI-195): set the self hosted runner for ios build

---------

Signed-off-by: dhivya0413 <120356578+dhivya0413@users.noreply.github.com>

* Modify self hosted runner to github hosted runner  (#813)

* chore(INJI-195): add the pod install twice to remove duplicates via script workaround

* chore(INJI-195): set the signing team for ios build

* chore(INJI-195): set the self hosted runner for ios build

* chore(INJI-195): modify the self hosted to github hosted runner

---------

Signed-off-by: dhivya0413 <120356578+dhivya0413@users.noreply.github.com>

* chore(INJI-195): modify the self hosted to github hosted runner

* chore(INJI-195): set the code signing identity for ios build

* chore(INJI-195): assigned app icon files to asset

---------

Signed-off-by: dhivya0413 <120356578+dhivya0413@users.noreply.github.com>
Signed-off-by: Swati Goel <meet2swati@gmail.com>
Co-authored-by: Swati Goel <meet2swati@gmail.com>
2023-09-13 11:53:59 +05:30

1111 lines
38 KiB
TypeScript

/* eslint-disable sonarjs/no-duplicate-string */
import tuvali from 'react-native-tuvali';
import BluetoothStateManager from 'react-native-bluetooth-state-manager';
import {
ActorRefFrom,
assign,
DoneInvokeEvent,
EventFrom,
send,
spawn,
StateFrom,
} from 'xstate';
import {createModel} from 'xstate/lib/model';
import {EmitterSubscription, Linking, Platform} from 'react-native';
import {DeviceInfo} from '../../../components/DeviceInfoList';
import {getDeviceNameSync} from 'react-native-device-info';
import {VC, VerifiablePresentation} from '../../../types/vc';
import {AppServices} from '../../../shared/GlobalContext';
import {ActivityLogEvents, ActivityLogType} from '../../activityLog';
import {MY_LOGIN_STORE_KEY, VC_ITEM_STORE_KEY} from '../../../shared/constants';
import {subscribe} from '../../../shared/openIdBLE/walletEventHandler';
import {
check,
checkMultiple,
PermissionStatus,
PERMISSIONS,
requestMultiple,
RESULTS,
} from 'react-native-permissions';
import {
checkLocationPermissionStatus,
requestLocationPermission,
} from '../../../shared/location';
import {CameraCapturedPicture} from 'expo-camera';
import {log} from 'xstate/lib/actions';
import {createQrLoginMachine, qrLoginMachine} from '../../QrLoginMachine';
import {StoreEvents} from '../../store';
import {WalletDataEvent} from 'react-native-tuvali/lib/typescript/types/events';
import {BLEError} from '../types';
import Storage from '../../../shared/storage';
import {logState} from '../../app';
const {wallet, EventTypes, VerificationStatus} = tuvali;
const model = createModel(
{
serviceRefs: {} as AppServices,
senderInfo: {} as DeviceInfo,
receiverInfo: {} as DeviceInfo,
selectedVc: {} as VC,
bleError: {} as BLEError,
stayInProgress: false,
createdVp: null as VC,
reason: '',
loggers: [] as EmitterSubscription[],
vcName: '',
verificationImage: {} as CameraCapturedPicture,
openId4VpUri: '',
shareLogType: '' as ActivityLogType,
QrLoginRef: {} as ActorRefFrom<typeof qrLoginMachine>,
linkCode: '',
readyForBluetoothStateCheck: false,
},
{
events: {
SELECT_VC: (vc: VC) => ({vc}),
SCAN: (params: string) => ({params}),
ACCEPT_REQUEST: () => ({}),
VERIFY_AND_ACCEPT_REQUEST: () => ({}),
VC_ACCEPTED: () => ({}),
VC_REJECTED: () => ({}),
VC_SENT: () => ({}),
CANCEL: () => ({}),
STAY_IN_PROGRESS: () => ({}),
RETRY: () => ({}),
DISMISS: () => ({}),
CONNECTED: () => ({}),
DISCONNECT: () => ({}),
BLE_ERROR: (bleError: BLEError) => ({bleError}),
CONNECTION_DESTROYED: () => ({}),
SCREEN_BLUR: () => ({}),
SCREEN_FOCUS: () => ({}),
BLUETOOTH_PERMISSION_ENABLED: () => ({}),
BLUETOOTH_PERMISSION_DENIED: () => ({}),
BLUETOOTH_STATE_ENABLED: () => ({}),
BLUETOOTH_STATE_DISABLED: () => ({}),
NEARBY_ENABLED: () => ({}),
NEARBY_DISABLED: () => ({}),
GOTO_SETTINGS: () => ({}),
START_PERMISSION_CHECK: () => ({}),
UPDATE_REASON: (reason: string) => ({reason}),
LOCATION_ENABLED: () => ({}),
LOCATION_DISABLED: () => ({}),
LOCATION_REQUEST: () => ({}),
UPDATE_VC_NAME: (vcName: string) => ({vcName}),
STORE_RESPONSE: (response: any) => ({response}),
APP_ACTIVE: () => ({}),
FACE_VALID: () => ({}),
FACE_INVALID: () => ({}),
RETRY_VERIFICATION: () => ({}),
VP_CREATED: (vp: VerifiablePresentation) => ({vp}),
TOGGLE_USER_CONSENT: () => ({}),
RESET: () => ({}),
},
},
);
const QR_LOGIN_REF_ID = 'QrLogin';
export const ScanEvents = model.events;
export const scanMachine =
/** @xstate-layout N4IgpgJg5mDOIC5SwMYEMB2BiAygYQCUBRIgOQH0AhAGQFUCBtABgF1FQAHAe1gEsAXXlwzsQAD0QAWAEwAaEAE9EARgCsygHSSAnLu1N1ypkwBs06QF8L81JlyESFAGIB5PLRzM2SENz6DhUQkEGXklBFVVJg0zQ20TJn0AdmlVKxt0bBoiciICAhdGVlE-ASERH2CADjlFRCqTJI1lVJbzYyYUqvSQWwwNFAALMBQAaxx+LgAnNBgsCGEwDV4MADcuUaW+geGxienZsAQV9fQAjC8vEp4ywMrEKJMNJKYqgGYTN+k3qKrtWvCqhMTzeSSiYMkb0kyn+SR62yGI3GkxmcwWGCWJw2W0yOyR+1RRyxZ3Kl2U3k4N3OQQepmerw+Xx+r3+YVpTSqSR0qiSMOUkhMaWsvVxsH4aCm-AACmApgBbXiwPjCPC7Ua4AAqAEECBryFK8gBZACSOBwxpcFDwAAkiHgANJXHylan3BBGeIaJjQpJgzoyVImNkhfkaPS6N5GaTGQUmeGi8WSmXyxXKjCqpGanV6g0EE1mi1W20Ohjk67+co091GJ7e5S+8EBoHBgWqLR6Ixg741OHC7ZiiXS2UKpXlDNjLO6-VG03my3kG12x3SCm+KmVt0tbSSL1mBJffTaA-B9RtvQ1KqSJjSMFVbp93GIsakMASgBGCgAImBVrwUHBkxHNM8TGFYoCwUgiB1SgAE1clILVsk-J1KQrO5QGCaEQVUaFtBaNRpB0RoT0kGpmkkHkrxaAxtHjTAQNGF93y-H8-wA4dU3KBiwIgqCCFg8hP1NRDqCIZDimddd0PEKRlGw3D8NUQj4iSEjjHI1R3m0TTVChe8Mnop9GNfKYP2-X9-1gQDOOEDQpjAABHABXOBBAwcDIOguCyBEsSULXNCKgwxBdDbKprzUMFlDeH5BRPRkND+HkkjMUwjGUOj+iMpjTJYiz2JTUdbPs5zXJ4zz+LgoScF88TVxdDdgoQULEoinl1BioFVBI0M3m0pJtySPrlH0kVDLVHKzNYyzrKK-owAwNA3wAG0gLB-Ia6Tgha8LpEijrYu6uoIlIt4Ygogb9F01R-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-jLTLQ8QK5-BK41h7KRhq7VpKRnTXivBRCw5Z5+7bCgEz544QYh4k45p5py6wHbIIH4YHKcjVrZ684GCpCnQqQCaT7l5i6EGS4k4274qkAP6pbIYhCMKozxD8wzLMLhq-p0joFmAwhlaIKH5gGV4EFkah54Cx5EDx6cz8EabViJzPBlrFqYH-CGqwyNBoHRjAgdyaSxAKH4ELQqFcFqHEAABScuWhdG7eOWbYvInIhh14xhd2Hw9BMh+gPYUIth7BDhgOwOoOCumuPhzCic-h5gKBKQXo0Y7wQWPInIrOERxu9hRB+2M2cAsA82XaSwS2soA6OA1oOoxoRK5AG2U6s686rBge+RHBteRRh2JR9OukXG3m4I4IrmtB-MzwLwOEHwZh14OBbRR+HRDhX2vAP2f2AOi+y+cRkQ+hvh-GyRJhqQMCSkiQck4UzCYIeRShBRnBs+GAaxMRK+2hnh9Y3hBhuxRSKR+6Zgga0YXYRgciwBBu8xleaAKA-4HAPsOsjms4duAozwscmkrw58bwOGQ06RuyVhDQCsIBbBE29kAAVu2mtFCWaOQfAbslQcgb+kEUcSEXIbMfRHgZ9qsLKLwD2goGBMaBAAtIIPwAoFgE4B8jkN8lqNQMaM1rojoZfHdroEjgYKcTFAKA0oCYoRoMyS2myRyVybcQIHyQKSDuQPUcKaKeKfrAIZ3hnj3tnkeK7t-onCWnuOFGYD2MFpgu0ZXmqayeye5JydyTqdEUvrEY-m6Oad3lnn3jaSoMep7oFrFHtPyBPgyTiZXigPZN7CHhwLJhiPJusJsFXnMSqSma+GbhwMcApiSMIJcEGeQkfPupbBMjCAiVljeBcQMKmcWVgLKNNlMBoBwMtN7D2tMHKHmYmW6a2UWemaWacGmRWawPTqyv1tIrIfENuMiW7uoLWHzvzHDOhgmf0IyRNicGgNARAD6dqbydLjgE5lWTDKzhEqkMYH1KVgYKud-gkK3KehSjUoci2YeceaeTyXycQBqAQHBGHhHsaEDl8nTteXzKjDEPeSVlvrpFzhVolC0KlP8OPkUndNeqgLtkSaaFeY8X6uYB7jdFeF+QcvELQWoOMQYF8Q+TUPSf0IMJgBANAe5A9GAEQFMF2ReURR4X6leDuMCDxkiSxvzMGHxolDhLeZgXIueqPL+bwJCYRdCTBShqRCWtCNuhWvhNWqCAjHtAojeMjp-jfKBO5JJuWRgDgLKPlOXM6qXE5KUdQG4EvGDD-O4S1gIVCBRLzv1OfNIflggIZW2DWq0JUpEAmlVtPFZVwDZXZVMA5X7FAE5Y-C5VgG5ZBWDFkiQhpf6ryIlEFmWgYFyLEBUhKs8I0HGSNKzl8C6aldZdObZfZWxJgqVK-O5BqFwEQItPwlle5VBRQF5SaY3FWN2OdkYEgTFOfJiRvqFbpGdNLPWHspeN8BZaMGBM1ecElQ5c-GVN1b1f1atINTlfOHld5RKe3redEFeFQvUq2LyJVVQpYgYDGtCFCHYiNnFVADteUHte1VqZilgJqjyjqiloJRNduLWDGDhJyMnD+qMtCNEOoCkK8OjENDYT9akvFYlW1f+BoMDWtNlR5fOInsnnwVDVvN8G2Map0IXk6QtVCEAojFECzdItruesKBgFwFyfAD4H0NTU1AALRI2IAi1njhjS26CRJ3QrAgmCDMnC03mHqfA4SJCfB1Vwxc6s6bodCmAURfqJy3Q417AoiHAq18yESJRCypCXI5zvAMpNCxDRSRCGBKQxW4GuQtqZo4CsUtqcFW0qAXwmo1BFI57ox87i0RCCiSzMI1VfA56l79iJhDiFRpjjijDB0IBoxGLGGWlyLWxPCcgyp-qtCVYcJIiTRKozSqrSTjVbwx17znaRKJx4yciKWxXV0mRTT5QqoZ1cSpU500HHS6R4RMqs5vnaSbU12Op12D3FRYKHVQA50yGSyJxBZiLTXRTxS+hMJyxmDFop2PgTS9210FRAQawnWQA51dBhi0pXiaSs4vBf4PCkQ+YURyzqDxCJCbVqLkzOo50jQ-AdZmy76QrWwNCnxHixymX-0ooUyL0qJbXuTAN3hnQgrgMWyQNj0jQIwtDs0NlfC6QIO3L13zQ30QA52tDRAdCO5DQRg3iYwBK7zoxMXq2NWLLqI8Ij0x0yABI4yJxfCfB8ZkOJLcJtLD0+WSmghpw3hGLlrQhZHMVjwtIYIHVdWr0yPt7hR9xSqvZJ1mEtgywiUXTlozJHjiNLJtIrLAMNUxBSxchfrXiXZpyQinz+aaSgJxLNISOtI+x8MVLAgmrir-B1UNmQi+PcNJJlzSPXVr5yNhLMKmqXhAi97WHROoL+MaJUNr1kTCHAjejnz1h3gVJghTVf2RhHi2IAk2o3L+POrqo0OoyaBwE0Q3R3itjiyYOyUywyzywukNPFzNM6Nr7+YyXSyJC+HM0Kla76AM0Mh1PbB2oOrTSX02QuoLSYrAP4QxBY3RXfATELVSqYPaQLMWogMsGGSLpQYfYN25LPqfW87GLvH8Y1mjLoyaAdy8gLMZGvAumkY3qwYPMbIMa8gmrLn4PRTIy0Epy7jmA3gZamNd2CZhZ2aqY506VM46Y4z6YSHhCEQKLGaRj1W6BdOqOha2YqYrDLDsVgBYtEuOPWEY3fBHg4Y22fBfpcgtyO13RUvhaqZV5RZoNjNVimwcgC4YH74x2FYRImblpfMyz8s2aCu0uLS-hQBplHXWiKgojhAJPisYZhgywVqRBHjbiGYmrRTG0UTRiNojYfaiuGtuhYSt2+jwzRXvC020FbGh1FIyGZGNVOuV4rBTbFFKiMv1j7O+j7z1U+u-p-AxCyIBYDKkOOtjZ7bfa-b-ZYsfrPD-CDw55MV3ZJuPZ3h7wfDpvKkV530x2o7RBGACOyFXYn0jlAmz7B5B1ituhKRtjTNHhAJggevKB56JDzNQiERfnvAtkQH44cXaMuvkIuMXZ3h7KYWJwoHmBom0KnE6CV35l2GdHE450-BnSFOiG6DiE4YT03hDsvYnGXgtnKGFFhsHZHZYtCFwGXsdw6AEsFZlJ0WQg4RFOAHPtXFdHZurHAPGCaAyyIotzcgDSjHeHv5SygeqP7mXHHsoA3H8A539HaaEOdDDHny0GXiym4bfrwwxQtkglgkQn2M8iJRRQos1AKKjufFRBonq320-DNnYmjn4mEnUM9vLvEuipf0jR0rNhUkF4WF7ShHAYulYe2poCavatQA9XWhcA-bAO9wxDAjCOQg6AvkFZmHBEpy3ixAqdJmqkskanelakAUwcKMWMhHFLMNu5DSQt7gsjwzhQyCzttnpnANeff5RB0OweQgHHxB9Q-lrBHkqX-k6lhcLVlrLXr7HFAh1Jtv9C4WjYrq31ic3k0RaCmAgfoH6BmewyH2UfoU+jhGNKsUQYLtcU8Vdk0NFLtjhhAJt0jRSWGJSxyXXgKXy2JfHlYtySaD3kpBxkpBQL7rSyOORjTNGcH5m2oN-UJUtWA3-g0Ps7NDFNPYpxmz-uhWRjeHs3DSNCMOoun243bf43JXtWpXpXewuUHeehNtmAdyncJDnfoeIzywPlZadCbXbU7e7UE1LCaM4I9V9VawMsld8w3RnS2IY1BY3hBaVX+XMhAiiGTvXMoOQ-PcOXE2idLv6L8jLW7JBWHOA-GJvUgdcjRQChWBWBAA */
model.createMachine(
{
predictableActionArguments: true,
preserveActionOrder: true,
tsTypes: {} as import('./scanMachine.typegen').Typegen0,
schema: {
context: model.initialContext,
events: {} as EventFrom<typeof model>,
services: {} as {
createVp: {
data: VC;
};
},
},
invoke: {
src: 'monitorConnection',
},
id: 'scan',
initial: 'inactive',
on: {
SCREEN_BLUR: {
target: '#scan.disconnectDevice',
},
SCREEN_FOCUS: {
target: '.checkStorage',
},
BLE_ERROR: {
target: '.handlingBleError',
actions: 'setBleError',
},
RESET: {
target: '.checkStorage',
},
DISMISS: {
target: '#scan.reviewing.navigatingToHome',
},
},
states: {
inactive: {
entry: 'removeLoggers',
},
disconnectDevice: {
invoke: {
src: 'disconnect',
},
on: {
DISCONNECT: {
target: '#scan.inactive',
},
},
},
checkStorage: {
invoke: {
src: 'checkStorageAvailability',
onDone: [
{
cond: 'isMinimumStorageRequiredForAuditEntryReached',
target: 'restrictSharingVc',
},
{
target: 'startPermissionCheck',
},
],
},
},
restrictSharingVc: {},
startPermissionCheck: {
on: {
START_PERMISSION_CHECK: [
{
cond: 'uptoAndroid11',
target: '#scan.checkBluetoothPermission',
},
{
cond: 'isIOS',
target: '#scan.checkBluetoothPermission',
},
{
target: '#scan.checkNearbyDevicesPermission',
},
],
},
},
checkNearbyDevicesPermission: {
initial: 'checking',
states: {
checking: {
invoke: {
src: 'checkNearByDevicesPermission',
},
on: {
NEARBY_ENABLED: {
target: 'enabled',
},
NEARBY_DISABLED: {
target: 'requesting',
},
},
},
requesting: {
invoke: {
src: 'requestNearByDevicesPermission',
},
on: {
NEARBY_ENABLED: {
target: '#scan.checkBluetoothPermission',
},
NEARBY_DISABLED: {
target: '#scan.nearByDevicesPermissionDenied',
},
},
},
enabled: {
always: {
target: '#scan.checkBluetoothPermission',
},
},
},
},
checkBluetoothPermission: {
initial: 'checking',
states: {
checking: {
invoke: {
src: 'checkBluetoothPermission',
},
on: {
BLUETOOTH_PERMISSION_ENABLED: {
actions: 'setReadyForBluetoothStateCheck',
target: 'enabled',
},
BLUETOOTH_PERMISSION_DENIED: {
target: '#scan.bluetoothPermissionDenied',
},
},
},
enabled: {
always: {
target: '#scan.checkBluetoothState',
},
},
},
},
checkBluetoothState: {
initial: 'checking',
states: {
checking: {
invoke: {
src: 'checkBluetoothState',
},
on: {
BLUETOOTH_STATE_ENABLED: {
target: 'enabled',
},
BLUETOOTH_STATE_DISABLED: [
{
cond: 'isIOS',
target: '#scan.checkBluetoothPermission',
},
{
target: 'requesting',
},
],
},
},
requesting: {
invoke: {
src: 'requestBluetooth',
},
on: {
BLUETOOTH_STATE_ENABLED: {
target: 'enabled',
},
BLUETOOTH_STATE_DISABLED: {
target: '#scan.bluetoothDenied',
},
},
},
enabled: {
always: [
{
cond: 'uptoAndroid11',
target: '#scan.checkingLocationService',
},
{
target: '#scan.clearingConnection',
},
],
},
},
},
recheckBluetoothState: {
initial: 'checking',
states: {
checking: {
invoke: {
src: 'checkBluetoothState',
},
on: {
BLUETOOTH_STATE_ENABLED: {
target: 'enabled',
},
BLUETOOTH_STATE_DISABLED: {
target: '#scan.bluetoothDenied',
},
},
},
enabled: {
always: [
{
cond: 'uptoAndroid11',
target: '#scan.checkingLocationService',
},
{
target: '#scan.clearingConnection',
},
],
},
},
},
bluetoothPermissionDenied: {
on: {
APP_ACTIVE: '#scan.checkBluetoothState',
GOTO_SETTINGS: {
actions: 'openBluetoothSettings',
},
},
},
bluetoothDenied: {
on: {
APP_ACTIVE: '#scan.recheckBluetoothState',
},
},
nearByDevicesPermissionDenied: {
on: {
APP_ACTIVE: '#scan.checkNearbyDevicesPermission',
GOTO_SETTINGS: {
actions: 'openAppPermission',
},
},
},
clearingConnection: {
invoke: {
src: 'disconnect',
},
on: {
DISCONNECT: {
target: '#scan.findingConnection',
actions: [],
internal: false,
},
},
after: {
DESTROY_TIMEOUT: {
target: '#scan.findingConnection',
actions: [],
internal: false,
},
},
},
findingConnection: {
entry: [
'removeLoggers',
'registerLoggers',
'clearUri',
'setChildRef',
],
on: {
SCAN: [
{
target: 'connecting',
cond: 'isOpenIdQr',
actions: 'setUri',
},
{
target: 'showQrLogin',
cond: 'isQrLogin',
actions: 'setLinkCode',
},
{
target: 'invalid',
},
],
},
},
showQrLogin: {
invoke: {
id: 'QrLogin',
src: qrLoginMachine,
onDone: '.storing',
},
on: {
DISMISS: 'findingConnection',
},
initial: 'idle',
states: {
idle: {},
storing: {
entry: ['storeLoginItem'],
on: {
STORE_RESPONSE: {
target: 'navigatingToHistory',
actions: ['storingActivityLog'],
},
},
},
navigatingToHistory: {},
},
entry: 'sendScanData',
},
connecting: {
invoke: {
src: 'startConnection',
},
initial: 'inProgress',
states: {
inProgress: {
after: {
CONNECTION_TIMEOUT: {
target: '#scan.connecting.timeout',
actions: 'setStayInProgress',
internal: false,
},
},
},
timeout: {
on: {
STAY_IN_PROGRESS: {
actions: 'setStayInProgress',
},
CANCEL: {
target: '#scan.reviewing.cancelling',
actions: 'setCloseTimeoutHint',
},
RETRY: {
target: '#scan.reviewing.cancelling',
actions: 'setCloseTimeoutHint',
},
},
},
},
on: {
CONNECTED: {
target: 'reviewing',
actions: ['setSenderInfo', 'setReceiverInfo'],
},
},
},
reviewing: {
entry: ['resetShouldVerifyPresence'],
exit: ['clearReason', 'clearCreatedVp'],
initial: 'selectingVc',
states: {
selectingVc: {
on: {
UPDATE_REASON: {
actions: 'setReason',
},
DISCONNECT: {
target: '#scan.disconnected',
},
SELECT_VC: {
actions: 'setSelectedVc',
},
VERIFY_AND_ACCEPT_REQUEST: {
target: 'verifyingIdentity',
},
ACCEPT_REQUEST: {
target: 'sendingVc',
actions: 'setShareLogTypeUnverified',
},
CANCEL: {
target: 'cancelling',
},
TOGGLE_USER_CONSENT: {
actions: 'toggleShouldVerifyPresence',
},
},
},
cancelling: {
always: {
target: '#scan.clearingConnection',
},
},
sendingVc: {
invoke: {
src: 'sendVc',
},
initial: 'inProgress',
states: {
inProgress: {
after: {
SHARING_TIMEOUT: {
target: '#scan.reviewing.sendingVc.timeout',
actions: [],
internal: false,
},
},
},
timeout: {
on: {
CANCEL: {
target: '#scan.reviewing.cancelling',
},
},
},
sent: {
description:
'VC data has been shared and the receiver should now be viewing it',
on: {
CANCEL: {
target: '#scan.reviewing.cancelling',
},
},
},
},
on: {
DISCONNECT: {
target: '#scan.disconnected',
},
VC_SENT: {
target: '.sent',
},
VC_ACCEPTED: {
target: '#scan.reviewing.accepted',
},
VC_REJECTED: {
target: '#scan.reviewing.rejected',
},
CANCEL: {
target: '#scan.reviewing.cancelling',
},
},
},
accepted: {
entry: 'logShared',
on: {
DISMISS: {
target: 'navigatingToHome',
},
},
},
rejected: {
on: {
DISMISS: {
target: '#scan.clearingConnection',
},
},
},
navigatingToHome: {},
verifyingIdentity: {
on: {
FACE_VALID: {
target: 'sendingVc',
actions: 'setShareLogTypeVerified',
},
FACE_INVALID: {
target: 'invalidIdentity',
actions: 'logFailedVerification',
},
CANCEL: {
target: 'selectingVc',
},
},
},
creatingVp: {
invoke: {
src: 'createVp',
onDone: [
{
target: 'sendingVc',
actions: 'setCreatedVp',
},
],
onError: [
{
target: 'selectingVc',
actions: log('Could not create Verifiable Presentation'),
},
],
},
},
invalidIdentity: {
on: {
DISMISS: {
target: 'selectingVc',
},
RETRY_VERIFICATION: {
target: 'verifyingIdentity',
},
},
},
},
},
disconnected: {
on: {
DISMISS: {
target: '#scan.clearingConnection',
},
},
},
handlingBleError: {
on: {
DISMISS: {
target: '#scan.clearingConnection',
},
},
},
invalid: {
on: {
DISMISS: {
target: '#scan.clearingConnection',
},
},
},
checkingLocationService: {
initial: 'checkingPermissionStatus',
states: {
checkingPermissionStatus: {
invoke: {
src: 'checkLocationPermission',
},
on: {
LOCATION_ENABLED: {
target: '#scan.clearingConnection',
},
LOCATION_DISABLED: {
target: 'requestToEnableLocation',
},
},
},
requestToEnableLocation: {
invoke: {
src: 'requestToEnableLocationPermission',
},
on: {
LOCATION_ENABLED: {
target: '#scan.clearingConnection',
},
LOCATION_DISABLED: {
target: 'denied',
},
},
},
denied: {
on: {
APP_ACTIVE: {
target: 'checkingPermissionStatus',
},
LOCATION_REQUEST: {
actions: 'openAppPermission',
},
},
},
},
},
},
},
{
actions: {
setChildRef: assign({
QrLoginRef: context => {
const service = spawn(
createQrLoginMachine(context.serviceRefs),
QR_LOGIN_REF_ID,
);
service.subscribe(logState);
return service;
},
}),
sendScanData: context =>
context.QrLoginRef.send({
type: 'GET',
value: context.linkCode,
}),
openBluetoothSettings: () => {
Platform.OS === 'android'
? BluetoothStateManager.openSettings().catch()
: Linking.openURL('App-Prefs:Bluetooth');
},
openAppPermission: () => Linking.openSettings(),
setUri: model.assign({
openId4VpUri: (_context, event) => event.params,
}),
clearUri: assign({
openId4VpUri: '',
}),
setSenderInfo: assign({
senderInfo: () => {
return {name: 'Wallet', deviceName: 'Wallet', deviceId: ''};
},
}),
setReceiverInfo: assign({
receiverInfo: () => {
return {name: 'Verifier', deviceName: 'Verifier', deviceId: ''};
},
}),
setReadyForBluetoothStateCheck: model.assign({
readyForBluetoothStateCheck: () => true,
}),
setBleError: assign({
bleError: (_context, event) => event.bleError,
}),
setReason: model.assign({
reason: (_context, event) => event.reason,
}),
clearReason: assign({reason: ''}),
setSelectedVc: assign({
selectedVc: (context, event) => {
return {
...event.vc,
shouldVerifyPresence: context.selectedVc.shouldVerifyPresence,
};
},
}),
setCreatedVp: assign({
createdVp: (_context, event) => event.data,
}),
clearCreatedVp: assign({
createdVp: () => null,
}),
registerLoggers: assign({
loggers: () => {
if (__DEV__) {
return [
wallet.handleDataEvents(event => {
console.log(
getDeviceNameSync(),
'<Sender.Event>',
JSON.stringify(event).slice(0, 100),
);
}),
];
} else {
return [];
}
},
}),
removeLoggers: assign({
loggers: ({loggers}) => {
loggers?.forEach(logger => logger.remove());
return [];
},
}),
setShareLogTypeUnverified: model.assign({
shareLogType: 'VC_SHARED',
}),
setShareLogTypeVerified: model.assign({
shareLogType: 'PRESENCE_VERIFIED_AND_VC_SHARED',
}),
logShared: send(
context =>
ActivityLogEvents.LOG_ACTIVITY({
_vcKey: VC_ITEM_STORE_KEY(context.selectedVc),
type: context.selectedVc.shouldVerifyPresence
? 'VC_SHARED_WITH_VERIFICATION_CONSENT'
: context.shareLogType,
timestamp: Date.now(),
deviceName:
context.receiverInfo.name || context.receiverInfo.deviceName,
vcLabel: context.selectedVc.tag || context.selectedVc.id,
}),
{to: context => context.serviceRefs.activityLog},
),
logFailedVerification: send(
context =>
ActivityLogEvents.LOG_ACTIVITY({
_vcKey: VC_ITEM_STORE_KEY(context.selectedVc),
type: 'PRESENCE_VERIFICATION_FAILED',
timestamp: Date.now(),
deviceName:
context.receiverInfo.name || context.receiverInfo.deviceName,
vcLabel: context.selectedVc.tag || context.selectedVc.id,
}),
{to: context => context.serviceRefs.activityLog},
),
toggleShouldVerifyPresence: assign({
selectedVc: context => ({
...context.selectedVc,
shouldVerifyPresence: !context.selectedVc.shouldVerifyPresence,
}),
}),
setLinkCode: assign({
linkCode: (_context, event) =>
event.params.substring(
event.params.indexOf('linkCode=') + 9,
event.params.indexOf('&'),
),
}),
setStayInProgress: assign({
stayInProgress: context => !context.stayInProgress,
}),
setCloseTimeoutHint: assign({
stayInProgress: context => (context.stayInProgress = false),
}),
resetShouldVerifyPresence: assign({
selectedVc: context => ({
...context.selectedVc,
shouldVerifyPresence: false,
}),
}),
storeLoginItem: send(
(_context, event) => {
return StoreEvents.PREPEND(
MY_LOGIN_STORE_KEY,
(event as DoneInvokeEvent<string>).data,
);
},
{to: context => context.serviceRefs.store},
),
storingActivityLog: send(
(_, event) =>
ActivityLogEvents.LOG_ACTIVITY({
_vcKey: '',
type: 'QRLOGIN_SUCCESFULL',
timestamp: Date.now(),
deviceName: '',
vcLabel: String(event.response.selectedVc.id),
}),
{
to: context => context.serviceRefs.activityLog,
},
),
},
services: {
checkBluetoothPermission: () => async callback => {
// wait a bit for animation to finish when app becomes active
await new Promise(resolve => setTimeout(resolve, 250));
try {
// Passing Granted for android since permission status is always granted even if its denied.
let response: PermissionStatus = RESULTS.GRANTED;
if (Platform.OS === 'ios') {
response = await check(PERMISSIONS.IOS.BLUETOOTH_PERIPHERAL);
}
if (response === RESULTS.GRANTED) {
callback(model.events.BLUETOOTH_PERMISSION_ENABLED());
} else {
callback(model.events.BLUETOOTH_PERMISSION_DENIED());
}
} catch (e) {
console.error(e);
}
},
checkBluetoothState: () => callback => {
const subscription = BluetoothStateManager.onStateChange(state => {
if (state === 'PoweredOn') {
callback(model.events.BLUETOOTH_STATE_ENABLED());
} else {
callback(model.events.BLUETOOTH_STATE_DISABLED());
}
}, true);
return () => subscription.remove();
},
requestBluetooth: () => callback => {
BluetoothStateManager.requestToEnable()
.then(() => callback(model.events.BLUETOOTH_STATE_ENABLED()))
.catch(() => callback(model.events.BLUETOOTH_STATE_DISABLED()));
},
requestToEnableLocationPermission: () => callback => {
requestLocationPermission(
() => callback(model.events.LOCATION_ENABLED()),
() => callback(model.events.LOCATION_DISABLED()),
);
},
monitorConnection: () => callback => {
const subscription = wallet.handleDataEvents(event => {
if (event.type === EventTypes.onDisconnected) {
callback({type: 'DISCONNECT'});
}
if (event.type === EventTypes.onError) {
callback({
type: 'BLE_ERROR',
bleError: {message: event.message, code: event.code},
});
console.log('BLE Exception: ' + event.message);
}
});
return () => subscription.remove();
},
checkNearByDevicesPermission: () => callback => {
checkMultiple([
PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
])
.then(response => {
if (
response[PERMISSIONS.ANDROID.BLUETOOTH_ADVERTISE] ===
RESULTS.GRANTED &&
response[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] ===
RESULTS.GRANTED
) {
callback(model.events.NEARBY_ENABLED());
} else {
callback(model.events.NEARBY_DISABLED());
}
})
.catch(err => {
callback(model.events.NEARBY_DISABLED());
});
},
requestNearByDevicesPermission: () => callback => {
requestMultiple([
PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
])
.then(response => {
if (
response[PERMISSIONS.ANDROID.BLUETOOTH_SCAN] ===
RESULTS.GRANTED &&
response[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] ===
RESULTS.GRANTED
) {
callback(model.events.NEARBY_ENABLED());
} else {
callback(model.events.NEARBY_DISABLED());
}
})
.catch(err => {
callback(model.events.NEARBY_DISABLED());
});
},
checkLocationPermission: () => callback => {
checkLocationPermissionStatus(
() => callback(model.events.LOCATION_ENABLED()),
() => callback(model.events.LOCATION_DISABLED()),
);
},
startConnection: context => callback => {
wallet.startConnection(context.openId4VpUri);
const statusCallback = (event: WalletDataEvent) => {
if (event.type === EventTypes.onSecureChannelEstablished) {
callback({type: 'CONNECTED'});
}
};
const subscription = subscribe(statusCallback);
return () => subscription?.remove();
},
sendVc: context => callback => {
const vp = context.createdVp;
const vc = {
...(vp != null ? vp : context.selectedVc),
tag: '',
};
const reason = [];
if (context.reason.trim() !== '') {
reason.push({message: context.reason, timestamp: Date.now()});
}
const statusCallback = (event: WalletDataEvent) => {
if (event.type === EventTypes.onDataSent) {
callback({type: 'VC_SENT'});
} else if (event.type === EventTypes.onVerificationStatusReceived) {
callback({
type:
event.status === VerificationStatus.ACCEPTED
? 'VC_ACCEPTED'
: 'VC_REJECTED',
});
}
};
wallet.sendData(
JSON.stringify({
...vc,
reason,
}),
);
const subscription = subscribe(statusCallback);
return () => subscription?.remove();
},
disconnect: () => () => {
try {
wallet.disconnect();
} catch (e) {
// pass
}
},
createVp: context => async () => {
// const verifiablePresentation = await createVerifiablePresentation(...);
const verifiablePresentation: VerifiablePresentation = {
'@context': [''],
proof: null,
type: 'VerifiablePresentation',
verifiableCredential: [context.selectedVc.verifiableCredential],
};
const vc: VC = {
...context.selectedVc,
verifiableCredential: null,
verifiablePresentation,
};
return Promise.resolve(vc);
},
checkStorageAvailability: () => async () => {
return Promise.resolve(
Storage.isMinimumLimitReached('minStorageRequiredForAuditEntry'),
);
},
},
guards: {
isOpenIdQr: (_context, event) => event.params.includes('OPENID4VP://'),
isQrLogin: (_context, event) => {
let linkCode = '';
try {
linkCode = event.params.substring(
event.params.indexOf('linkCode=') + 9,
event.params.indexOf('&'),
);
return linkCode !== null;
} catch (e) {
return false;
}
},
uptoAndroid11: () => Platform.OS === 'android' && Platform.Version < 31,
isIOS: () => Platform.OS === 'ios',
isMinimumStorageRequiredForAuditEntryReached: (_context, event) =>
Boolean(event.data),
},
delays: {
DESTROY_TIMEOUT: 500,
CONNECTION_TIMEOUT: 5 * 1000,
SHARING_TIMEOUT: 15 * 1000,
},
},
);
type State = StateFrom<typeof scanMachine>;
export function createScanMachine(serviceRefs: AppServices) {
return scanMachine.withContext({
...scanMachine.context,
serviceRefs,
});
}
export function selectIsMinimumStorageRequiredForAuditEntryLimitReached(
state: State,
) {
return state.matches('restrictSharingVc');
}