Files
inji-wallet/machines/app.ts
PoojaBabusing 0af87909c9 Sonar Pipeline tw mosip (#768)
* 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

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

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

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

* 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

* version code for beta build in pipeline

* 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

* 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-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>

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

* Implemented Receive Card & Received Cards options in Settings

* Fixed all the locals

* [Pooja] add quality gate check for critical open bugs in sonar

---------

Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com>
Co-authored-by: vijay151096 <94220135+vijay151096@users.noreply.github.com>
Co-authored-by: Harsh Vardhan <harsh59v@gmail.com>
Co-authored-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com>
Co-authored-by: adityankannan-tw <adityan.kannan@thoughtworks.com>
Co-authored-by: Alka <prasadalka1998@gmail.com>
Co-authored-by: anil_majji <majjianilkumar050@gmail.com>
Co-authored-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com>
2023-08-09 11:55:50 +05:30

448 lines
13 KiB
TypeScript

import NetInfo, { NetInfoStateType } from '@react-native-community/netinfo';
import { AppState, AppStateStatus, Platform } from 'react-native';
import {
getDeviceId,
getDeviceName,
getDeviceNameSync,
} from 'react-native-device-info';
import { EventFrom, spawn, StateFrom, send, assign, AnyState } from 'xstate';
import { createModel } from 'xstate/lib/model';
import { authMachine, createAuthMachine } from './auth';
import { createSettingsMachine, settingsMachine } from './settings';
import { StoreEvents, storeMachine } from './store';
import { createVcMachine, vcMachine } from './vc';
import { createActivityLogMachine, activityLogMachine } from './activityLog';
import {
createRequestMachine,
requestMachine,
} from './bleShare/request/requestMachine';
import { createScanMachine, scanMachine } from './bleShare/scan/scanMachine';
import { createRevokeMachine, revokeVidsMachine } from './revoke';
import { pure, respond } from 'xstate/lib/actions';
import { AppServices } from '../shared/GlobalContext';
import { request } from '../shared/request';
import {
changeCrendetialRegistry,
SETTINGS_STORE_KEY,
} from '../shared/constants';
import { MIMOTO_HOST } from 'react-native-dotenv';
const model = createModel(
{
info: {} as AppInfo,
backendInfo: {} as BackendInfo,
serviceRefs: {} as AppServices,
isReadError: true,
isDecryptError: false,
},
{
events: {
ACTIVE: () => ({}),
INACTIVE: () => ({}),
ERROR: () => ({}),
DECRYPT_ERROR: () => ({}),
DECRYPT_ERROR_DISMISS: () => ({}),
OFFLINE: () => ({}),
ONLINE: (networkType: NetInfoStateType) => ({ networkType }),
REQUEST_DEVICE_INFO: () => ({}),
READY: (data?: unknown) => ({ data }),
APP_INFO_RECEIVED: (info: AppInfo) => ({ info }),
BACKEND_INFO_RECEIVED: (info: BackendInfo) => ({ info }),
STORE_RESPONSE: (response: unknown) => ({ response }),
},
}
);
export const appMachine = model.createMachine(
{
/** @xstate-layout N4IgpgJg5mDOIC5QEMAOqB0BLAdlgLhrPgPYBOYAxAEoCiAggCICaA2gAwC6ioqJsBLCRw8QAD0QBmdgE4MMgIwAmSaoDsADmkb2agDQgAnoiUAWJRnNqZayVvYKArDdMBfVwbSZcBIqQqUtNTUAPLUHNxIIHwC+EIiURIICpJypgBsaqaOSjmOqfmmBsbJjuzyaimaMpJq7BqOOe6e6Nh4hLBgZABuWADGcDQMLBGiMYLCokmZ6RiSSro5NQrsjcUm5pZK1rb2Ti7NIF5tvn0UEGA4ccgANtRgUFjEZIaUAMoAKmG0APp0bwAFEIAOTetFGUXGcUmiUQCgU6VMFW0tVkOXY6XWCCUCjkakqBNMMl0ZjcHiOrR8hFwADMSJR6ACAT8AJLAgBiIT+tAAwrQWQA1WiMCG8fgTBKgJIIjJzdRqRGmDQEjRYnF4gkKLLEtSkw7HKkYC69HB0ygAIXoPIA0rRgYxWRyuXQ+YLhaLouLoZLxCZlI55DjTJJ0jV0vlJGrcRh8VqtUSSaYyS1MBRkBBXnQAIoAVVonx+jFoApZfMdnI9UPiUxM9Rj6URGmJMh0Zg0mKMiEc6Q0GA0GmyPd180kyYpqbA6cMGDpfQArrAGTyPm7K17q7Dkroscre+l2KknAoB8qbPrWmmMzOSPPF2yrSuhWvYhupXDt52EDIbBhGnVJN27CyA2SjnhOU4YDgYD4AA7uQADWlAggAMmy4JcGM64wm+W76J+yhEpYaiOKYGL4jI+4hmBGCXtOUGwQhSHsuyqHAuhkRii+2G+rhWIpD2WwNNsuLdjI7jkjgJAXPAUReJhXE+kkAC0tRYkpZgYEBWnaTpajUVS8kSjW2IyFi6SSJYDS2FqjgaPCMi2fp7R+OQYCGd6xn4mqGKWKimimMoTaOLqTm+J0PT9HA7mvjx5gWeYChJmYEbth2JQLLMwZ1P5gUOSF5IGs5ZyQJc1x3A8Tz4C80XcUk1R9koLbpAiDghgoUZpNImgkSsGhmLUoXUqaJA1YpcJJrM4aVCkShaDI5hFJ+6q+bouz4tIqgaINRpgCadKjcZx7pBYDakZoo6OCkjgdSt1ihkmF1UQVF6ThmB2bvCZmqBgIGaPU5FONRtHXre704c4WK6goGCJQOs2rD2Ohic94FXrOC4YH0AAWYB9PBuBQGDPHwtDWoLDUtn1LN35YjUcgYhRiUNhiTbIymNGvdO6OwBgyB9HE3RuZCWFjdipE-YopE1O2f12DujQ-WR0jNUBl2OEDnMgxjuB8wLQucUZm5mOUoaJbIdiZDocufi4v7OA0KxKM1tRbSjHMQfRcFkPBRNJBD+GyBYDjfri4YtRkGse9BXvwZjON4wTvvvnhJQpLI8gBSsx2NAFdiR1ensIRgwg3Lg+uegpxkqBYo66ko9e5MoZh8fXmWaA3CymPiTRu8Dhfe8XNI0qXUFJ9i8xzF3HeN-Xi2pxdvkpOYx31M16Tia4QA */
predictableActionArguments: true,
preserveActionOrder: true,
tsTypes: {} as import('./app.typegen').Typegen0,
schema: {
context: model.initialContext,
events: {} as EventFrom<typeof model>,
},
id: 'app',
initial: 'init',
on: {
DECRYPT_ERROR: {
actions: ['setIsDecryptError'],
},
DECRYPT_ERROR_DISMISS: {
actions: ['unsetIsDecryptError'],
},
},
states: {
init: {
initial: 'store',
states: {
store: {
entry: ['spawnStoreActor', 'logStoreEvents'],
on: {
READY: {
actions: ['unsetIsReadError', 'unsetIsDecryptError'],
target: 'services',
},
ERROR: {
actions: 'setIsReadError',
},
},
},
services: {
entry: ['spawnServiceActors', 'logServiceEvents'],
on: {
READY: 'credentialRegistry',
},
},
credentialRegistry: {
entry: ['loadCredentialRegistryHostFromStorage'],
on: {
STORE_RESPONSE: {
actions: ['loadCredentialRegistryInConstants'],
target: 'info',
},
},
},
info: {
invoke: {
src: 'getAppInfo',
},
on: {
APP_INFO_RECEIVED: {
target: 'devinfo',
actions: ['setAppInfo'],
},
},
},
devinfo: {
invoke: {
src: 'getBackendInfo',
},
on: {
BACKEND_INFO_RECEIVED: {
target: '#ready',
actions: ['setBackendInfo'],
},
},
},
},
},
ready: {
id: 'ready',
type: 'parallel',
on: {
REQUEST_DEVICE_INFO: {
actions: ['requestDeviceInfo'],
},
},
states: {
focus: {
invoke: {
src: 'checkFocusState',
},
on: {
ACTIVE: '.active',
INACTIVE: '.inactive',
},
initial: 'checking',
states: {
checking: {},
active: {
entry: ['forwardToServices'],
},
inactive: {
entry: ['forwardToServices'],
},
},
},
network: {
invoke: {
src: 'checkNetworkState',
},
on: {
ONLINE: '.online',
OFFLINE: '.offline',
},
initial: 'checking',
states: {
checking: {},
online: {
entry: ['forwardToServices'],
},
offline: {
entry: ['forwardToServices'],
},
},
},
},
},
},
},
{
actions: {
forwardToServices: pure((context, event) =>
Object.values(context.serviceRefs).map((serviceRef) =>
send({ ...event, type: `APP_${event.type}` }, { to: serviceRef })
)
),
setIsReadError: assign({
isReadError: true,
}),
setIsDecryptError: assign({
isDecryptError: true,
}),
unsetIsDecryptError: assign({
isDecryptError: false,
}),
unsetIsReadError: assign({
isReadError: false,
}),
requestDeviceInfo: respond((context) => ({
type: 'RECEIVE_DEVICE_INFO',
info: {
...context.info,
name: context.serviceRefs.settings.getSnapshot().context.name,
},
})),
spawnStoreActor: assign({
serviceRefs: (context) => ({
...context.serviceRefs,
store: spawn(storeMachine, storeMachine.id),
}),
}),
logStoreEvents: (context) => {
if (__DEV__) {
context.serviceRefs.store.subscribe(logState);
}
},
spawnServiceActors: model.assign({
serviceRefs: (context) => {
const serviceRefs = {
...context.serviceRefs,
};
serviceRefs.auth = spawn(
createAuthMachine(serviceRefs),
authMachine.id
);
serviceRefs.vc = spawn(createVcMachine(serviceRefs), vcMachine.id);
serviceRefs.settings = spawn(
createSettingsMachine(serviceRefs),
settingsMachine.id
);
serviceRefs.activityLog = spawn(
createActivityLogMachine(serviceRefs),
activityLogMachine.id
);
serviceRefs.scan = spawn(
createScanMachine(serviceRefs),
scanMachine.id
);
if (Platform.OS === 'android') {
serviceRefs.request = spawn(
createRequestMachine(serviceRefs),
requestMachine.id
);
}
serviceRefs.revoke = spawn(
createRevokeMachine(serviceRefs),
revokeVidsMachine.id
);
return serviceRefs;
},
}),
logServiceEvents: (context) => {
if (__DEV__) {
context.serviceRefs.auth.subscribe(logState);
context.serviceRefs.vc.subscribe(logState);
context.serviceRefs.settings.subscribe(logState);
context.serviceRefs.activityLog.subscribe(logState);
context.serviceRefs.scan.subscribe(logState);
if (Platform.OS === 'android') {
context.serviceRefs.request.subscribe(logState);
}
context.serviceRefs.revoke.subscribe(logState);
}
},
setAppInfo: model.assign({
info: (_, event) => event.info,
}),
setBackendInfo: model.assign({
backendInfo: (_, event) => event.info,
}),
loadCredentialRegistryHostFromStorage: send(
StoreEvents.GET(SETTINGS_STORE_KEY),
{
to: (context) => context.serviceRefs.store,
}
),
loadCredentialRegistryInConstants: (_context, event) => {
changeCrendetialRegistry(
!event.response?.credentialRegistry
? MIMOTO_HOST
: event.response?.credentialRegistry
);
},
},
services: {
getAppInfo: () => async (callback) => {
const appInfo = {
deviceId: getDeviceId(),
deviceName: await getDeviceName(),
};
callback(model.events.APP_INFO_RECEIVED(appInfo));
},
getBackendInfo: () => async (callback) => {
let backendInfo = {
application: {
name: '',
version: '',
},
build: {},
config: {},
};
try {
backendInfo = await request('GET', '/residentmobileapp/info');
callback(model.events.BACKEND_INFO_RECEIVED(backendInfo));
} catch {
callback(model.events.BACKEND_INFO_RECEIVED(backendInfo));
}
},
checkFocusState: () => (callback) => {
const changeHandler = (newState: AppStateStatus) => {
switch (newState) {
case 'background':
case 'inactive':
callback({ type: 'INACTIVE' });
break;
case 'active':
callback({ type: 'ACTIVE' });
break;
}
};
const blurHandler = () => callback({ type: 'INACTIVE' });
const focusHandler = () => callback({ type: 'ACTIVE' });
AppState.addEventListener('change', changeHandler);
// android only
if (Platform.OS === 'android') {
AppState.addEventListener('blur', blurHandler);
AppState.addEventListener('focus', focusHandler);
}
return () => {
AppState.removeEventListener('change', changeHandler);
if (Platform.OS === 'android') {
AppState.removeEventListener('blur', blurHandler);
AppState.removeEventListener('focus', focusHandler);
}
};
},
checkNetworkState: () => (callback) => {
return NetInfo.addEventListener((state) => {
if (state.isConnected) {
callback({ type: 'ONLINE', networkType: state.type });
} else {
callback({ type: 'OFFLINE' });
}
});
},
},
}
);
interface AppInfo {
deviceId: string;
deviceName: string;
}
interface BackendInfo {
application: {
name: string;
version: string;
};
build: object;
config: object;
}
type State = StateFrom<typeof appMachine>;
export function selectAppInfo(state: State) {
return state.context.info;
}
export function selectBackendInfo(state: State) {
return state.context.backendInfo;
}
export function selectIsReady(state: State) {
return state.matches('ready');
}
export function selectIsOnline(state: State) {
return state.matches('ready.network.online');
}
export function selectIsActive(state: State) {
return state.matches('ready.focus.active');
}
export function selectIsFocused(state: State) {
return state.matches('ready.focus');
}
export function logState(state: AnyState) {
const data = JSON.stringify(
state.event,
(key, value) => {
if (key === 'type') return undefined;
if (typeof value === 'string' && value.length >= 100) {
return value.slice(0, 100) + '...';
}
return value;
},
2
);
console.log(
`[${getDeviceNameSync()}] ${state.machine.id}: ${
state.event.type
} -> ${state.toStrings().pop()}\n${
data.length > 300 ? data.slice(0, 300) + '...' : data
}
`
);
}
export function selectIsReadError(state: State) {
return state.context.isReadError;
}
export function selectIsDecryptError(state: State) {
return state.context.isDecryptError;
}