TP-84794 | Depricate watermelon DB

This commit is contained in:
yashmantri
2024-09-20 15:26:05 +05:30
parent c5532ab3db
commit c83f8e7a17
9 changed files with 71 additions and 380 deletions

View File

@@ -105,7 +105,6 @@ export const syncCaseDetail =
offlineCaseKey: payload.data.offlineCaseKey,
})
);
OfflineImageDAO.deleteImages(offlineImageIdList);
toast({
type: 'success',
text1: ToastMessages.FEEDBACK_SUCCESSFUL,
@@ -117,10 +116,15 @@ export const syncCaseDetail =
.catch((errObj) => {
const statusCode = errObj?.response?.status;
const errorCode = errObj?.response?.data?.error_code;
const errorMessage = errObj?.response?.data?.message;
const isImageNotFoundError = errorMessage?.includes('base64');
if (isAxiosError(errObj) && statusCode !== API_STATUS_CODE.UNPROCESSABLE_CONTENT) {
toast({
type: 'error',
text1: ToastMessages.FEEDBACK_FAILED,
text1: isImageNotFoundError
? ToastMessages.FEEDBACK_IMAGE_NOT_FOUND
: ToastMessages.FEEDBACK_FAILED,
});
}
if (callbacks?.onErrorCB != null && typeof callbacks?.onErrorCB === 'function') {
@@ -186,11 +190,11 @@ export const getSignedApi = async (
return new Promise((res) => {
if (shouldBatch) {
batchSignedApiRequest(signedRequestPayload, (results: any) => {
res({ imageUrl: results?.[signedRequestPayload[0].documentReferenceId] || '' });
res({ imageUrl: results?.[signedRequestPayload[0].documentReferenceId] || '' });
}, skipFirebaseUpdate);
} else {
makeBulkSignedApiRequest(signedRequestPayload, (results: any) => {
res({ imageUrl: results?.[signedRequestPayload[0].documentReferenceId] || '' });
res({ imageUrl: results?.[signedRequestPayload[0].documentReferenceId] || '' });
}, skipFirebaseUpdate);
}
});

View File

@@ -1294,6 +1294,14 @@ export const CLICKSTREAM_EVENT_NAMES = {
description: 'Feedback nudge closed'
},
FA_FEEDBACK_IMAGE_NOT_FOUND: {
name: 'FA_FEEDBACK_IMAGE_NOT_FOUND',
description: 'Feedback image not found'
},
FA_UNSYNC_FEEDBACK_CAPTURED: {
name: 'FA_UNSYNC_FEEDBACK_CAPTURED',
description: 'Unsync feedback captured'
},
} as const;
export enum MimeType {

View File

@@ -62,7 +62,7 @@ const AddressSelection: React.FC<IAddressSelection> = (props) => {
);
const caseType = currentCase?.caseType || CaseAllocationType.ADDRESS_VERIFICATION_CASE;
const template = useAppSelector((state) => state.case.templateData[caseType]);
const question = template.questions[questionId];
const question = template?.questions?.[questionId];
const dispatch = useAppDispatch();

View File

@@ -1,290 +0,0 @@
import React, { useEffect, useState } from 'react';
import { Control, Controller } from 'react-hook-form';
import {
ActivityIndicator,
ImageBackground,
StyleSheet,
TouchableOpacity,
View,
} from 'react-native';
import PhotoUpload, {
IImageDetails,
LAUNCH_REQUEST,
} 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 { 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,
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 {
questionType: string;
questionId: string;
widgetId: string;
journeyId: string;
caseId: string;
sectionId: string;
control: Control<any, any>;
error: any;
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('');
const [loading, setImageLoading] = useState(false);
const caseType = useAppSelector(
(state) =>
state.allCases.caseDetails[caseId]?.caseType || CaseAllocationType.ADDRESS_VERIFICATION_CASE
);
const template = useAppSelector((state) => state.case.templateData[caseType]);
const question = template.questions[questionId as keyof typeof template.questions];
const dataFromRedux = useSelector(
(state: RootState) =>
state.case.caseForm?.[caseId]?.[journeyId]?.widgetContext?.[widgetId]?.sectionContext?.[
sectionId
]?.questionContext?.[questionId]?.answer
);
const dispatch = useAppDispatch();
useEffect(() => {
if (dataFromRedux) {
setImageId(dataFromRedux);
}
}, [dataFromRedux]);
if (!question) {
return null;
}
const addOriginalFileUriToDocs = (caseId: string, fileUri: string, questionKey: string) => {
if (!fileUri) {
return;
}
dispatch(addIntermediateDocument({ caseId, fileUri, questionKey }));
};
const addImageUploadSuccessClickstream = (openRequest: LAUNCH_REQUEST) => {
if (openRequest === LAUNCH_REQUEST.CAMERA) {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_FEEDBACK_PHOTO_CAPTURED_SUCCESSFULLY, {
caseId,
questionType,
questionId,
sectionId,
widgetId,
});
} else if (openRequest === LAUNCH_REQUEST.IMAGE_LIBRARY) {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_FEEDBACK_PHOTO_UPLOADED_SUCCESSFULLY, {
caseId,
questionType,
questionId,
sectionId,
widgetId,
});
}
};
const handleChange = async (clickedImage: IImageDetails, onChange: (...event: any[]) => void) => {
const { base64 = '', uri = '', openRequest } = clickedImage;
addImageUploadSuccessClickstream(openRequest);
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, {
caseId,
questionType,
value: uniqueId,
questionId,
error,
sectionId,
widgetId,
});
onChange({
answer: uniqueId,
type: AnswerType.image,
});
await OfflineImageDAO.addImage(base64Image, uri, uniqueId);
};
const handleImageDelete = () => {
setImageId('');
dispatch(deleteIntermediateDocument({ caseId, questionKey: questionId }));
};
const handleError = (error: string) => {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_PHOTO_UPLOAD_ERROR, {
caseId,
questionType,
questionId,
error,
sectionId,
widgetId,
});
};
const handlePictureAction = (openRequest: LAUNCH_REQUEST) => {
if (openRequest === LAUNCH_REQUEST.CAMERA) {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_FEEDBACK_CLICKED_TAKE_PHOTO, {
caseId,
questionType,
questionId,
sectionId,
widgetId,
});
} else if (openRequest === LAUNCH_REQUEST.IMAGE_LIBRARY) {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_FEEDBACK_CLICKED_UPLOAD_PHOTO, {
caseId,
questionType,
questionId,
sectionId,
widgetId,
});
}
};
// TODO : add the validator back when firestore is fixed.
return (
<View style={[GenericStyles.mt12]}>
<Text dark bold style={GenericStyles.mb4}>
{question.text}{' '}
{isQuestionMandatory(question) && <Text style={GenericStyles.redText}>*</Text>}
</Text>
{!imageId ? (
<Controller
control={props.control}
rules={{ validate: (data) => validateInput(data, question.metadata.validators) }}
render={({ field: { onChange } }) => (
<PhotoUpload
onPictureAction={handlePictureAction}
onPictureClickSuccess={(clickedImage) => handleChange(clickedImage, onChange)}
showUploadFromGalleryOption={true}
containerStyle={[GenericStyles.containerStyle, loading ? styles.displayNone : null]}
imageLoading={setImageLoading}
onError={handleError}
/>
)}
name={`widgetContext.${widgetId}.sectionContext.${props.sectionId}.questionContext.${questionId}`}
/>
) : (
<View>
<ImageBackground
style={styles.image}
imageStyle={styles.br8}
source={{
uri: getImageUri(props.offlineImages, imageId),
}}
onError={(error) => handleError('Error in image rendering')}
// onLoadStart={() => setImageLoading(true)}
// onLoadEnd={() => setImageLoading(false)}
>
<TouchableOpacity onPress={handleImageDelete} style={styles.deleteButton}>
<DeleteIcon />
</TouchableOpacity>
{loading ? (
<ActivityIndicator
size={'small'}
style={styles.centerAllign}
color={COLORS.TEXT.BLUE}
/>
) : null}
</ImageBackground>
</View>
)}
<ErrorMessage
show={
error?.widgetContext?.[widgetId]?.sectionContext?.[sectionId]?.questionContext?.[
questionId
]
}
/>
<ImageBackground
style={[styles.image, loading ? styles.loadingState : styles.displayNone]}
imageStyle={styles.br8}
>
<ActivityIndicator size={'small'} color={COLORS.TEXT.BLUE} />
</ImageBackground>
</View>
);
};
const styles = StyleSheet.create({
image: {
width: '100%',
height: 350,
resizeMode: 'contain',
},
deleteButton: {
position: 'absolute',
right: 8,
top: 8,
backgroundColor: COLORS.BACKGROUND.PRIMARY,
height: 28,
width: 28,
borderRadius: 14,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 1,
borderColor: COLORS.BORDER.PRIMARY,
},
br8: {
borderRadius: 8,
},
displayNone: {
display: 'none',
},
loadingState: {
justifyContent: 'center',
backgroundColor: COLORS.BACKGROUND.PRIMARY,
alignItems: 'center',
borderRadius: 8,
},
centerAllign: {
position: 'absolute',
top: '50%',
left: '50%',
},
});
const enhance = withObservables([], () => ({
offlineImages: OfflineImageDAO.observeOfflineImage(),
}));
export default enhance(ImageUpload);

View File

@@ -205,7 +205,6 @@ const ImageUploadV2: React.FC<IImageUpload> = (props) => {
answer: uniqueId,
type: AnswerType.image,
});
await OfflineImageDAO.addImage(base64Image, uri, uniqueId, imageWidth, imageHeight);
toast({ type: 'success', text1: 'Geolocation & Timestamp added successfully' });
setImageLoading(false);
addOriginalFileUriToDocs(caseId, uri, questionId, imageWidth, imageHeight);
@@ -221,11 +220,7 @@ const ImageUploadV2: React.FC<IImageUpload> = (props) => {
}
};
const {
fileUri,
imageHeight = 350,
imageWidth = 350
} = imageDoc || {};
const { fileUri, imageHeight = 350, imageWidth = 350 } = imageDoc || {};
const imageHeightWrtAspectRatio = getImageHeightWrtAspectRatio(
imageWidth,
@@ -242,13 +237,12 @@ const ImageUploadV2: React.FC<IImageUpload> = (props) => {
) => {
setImageLoading(false);
const { base64 = '', uri = '', openRequest } = clickedImage;
const uniqueId = 'id' + new Date().getTime();
addImageUploadSuccessClickstream(openRequest);
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, {
caseId,
@@ -263,7 +257,7 @@ const ImageUploadV2: React.FC<IImageUpload> = (props) => {
answer: uniqueId,
type: AnswerType.image,
});
await OfflineImageDAO.addImage(base64Image, uri, uniqueId);
addOriginalFileUriToDocs(caseId, uri, questionId, imageWidth, imageHeight);
};
const handleImageUpload = async (onChange: (...event: any[]) => void) => {

View File

@@ -10,7 +10,6 @@ import AddressSelection from '../components/AddressSelection';
import PhoneNumberSelection from '../components/PhoneNumberSelection';
import DateInput from '../components/DateInput';
import TimeInput from '../components/TimeInput';
import ImageUpload from '../components/ImageUpload';
export const FormComponentList = {
TextInput,

View File

@@ -64,6 +64,7 @@ export const ToastMessages = {
SUCCESS_COPYING_EMPLOYER_NAME: 'Employer Name Copied Successfully!!',
FEEDBACK_SUCCESSFUL: 'Feedback submitted successfully!',
FEEDBACK_FAILED: 'Feedback submission failed',
FEEDBACK_IMAGE_NOT_FOUND: 'Feedback submission failed. Please try uploading image again',
FIRESTORE_SIGNIN_FAILED: 'Error signing in to Firestore',
PAYMENT_LINK_ERROR: 'Payment link could not be shared',
PAYMENT_LINK_SUCCESS: 'Link has been generated and shared with customer',

View File

@@ -4,10 +4,7 @@ import { syncCaseDetail } from '../../action/dataActions';
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
import { useAppDispatch, useAppSelector } from '../../hooks';
import useIsOnline from '../../hooks/useIsOnline';
import {
getTransformedAvCase,
getTransformedCollectionCaseItem,
} from '../../services/casePayload.transformer';
import { getTransformedCollectionCaseItem } from '../../services/casePayload.transformer';
import { addClickstreamEvent } from '../../services/clickstreamEventService';
import { CaseAllocationType } from '../allCases/interface';
import { CaseDetail } from './interface';
@@ -64,12 +61,13 @@ const interactionsHandler = () => {
for (const caseItem of notSyncedCases) {
if (isOnline) {
const caseKey = caseItem.offlineCaseKey || caseItem.id;
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_UNSYNC_FEEDBACK_CAPTURED, {
caseId: caseKey,
});
inProgressCaseIds.current.push(caseKey);
let modifiedCaseItem: any;
if (caseItem?.caseType === CaseAllocationType.COLLECTION_CASE) {
modifiedCaseItem = await getTransformedCollectionCaseItem(caseItem, true);
} else {
modifiedCaseItem = await getTransformedAvCase(caseItem, templateId);
}
dispatch(
syncCaseDetail(modifiedCaseItem, {

View File

@@ -1,16 +1,13 @@
import { CONTEXT_TASK_STATUSES, CaseDetail } from './../screens/caseDetails/interface';
import { CaseDetail } from './../screens/caseDetails/interface';
import { AnswerType } from '../components/form/interface';
import OfflineImageDAO from '../wmDB/dao/OfflineImageDAO';
import {
CaseAllocationType,
IAvCasePayload,
IAvTaskFeedbackItem,
IQuestionContextOutput,
TaskTitle,
} from '../screens/allCases/interface';
import { IQuestionContextOutput } from '../screens/allCases/interface';
import Geolocation from 'react-native-geolocation-service';
const AV_TEMPLATE_VERSION_NUMBER = 3;
import store from '@store';
import RNFS from 'react-native-fs';
import { addClickstreamEvent } from './clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
import ImageResizer from '@bam.tech/react-native-image-resizer';
interface QuestionContext {
answer: string;
@@ -36,8 +33,17 @@ interface Answer {
allocationReferenceId: string;
}
export const extractQuestionContext = async (answer: Answer): Promise<IQuestionContextOutput[]> => {
const IMAGE_QUALITY = 50;
const MAX_WIDTH = 500;
const MAX_HEIGHT = 500;
export const extractQuestionContext = async (
answer: Answer,
caseReferenceId: string
): Promise<IQuestionContextOutput[]> => {
const questionContexts: IQuestionContextOutput[] = [];
const docsData =
store?.getState()?.feedbackImages?.intermediateDocsToBeUploaded?.[caseReferenceId]?.documents;
const { widgetContext } = answer;
for (const widgetKey in widgetContext) {
@@ -64,12 +70,26 @@ export const extractQuestionContext = async (answer: Answer): Promise<IQuestionC
answer = { ...answer, type: AnswerType.text };
}
if (answer.type === AnswerType.image && answer.answer?.length) {
const offlineImageId = answer.answer;
const data = await getBase64ImageFromOfflineDb(offlineImageId);
answer = {
...answer,
answer: data,
};
const imageData = docsData?.[questionKey];
try {
const response = await ImageResizer.createResizedImage(
imageData?.fileUri ?? '',
MAX_WIDTH,
MAX_HEIGHT,
'JPEG',
IMAGE_QUALITY,
undefined,
undefined,
true
);
const base64Image = await RNFS.readFile(response?.uri, 'base64');
answer = {
...answer,
answer: `data:image/jpeg;base64,${base64Image}`,
};
} catch (error) {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_FEEDBACK_IMAGE_NOT_FOUND, { error });
}
}
questionContexts.push({
answerContextDTO: answer,
@@ -82,13 +102,11 @@ export const extractQuestionContext = async (answer: Answer): Promise<IQuestionC
return questionContexts;
};
export const getImageFromDB = async (imageId: string) => {
let imageList = await OfflineImageDAO.getImage(imageId);
// returns the latest image in the database
return imageList?.[0];
}
export const getImageHeightWrtAspectRatio = (imageWidth: number, imageHeight: number, requiredImageWidth: number) => {
export const getImageHeightWrtAspectRatio = (
imageWidth: number,
imageHeight: number,
requiredImageWidth: number
) => {
if (!imageWidth || !imageHeight) {
return 0;
}
@@ -101,11 +119,6 @@ export const getBase64ImageFromOfflineDb = async (imageId: string) => {
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;
@@ -118,7 +131,10 @@ export const getTransformedCollectionCaseItem = async (
forceSubmit = false
) => {
let cloneCaseItem = { ...caseItem };
let answerContextArray = await extractQuestionContext(cloneCaseItem?.answer);
let answerContextArray = await extractQuestionContext(
cloneCaseItem?.answer,
caseItem?.caseReferenceId
);
let data = {
caseReferenceId: caseItem.caseReferenceId,
answers: answerContextArray,
@@ -129,42 +145,3 @@ export const getTransformedCollectionCaseItem = async (
};
return { caseType: caseItem.caseType, data, forceSubmit };
};
export const getTransformedAvCase = async (
caseItem: IGetTransformedCaseItem,
templateId: string
) => {
const { caseType, allocationReferenceId, caseId, coords } = caseItem;
const transformedAvCase: IAvCasePayload = {
caseType: caseType || CaseAllocationType.ADDRESS_VERIFICATION_CASE,
data: {
version: AV_TEMPLATE_VERSION_NUMBER,
templateId,
allocationReferenceId: allocationReferenceId as string,
taskFeedbacks: [],
},
};
const taskContext = caseItem?.context?.taskContext;
for (let taskId in taskContext) {
const taskItems: any[] = taskContext[taskId];
for (const taskItem of taskItems) {
const { taskStatus, createdAt } = taskItem;
if (taskId !== TaskTitle.CALLING_TASK && taskStatus === CONTEXT_TASK_STATUSES.OPEN) continue;
let taskFeedbackItem: IAvTaskFeedbackItem = {
taskType: taskId as TaskTitle,
location: coords,
answers: [],
taskStatus,
capturedAt: createdAt || new Date().getTime(),
};
const answersList = await extractQuestionContext(taskItem);
if (!answersList.length) continue;
taskFeedbackItem.answers = answersList;
transformedAvCase.data.taskFeedbacks.push(taskFeedbackItem);
}
}
return transformedAvCase;
};