Merge pull request #513 from medici/feat/TP-31697
TP-31697 | Feedback image optimisation changes
This commit is contained in:
Submodule RN-UI-LIB updated: 294011df80...a1b7dac283
@@ -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",
|
||||
|
||||
@@ -12,6 +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 { 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 {
|
||||
@@ -21,6 +24,12 @@ interface IUnifiedData {
|
||||
repayments: Array<any>;
|
||||
}
|
||||
|
||||
export interface IUploadImagePayload {
|
||||
interactionId: string;
|
||||
questionKey: string;
|
||||
originalImageDocumentReferenceId: string;
|
||||
}
|
||||
|
||||
export enum UnifiedCaseDetailsTypes {
|
||||
ADDRESS_AND_GEOLOCATIONS = 'includeAddressesAndGeoLocations',
|
||||
FEEDBACKS = 'includeFeedbacks',
|
||||
@@ -135,3 +144,46 @@ export const getCaseUnifiedData =
|
||||
dispatch(setAddressLoading({ isLoading: false, loanAccountNumbers }));
|
||||
});
|
||||
};
|
||||
|
||||
export const uploadImages =
|
||||
(caseKey: string, documents: Record<string, IDocument>, interactionReferenceId: string) =>
|
||||
(dispatch: AppDispatch) => {
|
||||
if (!documents || !interactionReferenceId) {
|
||||
return;
|
||||
}
|
||||
_map(documents, (questionKey, index) => {
|
||||
const fileDoc = documents[questionKey];
|
||||
if (!fileDoc) {
|
||||
return;
|
||||
}
|
||||
const { fileUri } = fileDoc;
|
||||
const formData = new FormData();
|
||||
formData.append(
|
||||
'originalImageData',
|
||||
JSON.stringify({
|
||||
interactionReferenceId,
|
||||
questionKey,
|
||||
})
|
||||
);
|
||||
formData.append('image', {
|
||||
uri: fileUri,
|
||||
name: `image_${interactionReferenceId}_${index}`,
|
||||
type: 'image/jpeg',
|
||||
} as any);
|
||||
const url = getApiUrl(ApiKeys.UPLOAD_FEEDBACK_IMAGES);
|
||||
axiosInstance
|
||||
.put(url, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
donotHandleError: true,
|
||||
},
|
||||
timeout: 5 * MILLISECONDS_IN_A_MINUTE,
|
||||
})
|
||||
.then((res) => {
|
||||
dispatch(removeDocumentByQuestionKey({ caseKey, questionKey }));
|
||||
})
|
||||
.catch((err) => {
|
||||
logError(err as Error, 'Error uploading image to document service');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -7,15 +7,9 @@ import {
|
||||
resetTodoList,
|
||||
setLoading,
|
||||
setVisitPlansUpdating,
|
||||
updateCaseDetail,
|
||||
updateSingleCase,
|
||||
} from '../reducer/allCasesSlice';
|
||||
import {
|
||||
CaseAllocationType,
|
||||
CaseType,
|
||||
ICaseItem,
|
||||
IPinnedCasesPayload,
|
||||
} from '../screens/allCases/interface';
|
||||
import { CaseAllocationType, ICaseItem, IPinnedCasesPayload } from '../screens/allCases/interface';
|
||||
import { AppDispatch } from '../store/store';
|
||||
import { addClickstreamEvent } from '../services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '../common/Constants';
|
||||
@@ -70,12 +64,10 @@ export const postPinnedList =
|
||||
export const syncCaseDetail =
|
||||
(
|
||||
payload: any,
|
||||
updatedCaseDetail?: any,
|
||||
callbacks?: {
|
||||
onSuccessCB?: (data: any, actions?: any) => void;
|
||||
onSuccessCB?: (data: any, actions?: any, interactionId?: string) => void;
|
||||
onErrorCB?: (e: Error) => void;
|
||||
},
|
||||
nextActions?: any
|
||||
}
|
||||
) =>
|
||||
(dispatch: AppDispatch) => {
|
||||
const offlineImageIdList = getOfflineImageId(payload);
|
||||
@@ -88,6 +80,7 @@ export const syncCaseDetail =
|
||||
})
|
||||
.then((res) => {
|
||||
const caseType = payload.caseType;
|
||||
const interactionId = res.data?.referenceId;
|
||||
dispatch(
|
||||
updateSingleCase({
|
||||
data: res.data,
|
||||
@@ -102,7 +95,7 @@ export const syncCaseDetail =
|
||||
text1: ToastMessages.FEEDBACK_SUCCESSFUL,
|
||||
});
|
||||
if (callbacks?.onSuccessCB != null && typeof callbacks?.onSuccessCB === 'function') {
|
||||
callbacks?.onSuccessCB(payload.data.answers, nextActions);
|
||||
callbacks?.onSuccessCB(payload.data.answers, interactionId);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -111,14 +104,6 @@ export const syncCaseDetail =
|
||||
type: 'error',
|
||||
text1: ToastMessages.FEEDBACK_FAILED,
|
||||
});
|
||||
if (updatedCaseDetail) {
|
||||
dispatch(
|
||||
updateCaseDetail({
|
||||
caseId: payload.data.caseReferenceId,
|
||||
updatedCaseDetail,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
if (callbacks?.onErrorCB != null && typeof callbacks?.onErrorCB === 'function') {
|
||||
callbacks?.onErrorCB(e);
|
||||
|
||||
@@ -8,9 +8,10 @@ interface IExpandableImage {
|
||||
imageSrc?: string;
|
||||
close?: () => void;
|
||||
title?: string;
|
||||
fallbackImage?: string;
|
||||
}
|
||||
|
||||
const imageHtml = (imageSrc?: string) => `
|
||||
const imageHtml = (imageSrc?: string, fallbackImage?: string) => `
|
||||
<html>
|
||||
<head>
|
||||
<title>Image Viewer</title>
|
||||
@@ -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)
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page pinch-zoom-parent">
|
||||
<div class="pinch-zoom">
|
||||
<img style="width: calc(100vw - 32px); max-height: calc(100vh - 32px)" src=${imageSrc} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="parent">
|
||||
<img class="image1" src=${fallbackImage} />
|
||||
<img class="image2" src=${imageSrc} />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
const ExpandableImage: React.FC<IExpandableImage> = ({ imageSrc, title, close }) => {
|
||||
const ExpandableImage: React.FC<IExpandableImage> = ({ imageSrc, fallbackImage, title, close }) => {
|
||||
return (
|
||||
<View style={GenericStyles.fill}>
|
||||
<NavigationHeader onBack={close} title={title} />
|
||||
@@ -44,7 +63,7 @@ const ExpandableImage: React.FC<IExpandableImage> = ({ imageSrc, title, close })
|
||||
scalesPageToFit={true}
|
||||
bounces={false}
|
||||
scrollEnabled={false}
|
||||
source={{ html: imageHtml(imageSrc) }}
|
||||
source={{ html: imageHtml(imageSrc, fallbackImage) }}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@ import { GenericObject, GenericType } from '../../common/GenericTypes';
|
||||
import StarRating from '../../../RN-UI-LIB/src/components/star_rating/StarRating';
|
||||
import { CaseAllocationType } from '../../screens/allCases/interface';
|
||||
import { getAddressString, getPhoneNumberString, memoize } from '../utlis/commonFunctions';
|
||||
import { getImageFromOfflineDb } from '../../services/casePayload.transformer';
|
||||
import { getBase64ImageFromOfflineDb } from '../../services/casePayload.transformer';
|
||||
import { formatAmount } from '../../../RN-UI-LIB/src/utlis/amount';
|
||||
|
||||
const RATING_COMPONENT = 'Rating';
|
||||
@@ -61,7 +61,7 @@ const AnswerRender: React.FC<IAnswerRender> = (props) => {
|
||||
React.useEffect(() => {
|
||||
if (answer?.type === AnswerType.image) {
|
||||
(async () => {
|
||||
const data = await getImageFromOfflineDb(answer.answer);
|
||||
const data = await getBase64ImageFromOfflineDb(answer.answer);
|
||||
setImageUrl(data);
|
||||
})();
|
||||
}
|
||||
|
||||
@@ -1,26 +1,37 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Control, Controller } from 'react-hook-form';
|
||||
import { ImageBackground, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import PhotoUpload from '../../../../RN-UI-LIB/src/components/photoUpload/PhotoUpload';
|
||||
import PhotoUpload, {
|
||||
IImageDetails,
|
||||
} from '../../../../RN-UI-LIB/src/components/photoUpload/PhotoUpload';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Text from '../../../../RN-UI-LIB/src/components/Text';
|
||||
import DeleteIcon from '../../../../RN-UI-LIB/src/Icons/DeleteIcon';
|
||||
import { GenericStyles } from '../../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../../RN-UI-LIB/src/styles/colors';
|
||||
import { useAppSelector } from '../../../hooks';
|
||||
import { useAppDispatch, useAppSelector } from '../../../hooks';
|
||||
import { RootState } from '../../../store/store';
|
||||
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 {
|
||||
addIntermediateDocument,
|
||||
deleteIntermediateDocument,
|
||||
} from '../../../reducer/feedbackImagesSlice';
|
||||
|
||||
interface IOfflineImage {
|
||||
idx: string;
|
||||
imageData: string;
|
||||
originalImageUri: string;
|
||||
}
|
||||
|
||||
interface IImageUpload {
|
||||
@@ -35,6 +46,16 @@ interface IImageUpload {
|
||||
offlineImages: IOfflineImage[];
|
||||
}
|
||||
|
||||
const getImageUri = (imageList: IOfflineImage[], imageId: string) => {
|
||||
const image = imageList?.find((image) => image.idx === imageId);
|
||||
let uri = '';
|
||||
if (image) {
|
||||
const { originalImageUri, imageData } = image;
|
||||
uri = originalImageUri || imageData || '';
|
||||
}
|
||||
return uri;
|
||||
};
|
||||
|
||||
const ImageUpload: React.FC<IImageUpload> = (props) => {
|
||||
const { questionId, error, sectionId, caseId, journeyId, widgetId, questionType } = props;
|
||||
const [imageId, setImageId] = useState('');
|
||||
@@ -51,6 +72,8 @@ const ImageUpload: React.FC<IImageUpload> = (props) => {
|
||||
]?.questionContext?.[questionId]?.answer
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
if (dataFromRedux) {
|
||||
setImageId(dataFromRedux);
|
||||
@@ -61,8 +84,20 @@ const ImageUpload: React.FC<IImageUpload> = (props) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleChange = async (clickedImage: string | null, onChange: (...event: any[]) => void) => {
|
||||
const data = `${PrefixJpegBase64Image}${clickedImage}`;
|
||||
const addOriginalFileUriToDocs = (caseId: string, fileUri: string, questionKey: string) => {
|
||||
if (!fileUri) {
|
||||
return;
|
||||
}
|
||||
dispatch(addIntermediateDocument({ caseId, fileUri, questionKey }));
|
||||
};
|
||||
|
||||
const handleChange = async (clickedImage: IImageDetails, onChange: (...event: any[]) => void) => {
|
||||
const { base64, uri = '' } = clickedImage;
|
||||
addOriginalFileUriToDocs(caseId, uri, questionId);
|
||||
if (!base64) {
|
||||
return;
|
||||
}
|
||||
const base64Image = `${PrefixJpegBase64Image}${base64}`;
|
||||
var uniqueId = 'id' + new Date().getTime();
|
||||
setImageId(uniqueId);
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_ELEMENT_CHANGED, {
|
||||
@@ -78,8 +113,14 @@ const ImageUpload: React.FC<IImageUpload> = (props) => {
|
||||
answer: uniqueId,
|
||||
type: AnswerType.image,
|
||||
});
|
||||
await OfflineImageDAO.addImage(data, uniqueId);
|
||||
await OfflineImageDAO.addImage(base64Image, uri, uniqueId);
|
||||
};
|
||||
|
||||
const handleImageDelete = () => {
|
||||
setImageId('');
|
||||
dispatch(deleteIntermediateDocument({ caseId, questionKey: questionId }));
|
||||
};
|
||||
|
||||
// TODO : add the validator back when firestore is fixed.
|
||||
return (
|
||||
<View style={[GenericStyles.mt12]}>
|
||||
@@ -105,10 +146,10 @@ const ImageUpload: React.FC<IImageUpload> = (props) => {
|
||||
style={styles.image}
|
||||
imageStyle={styles.br8}
|
||||
source={{
|
||||
uri: props.offlineImages?.find((image) => image.idx === imageId)?.imageData || '',
|
||||
uri: getImageUri(props.offlineImages, imageId),
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity onPress={() => setImageId('')} style={styles.deleteButton}>
|
||||
<TouchableOpacity onPress={handleImageDelete} style={styles.deleteButton}>
|
||||
<DeleteIcon />
|
||||
</TouchableOpacity>
|
||||
</ImageBackground>
|
||||
|
||||
@@ -41,7 +41,9 @@ import { CaptureGeolocation } from './services/geoLocation.service';
|
||||
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 { GenericFunctionArgs } from '../../common/GenericTypes';
|
||||
import { setDocumentInteractionId, setDocumentsToUpload } from '../../reducer/feedbackImagesSlice';
|
||||
|
||||
interface IWidget {
|
||||
route: {
|
||||
@@ -60,13 +62,27 @@ const Widget: React.FC<IWidget> = (props) => {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const { params } = props.route;
|
||||
const { caseId, journey, handleCloseRouting } = params;
|
||||
const caseType = useAppSelector(
|
||||
(state) =>
|
||||
state.allCases.caseDetails[caseId]?.caseType || CaseAllocationType.ADDRESS_VERIFICATION_CASE
|
||||
);
|
||||
const caseKey = useRef<string>('');
|
||||
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 templateData: FormTemplateV1 = useAppSelector((state) => state.case.templateData[caseType]);
|
||||
const caseData = useAppSelector((state) => state.allCases.caseDetails[caseId]);
|
||||
const { sections, conditionActions: widgetConditionActions, isLeaf } = templateData.widget[name];
|
||||
const sectionMap = templateData.sections;
|
||||
const [error, setError] = useState();
|
||||
@@ -82,7 +98,6 @@ const Widget: React.FC<IWidget> = (props) => {
|
||||
setIsJourneyFirstScreen(isFirst);
|
||||
}, [templateData, name]);
|
||||
|
||||
const dataToBeValidated = useAppSelector((state) => state.case.caseForm?.[caseId]?.[journey]);
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
@@ -130,7 +145,16 @@ const Widget: React.FC<IWidget> = (props) => {
|
||||
setError(data);
|
||||
};
|
||||
|
||||
const onSuccessfulSubmit = (data: any, nextActions?: any) => {
|
||||
const uploadOriginalDocs = (interactionId: string) => {
|
||||
const docs = intermediateDocsToBeUploaded?.[caseId]?.documents;
|
||||
if (!docs) {
|
||||
return;
|
||||
}
|
||||
dispatch(setDocumentInteractionId({ caseKey: caseKey.current, interactionId }));
|
||||
dispatch(uploadImages(caseKey.current, docs, interactionId));
|
||||
};
|
||||
|
||||
const onSuccessfulSubmit = (data: any, interactionId: string, nextActions?: any) => {
|
||||
setIsSubmitting(false);
|
||||
navigateToScreen(
|
||||
caseType === CaseAllocationType.COLLECTION_CASE ? 'collectionCaseDetail' : 'caseDetail',
|
||||
@@ -149,6 +173,9 @@ const Widget: React.FC<IWidget> = (props) => {
|
||||
nextActions,
|
||||
})
|
||||
);
|
||||
if (interactionId) {
|
||||
uploadOriginalDocs(interactionId);
|
||||
}
|
||||
};
|
||||
|
||||
async function fetchLocation(): Promise<Geolocation.GeoCoordinates | undefined> {
|
||||
@@ -168,6 +195,16 @@ const Widget: React.FC<IWidget> = (props) => {
|
||||
});
|
||||
};
|
||||
|
||||
const onErrorSubmit = (updatedCaseDetails: any) => {
|
||||
setIsSubmitting(false);
|
||||
dispatch(
|
||||
updateCaseDetail({
|
||||
caseKey: caseKey.current,
|
||||
updatedCaseDetails,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleSubmitJourney = async (data: any, coords: Geolocation.GeoCoordinates) => {
|
||||
dispatch(
|
||||
updateInteraction({
|
||||
@@ -178,6 +215,8 @@ const Widget: React.FC<IWidget> = (props) => {
|
||||
})
|
||||
);
|
||||
if (caseType === CaseAllocationType.COLLECTION_CASE) {
|
||||
caseKey.current = `${caseId}_${Date.now()}`;
|
||||
dispatch(setDocumentsToUpload({ caseId, caseKey: caseKey.current }));
|
||||
const updatedCase = getUpdatedCollectionCaseDetail({
|
||||
caseData,
|
||||
answer: data,
|
||||
@@ -190,17 +229,16 @@ const Widget: React.FC<IWidget> = (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(
|
||||
updateCaseDetail({
|
||||
caseId,
|
||||
caseKey: caseKey.current,
|
||||
updatedCaseDetail: updatedCase,
|
||||
})
|
||||
);
|
||||
@@ -242,22 +280,16 @@ const Widget: React.FC<IWidget> = (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(
|
||||
updateCaseDetail({
|
||||
caseId,
|
||||
caseKey: caseId,
|
||||
updatedCaseDetail: updatedCase,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -49,6 +49,8 @@ export enum ApiKeys {
|
||||
CASES_SEND_ID = 'CASES_SEND_ID',
|
||||
FETCH_CASES = 'FETCH_CASES',
|
||||
GET_FORECLOSURE_AMOUNT = 'GET_FORECLOSURE_AMOUNT',
|
||||
UPLOAD_FEEDBACK_IMAGES = 'UPLOAD_FEEDBACK_IMAGES',
|
||||
ORIGINAL_IMAGES = 'ORIGINAL_IMAGES',
|
||||
GLOBAL_CONFIG = 'GLOBAL_CONFIG',
|
||||
}
|
||||
|
||||
@@ -85,6 +87,7 @@ 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_FEEDBACK_IMAGES] = '/feedback/persist-original-images';
|
||||
API_URLS[ApiKeys.GLOBAL_CONFIG] = '/global-config';
|
||||
|
||||
export const API_STATUS_CODE = {
|
||||
|
||||
@@ -13,12 +13,7 @@ import {
|
||||
caseVerdict,
|
||||
ICaseItem,
|
||||
} from '../screens/allCases/interface';
|
||||
import {
|
||||
CaseDetail,
|
||||
CONTEXT_TASK_STATUSES,
|
||||
DOCUMENT_TYPE,
|
||||
IDocument,
|
||||
} from '../screens/caseDetails/interface';
|
||||
import { CaseDetail, CONTEXT_TASK_STATUSES, DOCUMENT_TYPE } from '../screens/caseDetails/interface';
|
||||
import { addClickstreamEvent } from '../services/clickstreamEventService';
|
||||
import { getLoanAccountNumber } from '../components/utlis/commonFunctions';
|
||||
import { getVisitedWidgetsNodeList } from '../components/form/services/forms.service';
|
||||
@@ -26,7 +21,6 @@ import { CollectionCaseWidgetId, CommonCaseWidgetId } from '../types/template.ty
|
||||
import { IAvatarUri } from '../action/caseListAction';
|
||||
|
||||
export type ICasesMap = { [key: string]: ICaseItem };
|
||||
|
||||
interface IAllCasesSlice {
|
||||
casesList: ICaseItem[];
|
||||
casesListMap: ICasesMap;
|
||||
@@ -397,10 +391,8 @@ const allCasesSlice = createSlice({
|
||||
}
|
||||
},
|
||||
updateCaseDetail: (state, action) => {
|
||||
const { caseId, updatedCaseDetail } = action.payload;
|
||||
let caseKey: string = caseId;
|
||||
const { caseKey, updatedCaseDetail } = action.payload;
|
||||
if (updatedCaseDetail.caseType === CaseAllocationType.COLLECTION_CASE) {
|
||||
caseKey += '_' + Date.now();
|
||||
updatedCaseDetail.offlineCaseKey = caseKey;
|
||||
}
|
||||
state.caseDetails[caseKey] = updatedCaseDetail;
|
||||
@@ -480,7 +472,9 @@ const allCasesSlice = createSlice({
|
||||
...state.caseDetails[computedKey],
|
||||
isSynced: true,
|
||||
};
|
||||
delete state.caseDetails[offlineCaseKey];
|
||||
if (offlineCaseKey) {
|
||||
delete state.caseDetails[offlineCaseKey];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
93
src/reducer/feedbackImagesSlice.ts
Normal file
93
src/reducer/feedbackImagesSlice.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
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<string, IDocument>;
|
||||
}
|
||||
|
||||
interface IImagesSlice {
|
||||
intermediateDocsToBeUploaded: Record<string, IDocumentDetail>;
|
||||
docsToBeUploaded: Record<string, IDocumentDetail>;
|
||||
}
|
||||
|
||||
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]?.documents) {
|
||||
state.intermediateDocsToBeUploaded[caseId].documents[questionKey] = doc;
|
||||
} 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;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { RefreshControl, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import Accordion from '../../../../RN-UI-LIB/src/components/accordian/Accordian';
|
||||
import NavigationHeader from '../../../../RN-UI-LIB/src/components/NavigationHeader';
|
||||
@@ -67,14 +67,7 @@ const FeedbackDetailContainer: React.FC<IFeedbackDetailContainer> = ({ route: ro
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPastFeedbackOnAddress && currentPage == 1) {
|
||||
setFeedbackList(feedbackListFromCache);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setFeedbackList([]);
|
||||
const fetchFeedbacks = useCallback(() => {
|
||||
const getPastFeedbackApiFn = isPastFeedbackOnAddress
|
||||
? getPastFeedbacksOnAddresses
|
||||
: getPastFeedbacks;
|
||||
@@ -112,6 +105,16 @@ const FeedbackDetailContainer: React.FC<IFeedbackDetailContainer> = ({ route: ro
|
||||
});
|
||||
}, [currentPage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPastFeedbackOnAddress && currentPage == 1) {
|
||||
setFeedbackList(feedbackListFromCache);
|
||||
} else {
|
||||
setLoading(true);
|
||||
setFeedbackList([]);
|
||||
}
|
||||
fetchFeedbacks();
|
||||
}, [currentPage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOnline) {
|
||||
setCurrentPage(1);
|
||||
@@ -167,7 +170,7 @@ const FeedbackDetailContainer: React.FC<IFeedbackDetailContainer> = ({ route: ro
|
||||
onBack={goBack}
|
||||
/>
|
||||
<ScrollView
|
||||
refreshControl={<RefreshControl refreshing={loading} />}
|
||||
refreshControl={<RefreshControl refreshing={loading} onRefresh={fetchFeedbacks} />}
|
||||
style={[GenericStyles.ph16, GenericStyles.mt16]}
|
||||
ref={(x) => setRef(x)}
|
||||
>
|
||||
|
||||
@@ -28,7 +28,7 @@ const FeedbackDetailImageItem: React.FC<IFeedbackDetailImageItem> = ({ image })
|
||||
};
|
||||
|
||||
const questionText = getQuestionText(image);
|
||||
|
||||
const originalImageUri = image.metadata?.originalDocumentSignedUri || image.inputText;
|
||||
return (
|
||||
<View style={[GenericStyles.mv8]}>
|
||||
<Text style={[styles.textContainer, styles.questionText]}>{questionText}</Text>
|
||||
@@ -54,7 +54,8 @@ const FeedbackDetailImageItem: React.FC<IFeedbackDetailImageItem> = ({ image })
|
||||
animationType="fade"
|
||||
>
|
||||
<ExpandableImage
|
||||
imageSrc={image.inputText}
|
||||
imageSrc={originalImageUri}
|
||||
fallbackImage={image.inputText}
|
||||
title={questionText}
|
||||
close={handleExpandedImageClose}
|
||||
/>
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import { CaseAllocationType } from '../allCases/interface';
|
||||
import { CaseDetail } from './interface';
|
||||
import { uploadImages } from '../../action/caseApiActions';
|
||||
import { setDocumentInteractionId } from '../../reducer/feedbackImagesSlice';
|
||||
|
||||
export const getUnSyncedCase = (updatedCaseDetail: CaseDetail | undefined): any => {
|
||||
const caseId = updatedCaseDetail?.id;
|
||||
@@ -21,7 +23,10 @@ export const getUnSyncedCase = (updatedCaseDetail: CaseDetail | undefined): any
|
||||
const interactionsHandler = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const isOnline = useIsOnline();
|
||||
const allCasesDetails = useAppSelector((state) => state.allCases.caseDetails);
|
||||
const { allCasesDetails, docsToBeUploaded } = useAppSelector((state) => ({
|
||||
allCasesDetails: state.allCases.caseDetails,
|
||||
docsToBeUploaded: state.feedbackImages.docsToBeUploaded,
|
||||
}));
|
||||
const { templateId } = useAppSelector(
|
||||
(state) => state.case.templateData[CaseAllocationType.ADDRESS_VERIFICATION_CASE]
|
||||
);
|
||||
@@ -32,9 +37,19 @@ const interactionsHandler = () => {
|
||||
|
||||
const inProgressCaseIds = useRef<string[]>([]);
|
||||
|
||||
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 (!allCasesDetails) return;
|
||||
let notSyncedCases: Array<any> = [];
|
||||
// [DISCUSS]: isApiCalled not getting true anywhere.
|
||||
_map(allCasesDetails, (el) => {
|
||||
if (
|
||||
allCasesDetails[el]?.isSynced === false &&
|
||||
@@ -49,19 +64,44 @@ const interactionsHandler = () => {
|
||||
//TODO: use batched api call
|
||||
for (const caseItem of notSyncedCases) {
|
||||
if (isOnline) {
|
||||
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),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, [allCasesDetails, isOnline]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOnline) {
|
||||
return;
|
||||
}
|
||||
if (!docsToBeUploaded) {
|
||||
return;
|
||||
}
|
||||
_map(docsToBeUploaded, (caseId) => {
|
||||
const interactionId = docsToBeUploaded[caseId]?.interactionId;
|
||||
// No interactionId means form is not submitted yet
|
||||
if (!interactionId) {
|
||||
return;
|
||||
}
|
||||
const docs = docsToBeUploaded[caseId]?.documents;
|
||||
if (docs) {
|
||||
dispatch(uploadImages(caseId, docs, interactionId));
|
||||
}
|
||||
});
|
||||
}, [isOnline]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ export const extractQuestionContext = async (answer: Answer): Promise<IQuestionC
|
||||
}
|
||||
if (answer.type === AnswerType.image && answer.answer?.length) {
|
||||
const offlineImageId = answer.answer;
|
||||
const data = await getImageFromOfflineDb(offlineImageId);
|
||||
const data = await getBase64ImageFromOfflineDb(offlineImageId);
|
||||
answer = {
|
||||
...answer,
|
||||
answer: data,
|
||||
@@ -81,11 +81,16 @@ export const extractQuestionContext = async (answer: Answer): Promise<IQuestionC
|
||||
return questionContexts;
|
||||
};
|
||||
|
||||
export const getImageFromOfflineDb = async (imageId: string) => {
|
||||
export const getBase64ImageFromOfflineDb = async (imageId: string) => {
|
||||
let imageList = await OfflineImageDAO.getImage(imageId);
|
||||
return imageList?.[0]?.imageData;
|
||||
};
|
||||
|
||||
export const getOriginalImageFromOfflineDb = async (imageId: string) => {
|
||||
let imageList = await OfflineImageDAO.getImage(imageId);
|
||||
return imageList?.[0]?.originalImageUri;
|
||||
};
|
||||
|
||||
export interface IGetTransformedCaseItem extends CaseDetail {
|
||||
answer: any;
|
||||
caseId: string;
|
||||
|
||||
@@ -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';
|
||||
import configSlice from '../reducer/configSlice';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
@@ -45,6 +46,7 @@ const rootReducer = combineReducers({
|
||||
notifications: notificationsSlice,
|
||||
metadata: MetadataSlice,
|
||||
foregroundService: foregroundServiceSlice,
|
||||
feedbackImages: feedbackImagesSlice,
|
||||
config: configSlice,
|
||||
});
|
||||
|
||||
@@ -63,6 +65,7 @@ const persistConfig = {
|
||||
'repayments',
|
||||
'feedbackHistory',
|
||||
'address',
|
||||
'feedbackImages',
|
||||
'config',
|
||||
],
|
||||
blackList: ['case', 'filters'],
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -4,6 +4,6 @@ export enum TableName {
|
||||
CLICKSTREAM_EVENTS = 'clickstream_events',
|
||||
}
|
||||
|
||||
export const DB_VERSION = 4;
|
||||
export const DB_VERSION = 5;
|
||||
|
||||
export const DB_NAME = 'AVAPP';
|
||||
|
||||
@@ -6,12 +6,13 @@ const offlineImage = database.get(TableName.OFFLINE_IMAGES);
|
||||
|
||||
export default {
|
||||
observeOfflineImage: () => offlineImage.query().observe(),
|
||||
addImage: async (imageData: string, imageIdx: string) => {
|
||||
addImage: async (imageData: string, originalImageUri: string, imageIdx: string) => {
|
||||
try {
|
||||
return await database.action(async () => {
|
||||
return await offlineImage.create((image: any) => {
|
||||
image.idx = imageIdx;
|
||||
image.imageData = imageData;
|
||||
image.originalImageUri = originalImageUri;
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
|
||||
@@ -7,6 +7,7 @@ export default class OfflineImage extends Model {
|
||||
|
||||
@field('idx') idx!: string;
|
||||
@field('image_data') imageData!: string;
|
||||
@field('original_image_uri') originalImageUri!: string;
|
||||
@readonly @date('created_at') createdAt!: any;
|
||||
@readonly @date('updated_at') updatedAt!: any;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export default appSchema({
|
||||
columns: [
|
||||
{ name: 'idx', type: 'string' },
|
||||
{ name: 'image_data', type: 'string' },
|
||||
{ name: 'original_image_uri', type: 'string' },
|
||||
],
|
||||
}),
|
||||
tableSchema({
|
||||
|
||||
196
yarn.lock
196
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"
|
||||
|
||||
Reference in New Issue
Block a user