diff --git a/config/prod/config.js b/config/prod/config.js index 453f8ada..aa2681cc 100644 --- a/config/prod/config.js +++ b/config/prod/config.js @@ -1,4 +1,5 @@ export const BASE_AV_APP_URL = 'https://longhorn.navi.com/field-app'; -export const SENTRY_DSN = 'https://877396e88a2b4f78b911016c64f9121a@glitchtip.cmd.navi-tech.in/155'; +// sentry.io +export const SENTRY_DSN = 'https://e979a70a4d8649ca9f02583816505788@o4504994571157504.ingest.sentry.io/4504994577907712'; export const JANUS_SERVICE_URL = 'https://longhorn.navi.com/api/events/json'; export const ENV = 'prod'; diff --git a/src/action/dataActions.ts b/src/action/dataActions.ts index d4d466b5..b6e96013 100644 --- a/src/action/dataActions.ts +++ b/src/action/dataActions.ts @@ -22,6 +22,11 @@ import {setFilters} from "../reducer/filtersSlice"; import { toast } from "../../RN-UI-LIB/src/components/toast"; import { ToastMessages } from '../screens/allCases/constants'; +let _signedApiCallBucket: { req: any, added_At: number }[] = [] +let _signedApiCallBucketTimer: number = 0; +const SIGNED_API_BUCKET_SIZE = 10 +const SIGNED_API_BUCKET_TIMEOUT = 7*1000; + export const postPinnedList = (pinnedCases: IPinnedCasesPayload[], updatedCaseList: ICaseItem[], type: string) => (dispatch: AppDispatch) => { @@ -142,23 +147,56 @@ interface ISignedRequestItem { } export type ISignedRequest = ISignedRequestItem[] -export const getSignedApi = async (signedRequestPayload: ISignedRequest): Promise<{imageUrl: string}> => { - const url = getApiUrl(ApiKeys.GET_SIGNED_URL); - const response = await axiosInstance - .post(url, signedRequestPayload, { - headers: { - donotHandleError: true, - }, - }) - .then(response => { - if (response?.data) { - return { imageUrl: (Object.values(response?.data)?.[0] as string) ?? '' }; - } - return {imageUrl: ''}; - }) - .catch(err => { - logError(err); - return {imageUrl: ''}; - }); - return response; +// TODO : make a immediate resolve provision as well, that bypassses batching. +export const getSignedApi = async (signedRequestPayload: ISignedRequest, shouldBatch = false): Promise<{ imageUrl: string }> => { + return new Promise((res) => { + if(shouldBatch){ + batchSignedApiRequest(signedRequestPayload, (results: any) => { + res({imageUrl: results?.[signedRequestPayload[0].documentReferenceId] || ''}) + }) + } + else { + makeBulkSignedApiRequest(signedRequestPayload, (results: any) => { + res({imageUrl: results?.[signedRequestPayload[0].documentReferenceId] || ''}) + }) + } + + + }) }; + +async function batchSignedApiRequest(payload: ISignedRequestItem[], callback: any) { + payload.forEach(item => { + _signedApiCallBucket.push({req: item, added_At: Date.now()}) + }) + if (_signedApiCallBucket.length > SIGNED_API_BUCKET_SIZE) { + await makeBulkSignedApiRequest(_signedApiCallBucket.map(a => a.req), callback); + return + } + else if (!_signedApiCallBucketTimer){ + _signedApiCallBucketTimer = setTimeout(async () => { + await makeBulkSignedApiRequest(_signedApiCallBucket.map(a => a.req), callback); + }, SIGNED_API_BUCKET_TIMEOUT) + } +} + +async function makeBulkSignedApiRequest(payload: ISignedRequestItem[], callback: any){ + const url = getApiUrl(ApiKeys.GET_SIGNED_URL); + _signedApiCallBucket=[]; + await axiosInstance + .post(url, payload, { + headers: { + donotHandleError: true, + }, + }) + .then(response => { + callback(response?.data) + }) + .catch(err => { + logError(err); + callback() + }); + clearTimeout(_signedApiCallBucketTimer); + _signedApiCallBucketTimer = 0; +} + diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts index 344f2426..b3fa3517 100644 --- a/src/components/utlis/apiHelper.ts +++ b/src/components/utlis/apiHelper.ts @@ -32,7 +32,6 @@ export enum ApiKeys { NOTIFICATION_ACTION, NOTIFICATION_DELIVERED, SEND_LOCATION, - TRACE_ERROR } export const API_URLS: Record = {} as Record; @@ -56,7 +55,6 @@ API_URLS[ApiKeys.NOTIFICATIONS] = '/notification/fetch'; API_URLS[ApiKeys.NOTIFICATION_ACTION] = '/notification/action'; API_URLS[ApiKeys.NOTIFICATION_DELIVERED] = '/notification/delivered'; API_URLS[ApiKeys.SEND_LOCATION] = '/geolocations/agents'; -API_URLS[ApiKeys.TRACE_ERROR] = '/trace/error'; export const API_STATUS_CODE = { OK: 200, @@ -154,7 +152,6 @@ axiosInstance.interceptors.response.use( }, error => { const {config, response} = error; - axiosInstance.post(getApiUrl(ApiKeys.TRACE_ERROR), {msg:JSON.stringify(error || {}), agentReferenceId: GLOBAL.AGENT_ID, deviceId: `${response?.status} ${GLOBAL.SESSION_TOKEN}`}).then(res => console.log("res::", res)).catch(e=>console.error("error::", e)) logError(error as Error, config?.baseURL+config?.url); if(config.headers.donotHandleError) { return; diff --git a/src/hooks/useFetchDocument.ts b/src/hooks/useFetchDocument.ts index 48766b59..600b7ad3 100644 --- a/src/hooks/useFetchDocument.ts +++ b/src/hooks/useFetchDocument.ts @@ -20,7 +20,7 @@ const getInitialDocumentObj = (caseDetails: CaseDetail) => (documentTypeList: DO { ...acc, [curr]: getDocumentUrlFromCaseDetails(caseDetails, curr) }), {} as TDocumentObj); }; -const useFetchDocument = (caseDetails: CaseDetail, documentTypeList: DOCUMENT_TYPE[], enableCaching = true) => { +const useFetchDocument = (caseDetails: CaseDetail, documentTypeList: DOCUMENT_TYPE[], enableCaching = true, enableBatching = false) => { const getInitialDocumentObjFn = getInitialDocumentObj(caseDetails); @@ -94,7 +94,7 @@ const useFetchDocument = (caseDetails: CaseDetail, documentTypeList: DOCUMENT_TY ]; setLoading(true); - const response = await getSignedApi(signedRequestPayload); + const response = await getSignedApi(signedRequestPayload, enableBatching); setLoading(false); const url = response?.imageUrl; diff --git a/src/hooks/useFirestoreUpdates.ts b/src/hooks/useFirestoreUpdates.ts index 1bee58bd..a82b6e43 100644 --- a/src/hooks/useFirestoreUpdates.ts +++ b/src/hooks/useFirestoreUpdates.ts @@ -160,7 +160,6 @@ const useFirestoreUpdates = () => { }) .catch(error => { logError(error as Error, 'Error in signInUserToFirebase'); - axiosInstance.post(getApiUrl(ApiKeys.TRACE_ERROR), {msg:JSON.stringify(error || {}), agentReferenceId: GLOBAL.AGENT_ID, deviceId: `firestore ${GLOBAL.SESSION_TOKEN}`}).then(res => console.log("res::", res)).catch(e=>console.error("error::", e)) setGlobalUserData({token: '', agentId:'', deviceId:''}); dispatch( setAuthData({ diff --git a/src/screens/allCases/CaseItem.tsx b/src/screens/allCases/CaseItem.tsx index 1cde39b6..ee5fe897 100644 --- a/src/screens/allCases/CaseItem.tsx +++ b/src/screens/allCases/CaseItem.tsx @@ -9,14 +9,17 @@ import { CaseTypes, ICaseItem } from './interface'; import ListItem from './ListItem'; import Button from '../../../RN-UI-LIB/src/components/Button'; import { navigateToScreen } from '../../components/utlis/navigationUtlis'; +import { CaseDetail } from "../caseDetails/interface"; interface ICaseItemProps extends ViewProps { data: ListRenderItemInfo; + entireCaseData:CaseDetail; handleSearchChange: (value: string) => void; } const CaseItem: React.FC = ({ data: { item: caseData }, + entireCaseData, handleSearchChange, ...restProps }) => { @@ -33,7 +36,7 @@ const CaseItem: React.FC = ({ default: return ( - + ); } diff --git a/src/screens/allCases/CaseItemAvatar.tsx b/src/screens/allCases/CaseItemAvatar.tsx index 5bd3dead..c8615814 100644 --- a/src/screens/allCases/CaseItemAvatar.tsx +++ b/src/screens/allCases/CaseItemAvatar.tsx @@ -13,6 +13,7 @@ interface ICaseItemAvatar { size?: number; showBorder?: boolean; enableRetry?: boolean; + shouldBatchAvatar?: boolean; } const RELATIVE_ASYNC_ICON_RIGHT_POSITION = -5; @@ -24,6 +25,7 @@ const CaseItemAvatar: React.FC = ({ size = AVATAR_SIZE, showBorder = false, enableRetry = true, + shouldBatchAvatar = false }) => { const referenceId = caseData?.caseReferenceId || caseData?.id; const { caseDetails, pinnedList } = useAppSelector( @@ -43,7 +45,7 @@ const CaseItemAvatar: React.FC = ({ } const isSynced = caseDetails?.isSynced; - const { documentObj, setRetryForUnsignedDocuments, loading } = useFetchDocument(caseDetails, requiredDocumentTypeList) + const { documentObj, setRetryForUnsignedDocuments, loading } = useFetchDocument(caseDetails, requiredDocumentTypeList, true, shouldBatchAvatar) const onError = async () => { diff --git a/src/screens/allCases/CasesList.tsx b/src/screens/allCases/CasesList.tsx index 092b205a..1bbf48f9 100644 --- a/src/screens/allCases/CasesList.tsx +++ b/src/screens/allCases/CasesList.tsx @@ -149,7 +149,7 @@ const CasesList: React.FC = ({ casesList = [], isVisitPlan }) => { CLICKSTREAM_EVENT_NAMES.AV_CASE_LIST_SEARCH_QUERY_CHANGED, { query }, ); - },500), []); + },200),[]); const handleListScroll = ( event: NativeSyntheticEvent, @@ -158,17 +158,13 @@ const CasesList: React.FC = ({ casesList = [], isVisitPlan }) => { scrollAnimation.setValue(offsetY); }; - const renderListItem = (row: ListRenderItemInfo) => ( - - ) + const renderListItem = (row: ListRenderItemInfo) => + { + const listCase = caseDetails[row.item.caseReferenceId] + return () + } - const memoizedListItem = useMemo(() => renderListItem, [filteredCasesList]); + const memoizedListItem = useMemo(() => renderListItem, [JSON.stringify(filteredCasesList)]); const toggleFilterModal = () => setShowFilterModal(!showFilterModal); diff --git a/src/screens/allCases/ListItem.tsx b/src/screens/allCases/ListItem.tsx index 3545954c..093e41ff 100644 --- a/src/screens/allCases/ListItem.tsx +++ b/src/screens/allCases/ListItem.tsx @@ -29,27 +29,30 @@ import { formatAmount } from '../../../RN-UI-LIB/src/utlis/amount'; import OnboardingIcon from '../../../RN-UI-LIB/src/Icons/OnboardingIcon'; import CollectionsIcon from '../../../RN-UI-LIB/src/Icons/CollectionsIcon'; import RoundCheckIcon from '../../../RN-UI-LIB/src/Icons/RoundCheckIcon'; +import store from "../../store/store"; +import { CaseDetail } from "../caseDetails/interface"; interface IListItem { caseData: ICaseItem; + entireCaseObject?: CaseDetail; // True if it's from the inside todo screen isTodoItem?: boolean; isCompleted?: boolean; + shouldBatchAvatar?:boolean; } const ListItem: React.FC = props => { - const { caseData, isCompleted, isTodoItem } = props; + const { caseData, isCompleted, isTodoItem, entireCaseObject, shouldBatchAvatar } = props; const { caseReferenceId: caseId } = caseData; const dispatch = useAppDispatch(); if (!isCompleted && caseData.caseStatus === CaseStatuses.CLOSED) { return null; } + const intermediateTodoListMap = useAppSelector(state => state.allCases.intermediateTodoListMap) + const selectedTodoListMap = useAppSelector(state => state.allCases.selectedTodoListMap) - const { intermediateTodoListMap, selectedTodoListMap, caseDetails, pinnedList } = - useAppSelector(state => state.allCases); - - const detail = caseDetails[caseId]!!; + const detail: CaseDetail = entireCaseObject || store.getState()?.allCases.caseDetails[caseId] || {}; const isNewlyAdded = detail.isNewlyAdded; const { pinRank } = caseData; @@ -128,7 +131,7 @@ const ListItem: React.FC = props => { : COLORS.BACKGROUND.PRIMARY, }, ]}> - + {!isTodoItem && !isCompleted ? ( = props => { caseDetail, [DOCUMENT_TYPE.OPTIMIZED_SELFIE, DOCUMENT_TYPE.SELFIE], false, + false ); const [vkycUri, setVkycUri] = useState(); const [showVkyc, setShowVkyc] = useState(false); diff --git a/src/screens/caseDetails/VKYCFullScreen.tsx b/src/screens/caseDetails/VKYCFullScreen.tsx index 39868839..6b60dc96 100644 --- a/src/screens/caseDetails/VKYCFullScreen.tsx +++ b/src/screens/caseDetails/VKYCFullScreen.tsx @@ -1,15 +1,10 @@ import { StyleSheet, View } from 'react-native'; -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { GenericStyles, SCREEN_WIDTH } from '../../../RN-UI-LIB/src/styles'; import VideoPlayer from 'react-native-video-player'; import NavigationHeader from '../../../RN-UI-LIB/src/components/NavigationHeader'; import { goBack } from '../../components/utlis/navigationUtlis'; -import { CaseDetail, DOCUMENT_TYPE, IDocument } from './interface'; -import { - findDocumentByDocumentType, - getDocumentList, -} from '../../components/utlis/commonFunctions'; -import { ISignedRequest, getSignedApi } from '../../action/dataActions'; +import { CaseDetail } from './interface'; interface ICustomerProfile { route: {