From 2333e5a87824590c7d8ea7e02569c1baeec3e41f Mon Sep 17 00:00:00 2001 From: Varnit Goyal Date: Mon, 30 Oct 2023 06:49:47 +0530 Subject: [PATCH] TP-46779 | performance optimisations --- android/app/build.gradle | 6 +- android/build.gradle | 2 + package.json | 6 +- src/common/BlockerScreen.tsx | 4 + src/common/CachedImage.tsx | 6 +- src/common/TrackingComponent.tsx | 94 ++++++-------------- src/constants/config.js | 12 +-- src/hooks/useFirestoreUpdates.ts | 99 +++++++++++++--------- src/reducer/allCasesSlice.ts | 49 ++++++----- src/reducer/userSlice.ts | 6 ++ src/screens/Dashboard/PerformanceMeter.tsx | 7 +- src/screens/allCases/CasesList.tsx | 51 +++++------ src/screens/auth/AuthRouter.tsx | 12 +-- src/screens/auth/ProtectedRouter.tsx | 2 +- src/store/store.ts | 20 ++++- yarn.lock | 71 ++++++++++++++-- 16 files changed, 261 insertions(+), 186 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index ab90a5e0..af3f1747 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,5 +1,7 @@ apply plugin: "com.android.application" apply plugin: "com.google.firebase.crashlytics" +apply plugin: 'com.google.firebase.firebase-perf' + import com.android.build.OutputFile import org.apache.tools.ant.taskdefs.condition.Os @@ -131,8 +133,8 @@ def reactNativeArchitectures() { return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] } -def VERSION_CODE = 96 -def VERSION_NAME = "2.5.1" +def VERSION_CODE = 100 +def VERSION_NAME = "2.7.5" android { ndkVersion rootProject.ext.ndkVersion diff --git a/android/build.gradle b/android/build.gradle index 0486ddbb..8cd8db8e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -29,6 +29,8 @@ buildscript { // in the individual module build.gradle files classpath 'com.google.gms:google-services:4.3.14' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' + classpath 'com.google.firebase:perf-plugin:1.4.2' + } } diff --git a/package.json b/package.json index 63701426..02101bad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "AV_APP", - "version": "2.5.1", + "version": "2.7.5", "private": true, "scripts": { "android:dev": "yarn move:dev && react-native run-android", @@ -41,6 +41,7 @@ "@nozbe/with-observables": "1.4.1", "@react-native-async-storage/async-storage": "1.17.11", "@react-native-clipboard/clipboard": "^1.11.2", + "@react-native-community/cli-platform-android": "^11.3.9", "@react-native-community/netinfo": "9.3.7", "@react-native-firebase/analytics": "16.4.6", "@react-native-firebase/app": "16.4.6", @@ -49,6 +50,7 @@ "@react-native-firebase/database": "16.4.6", "@react-native-firebase/firestore": "16.5.0", "@react-native-firebase/messaging": "17.4.0", + "@react-native-firebase/perf": "^18.6.0", "@react-native-firebase/remote-config": "16.4.6", "@react-native-google-signin/google-signin": "9.0.2", "@react-navigation/bottom-tabs": "6.5.5", @@ -66,6 +68,7 @@ "d3-shape": "3.2.0", "dayjs": "1.11.9", "fuzzysort": "2.0.4", + "lodash.chunk": "^4.2.0", "lottie-react-native": "5.1.4", "react": "18.1.0", "react-hook-form": "7.40.0", @@ -82,6 +85,7 @@ "react-native-get-sms-android": "2.1.0", "react-native-gzip": "1.0.0", "react-native-image-picker": "4.10.2", + "react-native-mmkv": "2.5.1", "react-native-pager-view": "6.1.2", "react-native-pdf-renderer": "1.1.1", "react-native-permissions": "3.6.1", diff --git a/src/common/BlockerScreen.tsx b/src/common/BlockerScreen.tsx index 924872ee..78a5d93c 100644 --- a/src/common/BlockerScreen.tsx +++ b/src/common/BlockerScreen.tsx @@ -17,6 +17,7 @@ import handleBlacklistedAppsForBlockingCosmos, { } from '@services/blacklistedApps.service'; import { addClickstreamEvent } from '@services/clickstreamEventService'; import { setBlacklistedAppsInstalledData } from '@reducers/blacklistedAppsInstalledSlice'; +import perf from '@react-native-firebase/perf'; interface IBlockerScreen { children?: ReactNode; @@ -72,11 +73,14 @@ const BlockerScreen = (props: IBlockerScreen) => { ) ); const appStateChange = AppState.addEventListener('change', async (change) => { + const trace = await perf().startTrace('blacklistedAppsInstalled'); + handleBlacklistedAppsForBlockingCosmos().then((blacklistedAppsInstalled) => dispatch( setBlacklistedAppsInstalledData({ blacklistedAppsInstalled: blacklistedAppsInstalled }) ) ); + await trace.stop(); }); return () => appStateChange.remove(); }, [BLACKLISTED_APPS_LIST]); diff --git a/src/common/CachedImage.tsx b/src/common/CachedImage.tsx index bf9e69b3..2141d7f1 100644 --- a/src/common/CachedImage.tsx +++ b/src/common/CachedImage.tsx @@ -4,6 +4,7 @@ import RNFS from 'react-native-fs'; import RNFetchBlob from 'react-native-blob-util'; import FastImage from 'react-native-fast-image'; import { logError } from '../components/utlis/errorUtils'; +import perf from '@react-native-firebase/perf'; interface CachedImageProps { highQualityUri: string; @@ -18,8 +19,10 @@ interface CachedImageProps { export let GlobalImageMap: { [key: string]: string } = {}; export const hydrateGlobalImageMap = async () => { + const trace = await perf().startTrace('hydrateGlobalImageMap'); + RNFS.readdir(RNFS.CachesDirectoryPath) - .then((result) => { + .then(async (result) => { // Filter only files (remove directories from the list) const files = result.filter((name) => name.endsWith('jpg')); if (files.length) { @@ -28,6 +31,7 @@ export const hydrateGlobalImageMap = async () => { GlobalImageMap[cacheFilePath] = `file://${cacheFilePath}`; }); } + await trace.stop(); }) .catch((error) => { console.error('Error reading directory:', error); diff --git a/src/common/TrackingComponent.tsx b/src/common/TrackingComponent.tsx index aa7c085b..9fa8a46e 100644 --- a/src/common/TrackingComponent.tsx +++ b/src/common/TrackingComponent.tsx @@ -1,13 +1,22 @@ import { type ReactNode, useEffect, useRef, useCallback } from 'react'; import { type NativeEventSubscription, AppState, type AppStateStatus } from 'react-native'; import dayJs from 'dayjs'; +import RNFS from 'react-native-fs'; +import { get } from 'react-hook-form'; +import { setBlacklistedAppsInstalledData } from '@reducers/blacklistedAppsInstalledSlice'; +import handleBlacklistedAppsForBlockingCosmos, { + type Apps, +} from '@services/blacklistedApps.service'; +import fetchUpdatedRemoteConfig, { + FIREBASE_FETCH_TIMESTAMP, +} from '@services/firebaseFetchAndUpdate.service'; import { setItem, getItem } from '../components/utlis/storageHelper'; import CosmosForegroundService, { type IForegroundTask, } from '../services/foregroundServices/foreground.service'; import useIsOnline from '../hooks/useIsOnline'; import { - IGeolocationPayload, + type IGeolocationPayload, getSyncTime, sendLocationAndActivenessToServer, } from '../hooks/capturingApi'; @@ -30,7 +39,7 @@ import { import { getSyncCaseIds } from '../components/utlis/firebaseFallbackUtils'; import { syncCasesByFallback } from '../reducer/allCasesSlice'; import { MILLISECONDS_IN_A_MINUTE } from '../../RN-UI-LIB/src/utlis/common'; -import { setLockData } from '../reducer/userSlice'; +import { setCaseSyncLock, setLockData } from '../reducer/userSlice'; import { getConfigData } from '../action/configActions'; import { AppStates } from '../types/appStates'; import { StorageKeys } from '../types/storageKeys'; @@ -40,16 +49,9 @@ import { getActivityTimeWindowMedium, getActivityTimeWindowHigh, } from './AgentActivityConfigurableConstants'; -import RNFS from 'react-native-fs'; import { GlobalImageMap } from './CachedImage'; -import { get } from 'react-hook-form'; import { addClickstreamEvent } from '../services/clickstreamEventService'; import { CLICKSTREAM_EVENT_NAMES } from './Constants'; -import { setBlacklistedAppsInstalledData } from '@reducers/blacklistedAppsInstalledSlice'; -import handleBlacklistedAppsForBlockingCosmos, { Apps } from '@services/blacklistedApps.service'; -import fetchUpdatedRemoteConfig, { - FIREBASE_FETCH_TIMESTAMP, -} from '@services/firebaseFetchAndUpdate.service'; export enum FOREGROUND_TASKS { GEOLOCATION = 'GEOLOCATION', @@ -74,6 +76,7 @@ const TrackingComponent: React.FC = ({ children }) => { const dispatch = useAppDispatch(); const appState = useRef(AppState.currentState); const isTeamLead = useAppSelector((state) => state.user.isTeamLead); + const caseSyncLock = useAppSelector((state) => state.user.caseSyncLock); const { referenceId, @@ -104,8 +107,8 @@ const TrackingComponent: React.FC = ({ children }) => { const handleSendGeolocation = async () => { try { - const location = await CaptureGeolocation.fetchLocation(Date.now() + '', 0, appState.current); - if (!location) { + const location = await CaptureGeolocation.fetchLocation(`${Date.now()}`, 0, appState.current); + if (location == null) { return; } const isActiveOnApp: string | boolean = (await getItem(StorageKeys.IS_USER_ACTIVE)) || false; @@ -140,12 +143,16 @@ const TrackingComponent: React.FC = ({ children }) => { if (!isOnline) { return; } - if (geolocations.length) { + if (geolocations.length > 0) { dispatch(sendLocationAndActivenessToServer(geolocations, true)); } }, [geolocations, isOnline]); const handleGetCaseSyncStatus = async () => { + if (caseSyncLock) { + return; + } + dispatch(setCaseSyncLock(true)); try { if (!isOnline) { return; @@ -175,45 +182,12 @@ const TrackingComponent: React.FC = ({ children }) => { }) ); } + dispatch(setCaseSyncLock(false)); } catch (e) { logError(e as Error, 'Error during fetching case sync status'); } }; - const handleUpdateActiveness = async () => { - if (AppState.currentState === AppStates.ACTIVE) { - await setItem(StorageKeys.IS_USER_ACTIVE, 'true'); - return; - } - const foregroundTimestamp = await getItem(StorageKeys.APP_FOREGROUND_TIMESTAMP); - const backgroundTimestamp = await getItem(StorageKeys.APP_BACKGROUND_TIMESTAMP); - const foregroundTime = dayJs(foregroundTimestamp); - const backgroundTime = dayJs(backgroundTimestamp); - const diffBetweenBackgroundAndForegroundTime = dayJs(backgroundTime).diff( - foregroundTime, - 'seconds' - ); - const diffBetweenCurrentTimeAndForegroundTime = - dayJs().diff(foregroundTime, 'minutes') < 0 ? 0 : dayJs().diff(foregroundTime, 'minutes'); - const isForegroundTimeWithInRange = - diffBetweenCurrentTimeAndForegroundTime <= ACTIVITY_TIME_WINDOW; - const isForegroundTimeAfterBackground = dayJs(foregroundTimestamp).isAfter(backgroundTimestamp); - const ACTIVITY_TIME_ON_APP = getActivityTimeOnApp(); - - if (isForegroundTimeWithInRange) { - if ( - isForegroundTimeAfterBackground || - diffBetweenBackgroundAndForegroundTime >= ACTIVITY_TIME_ON_APP - ) { - await setItem(StorageKeys.IS_USER_ACTIVE, 'true'); - return; - } - await setItem(StorageKeys.IS_USER_ACTIVE, 'false'); - } - await setItem(StorageKeys.IS_USER_ACTIVE, 'false'); - return; - }; - const handleUpdateActivity = async () => { const foregroundTimestamp = await getItem(StorageKeys.APP_FOREGROUND_TIMESTAMP); const backgroundTimestamp = await getItem(StorageKeys.APP_BACKGROUND_TIMESTAMP); @@ -254,17 +228,12 @@ const TrackingComponent: React.FC = ({ children }) => { if (isForegroundTimeAfterBackground) { if (diffBetweenCurrentTimeAndForegroundTime >= ACTIVITY_TIME_ON_APP) { await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.HIGH); - return; } - return; } else if (isStateSetTimeWithinHighRange) { - return; } else if (isStateSetTimeWithinMediumRange) { await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.MEDIUM); - return; } else { await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.LOW); - return; } }; @@ -280,7 +249,7 @@ const TrackingComponent: React.FC = ({ children }) => { continue; } RNFS.stat(filePath) - .then((fileStat) => { + .then(async (fileStat) => { // Calculate the age of the file in milliseconds const fileAgeMs = currentDate - new Date(fileStat.mtime).getTime(); @@ -288,7 +257,7 @@ const TrackingComponent: React.FC = ({ children }) => { if (fileAgeMs > 30 * 24 * 60 * 60 * 1000) { delete GlobalImageMap[filePath]; - return RNFS.unlink(filePath); // Delete the file + await RNFS.unlink(filePath); // Delete the file } }) .then(() => { @@ -327,12 +296,6 @@ const TrackingComponent: React.FC = ({ children }) => { delay: 3 * MILLISECONDS_IN_A_MINUTE, // 3 minutes onLoop: true, }, - { - taskId: FOREGROUND_TASKS.UPDATE_AGENT_ACTIVENESS, - task: handleUpdateActiveness, - delay: ACTIVITY_TIME_WINDOW * MILLISECONDS_IN_A_MINUTE, // 10 minutes - onLoop: true, - }, { taskId: FOREGROUND_TASKS.UPDATE_AGENT_ACTIVITY, task: handleUpdateActivity, @@ -348,7 +311,7 @@ const TrackingComponent: React.FC = ({ children }) => { { taskId: FOREGROUND_TASKS.FETCH_DATA_FROM_FIREBASE, task: handleFetchUpdatedDataFromFirebase, - delay: 15 * MILLISECONDS_IN_A_MINUTE, // 15 minutes + delay: 60 * MILLISECONDS_IN_A_MINUTE, // 60 minutes onLoop: true, }, ]; @@ -392,9 +355,7 @@ const TrackingComponent: React.FC = ({ children }) => { if (diffBetweenBackgroundAndForegroundTime >= ACTIVITY_TIME_ON_APP) { await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.HIGH); await setItem(StorageKeys.STATE_SET_TIMESTAMP, dayJs().toString()); - return; } - return; }; const handleAppStateChange = async (nextAppState: AppStateStatus) => { @@ -405,11 +366,6 @@ const TrackingComponent: React.FC = ({ children }) => { addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_APP_FOREGROUND, { now }); handleGetCaseSyncStatus(); dispatch(getConfigData()); - handleBlacklistedAppsForBlockingCosmos().then((blacklistedAppsInstalled) => - dispatch( - setBlacklistedAppsInstalledData({ blacklistedAppsInstalled: blacklistedAppsInstalled }) - ) - ); CosmosForegroundService.start(tasks); } if (nextAppState === AppStates.BACKGROUND) { @@ -429,9 +385,7 @@ const TrackingComponent: React.FC = ({ children }) => { await handleGetCaseSyncStatus(); dispatch(getConfigData()); handleBlacklistedAppsForBlockingCosmos().then((blacklistedAppsInstalled) => - dispatch( - setBlacklistedAppsInstalledData({ blacklistedAppsInstalled: blacklistedAppsInstalled }) - ) + dispatch(setBlacklistedAppsInstalledData({ blacklistedAppsInstalled })) ); if (!isTeamLead && LAST_SYNC_STATUS !== SyncStatus.FETCH_CASES) { const updatedDetails: ISyncedCases = await fetchCasesToSync(referenceId); diff --git a/src/constants/config.js b/src/constants/config.js index 6fa05aeb..8ef9052b 100644 --- a/src/constants/config.js +++ b/src/constants/config.js @@ -1,14 +1,14 @@ import { MILLISECONDS_IN_A_MINUTE, MINUTES_IN_AN_HOUR } from '../../RN-UI-LIB/src/utlis/common'; -export const BASE_AV_APP_URL = 'https://qa-longhorn-portal.np.navi-tech.in/field-app'; +export const BASE_AV_APP_URL = 'https://longhorn.navi.com/field-app'; export const SENTRY_DSN = - 'https://acef93c884c1424cacc4ec899562e203@qa-longhorn-portal.np.navi-tech.in/glitchtip-events/173'; -export const JANUS_SERVICE_URL = 'https://qa-longhorn-portal.np.navi-tech.in/api/events/json'; -export const ENV = 'qa'; + 'https://5daa4832fade44b389b265de9b26c2fd@longhorn.navi.com/glitchtip-events/172'; +export const JANUS_SERVICE_URL = 'https://longhorn.navi.com/api/events/json'; +export const ENV = 'prod'; export const IS_SSO_ENABLED = true; export const APM_APP_NAME = 'cosmos-app'; -export const APM_BASE_URL = 'https://qa-longhorn-portal.np.navi-tech.in/apm-events'; +export const APM_BASE_URL = 'https://longhorn.navi.com/apm-events'; export const IS_DATA_SYNC_REQUIRED = true; export const DATA_SYNC_TIME_INTERVAL = 2 * MINUTES_IN_AN_HOUR * MILLISECONDS_IN_A_MINUTE; // 2hr export const GOOGLE_SSO_CLIENT_ID = - '60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com'; + '136591056725-ev8db4hrlud2m23n0o03or3cmmp3a3cq.apps.googleusercontent.com'; diff --git a/src/hooks/useFirestoreUpdates.ts b/src/hooks/useFirestoreUpdates.ts index bc69c563..9716b297 100644 --- a/src/hooks/useFirestoreUpdates.ts +++ b/src/hooks/useFirestoreUpdates.ts @@ -1,21 +1,24 @@ import { useEffect, useRef } from 'react'; -import firestore, { FirebaseFirestoreTypes } from '@react-native-firebase/firestore'; -import { RootState } from '../store/store'; +import firestore, { type FirebaseFirestoreTypes } from '@react-native-firebase/firestore'; +import auth from '@react-native-firebase/auth'; +import { setFeedbackFilterTemplate } from '@reducers/feedbackFiltersSlice'; +import perf from '@react-native-firebase/perf'; +import { InteractionManager } from 'react-native'; +import { type RootState } from '../store/store'; import { useAppDispatch, useAppSelector } from '.'; import { setLoading, updateCaseDetailsFirestore } from '../reducer/allCasesSlice'; -import { CaseDetail } from '../screens/caseDetails/interface'; +import { type CaseDetail } from '../screens/caseDetails/interface'; import { FirestoreUpdateTypes } from '../common/Constants'; import { toast } from '../../RN-UI-LIB/src/components/toast'; -import auth from '@react-native-firebase/auth'; import { updateAvTemplateData, updateCollectionTemplateData } from '../reducer/caseReducer'; -import { ILockData, MY_CASE_ITEM, setLockData, VisitPlanStatus } from '../reducer/userSlice'; +import { type ILockData, MY_CASE_ITEM, setLockData, VisitPlanStatus } from '../reducer/userSlice'; import { setFilters } from '../reducer/filtersSlice'; -import { FormTemplateV1 } from '../types/template.types'; +import { type FormTemplateV1 } from '../types/template.types'; import { ToastMessages } from '../screens/allCases/constants'; import { setForceUninstallData } from '../reducer/metadataSlice'; import { logError } from '../components/utlis/errorUtils'; -import { GenericFunctionArgs } from '../common/GenericTypes'; -import { setFeedbackFilterTemplate } from '@reducers/feedbackFiltersSlice'; +import { type GenericFunctionArgs } from '../common/GenericTypes'; +import chunks from 'lodash.chunk'; export interface CaseUpdates { updateType: string; @@ -28,9 +31,7 @@ export const loggedOutCurrentUser = async () => { } }; -const isUserSignedIn = () => { - return auth().currentUser ? true : false; -}; +const isUserSignedIn = () => auth().currentUser != null; const useFirestoreUpdates = () => { const { @@ -60,10 +61,10 @@ const useFirestoreUpdates = () => { const showCaseUpdationToast = (newlyAddedCases: number, deletedCases: number) => { let toastConfig: any = null; - let addedCasesText = newlyAddedCases + const addedCasesText = newlyAddedCases ? `${newlyAddedCases} new case${newlyAddedCases > 1 ? 's' : ''} allocated` : ''; - let deletedCasesText = deletedCases + const deletedCasesText = deletedCases ? `${deletedCases} case${deletedCases > 1 ? 's' : ''} de-allocated` : ''; if (newlyAddedCases && deletedCases) { @@ -81,36 +82,47 @@ const useFirestoreUpdates = () => { } }; - const handleCasesUpdate = (querySnapshot: FirebaseFirestoreTypes.QuerySnapshot) => { + const handleCasesUpdate = async (querySnapshot: FirebaseFirestoreTypes.QuerySnapshot) => { + console.log('tracing for firebase performance dashboard'); + const trace = await perf().startTrace('handleCasesUpdate'); + let newlyAddedCases = 0; let deletedCases = 0; const caseUpdates: CaseUpdates[] = []; querySnapshot .docChanges() .forEach((documentSnapshot: FirebaseFirestoreTypes.DocumentChange) => { - const updateType = documentSnapshot.type; - const updatedCaseDetail = documentSnapshot.doc.data() as CaseDetail; - if (updateType === FirestoreUpdateTypes.ADDED) { - if (!caseDetails[updatedCaseDetail.id]) { - newlyAddedCases++; + InteractionManager.runAfterInteractions(() => { + const updateType = documentSnapshot.type; + const updatedCaseDetail = documentSnapshot.doc.data() as CaseDetail; + if (updateType === FirestoreUpdateTypes.ADDED) { + if (!caseDetails[updatedCaseDetail.id]) { + newlyAddedCases++; + caseUpdates.push({ updateType, updatedCaseDetail }); + } + } else { + if (updateType === FirestoreUpdateTypes.REMOVED) { + deletedCases++; + } caseUpdates.push({ updateType, updatedCaseDetail }); } - } else { - if (updateType === FirestoreUpdateTypes.REMOVED) { - deletedCases++; - } - caseUpdates.push({ updateType, updatedCaseDetail }); - } + }, 0); }); const isInitialLoad = casesList.length === 0; - dispatch( - updateCaseDetailsFirestore({ - caseUpdates, - isInitialLoad, - isVisitPlanLocked: lockRef?.current?.visitPlanStatus === VisitPlanStatus.LOCKED, - selectedAgent: selectedAgent, - }) - ); + await trace.stop(); + const casesChunks = chunks(caseUpdates, 5); + casesChunks.forEach((chunk) => { + InteractionManager.runAfterInteractions(() => { + dispatch( + updateCaseDetailsFirestore({ + caseUpdates: chunk, + isInitialLoad, + isVisitPlanLocked: lockRef?.current?.visitPlanStatus === VisitPlanStatus.LOCKED, + selectedAgent, + }) + ); + }); + }); !isInitialLoad && showCaseUpdationToast(newlyAddedCases, deletedCases); }; @@ -153,7 +165,7 @@ const useFirestoreUpdates = () => { snapshot: FirebaseFirestoreTypes.DocumentSnapshot ) => { const lockData = snapshot.data(); - lockData && dispatch(setLockData(lockData)); + lockData != null && dispatch(setLockData(lockData)); }; const handleFeedbackFilters = ( @@ -169,7 +181,7 @@ const useFirestoreUpdates = () => { }; const signInUserToFirebase = () => { - if (!sessionDetails) { + if (sessionDetails == null) { return; } auth() @@ -187,11 +199,12 @@ const useFirestoreUpdates = () => { }); }; - const subscribeToDoc = (successCb: GenericFunctionArgs, collectionPath: string) => { - return firestore() + const subscribeToDoc = (successCb: GenericFunctionArgs, collectionPath: string) => + firestore() .doc(collectionPath) - .onSnapshot(successCb, (err) => handleError(err, collectionPath)); - }; + .onSnapshot(successCb, (err) => { + handleError(err, collectionPath); + }); const subscribeToCases = () => { let refId = user?.referenceId; @@ -202,7 +215,9 @@ const useFirestoreUpdates = () => { return firestore() .collection(collectionPath) .orderBy('totalOverdueAmount', 'asc') // It is descending order only, but acting weirdly. Need to check. - .onSnapshot(handleCasesUpdate, (err) => handleError(err, collectionPath)); + .onSnapshot(handleCasesUpdate, (err) => { + handleError(err, collectionPath); + }); }; const subscribeToAvTemplate = () => { @@ -268,7 +283,9 @@ const useFirestoreUpdates = () => { return; } if (isUserSignedIn()) { - subscribeToFirestore(); + InteractionManager.runAfterInteractions(() => { + subscribeToFirestore(); + }); } else { dispatch(setLoading(true)); signInUserToFirebase(); diff --git a/src/reducer/allCasesSlice.ts b/src/reducer/allCasesSlice.ts index 826943c0..dce1fc82 100644 --- a/src/reducer/allCasesSlice.ts +++ b/src/reducer/allCasesSlice.ts @@ -1,33 +1,36 @@ import { createSlice } from '@reduxjs/toolkit'; +import { InteractionManager } from 'react-native'; import { toast } from '../../RN-UI-LIB/src/components/toast'; import { _map } from '../../RN-UI-LIB/src/utlis/common'; -import { findDocumentByDocumentType } from '../components/utlis/commonFunctions'; +import { + findDocumentByDocumentType, + getLoanAccountNumber, +} from '../components/utlis/commonFunctions'; import { CLICKSTREAM_EVENT_NAMES, FirestoreUpdateTypes } from '../common/Constants'; import { getCurrentScreen, navigateToScreen } from '../components/utlis/navigationUtlis'; -import { CaseUpdates } from '../hooks/useFirestoreUpdates'; +import { type CaseUpdates } from '../hooks/useFirestoreUpdates'; import { COMPLETED_STATUSES } from '../screens/allCases/constants'; import { CaseStatuses, CaseAllocationType, caseVerdict, - ICaseItem, - IReportee, + type ICaseItem, + type IReportee, } from '../screens/allCases/interface'; import { - CaseDetail, + type CaseDetail, CONTEXT_TASK_STATUSES, DOCUMENT_TYPE, - IGeolocation, + type IGeolocation, } from '../screens/caseDetails/interface'; import { addClickstreamEvent } from '../services/clickstreamEventService'; -import { getLoanAccountNumber } from '../components/utlis/commonFunctions'; import { getVisitedWidgetsNodeList } from '../components/form/services/forms.service'; import { CollectionCaseWidgetId, CommonCaseWidgetId } from '../types/template.types'; -import { IAvatarUri } from '../action/caseListAction'; +import { type IAvatarUri } from '../action/caseListAction'; import { MY_CASE_ITEM } from './userSlice'; -export type ICasesMap = { [key: string]: ICaseItem }; +export type ICasesMap = Record; interface FilteredListToast { showToast: boolean; @@ -117,7 +120,7 @@ export const getUpdatedCollectionCaseDetail = ({ updatedValue.isApiCalled = false; updatedValue.taskStatus = 'completed'; // @deprecating - const visitedWidgets = answer.visitedWidgets; + const { visitedWidgets } = answer; const allWidget = answer.widgetContext; const widgetContext = {}; @@ -153,7 +156,7 @@ export const getUpdatedAVCaseDetail = ({ const updatedValue = { ...caseData }; updatedValue.isSynced = false; updatedValue.isApiCalled = false; - const visitedWidgets = answer.visitedWidgets; + const { visitedWidgets } = answer; const allWidget = answer.widgetContext; const widgetContext = {}; _map(visitedWidgets, (widget) => { @@ -242,13 +245,11 @@ const getCaseListItem = ( caseReferenceId: string, pinRank?: number | null, caseViewCreatedAt?: number -) => { - return { - caseReferenceId, - pinRank: pinRank || null, - caseViewCreatedAt, - }; -}; +) => ({ + caseReferenceId, + pinRank: pinRank || null, + caseViewCreatedAt, +}); const allCasesSlice = createSlice({ name: 'cases', @@ -264,9 +265,9 @@ const allCasesSlice = createSlice({ isVisitPlanLocked: boolean; selectedAgent: IReportee; }; - let newVisitCaseLoanIds: string[] = []; - let newVisitCollectionCases: string[] = []; - let removedVisitedCasesLoanIds: string[] = []; + const newVisitCaseLoanIds: string[] = []; + const newVisitCollectionCases: string[] = []; + const removedVisitedCasesLoanIds: string[] = []; caseUpdates.forEach(({ updateType, updatedCaseDetail }) => { const { caseType, caseReferenceId, id, pinRank, caseViewCreatedAt } = updatedCaseDetail; @@ -530,9 +531,7 @@ const allCasesSlice = createSlice({ state.caseDetails[action.payload].isNewlyAdded = false; } }, - resetCasesData: () => { - return initialState; - }, + resetCasesData: () => initialState, setVisitPlansUpdating: (state, action) => { state.visitPlansUpdating = action.payload; }, @@ -542,7 +541,7 @@ const allCasesSlice = createSlice({ syncCasesByFallback: (state, action) => { const { cases = [], deletedCaseIds = [], payloadCreatedAt } = action.payload; cases.forEach((caseItem: CaseDetail | null) => { - if (!caseItem) { + if (caseItem == null) { return; } const { caseViewCreatedAt, caseReferenceId, isSynced, pinRank } = caseItem; diff --git a/src/reducer/userSlice.ts b/src/reducer/userSlice.ts index ef0ca959..3edc4945 100644 --- a/src/reducer/userSlice.ts +++ b/src/reducer/userSlice.ts @@ -63,6 +63,7 @@ export interface IUserSlice extends IUser { isTeamLead: boolean; isExternalAgent: boolean; agentRoles: string[]; + caseSyncLock: boolean; } const initialState: IUserSlice = { @@ -79,6 +80,7 @@ const initialState: IUserSlice = { isTeamLead: false, isExternalAgent: false, agentRoles: [], + caseSyncLock: false, }; export const userSlice = createSlice({ @@ -125,6 +127,9 @@ export const userSlice = createSlice({ setIsExternalAgent: (state, action) => { state.isExternalAgent = action.payload; }, + setCaseSyncLock: (state, action) => { + state.caseSyncLock = action.payload; + }, }, }); @@ -135,6 +140,7 @@ export const { setSelectedAgent, setAgentRole, setIsExternalAgent, + setCaseSyncLock, } = userSlice.actions; export default userSlice.reducer; diff --git a/src/screens/Dashboard/PerformanceMeter.tsx b/src/screens/Dashboard/PerformanceMeter.tsx index c648f019..06f7bcf0 100644 --- a/src/screens/Dashboard/PerformanceMeter.tsx +++ b/src/screens/Dashboard/PerformanceMeter.tsx @@ -14,6 +14,7 @@ const PerformanceMeter = () => { const performanceData = useAppSelector((state) => state.agentPerformance.performanceData); const { performanceLevel } = performanceData || {}; const { isGraphExpanded } = useAppSelector((state) => state.agentPerformance); + const [isGraphCompletelyExpanded, setIsGraphCompletelyExpanded] = useState(false); const dispatch = useAppDispatch(); const [bodySectionHeight, setBodySectionHeight] = useState(0); @@ -38,13 +39,13 @@ const PerformanceMeter = () => { ...sharedAnimationConfig, toValue: 0, easing: Easing.bezier(0.4, 0.0, 0.2, 1), - }).start(); + }).start(() => setIsGraphCompletelyExpanded(false)); } else { Animated.timing(animatedController, { ...sharedAnimationConfig, toValue: 1, easing: Easing.bezier(0.4, 0.0, 0.2, 1), - }).start(); + }).start(() => setIsGraphCompletelyExpanded(true)); } dispatch(setIsGraphExpanded(!isGraphExpanded)); }; @@ -74,7 +75,7 @@ const PerformanceMeter = () => { - + {isGraphCompletelyExpanded ? : null} diff --git a/src/screens/allCases/CasesList.tsx b/src/screens/allCases/CasesList.tsx index cd2d9d73..cf881a74 100644 --- a/src/screens/allCases/CasesList.tsx +++ b/src/screens/allCases/CasesList.tsx @@ -1,19 +1,23 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Animated, - ListRenderItemInfo, + type ListRenderItemInfo, Modal, - NativeScrollEvent, - NativeSyntheticEvent, + type NativeScrollEvent, + type NativeSyntheticEvent, Pressable, StyleSheet, View, findNodeHandle, } from 'react-native'; +import { useFocusEffect } from '@react-navigation/native'; +import { FlashList } from '@shopify/flash-list'; +import ModalWrapperForAlfredV2 from '@common/ModalWrapperForAlfredV2'; +import perf from '@react-native-firebase/perf'; import { GenericStyles, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../../RN-UI-LIB/src/styles'; import { COLORS } from '../../../RN-UI-LIB/src/styles/colors'; -import { RootState } from '../../store/store'; -import { CaseTypes, ICaseItem, ICaseItemCaseDetailObj } from './interface'; +import { type RootState } from '../../store/store'; +import { CaseTypes, type ICaseItem, type ICaseItemCaseDetailObj } from './interface'; import { LIST_HEADER_ITEMS, ListHeaderItems } from './constants'; import CaseItem from './CaseItem'; import { Search } from '../../../RN-UI-LIB/src/utlis/search'; @@ -34,16 +38,14 @@ import { VISIT_PLAN_HEADER_HEIGHT_MIN, VISIT_PLAN_HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS, } from '../../common/Constants'; -import { CaseDetail } from '../caseDetails/interface'; +import { type CaseDetail } from '../caseDetails/interface'; import CaseListHeader from './CaseListHeader'; import { evaluateFilterForCases } from '../../components/screens/allCases/allCasesFilters/FilterUtils'; import { debounce } from '../../components/utlis/commonFunctions'; import { getCurrentScreen } from '../../components/utlis/navigationUtlis'; -import { useFocusEffect } from '@react-navigation/native'; -import { FlashList } from '@shopify/flash-list'; import { VisitPlanStatus } from '../../reducer/userSlice'; import { getAttemptedList, getNonAttemptedList } from './utils'; -import { GenericType } from '../../common/GenericTypes'; +import { type GenericType } from '../../common/GenericTypes'; import ModalWrapperForAlfred from '../../common/ModalWrapperForAlfred'; import Text from '../../../RN-UI-LIB/src/components/Text'; import BottomSheet from '../../../RN-UI-LIB/src/components/bottom_sheet/BottomSheet'; @@ -56,9 +58,8 @@ import BottomSheetWrapper from '../../common/BottomSheetWrapper'; import { toast } from '../../../RN-UI-LIB/src/components/toast'; import { setFilteredListToast } from '../../reducer/allCasesSlice'; import { getFilterCount, getSelectedFilters } from '../Dashboard/utils'; -import ModalWrapperForAlfredV2 from '@common/ModalWrapperForAlfredV2'; -export const getItem = (item: Array, index: number) => item[index]; +export const getItem = (item: ICaseItem[], index: number) => item[index]; export const ESTIMATED_ITEM_SIZE = 250; // Average height of List item export const ESTIMATED_LIST_SIZE = { height: SCREEN_HEIGHT - 192, width: SCREEN_WIDTH }; @@ -119,8 +120,12 @@ const CasesList: React.FC = ({ }; useFocusEffect( - useCallback(() => { + useCallback(async () => { + const trace = await perf().startTrace(getCurrentScreen()?.name); firePageLoadEvent(); + return () => { + trace.stop(); + }; }, []) ); @@ -140,7 +145,7 @@ const CasesList: React.FC = ({ setSearchQuery(''); }, [selectedAgent]); - //TODO: clean these different heights + // TODO: clean these different heights const headerHeightQuickFilters = scrollAnimation.interpolate({ inputRange: [0, HEADER_SCROLL_DISTANCE_WITH_QUICK_FILTERS], outputRange: isVisitPlan @@ -274,7 +279,7 @@ const CasesList: React.FC = ({ }; const renderListItem = (row: ListRenderItemInfo) => { - const caseDetailItem = caseDetails[row.item.caseReferenceId] as CaseDetail; + const caseDetailItem = caseDetails[row.item.caseReferenceId]; const { type } = row.item; const caseItemDetailObj: ICaseItemCaseDetailObj = { @@ -289,7 +294,7 @@ const CasesList: React.FC = ({ = ({ } else { listStyle = styles.visitPlanList; } + } else if (quickFiltersPresent) { + listStyle = styles.listWithQuickFilters; } else { - if (quickFiltersPresent) { - listStyle = styles.listWithQuickFilters; - } else { - listStyle = styles.list; - } + listStyle = styles.list; } const headerHeightValue = quickFiltersPresent ? headerHeightQuickFilters : headerHeight; - const showFilters = isVisitPlan && isLockedVisitPlanStatus ? false : !!casesList.length; + const showFilters = isVisitPlan && isLockedVisitPlanStatus ? false : !(casesList.length === 0); return ( @@ -337,14 +340,14 @@ const CasesList: React.FC = ({ isAgentDashboard={isAgentDashboard} /> {visitPlansUpdating ? ( - + ) : null} - {filteredCasesListWithCTA.length ? ( + {filteredCasesListWithCTA.length > 0 ? ( { +function AuthRouter() { const dispatch = useAppDispatch(); const user = useSelector((state: RootState) => state.user); const { isLoggedIn, deviceId, sessionDetails } = user; @@ -41,7 +41,7 @@ const AuthRouter = () => { useEffect(() => { const appStateChange = AppState.addEventListener('change', async (change) => { if (change !== 'active') return; - if (isLoggedIn && user.user && user.user.referenceId) { + if (isLoggedIn && user.user != null && user.user.referenceId) { alfredSetUserId(user.user?.referenceId); alfredSetPhoneNumber(user?.user?.phoneNumber); // user.user.emailId @@ -57,7 +57,7 @@ const AuthRouter = () => { }, [user]); useEffect(() => { - if (isLoggedIn && user.user && user.user.referenceId) { + if (isLoggedIn && user.user != null && user.user.referenceId) { alfredSetUserId(user.user?.referenceId); alfredSetPhoneNumber(user?.user?.phoneNumber); alfredSetEmailId(user?.user?.emailId ?? ''); @@ -112,7 +112,7 @@ const AuthRouter = () => { ); -}; +} export default AuthRouter; diff --git a/src/screens/auth/ProtectedRouter.tsx b/src/screens/auth/ProtectedRouter.tsx index e3c9a575..ea535d89 100644 --- a/src/screens/auth/ProtectedRouter.tsx +++ b/src/screens/auth/ProtectedRouter.tsx @@ -131,7 +131,7 @@ const ProtectedRouter = () => { // Firestore listener hook useFirestoreUpdates(); - + // React.useEffect(() => { // Watching Position for significant change CaptureGeolocation.watchLocation((location: DeviceLocation) => diff --git a/src/store/store.ts b/src/store/store.ts index 44cca84a..a95ae124 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -34,6 +34,24 @@ import reporteesSlice from '../reducer/reporteesSlice'; import blacklistedAppsInstalledSlice from '@reducers/blacklistedAppsInstalledSlice'; import feedbackFiltersSlice from '@reducers/feedbackFiltersSlice'; import agentPerformanceSlice from '../reducer/agentPerformanceSlice'; +import { MMKV } from 'react-native-mmkv'; + +const storage = new MMKV(); + +export const reduxStorage: Storage = { + setItem: (key, value) => { + storage.set(key, value); + return Promise.resolve(true); + }, + getItem: (key) => { + const value = storage.getString(key); + return Promise.resolve(value); + }, + removeItem: (key) => { + storage.delete(key); + return Promise.resolve(); + }, +}; const rootReducer = combineReducers({ case: caseReducer, @@ -63,7 +81,7 @@ const rootReducer = combineReducers({ const persistConfig = { key: 'root', version: 1, - storage: AsyncStorage, + storage: reduxStorage, whitelist: [ 'case', 'allCases', diff --git a/yarn.lock b/yarn.lock index 6214fed2..d154ad80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1511,6 +1511,17 @@ logkitty "^0.7.1" slash "^3.0.0" +"@react-native-community/cli-platform-android@^11.3.9": + version "11.3.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-11.3.9.tgz#578d6519750dcf2e554a774145e198de45affa97" + integrity sha512-bAwvwibTMw9njM2Wdnl1uvREhhZFEVJ9o5TfL+4gMimAi7Jo7c3ex1i1G/dac5mvUZgUSmmvfbx9N4qUrJPX5A== + dependencies: + "@react-native-community/cli-tools" "11.3.9" + chalk "^4.1.2" + execa "^5.0.0" + glob "^7.1.3" + logkitty "^0.7.1" + "@react-native-community/cli-platform-ios@9.3.0", "@react-native-community/cli-platform-ios@^9.3.0": version "9.3.0" resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-9.3.0.tgz#45abde2a395fddd7cf71e8b746c1dc1ee2260f9a" @@ -1553,6 +1564,21 @@ serve-static "^1.13.1" ws "^7.5.1" +"@react-native-community/cli-tools@11.3.9": + version "11.3.9" + resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-11.3.9.tgz#5e20610dcb0a381947a35eabd8ffa5d936590eb6" + integrity sha512-qWEUo/HQll/aj5xHYCw6itXGkPgultxTHa4UYSaBIRSfU15aSiwonJ/JP7fe+fHKbaFh6IdhaaRhj4ArfEOxuQ== + dependencies: + appdirsjs "^1.2.4" + chalk "^4.1.2" + find-up "^5.0.0" + mime "^2.4.1" + node-fetch "^2.6.0" + open "^6.2.0" + ora "^5.4.1" + semver "^7.5.2" + shell-quote "^1.7.3" + "@react-native-community/cli-tools@^9.2.1": version "9.2.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-9.2.1.tgz#c332324b1ea99f9efdc3643649bce968aa98191c" @@ -1648,6 +1674,11 @@ resolved "https://registry.yarnpkg.com/@react-native-firebase/messaging/-/messaging-17.4.0.tgz#9e1df987183d0ca367d0922a14b14b7a53a140cf" integrity sha512-RSiBBfyJ3K9G6TQfZc09XaGpxB9xlP5m9DYkqjbNIqnnTiahF90770lTAS65L1Ha78vCwVO2swIlk32XbcMcMQ== +"@react-native-firebase/perf@^18.6.0": + version "18.6.0" + resolved "https://registry.yarnpkg.com/@react-native-firebase/perf/-/perf-18.6.0.tgz#6edc13cbf770ed378ccbe1dadead40167bbf87d9" + integrity sha512-hVtG0mAs4LBSobBG4DQ0vtJOEvQRGg9+oVQUDb5VgWKW21IAfljEpeFo831xoolpaE/Oyz33tmyrBgqPKKzZ+w== + "@react-native-firebase/remote-config@16.4.6": version "16.4.6" resolved "https://registry.yarnpkg.com/@react-native-firebase/remote-config/-/remote-config-16.4.6.tgz#dec215f2448f555cdba893a31f5cdf419b47b33e" @@ -4275,6 +4306,21 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + execa@^7.0.0: version "7.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" @@ -4725,7 +4771,7 @@ get-stream@^5.0.0: dependencies: pump "^3.0.0" -get-stream@^6.0.1: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -5018,6 +5064,11 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + human-signals@^4.3.0: version "4.3.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" @@ -6273,6 +6324,11 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== +lodash.chunk@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.chunk/-/lodash.chunk-4.2.0.tgz#66e5ce1f76ed27b4303d8c6512e8d1216e8106bc" + integrity sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w== + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -7109,7 +7165,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -7259,7 +7315,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -7877,6 +7933,11 @@ react-native-image-picker@4.10.2: resolved "https://registry.yarnpkg.com/react-native-image-picker/-/react-native-image-picker-4.10.2.tgz#75b356c9eea70c2c4f5c1089f8758e2fa32f88a8" integrity sha512-3h9PrA1dQ84rVeipzQE4eWTELvflSHNtJZN6rz7NkZyaxo9YZV8H/TswBpHwiS5YWlyu+zlLzSoWVa1opSu7GA== +react-native-mmkv@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/react-native-mmkv/-/react-native-mmkv-2.5.1.tgz#29fc462077fab16a5e1b79570fbf8acaac9d87b4" + integrity sha512-5eQu25z3H6zf6w0NkJoTuFEFrbOu6luZxZ6+rK1W+XwY/rjPSFZFQPVtMaz3im90RbILFXXM/KrFGZrpaJJRoQ== + react-native-pager-view@6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-6.1.2.tgz#3522079b9a9d6634ca5e8d153bc0b4d660254552" @@ -8450,7 +8511,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: +semver@^7.0.0, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -8570,7 +8631,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.7: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==