diff --git a/package.json b/package.json
index c0b74a84..800326e5 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"prepare": "husky install"
},
"dependencies": {
+ "@bam.tech/react-native-image-resizer": "3.0.5",
"@cobo/apm-rum-react-native": "^0.6.0",
"@elastic/apm-rum-core": "^5.17.0",
"@nozbe/watermelondb": "0.24.0",
diff --git a/src/ImageUploader.tsx b/src/ImageUploader.tsx
deleted file mode 100644
index 44702974..00000000
--- a/src/ImageUploader.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import axios from 'axios';
-import React from 'react';
-import { View, Button, Image } from 'react-native';
-import ImagePicker, { ImageLibraryOptions, launchImageLibrary } from 'react-native-image-picker';
-
-class ImageUploader extends React.Component {
- state = {
- image: null,
- };
-
- selectImage = () => {
- const options: ImageLibraryOptions = {
- mediaType: 'photo',
- quality: 1,
- };
-
- launchImageLibrary(options, (response) => {
- if (!response.didCancel) {
- console.log('response:', response);
- this.setState({ image: response.assets?.[0] });
- }
- });
- };
-
- uploadImage = async () => {
- const { image } = this.state;
-
- if (image) {
- const formData = new FormData();
- console.log('image:', image);
-
- // try {
- // const response = await fetch(image.uri);
- // const blobData = await response.blob();
-
- // formData.append('avatar', blobData, 'avatar.png');
-
- // const result = await axios.post('https://0ac3-115-111-223-26.in.ngrok.io/upload', formData, {
- // headers: {
- // 'Content-Type': 'multipart/form-data',
- // },
- // });
-
- // console.log('Request successful', result.data);
- // } catch (error) {
- // console.error('Request failed', error);
- // }
- console.log('image', image);
- formData.append('file[]', {
- uri: image.uri,
- name: 'image1.png',
- type: image.type,
- });
- formData.append('file[]', {
- uri: image.uri,
- name: 'image2.png',
- type: image.type,
- });
- formData.append('file[]', {
- uri: image.uri,
- name: 'image3.png',
- type: image.type,
- });
-
- const response = await axios.post(
- 'https://8ee6-115-111-223-26.in.ngrok.io/upload',
- formData,
- {
- headers: {
- 'Content-Type': 'multipart/form-data',
- },
- }
- );
- }
- };
-
- render() {
- const { image } = this.state;
-
- return (
-
-
- {image && }
-
-
- );
- }
-}
-
-export default ImageUploader;
diff --git a/src/action/caseApiActions.ts b/src/action/caseApiActions.ts
index 23af6e36..4c3ce5eb 100644
--- a/src/action/caseApiActions.ts
+++ b/src/action/caseApiActions.ts
@@ -12,9 +12,9 @@ import { GenericType } from '../common/GenericTypes';
import { addClickstreamEvent } from '../services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '../common/Constants';
import { PageRouteEnum } from '../screens/auth/ProtectedRouter';
-import { IDocument, removeDocuments, setDocumentReferenceId } from '../reducer/allCasesSlice';
-import { _map } from '../../RN-UI-LIB/src/utlis/common';
+import { MILLISECONDS_IN_A_MINUTE, _map } from '../../RN-UI-LIB/src/utlis/common';
import { logError } from '../components/utlis/errorUtils';
+import { IDocument, removeDocumentByQuestionKey } from '../reducer/feedbackImagesSlice';
// TODO: Need to add respective interfaces instead of any
interface IUnifiedData {
@@ -145,58 +145,42 @@ export const getCaseUnifiedData =
});
};
-const uploadFeedbackImages =
- (caseId: string, payload: IUploadImagePayload[]) => (dispatch: AppDispatch) => {
- const url = getApiUrl(ApiKeys.UPLOAD_FEEDBACK_IMAGES);
- axiosInstance
- .post(url, payload)
- .then(() => {
- dispatch(removeDocuments({ caseId, docs: payload }));
- })
- .catch((err) => {
- // Add to retry queue
- });
- };
-
export const uploadImages =
- (caseId: string, documents: Record, interactionId: string) =>
+ (caseKey: string, documents: Record, interactionReferenceId: string) =>
(dispatch: AppDispatch) => {
_map(documents, (questionKey) => {
if (!documents) {
return;
}
const fileDoc = documents[questionKey];
- if (!interactionId || !fileDoc) {
+ if (!interactionReferenceId || !fileDoc) {
return;
}
const { fileUri } = fileDoc;
const formData = new FormData();
- formData.append('images', {
+ formData.append(
+ 'originalImageData',
+ JSON.stringify({
+ interactionReferenceId,
+ questionKey,
+ })
+ );
+ formData.append('image', {
uri: fileUri,
- name: `image_${caseId}_${new Date().getTime()}`,
+ name: `image_${interactionReferenceId}`,
type: 'image/jpeg',
} as any);
- const url = getApiUrl(ApiKeys.UPLOAD_IMAGES);
+ const url = getApiUrl(ApiKeys.UPLOAD_FEEDBACK_IMAGES);
axiosInstance
- .post(url, formData, {
+ .put(url, formData, {
headers: {
'Content-Type': 'multipart/form-data',
+ donotHandleError: true,
},
+ timeout: 5 * MILLISECONDS_IN_A_MINUTE,
})
.then((res) => {
- const originalImageDocumentReferenceId = res?.data?.referenceId;
- dispatch(
- setDocumentReferenceId({ caseId, questionKey, originalImageDocumentReferenceId })
- );
- dispatch(
- uploadFeedbackImages(caseId, [
- {
- interactionId,
- questionKey,
- originalImageDocumentReferenceId,
- },
- ])
- );
+ dispatch(removeDocumentByQuestionKey({ caseKey, questionKey }));
})
.catch((err) => {
logError(err as Error, 'Error uploading image to document service');
@@ -204,25 +188,8 @@ export const uploadImages =
});
};
-const getDocsWithAndWithoutReferenceId = (docs: Record) => {
- const docsWithReferenceId: IDocument[] = [];
- const docsWithoutReferenceId: IDocument[] = [];
- _map(docs, (questionKey: string) => {
- const docRefId = docs?.[questionKey]?.originalImageDocumentReferenceId;
- if (docRefId) {
- docsWithReferenceId.push(docs?.[questionKey]);
- } else {
- docsWithoutReferenceId.push(docs?.[questionKey]);
- }
- });
- return {
- docsWithReferenceId,
- docsWithoutReferenceId,
- };
-};
-
export const uploadUnsyncedImages =
- (caseId: string, docs: Record) => (dispatch: AppDispatch) => {
- const { docsWithReferenceId, docsWithoutReferenceId } = getDocsWithAndWithoutReferenceId(docs);
- // dispatch(uploadFeedbackImages(caseId, docs))
+ (caseId: string, interactionId: string, docs: Record) =>
+ (dispatch: AppDispatch) => {
+ dispatch(uploadImages(caseId, docs, interactionId));
};
diff --git a/src/action/dataActions.ts b/src/action/dataActions.ts
index 2bd6c782..ba1816ff 100644
--- a/src/action/dataActions.ts
+++ b/src/action/dataActions.ts
@@ -7,7 +7,6 @@ import {
resetTodoList,
setLoading,
setVisitPlansUpdating,
- updateUnsyncedCaseDetail,
updateSingleCase,
} from '../reducer/allCasesSlice';
import {
@@ -71,12 +70,10 @@ export const postPinnedList =
export const syncCaseDetail =
(
payload: any,
- updatedCaseDetail?: any,
callbacks?: {
onSuccessCB?: (data: any, actions?: any, interactionId?: string) => void;
onErrorCB?: (e: Error) => void;
- },
- nextActions?: any
+ }
) =>
(dispatch: AppDispatch) => {
const offlineImageIdList = getOfflineImageId(payload);
@@ -104,7 +101,7 @@ export const syncCaseDetail =
text1: ToastMessages.FEEDBACK_SUCCESSFUL,
});
if (callbacks?.onSuccessCB != null && typeof callbacks?.onSuccessCB === 'function') {
- callbacks?.onSuccessCB(payload.data.answers, nextActions, interactionId);
+ callbacks?.onSuccessCB(payload.data.answers, interactionId);
}
})
.catch((e) => {
@@ -113,14 +110,6 @@ export const syncCaseDetail =
type: 'error',
text1: ToastMessages.FEEDBACK_FAILED,
});
- if (updatedCaseDetail && !updatedCaseDetail.offlineCaseKey) {
- dispatch(
- updateUnsyncedCaseDetail({
- caseId: payload.data.caseReferenceId,
- updatedCaseDetail,
- })
- );
- }
}
if (callbacks?.onErrorCB != null && typeof callbacks?.onErrorCB === 'function') {
callbacks?.onErrorCB(e);
diff --git a/src/components/expandableImage/ExpandableImage.tsx b/src/components/expandableImage/ExpandableImage.tsx
index eb547e67..7abbd4c4 100644
--- a/src/components/expandableImage/ExpandableImage.tsx
+++ b/src/components/expandableImage/ExpandableImage.tsx
@@ -8,9 +8,10 @@ interface IExpandableImage {
imageSrc?: string;
close?: () => void;
title?: string;
+ fallbackImage?: string;
}
-const imageHtml = (imageSrc?: string) => `
+const imageHtml = (imageSrc?: string, fallbackImage?: string) => `
Image Viewer
@@ -24,19 +25,37 @@ const imageHtml = (imageSrc?: string) => `
justify-content: center;
align-items: center;
}
+ .parent {
+ position: relative;
+ top: 0;
+ left: 0;
+ }
+ .image1 {
+ position: relative;
+ top: 0;
+ left: 0;
+ width: calc(100vw - 32px);
+ max-height: calc(100vh - 32px)
+ }
+ .image2 {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: calc(100vw - 32px);
+ max-height: calc(100vh - 32px)
+ }
-
-
-

-
-
+
+

+

+
`;
-const ExpandableImage: React.FC = ({ imageSrc, title, close }) => {
+const ExpandableImage: React.FC = ({ imageSrc, fallbackImage, title, close }) => {
return (
@@ -44,7 +63,7 @@ const ExpandableImage: React.FC = ({ imageSrc, title, close })
scalesPageToFit={true}
bounces={false}
scrollEnabled={false}
- source={{ html: imageHtml(imageSrc) }}
+ source={{ html: imageHtml(imageSrc, fallbackImage) }}
/>
);
diff --git a/src/components/form/components/ImageUpload.tsx b/src/components/form/components/ImageUpload.tsx
index b2392f3c..54c9772f 100644
--- a/src/components/form/components/ImageUpload.tsx
+++ b/src/components/form/components/ImageUpload.tsx
@@ -15,11 +15,18 @@ import { AnswerType } from '../interface';
import ErrorMessage from './ErrorMessage';
import withObservables from '@nozbe/with-observables';
import OfflineImageDAO from '../../../wmDB/dao/OfflineImageDAO';
-import { CLICKSTREAM_EVENT_NAMES, PrefixJpegBase64Image } from '../../../common/Constants';
+import {
+ CLICKSTREAM_EVENT_NAMES,
+ PrefixJpegBase64Image,
+ getPrefixBase64Image,
+} from '../../../common/Constants';
import { addClickstreamEvent } from '../../../services/clickstreamEventService';
import { isQuestionMandatory, validateInput } from '../services/validation.service';
import { CaseAllocationType } from '../../../screens/allCases/interface';
-import { addDocumentToUpload } from '../../../reducer/allCasesSlice';
+import {
+ addIntermediateDocument,
+ deleteIntermediateDocument,
+} from '../../../reducer/feedbackImagesSlice';
interface IOfflineImage {
idx: string;
@@ -70,7 +77,7 @@ const ImageUpload: React.FC = (props) => {
if (!fileUri) {
return;
}
- dispatch(addDocumentToUpload({ caseId, fileUri, questionKey }));
+ dispatch(addIntermediateDocument({ caseId, fileUri, questionKey }));
};
const handleChange = async (clickedImage: IImageDetails, onChange: (...event: any[]) => void) => {
@@ -98,6 +105,11 @@ const ImageUpload: React.FC = (props) => {
await OfflineImageDAO.addImage(base64Image, uniqueId);
};
+ const handleImageDelete = () => {
+ setImageId('');
+ dispatch(deleteIntermediateDocument({ caseId, questionKey: questionId }));
+ };
+
// TODO : add the validator back when firestore is fixed.
return (
@@ -126,7 +138,7 @@ const ImageUpload: React.FC = (props) => {
uri: props.offlineImages?.find((image) => image.idx === imageId)?.imageData || '',
}}
>
- setImageId('')} style={styles.deleteButton}>
+
diff --git a/src/components/form/index.tsx b/src/components/form/index.tsx
index d5c720bb..82e55ee2 100644
--- a/src/components/form/index.tsx
+++ b/src/components/form/index.tsx
@@ -17,8 +17,7 @@ import useIsOnline from '../../hooks/useIsOnline';
import {
getUpdatedAVCaseDetail,
getUpdatedCollectionCaseDetail,
- setDocumentInteractionId,
- updateUnsyncedCaseDetail,
+ updateCaseDetail,
} from '../../reducer/allCasesSlice';
import { deleteInteraction, deleteJourney, updateInteraction } from '../../reducer/caseReducer';
import { CaseAllocationType } from '../../screens/allCases/interface';
@@ -43,8 +42,8 @@ import Submit from './Submit';
import { toast } from '../../../RN-UI-LIB/src/components/toast';
import { ToastMessages } from '../../screens/allCases/constants';
import { uploadImages } from '../../action/caseApiActions';
-import { isEmpty } from '../../../RN-UI-LIB/src/utlis/common';
import { GenericFunctionArgs } from '../../common/GenericTypes';
+import { setDocumentInteractionId, setDocumentsToUpload } from '../../reducer/feedbackImagesSlice';
interface IWidget {
route: {
@@ -63,20 +62,26 @@ const Widget: React.FC = (props) => {
const [isSubmitting, setIsSubmitting] = useState(false);
const { params } = props.route;
const { caseId, journey, handleCloseRouting } = params;
- const { caseType, templateData, caseData, dataToBeValidated, docsToBeUploaded } = useAppSelector(
- (state) => {
- const caseType =
- state.allCases.caseDetails[caseId]?.caseType ||
- CaseAllocationType.ADDRESS_VERIFICATION_CASE;
- return {
- caseType,
- templateData: state.case.templateData[caseType],
- caseData: state.allCases.caseDetails[caseId],
- dataToBeValidated: state.case.caseForm?.[caseId]?.[journey],
- docsToBeUploaded: state.allCases.docsToBeUploaded?.[caseId]?.documents,
- };
- }
- );
+ let caseKey = useRef('');
+ const {
+ caseType,
+ templateData,
+ caseData,
+ dataToBeValidated,
+ docsToBeUploaded,
+ intermediateDocsToBeUploaded,
+ } = useAppSelector((state) => {
+ const caseType =
+ state.allCases.caseDetails[caseId]?.caseType || CaseAllocationType.ADDRESS_VERIFICATION_CASE;
+ return {
+ caseType,
+ templateData: state.case.templateData[caseType],
+ caseData: state.allCases.caseDetails[caseId],
+ dataToBeValidated: state.case.caseForm?.[caseId]?.[journey],
+ docsToBeUploaded: state.feedbackImages.docsToBeUploaded,
+ intermediateDocsToBeUploaded: state.feedbackImages.intermediateDocsToBeUploaded,
+ };
+ });
const name = getWidgetNameFromRoute(props.route.name, caseType);
const { sections, conditionActions: widgetConditionActions, isLeaf } = templateData.widget[name];
const sectionMap = templateData.sections;
@@ -140,19 +145,16 @@ const Widget: React.FC = (props) => {
setError(data);
};
- // Upload original doc to document service => get docRefId
const uploadOriginalDocs = (interactionId: string) => {
- if (isEmpty(docsToBeUploaded)) {
+ const docs = intermediateDocsToBeUploaded?.[caseId]?.documents;
+ if (!docs) {
return;
}
- dispatch(setDocumentInteractionId({ caseId, interactionId }));
- if (isOnline) {
- dispatch(uploadImages(caseId, docsToBeUploaded, interactionId));
- } else {
- }
+ dispatch(setDocumentInteractionId({ caseKey: caseKey.current, interactionId }));
+ dispatch(uploadImages(caseKey.current, docs, interactionId));
};
- const onSuccessfulSubmit = (data: any, nextActions?: any, interactionId?: string) => {
+ const onSuccessfulSubmit = (data: any, interactionId: string, nextActions?: any) => {
setIsSubmitting(false);
navigateToScreen(
caseType === CaseAllocationType.COLLECTION_CASE ? 'collectionCaseDetail' : 'caseDetail',
@@ -193,6 +195,16 @@ const Widget: React.FC = (props) => {
});
};
+ const onErrorSubmit = (updatedCaseDetails: any) => {
+ setIsSubmitting(false);
+ dispatch(
+ updateCaseDetail({
+ caseKey: caseKey.current,
+ updatedCaseDetails,
+ })
+ );
+ };
+
const handleSubmitJourney = async (data: any, coords: Geolocation.GeoCoordinates) => {
dispatch(
updateInteraction({
@@ -203,6 +215,8 @@ const Widget: React.FC = (props) => {
})
);
if (caseType === CaseAllocationType.COLLECTION_CASE) {
+ caseKey.current = `${caseId}_${Date.now()}`;
+ dispatch(setDocumentsToUpload({ caseId, caseKey: caseKey.current }));
const updatedCase = getUpdatedCollectionCaseDetail({
caseData,
answer: data,
@@ -215,17 +229,16 @@ const Widget: React.FC = (props) => {
const unSyncedCase = getUnSyncedCase(updatedCase);
const transformedPayload = await getTransformedCollectionCaseItem(unSyncedCase);
dispatch(
- syncCaseDetail(transformedPayload, updatedCase, {
- onSuccessCB: onSuccessfulSubmit,
- onErrorCB: () => {
- setIsSubmitting(false);
- },
+ syncCaseDetail(transformedPayload, {
+ onSuccessCB: (apiCaseData, interactionId: string) =>
+ onSuccessfulSubmit(apiCaseData, interactionId),
+ onErrorCB: () => onErrorSubmit(updatedCase),
})
);
} else {
dispatch(
- updateUnsyncedCaseDetail({
- caseId,
+ updateCaseDetail({
+ caseKey: caseKey.current,
updatedCaseDetail: updatedCase,
})
);
@@ -267,22 +280,16 @@ const Widget: React.FC = (props) => {
const unSyncedCase = getUnSyncedCase(updatedCase);
const transformedPayload = await getTransformedAvCase(unSyncedCase, templateId);
dispatch(
- syncCaseDetail(
- transformedPayload,
- updatedCase,
- {
- onSuccessCB: onSuccessfulSubmit,
- onErrorCB: () => {
- setIsSubmitting(false);
- },
- },
- nextActions
- )
+ syncCaseDetail(transformedPayload, {
+ onSuccessCB: (apiCaseData, interactionId: string) =>
+ onSuccessfulSubmit(apiCaseData, interactionId, nextActions),
+ onErrorCB: () => onErrorSubmit(updatedCase),
+ })
);
} else {
dispatch(
- updateUnsyncedCaseDetail({
- caseId,
+ updateCaseDetail({
+ caseKey: caseId,
updatedCaseDetail: updatedCase,
})
);
diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts
index 344664a6..ec5e36d8 100644
--- a/src/components/utlis/apiHelper.ts
+++ b/src/components/utlis/apiHelper.ts
@@ -49,8 +49,8 @@ export enum ApiKeys {
CASES_SEND_ID = 'CASES_SEND_ID',
FETCH_CASES = 'FETCH_CASES',
GET_FORECLOSURE_AMOUNT = 'GET_FORECLOSURE_AMOUNT',
- UPLOAD_IMAGES = 'UPLOAD_IMAGES',
UPLOAD_FEEDBACK_IMAGES = 'UPLOAD_FEEDBACK_IMAGES',
+ ORIGINAL_IMAGES = 'ORIGINAL_IMAGES',
}
export const API_URLS: Record = {} as Record;
@@ -86,8 +86,8 @@ API_URLS[ApiKeys.CASES_SYNC_STATUS] = '/cases/agents/sync-status';
API_URLS[ApiKeys.CASES_SEND_ID] = '/cases/sync';
API_URLS[ApiKeys.FETCH_CASES] = '/cases/agents/{agentReferenceId}';
API_URLS[ApiKeys.GET_FORECLOSURE_AMOUNT] = '/{loanAccountNumber}/pre-closure-amount';
-API_URLS[ApiKeys.UPLOAD_IMAGES] = '/cases/images';
-API_URLS[ApiKeys.UPLOAD_FEEDBACK_IMAGES] = '/cases/feedback/original-images';
+API_URLS[ApiKeys.UPLOAD_FEEDBACK_IMAGES] = '/feedback/persist-original-images';
+API_URLS[ApiKeys.ORIGINAL_IMAGES] = '/feedback/original-images';
export const API_STATUS_CODE = {
OK: 200,
diff --git a/src/local-server.js b/src/local-server.js
deleted file mode 100644
index e7fb871f..00000000
--- a/src/local-server.js
+++ /dev/null
@@ -1,28 +0,0 @@
-const express = require('express');
-const multer = require('multer');
-const path = require('path');
-
-const app = express();
-// const storage = multer.diskStorage({
-// destination: 'uploads/',
-// filename: (req, file, cb) => {
-// cb(null, file.originalname);
-// },
-// });
-const upload = multer({ dest: 'uploads/' });
-
-app.post('/upload', upload.array('file[]'), (req, res) => {
- const files = req.files;
- console.log('request.file:::', files);
- // if (!req.files) {
- // res.status(400).json({ error: 'No image file provided' });
- // } else {
- // const imagePath = req.files[0].path;
- // res.json({ message: 'Image uploaded successfully', imagePath });
- // }
- res.send('Files uploaded successfully');
-});
-
-app.listen(3000, () => {
- console.log('Server is running on port 3000');
-});
diff --git a/src/reducer/allCasesSlice.ts b/src/reducer/allCasesSlice.ts
index 439a35fb..ad7e1d07 100644
--- a/src/reducer/allCasesSlice.ts
+++ b/src/reducer/allCasesSlice.ts
@@ -1,6 +1,6 @@
import { createSlice } from '@reduxjs/toolkit';
import { toast } from '../../RN-UI-LIB/src/components/toast';
-import { _map, isEmpty } from '../../RN-UI-LIB/src/utlis/common';
+import { _map } from '../../RN-UI-LIB/src/utlis/common';
import { findDocumentByDocumentType } from '../components/utlis/commonFunctions';
import { CLICKSTREAM_EVENT_NAMES, FirestoreUpdateTypes } from '../common/Constants';
import { getCurrentScreen, navigateToScreen } from '../components/utlis/navigationUtlis';
@@ -18,21 +18,9 @@ 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 { IUploadImagePayload } from '../action/caseApiActions';
import { IAvatarUri } from '../action/caseListAction';
export type ICasesMap = { [key: string]: ICaseItem };
-
-export interface IDocument {
- questionKey: string;
- originalImageDocumentReferenceId: string;
- fileUri?: string;
-}
-
-interface IDocumentDetail {
- interactionId: string;
- documents: Record;
-}
interface IAllCasesSlice {
casesList: ICaseItem[];
casesListMap: ICasesMap;
@@ -47,8 +35,6 @@ interface IAllCasesSlice {
newlyPinnedCases: number;
completedCases: number;
caseDetails: Record;
- unsyncedCaseDetail: Record;
- docsToBeUploaded: Record;
searchQuery: string;
isOnboarded: boolean;
visitPlansUpdating: boolean;
@@ -72,8 +58,6 @@ const initialState: IAllCasesSlice = {
newlyPinnedCases: 0,
completedCases: 0,
caseDetails: {},
- unsyncedCaseDetail: {},
- docsToBeUploaded: {},
searchQuery: '',
isOnboarded: false,
visitPlansUpdating: false,
@@ -406,14 +390,12 @@ const allCasesSlice = createSlice({
}
}
},
- updateUnsyncedCaseDetail: (state, action) => {
- const { caseId, updatedCaseDetail } = action.payload;
- let caseKey: string = caseId;
+ updateCaseDetail: (state, action) => {
+ const { caseKey, updatedCaseDetail } = action.payload;
if (updatedCaseDetail.caseType === CaseAllocationType.COLLECTION_CASE) {
- caseKey += '_' + Date.now();
updatedCaseDetail.offlineCaseKey = caseKey;
}
- state.unsyncedCaseDetail[caseKey] = updatedCaseDetail;
+ state.caseDetails[caseKey] = updatedCaseDetail;
},
updateSingleCase: (state, action) => {
const { data, id, caseType, offlineCaseKey } = action.payload;
@@ -425,7 +407,7 @@ const allCasesSlice = createSlice({
isSynced: true,
};
if (offlineCaseKey) {
- delete state.unsyncedCaseDetail[offlineCaseKey];
+ delete state.caseDetails[offlineCaseKey];
}
}
return;
@@ -583,55 +565,6 @@ const allCasesSlice = createSlice({
}
});
},
- addDocumentToUpload: (state, action) => {
- const { caseId, questionKey, fileUri } = action.payload;
- const doc = {
- questionKey,
- fileUri,
- originalImageDocumentReferenceId: '',
- };
- if (state.docsToBeUploaded?.[caseId]) {
- const newDocsMap = { ...state.docsToBeUploaded[caseId].documents, doc };
- state.docsToBeUploaded[caseId].documents = newDocsMap;
- } else {
- state.docsToBeUploaded[caseId] = {
- interactionId: '',
- documents: {
- [questionKey]: doc,
- },
- };
- }
- },
- removeDocuments: (state, action) => {
- const { docs, caseId } = action.payload;
- docs?.forEach((doc: IUploadImagePayload) => {
- const { questionKey } = doc;
- // delete respective document
- if (state.docsToBeUploaded?.[caseId]?.documents?.[questionKey]) {
- delete state.docsToBeUploaded?.[caseId]?.documents?.[questionKey];
- }
- // delete the whole object if no documents remaining
- if (isEmpty(state.docsToBeUploaded?.[caseId]?.documents)) {
- delete state.docsToBeUploaded?.[caseId];
- }
- });
- },
- setDocumentInteractionId: (state, action) => {
- const { caseId, interactionId } = action.payload;
- if (state.docsToBeUploaded[caseId]) {
- state.docsToBeUploaded[caseId].interactionId = interactionId;
- }
- },
- setDocumentReferenceId: (state, action) => {
- const { caseId, questionKey, originalImageDocumentReferenceId } = action.payload;
- if (state.docsToBeUploaded[caseId]) {
- const doc = state.docsToBeUploaded[caseId].documents[questionKey];
- state.docsToBeUploaded[caseId].documents[questionKey] = {
- ...doc,
- originalImageDocumentReferenceId,
- };
- }
- },
setCasesImageUri: (state, action) => {
const imageUris: IAvatarUri[] = action.payload;
imageUris.forEach(({ caseId, imageUri }) => {
@@ -651,7 +584,7 @@ export const {
deleteIntermediateTodoListItem,
setSelectedTodoListMap,
resetSelectedTodoList,
- updateUnsyncedCaseDetail,
+ updateCaseDetail,
updateSingleCase,
updateCaseDetailsFirestore,
toggleNewlyAddedCase,
@@ -660,10 +593,6 @@ export const {
setVisitPlansUpdating,
resetNewVisitedCases,
syncCasesByFallback,
- addDocumentToUpload,
- setDocumentReferenceId,
- removeDocuments,
- setDocumentInteractionId,
setCasesImageUri,
} = allCasesSlice.actions;
diff --git a/src/reducer/feedbackImagesSlice.ts b/src/reducer/feedbackImagesSlice.ts
new file mode 100644
index 00000000..fff872af
--- /dev/null
+++ b/src/reducer/feedbackImagesSlice.ts
@@ -0,0 +1,94 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { isEmpty } from '../../RN-UI-LIB/src/utlis/common';
+
+export interface IDocument {
+ fileUri?: string;
+}
+
+interface IDocumentDetail {
+ interactionId: string;
+ documents: Record;
+}
+
+interface IImagesSlice {
+ intermediateDocsToBeUploaded: Record;
+ docsToBeUploaded: Record;
+}
+
+const initialState: IImagesSlice = {
+ intermediateDocsToBeUploaded: {},
+ docsToBeUploaded: {},
+};
+
+const feedbackImagesSlice = createSlice({
+ name: 'feedbackImages',
+ initialState,
+ reducers: {
+ addIntermediateDocument: (state, action) => {
+ const { caseId, questionKey, fileUri } = action.payload;
+ const doc = {
+ questionKey,
+ fileUri,
+ originalImageDocumentReferenceId: '',
+ };
+ if (state.intermediateDocsToBeUploaded?.[caseId]) {
+ const newDocsMap = { ...state.intermediateDocsToBeUploaded[caseId].documents, doc };
+ state.intermediateDocsToBeUploaded[caseId].documents = newDocsMap;
+ } else {
+ state.intermediateDocsToBeUploaded[caseId] = {
+ interactionId: '',
+ documents: {
+ [questionKey]: doc,
+ },
+ };
+ }
+ },
+ deleteIntermediateDocument: (state, action) => {
+ const { caseId, questionKey } = action.payload;
+ // delete respective document
+ if (state.intermediateDocsToBeUploaded?.[caseId]?.documents?.[questionKey]) {
+ delete state.intermediateDocsToBeUploaded?.[caseId]?.documents?.[questionKey];
+ }
+ // delete the whole object if no documents remaining
+ if (isEmpty(state.intermediateDocsToBeUploaded?.[caseId]?.documents)) {
+ delete state.intermediateDocsToBeUploaded?.[caseId];
+ }
+ },
+ setDocumentsToUpload: (state, action) => {
+ const { caseId, caseKey } = action.payload;
+ if (state.intermediateDocsToBeUploaded?.[caseId]) {
+ state.docsToBeUploaded[caseKey] = {
+ ...state.intermediateDocsToBeUploaded[caseId],
+ };
+ delete state.intermediateDocsToBeUploaded?.[caseId];
+ }
+ },
+ setDocumentInteractionId: (state, action) => {
+ const { caseKey, interactionId } = action.payload;
+ if (state.docsToBeUploaded[caseKey]) {
+ state.docsToBeUploaded[caseKey].interactionId = interactionId;
+ }
+ },
+ removeDocumentByQuestionKey: (state, action) => {
+ const { caseKey, questionKey } = action.payload;
+ // delete respective document
+ if (state.docsToBeUploaded[caseKey]?.documents?.[questionKey]) {
+ delete state.docsToBeUploaded[caseKey]?.documents?.[questionKey];
+ }
+ // delete the whole object if no documents remaining
+ if (isEmpty(state.docsToBeUploaded?.[caseKey]?.documents)) {
+ delete state.docsToBeUploaded?.[caseKey];
+ }
+ },
+ },
+});
+
+export const {
+ addIntermediateDocument,
+ deleteIntermediateDocument,
+ setDocumentsToUpload,
+ setDocumentInteractionId,
+ removeDocumentByQuestionKey,
+} = feedbackImagesSlice.actions;
+
+export default feedbackImagesSlice.reducer;
diff --git a/src/screens/caseDetails/feedback/FeedbackDetailImageItem.tsx b/src/screens/caseDetails/feedback/FeedbackDetailImageItem.tsx
index b35953a6..54366cd5 100644
--- a/src/screens/caseDetails/feedback/FeedbackDetailImageItem.tsx
+++ b/src/screens/caseDetails/feedback/FeedbackDetailImageItem.tsx
@@ -28,7 +28,10 @@ const FeedbackDetailImageItem: React.FC = ({ image })
};
const questionText = getQuestionText(image);
-
+ const originalImageUri = image.metadata?.originalDocumentSignedUri || image.inputText;
+ // [TODO]: Lat long is merely for testing in UAT, will remove it once get the signoff.
+ const latitude = image.metadata?.image_latitude;
+ const longitude = image.metadata?.image_longitude;
return (
{questionText}
@@ -47,6 +50,9 @@ const FeedbackDetailImageItem: React.FC = ({ image })
onPress={handleExpandImage}
/>
+
+ Geolocation: (Lat:{latitude}, Long:{longitude})
+
= ({ image })
animationType="fade"
>
diff --git a/src/screens/caseDetails/interactionsHandler.tsx b/src/screens/caseDetails/interactionsHandler.tsx
index f8c298a8..ac38b087 100644
--- a/src/screens/caseDetails/interactionsHandler.tsx
+++ b/src/screens/caseDetails/interactionsHandler.tsx
@@ -11,7 +11,8 @@ import {
import { addClickstreamEvent } from '../../services/clickstreamEventService';
import { CaseAllocationType } from '../allCases/interface';
import { CaseDetail } from './interface';
-import { uploadUnsyncedImages } from '../../action/caseApiActions';
+import { uploadImages, uploadUnsyncedImages } from '../../action/caseApiActions';
+import { setDocumentInteractionId } from '../../reducer/feedbackImagesSlice';
export const getUnSyncedCase = (updatedCaseDetail: CaseDetail | undefined): any => {
const caseId = updatedCaseDetail?.id;
@@ -22,9 +23,9 @@ export const getUnSyncedCase = (updatedCaseDetail: CaseDetail | undefined): any
const interactionsHandler = () => {
const dispatch = useAppDispatch();
const isOnline = useIsOnline();
- const { unsyncedCasesDetails, docsToBeUploaded } = useAppSelector((state) => ({
- unsyncedCasesDetails: state.allCases.unsyncedCaseDetail,
- docsToBeUploaded: state.allCases.docsToBeUploaded,
+ const { allCasesDetails, docsToBeUploaded } = useAppSelector((state) => ({
+ allCasesDetails: state.allCases.caseDetails,
+ docsToBeUploaded: state.feedbackImages.docsToBeUploaded,
}));
const { templateId } = useAppSelector(
(state) => state.case.templateData[CaseAllocationType.ADDRESS_VERIFICATION_CASE]
@@ -36,17 +37,26 @@ const interactionsHandler = () => {
const inProgressCaseIds = useRef([]);
+ const handleSuccessSubmit = (caseKey: string, interactionId: string) => {
+ const docs = docsToBeUploaded?.[caseKey]?.documents;
+ if (!docs) {
+ return;
+ }
+ dispatch(setDocumentInteractionId({ caseKey, interactionId }));
+ dispatch(uploadImages(caseKey, docs, interactionId));
+ };
+
useEffect(() => {
- if (!unsyncedCasesDetails) return;
+ if (!allCasesDetails) return;
let notSyncedCases: Array = [];
// [DISCUSS]: isApiCalled not getting true anywhere.
- _map(unsyncedCasesDetails, (el) => {
+ _map(allCasesDetails, (el) => {
if (
- unsyncedCasesDetails[el]?.isSynced === false &&
- unsyncedCasesDetails[el]?.isApiCalled === false &&
+ allCasesDetails[el]?.isSynced === false &&
+ allCasesDetails[el]?.isApiCalled === false &&
!inProgressCaseIds.current.includes(el)
) {
- const unSyncedCase = getUnSyncedCase(unsyncedCasesDetails[el]);
+ const unSyncedCase = getUnSyncedCase(allCasesDetails[el]);
notSyncedCases.push(unSyncedCase);
}
});
@@ -59,18 +69,23 @@ const interactionsHandler = () => {
* inProgressCaseIds not removing the processed ID,
* this will prevent processing of failed api cases again.
*/
- inProgressCaseIds.current.push(caseItem.offlineCaseKey || caseItem.id);
+ const caseKey = caseItem.offlineCaseKey || caseItem.id;
+ inProgressCaseIds.current.push(caseKey);
let modifiedCaseItem: any;
if (caseItem?.caseType === CaseAllocationType.COLLECTION_CASE) {
modifiedCaseItem = await getTransformedCollectionCaseItem(caseItem);
} else {
modifiedCaseItem = await getTransformedAvCase(caseItem, templateId);
}
- dispatch(syncCaseDetail(modifiedCaseItem));
+ dispatch(
+ syncCaseDetail(modifiedCaseItem, {
+ onSuccessCB: (_, interactionId) => handleSuccessSubmit(caseKey, interactionId),
+ })
+ );
}
}
})();
- }, [unsyncedCasesDetails, isOnline]);
+ }, [allCasesDetails, isOnline]);
useEffect(() => {
if (!isOnline) {
@@ -81,13 +96,14 @@ const interactionsHandler = () => {
}
_map(docsToBeUploaded, (caseId) => {
const interactionId = docsToBeUploaded[caseId]?.interactionId;
+ // No interactionId means form is not submitted yet
if (!interactionId) {
return;
}
const doc = docsToBeUploaded[caseId].documents;
- dispatch(uploadUnsyncedImages(caseId, doc));
+ dispatch(uploadUnsyncedImages(caseId, interactionId, doc));
});
- }, [docsToBeUploaded, isOnline]);
+ }, [isOnline]);
return null;
};
diff --git a/src/store/store.ts b/src/store/store.ts
index ada64ea9..cf3282a6 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -27,6 +27,7 @@ import feedbackHistorySlice from '../reducer/feedbackHistorySlice';
import notificationsSlice from '../reducer/notificationsSlice';
import MetadataSlice from '../reducer/metadataSlice';
import foregroundServiceSlice from '../reducer/foregroundServiceSlice';
+import feedbackImagesSlice from '../reducer/feedbackImagesSlice';
const rootReducer = combineReducers({
case: caseReducer,
@@ -44,6 +45,7 @@ const rootReducer = combineReducers({
notifications: notificationsSlice,
metadata: MetadataSlice,
foregroundService: foregroundServiceSlice,
+ feedbackImages: feedbackImagesSlice,
});
const persistConfig = {
@@ -61,6 +63,7 @@ const persistConfig = {
'repayments',
'feedbackHistory',
'address',
+ 'feedbackImages',
],
blackList: ['case', 'filters'],
};
diff --git a/src/types/feedback.types.ts b/src/types/feedback.types.ts
index 0cb64968..e92bee14 100644
--- a/src/types/feedback.types.ts
+++ b/src/types/feedback.types.ts
@@ -16,6 +16,11 @@ export enum OPTION_TAG {
IMAGE_UPLOAD = 'IMAGE_UPLOAD',
}
+interface IImageMetadata {
+ image_created_at: string;
+ originalDocumentSignedUri: string;
+}
+
export interface IAnswerView {
interactionId?: number;
referenceId?: string;
@@ -30,6 +35,7 @@ export interface IAnswerView {
inputDate?: string;
inputText?: string;
questionTag?: OPTION_TAG;
+ metadata?: IImageMetadata;
}
export enum FEEDBACK_TYPE {
diff --git a/uploads/25ed825bd95fc05adfe97a408d814af0.jpg b/uploads/25ed825bd95fc05adfe97a408d814af0.jpg
deleted file mode 100644
index 2b32a2f7..00000000
Binary files a/uploads/25ed825bd95fc05adfe97a408d814af0.jpg and /dev/null differ
diff --git a/uploads/40bfe3d5000611c90feb89a53a1a2a5b b/uploads/40bfe3d5000611c90feb89a53a1a2a5b
deleted file mode 100644
index 2b32a2f7..00000000
Binary files a/uploads/40bfe3d5000611c90feb89a53a1a2a5b and /dev/null differ
diff --git a/uploads/558ba9f23d0e23a0ffc665315ca1ba89 b/uploads/558ba9f23d0e23a0ffc665315ca1ba89
deleted file mode 100644
index 2b32a2f7..00000000
Binary files a/uploads/558ba9f23d0e23a0ffc665315ca1ba89 and /dev/null differ
diff --git a/uploads/file.jpeg b/uploads/file.jpeg
deleted file mode 100644
index 6cb21670..00000000
Binary files a/uploads/file.jpeg and /dev/null differ
diff --git a/yarn.lock b/yarn.lock
index e89e004f..bf83f2c3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -957,6 +957,11 @@
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
+"@bam.tech/react-native-image-resizer@3.0.5":
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/@bam.tech/react-native-image-resizer/-/react-native-image-resizer-3.0.5.tgz#6661ba020de156268f73bdc92fbb93ef86f88a13"
+ integrity sha512-u5QGUQGGVZiVCJ786k9/kd7pPRZ6eYfJCYO18myVCH8FbVI7J8b5GT2Svjj2x808DlWeqfaZOOzxPqo27XYvrQ==
+
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -1729,48 +1734,123 @@
redux-thunk "^2.4.2"
reselect "^4.1.7"
-"@sentry/browser@7.29.0":
- version "7.29.0"
- resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.29.0.tgz#eb162b50adec33ac49ecd3dc930bdffbfda8098e"
- integrity sha512-Af+dIcntaw405Wt7myDOMGDxiszfy4aBdshrEKYbGgcfHjgXBIdF3iKlNatvl6nrOm+IOVuKgSpCLOr2hiCwzw==
+"@sentry-internal/tracing@7.52.0":
+ version "7.52.0"
+ resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.52.0.tgz#c4e0750ad0c3949c5bb4b59cbb708b0fef274080"
+ integrity sha512-o1YPcRGtC9tjeFCvWRJsbgK94zpExhzfxWaldAKvi3PuWEmPeewSdO/Q5pBIY1QonvSI+Q3gysLRcVlLYHhO5A==
dependencies:
- "@sentry/core" "7.29.0"
- "@sentry/replay" "7.29.0"
- "@sentry/types" "7.29.0"
- "@sentry/utils" "7.29.0"
+ "@sentry/core" "7.52.0"
+ "@sentry/types" "7.52.0"
+ "@sentry/utils" "7.52.0"
tslib "^1.9.3"
-"@sentry/core@7.29.0":
- version "7.29.0"
- resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.29.0.tgz#bc4b54d56cf7652598d4430cf43ea97cc069f6fe"
- integrity sha512-+e9aIp2ljtT4EJq3901z6TfEVEeqZd5cWzbKEuQzPn2UO6If9+Utd7kY2Y31eQYb4QnJgZfiIEz1HonuYY6zqQ==
+"@sentry/browser@7.52.0":
+ version "7.52.0"
+ resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.52.0.tgz#55d266c89ed668389ff687e5cc885c27016ea85c"
+ integrity sha512-Sib0T24cQCqqqAhg+nZdfI7qNYGE03jiM3RbY7yG5UoycdnJzWEwrBVSzRTgg3Uya9TRTEGJ+d9vxPIU5TL7TA==
dependencies:
- "@sentry/types" "7.29.0"
- "@sentry/utils" "7.29.0"
+ "@sentry-internal/tracing" "7.52.0"
+ "@sentry/core" "7.52.0"
+ "@sentry/replay" "7.52.0"
+ "@sentry/types" "7.52.0"
+ "@sentry/utils" "7.52.0"
tslib "^1.9.3"
-"@sentry/replay@7.29.0":
- version "7.29.0"
- resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.29.0.tgz#75d5bb9df39e0a31994be245032c9998af62a304"
- integrity sha512-Gw7HgviJQu6pX5RFQGVY38Av4qFn9otrZdwSSl/QK5hIyg6yhlh5h7U0ydZkrYYGiW6Z6SYYRpEWCJc/Wbh+ZQ==
+"@sentry/cli@2.17.5":
+ version "2.17.5"
+ resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.17.5.tgz#d41e24893a843bcd41e14274044a7ddea9332824"
+ integrity sha512-0tXjLDpaKB46851EMJ6NbP0o9/gdEaDSLAyjEtXxlVO6+RyhUj6x6jDwn0vis8n/7q0AvbIjAcJrot+TbZP+WQ==
dependencies:
- "@sentry/core" "7.29.0"
- "@sentry/types" "7.29.0"
- "@sentry/utils" "7.29.0"
+ https-proxy-agent "^5.0.0"
+ node-fetch "^2.6.7"
+ progress "^2.0.3"
+ proxy-from-env "^1.1.0"
+ which "^2.0.2"
-"@sentry/types@7.29.0":
- version "7.29.0"
- resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.29.0.tgz#ed829b6014ee19049035fec6af2b4fea44ff28b8"
- integrity sha512-DmoEpoqHPty3VxqubS/5gxarwebHRlcBd/yuno+PS3xy++/i9YPjOWLZhU2jYs1cW68M9R6CcCOiC9f2ckJjdw==
-
-"@sentry/utils@7.29.0":
- version "7.29.0"
- resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.29.0.tgz#cbf8f87dd851b0fdc7870db9c68014c321c3bab8"
- integrity sha512-ICcBwTiBGK8NQA8H2BJo0JcMN6yCeKLqNKNMVampRgS6wSfSk1edvcTdhRkW3bSktIGrIPZrKskBHyMwDGF2XQ==
+"@sentry/core@7.52.0":
+ version "7.52.0"
+ resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.52.0.tgz#6c820ca48fe2f06bfd6b290044c96de2375f2ad4"
+ integrity sha512-BWdG6vCMeUeMhF4ILpxXTmw70JJvT1MGJcnv09oSupWHTmqy6I19YP6YcEyFuBL4jXPN51eCl7luIdLGJrPbOg==
dependencies:
- "@sentry/types" "7.29.0"
+ "@sentry/types" "7.52.0"
+ "@sentry/utils" "7.52.0"
tslib "^1.9.3"
+"@sentry/hub@7.52.0":
+ version "7.52.0"
+ resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-7.52.0.tgz#ffc087d58c745d57108862faa0f701b15503dcc2"
+ integrity sha512-w3d8Pmp3Fx2zbbjz6hAeIbsFEkLyrUs9YTGG2y8oCoTlAtGK+AjdG+Z0H/clAZONflD/je2EmFHCI0EuXE9tEw==
+ dependencies:
+ "@sentry/core" "7.52.0"
+ "@sentry/types" "7.52.0"
+ "@sentry/utils" "7.52.0"
+ tslib "^1.9.3"
+
+"@sentry/integrations@7.52.0":
+ version "7.52.0"
+ resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.52.0.tgz#632aa5e54bdfdab910a24057c2072634a2670409"
+ integrity sha512-tqxYzgc71XdFD8MTCsVMCPef08lPY9jULE5Zi7TzjyV2AItDRJPkixG0qjwjOGwCtN/6KKz0lGPGYU8ZDxvsbg==
+ dependencies:
+ "@sentry/types" "7.52.0"
+ "@sentry/utils" "7.52.0"
+ localforage "^1.8.1"
+ tslib "^1.9.3"
+
+"@sentry/react-native@5.5.0":
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/@sentry/react-native/-/react-native-5.5.0.tgz#b1283f68465b1772ad6059ebba149673cef33f2d"
+ integrity sha512-xrES+OAIu3HFhoQSuJjd16Hh02/mByuNoKUjF7e4WDGIiTew3aqlqeLjU7x4npmg5Vbt+ND5jR12u/NmdfArwg==
+ dependencies:
+ "@sentry/browser" "7.52.0"
+ "@sentry/cli" "2.17.5"
+ "@sentry/core" "7.52.0"
+ "@sentry/hub" "7.52.0"
+ "@sentry/integrations" "7.52.0"
+ "@sentry/react" "7.52.0"
+ "@sentry/types" "7.52.0"
+ "@sentry/utils" "7.52.0"
+
+"@sentry/react@7.52.0":
+ version "7.52.0"
+ resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.52.0.tgz#d12d270ec82dea0474e69deb9112181affe7c524"
+ integrity sha512-VQxquyFFlvB81k7UER7tTJxjzbczNI2jqsw6nN1TVDrAIDt8/hT2x7m/M0FlWc88roBKuaMmbvzfNGWaL9abyQ==
+ dependencies:
+ "@sentry/browser" "7.52.0"
+ "@sentry/types" "7.52.0"
+ "@sentry/utils" "7.52.0"
+ hoist-non-react-statics "^3.3.2"
+ tslib "^1.9.3"
+
+"@sentry/replay@7.52.0":
+ version "7.52.0"
+ resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.52.0.tgz#4d78e88282d2c1044ea4b648a68d1b22173e810d"
+ integrity sha512-RRPALjDST2s7MHiMcUJ7Wo4WW7EWfUDYSG0LuhMT8DNc+ZsxQoFsLYX/yz8b3f0IUSr7xKBXP+aPeIy3jDAS2g==
+ dependencies:
+ "@sentry/core" "7.52.0"
+ "@sentry/types" "7.52.0"
+ "@sentry/utils" "7.52.0"
+
+"@sentry/types@7.52.0":
+ version "7.52.0"
+ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.52.0.tgz#b7d5372f17355e3991cbe818ad567f3fe277cc6b"
+ integrity sha512-XnEWpS6P6UdP1FqbmeqhI96Iowqd2jM5R7zJ97txTdAd5NmdHHH0pODTR9NiQViA1WlsXDut7ZLxgPzC9vIcMA==
+
+"@sentry/utils@7.52.0":
+ version "7.52.0"
+ resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.52.0.tgz#cacc36d905036ba7084c14965e964fc44239d7f0"
+ integrity sha512-X1NHYuqW0qpZfP731YcVe+cn36wJdAeBHPYPIkXCl4o4GePCJfH/CM/+9V9cZykNjyLrs2Xy/TavSAHNCj8j7w==
+ dependencies:
+ "@sentry/types" "7.52.0"
+ tslib "^1.9.3"
+
+"@shopify/flash-list@1.4.3":
+ version "1.4.3"
+ resolved "https://registry.yarnpkg.com/@shopify/flash-list/-/flash-list-1.4.3.tgz#b7a4fe03d64f3c5ce9646859b49b9d95307f203d"
+ integrity sha512-jtIReAbwWzYBV0dQ6Io9wBX+pD0C4qQFMrb5/fkEvX8PYDgBl5KRYvpfr9WLLj8CV2Jsn1X0mYOsB+ysWrI/8g==
+ dependencies:
+ recyclerlistview "4.2.0"
+ tslib "2.4.0"
+
"@sideway/address@^4.1.3":
version "4.1.4"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0"
@@ -4858,6 +4938,11 @@ image-size@^0.6.0:
resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2"
integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA==
+immediate@~3.0.5:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
+ integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
+
immer@^9.0.16:
version "9.0.16"
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.16.tgz#8e7caab80118c2b54b37ad43e05758cdefad0198"
@@ -5977,6 +6062,13 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
+lie@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
+ integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==
+ dependencies:
+ immediate "~3.0.5"
+
lilconfig@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52"
@@ -6020,6 +6112,13 @@ listr2@^5.0.7:
through "^2.3.8"
wrap-ansi "^7.0.0"
+localforage@^1.8.1:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
+ integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==
+ dependencies:
+ lie "3.1.1"
+
locate-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
@@ -6062,7 +6161,7 @@ lodash.compact@^3.0.1:
resolved "https://registry.yarnpkg.com/lodash.compact/-/lodash.compact-3.0.1.tgz#540ce3837745975807471e16b4a2ba21e7256ca5"
integrity sha512-2ozeiPi+5eBXW1CLtzjk8XQFhQOEMwwfxblqeq6EGyTxZJ1bPATqilY0e6g2SLQpP4KuMeuioBhEnWz5Pr7ICQ==
-lodash.debounce@^4.0.8:
+lodash.debounce@4.0.8, lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
@@ -6825,6 +6924,13 @@ node-fetch@^2.2.0, node-fetch@^2.6.0:
dependencies:
whatwg-url "^5.0.0"
+node-fetch@^2.6.7:
+ version "2.6.11"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25"
+ integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==
+ dependencies:
+ whatwg-url "^5.0.0"
+
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@@ -7387,6 +7493,11 @@ process-nextick-args@~2.0.0:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+progress@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
+ integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+
promise-polyfill@^8.1.3:
version "8.3.0"
resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.3.0.tgz#9284810268138d103807b11f4e23d5e945a4db63"
@@ -7407,7 +7518,7 @@ prompts@^2.0.1, prompts@^2.4.0:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@^15.7.2, prop-types@^15.8.0, prop-types@^15.8.1:
+prop-types@15.8.1, prop-types@^15.7.2, prop-types@^15.8.0, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -7872,6 +7983,15 @@ recursive-fs@^2.1.0:
resolved "https://registry.yarnpkg.com/recursive-fs/-/recursive-fs-2.1.0.tgz#1e20cf7836b292ed81208c4817550a58ad0e15ff"
integrity sha512-oed3YruYsD52Mi16s/07eYblQOLi5dTtxpIJNdfCEJ7S5v8dDgVcycar0pRWf4IBuPMIkoctC8RTqGJzIKMNAQ==
+recyclerlistview@4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/recyclerlistview/-/recyclerlistview-4.2.0.tgz#a140149aaa470c9787a1426452651934240d69ef"
+ integrity sha512-uuBCi0c+ggqHKwrzPX4Z/mJOzsBbjZEAwGGmlwpD/sD7raXixdAbdJ6BTcAmuWG50Cg4ru9p12M94Njwhr/27A==
+ dependencies:
+ lodash.debounce "4.0.8"
+ prop-types "15.8.1"
+ ts-object-utils "0.0.5"
+
redux-persist@6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8"
@@ -9004,6 +9124,11 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
+ts-object-utils@0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/ts-object-utils/-/ts-object-utils-0.0.5.tgz#95361cdecd7e52167cfc5e634c76345e90a26077"
+ integrity sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA==
+
tsconfig-paths@^3.14.1:
version "3.14.1"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"
@@ -9014,6 +9139,11 @@ tsconfig-paths@^3.14.1:
minimist "^1.2.6"
strip-bom "^3.0.0"
+tslib@2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
+ integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
+
tslib@^1.8.1, tslib@^1.9.3:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"