Tp 21873 sethi (#165)

* intermediate screen text fix TP-21873

* image bug fix TP-21873

* intermediate screen text issue fix TP-21873

* otp screen fix TP-21873

* remove commented code TP-21873

* success image changes TP-21873

* submodule update TP-21873

* fix images issue TP-21873
This commit is contained in:
Aman Sethi
2023-03-21 13:00:00 +05:30
committed by GitHub Enterprise
parent 669c3d41c4
commit 6791014fff
10 changed files with 137 additions and 52 deletions

View File

@@ -1,12 +1,13 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import RNFetchBlob from "rn-fetch-blob";
import { getPrefixBase64Image, MimeType } from "../../common/Constants";
import { getPrefixBase64Image, LocalStorageKeys, MimeType } from "../../common/Constants";
import { Address, PhoneNumber } from "../../screens/caseDetails/interface";
import NetInfo from "@react-native-community/netinfo";
import Clipboard from '@react-native-clipboard/clipboard';
import { stringify } from './stringifyUtils';
import address from "../form/components/Address";
import { useWindowDimensions } from 'react-native';
import { GlobalImageMap } from '../../../App';
import { GenericType } from '../../common/GenericTypes';
@@ -124,7 +125,14 @@ export const getDynamicBottomSheetHeightPercentageFn = (headerOffset = 100, rowH
})
}
export const saveToGlobalImageMap = async (caseId: string, image: string) => {
GlobalImageMap[caseId] = image;
await setAsyncStorageItem(
LocalStorageKeys.GLOBAL_IMAGE_MAP,
GlobalImageMap,
);
};
export const allSettled = (promises: Promise<GenericType>[]) =>
Promise.all(
promises.map(p => p.then(value => ({ status: 'fulfilled', value})).catch(value => ({ status: 'rejected', value}))),
);
);

View File

@@ -1,11 +1,14 @@
import React, { useEffect, useState } from 'react';
import { setAsyncStorageItem } from './../components/utlis/commonFunctions';
import {
setAsyncStorageItem,
} from './../components/utlis/commonFunctions';
import { CaseDetail } from '../screens/caseDetails/interface';
import { CaseAllocationType } from '../screens/allCases/interface';
import { LocalStorageKeys } from '../common/Constants';
import { getSignedApi, ISignedRequest } from '../action/dataActions';
import { getBase64FromUrl } from '../components/utlis/commonFunctions';
import { GlobalImageMap } from '../../App';
import { LocalStorageKeys } from '../common/Constants';
import { useAppSelector } from '.';
const useCustomerImage = (caseDetails: CaseDetail) => {
const [imageUrl, setImageUrl] = useState(
@@ -13,6 +16,7 @@ const useCustomerImage = (caseDetails: CaseDetail) => {
caseDetails?.customerInfo?.imageURL ||
caseDetails?.imageUrl,
);
const pinnedList = useAppSelector(state => state.allCases.pinnedList);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
@@ -45,11 +49,17 @@ const useCustomerImage = (caseDetails: CaseDetail) => {
const responseImage64 = await getBase64FromUrl(url);
if (responseImage64) {
setImageUrl(responseImage64);
GlobalImageMap[caseId] = responseImage64;
await setAsyncStorageItem(
LocalStorageKeys.GLOBAL_IMAGE_MAP,
GlobalImageMap,
);
if (
pinnedList.find(
item => item.caseReferenceId === caseId,
)
) {
GlobalImageMap[caseId] = responseImage64;
await setAsyncStorageItem(
LocalStorageKeys.GLOBAL_IMAGE_MAP,
GlobalImageMap,
);
}
}
} else {
setImageUrl('');

View File

@@ -34,6 +34,9 @@ const loginSlice = createSlice({
state.verifyOTPError = action.payload;
state.isLoading = false;
},
resetVerifyOTPError: state => {
state.verifyOTPError = '';
},
setFormLoading: (state, action) => {
state.isLoading = action.payload;
},
@@ -55,6 +58,7 @@ export const {
setShowOTPScreen,
setFormLoading,
resetLoginForm,
resetVerifyOTPError,
} = loginSlice.actions;
export default loginSlice.reducer;

View File

@@ -1,9 +1,14 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { GlobalImageMap } from '../../../App';
import Avatar from '../../../RN-UI-LIB/src/components/Avatar';
import UnsyncedIcon from '../../../RN-UI-LIB/src/Icons/UnsyncedIcon';
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
import { useAppDispatch, useAppSelector } from '../../hooks';
import {
getBase64FromUrl,
saveToGlobalImageMap,
} from '../../components/utlis/commonFunctions';
import { useAppSelector } from '../../hooks';
import useCustomerImage from '../../hooks/useCustomerImage';
import { CaseDetail } from '../caseDetails/interface';
import { ICaseItem } from './interface';
@@ -12,6 +17,7 @@ interface ICaseItemAvatar {
caseData: ICaseItem;
size?: number;
showBorder?: boolean;
shouldHandleError?: boolean;
}
const RELATIVE_ASYNC_ICON_RIGHT_POSITION = -5;
@@ -21,19 +27,33 @@ const CaseItemAvatar: React.FC<ICaseItemAvatar> = ({
caseData,
size = 40,
showBorder = false,
shouldHandleError,
}) => {
const dispatch = useAppDispatch();
const refrenceId = caseData?.caseReferenceId || caseData?.id;
const caseDetails: CaseDetail = useAppSelector(
state => state.allCases.caseDetails?.[refrenceId]!!,
);
const [apiErrorCount, setApiErrorCount] = React.useState(0);
const isSynced = caseDetails?.isSynced;
const { imageUrl, setError, loading } = useCustomerImage(caseDetails);
const { imageUrl, setImageUrl, setError, loading } =
useCustomerImage(caseDetails);
const onError = async () => {
setError(true);
if (shouldHandleError) {
setError(true);
} else {
setImageUrl('');
}
};
const onImageLoadSuccess = async () => {
if (!GlobalImageMap[caseDetails?.id]) {
const responseImage64 = await getBase64FromUrl(imageUrl);
if (responseImage64) {
const caseId = caseDetails?.id;
await saveToGlobalImageMap(caseId, responseImage64);
}
}
};
const customerName =
@@ -51,6 +71,7 @@ const CaseItemAvatar: React.FC<ICaseItemAvatar> = ({
onErrorFallback={onError}
style={[showBorder && styles.border]}
loading={loading}
onSuccess={onImageLoadSuccess}
/>
{!isSynced ? (
<View style={styles.unsyncedIcon}>

View File

@@ -46,7 +46,7 @@ const ListItem: React.FC<IListItem> = props => {
return null;
}
const { intermediateTodoListMap, selectedTodoListMap, caseDetails } =
const { intermediateTodoListMap, selectedTodoListMap, caseDetails, pinnedList } =
useAppSelector(state => state.allCases);
const detail = caseDetails[caseId]!!;
@@ -128,7 +128,7 @@ const ListItem: React.FC<IListItem> = props => {
: COLORS.BACKGROUND.PRIMARY,
},
]}>
<CaseItemAvatar caseData={caseData} />
<CaseItemAvatar shouldHandleError={pinnedList?.find(item => item?.caseReferenceId === caseId) ? true: false} caseData={caseData} />
{!isTodoItem && !isCompleted ? (
<Pressable
onPress={handleAvatarClick}

View File

@@ -45,7 +45,9 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
);
const ANIMATION_DURATION = 500;
const [openImage, setOpenImage] = useState<boolean>(false);
const { imageUrl, setError, loading } = useCustomerImage(caseDetail);
const { imageUrl, setError } = useCustomerImage(caseDetail);
const [errorModalImage, setErrorModalImage] = useState<boolean>(false);
const handleOpenClose = useCallback(() => {
setOpenImage(prev => !prev);
@@ -86,6 +88,7 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
showBorder
size={64}
caseData={caseDetail}
shouldHandleError
/>
</TouchableOpacity>
<View style={[styles.infoContainer]}>
@@ -155,7 +158,7 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
''
)}
{ GlobalImageMap[caseDetail?.id] ? <Modal
{ (GlobalImageMap[caseDetail?.id] || imageUrl) ? <Modal
animationType={'slide'}
visible={openImage}
onRequestClose={handleOpenClose}>
@@ -174,13 +177,31 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
GenericStyles.fill,
GenericStyles.alignCenter,
GenericStyles.row,
{
position: 'relative',
},
]}>
<RNFastImage
style={[GenericStyles.fill, styles.imageStyle]}
source={{ uri: imageUrl }}
source={{ uri: GlobalImageMap[caseDetail?.id] ?? imageUrl }}
resizeMode={'stretch'}
onError={() => setError(true)}
onError={() => {
setError(true)
setErrorModalImage(true)
}}
onLoad={()=>{
setErrorModalImage(false)
}}
/>
{
errorModalImage ?
<Text style={[GenericStyles.whiteText, {
position: 'absolute',
top: '50%',
left: '35%',
}]}>Image not available</Text>
: null
}
</View>
</SafeAreaView>
</Modal> : null}

View File

@@ -19,6 +19,7 @@ import { addClickstreamEvent } from '../../services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
import Layout from '../layout/Layout';
import otpText from "./OtpText";
import { resetVerifyOTPError } from '../../reducer/loginSlice';
interface IOtpForm {
otp: string;
@@ -97,7 +98,12 @@ const OtpInput = () => {
style={[styles.otpText]}
keyboardType="phone-pad"
onBlur={onBlur}
onChangeText={value => onChange(value)}
onChangeText={value => {
onChange(value)
if(verifyOTPError) {
dispatch(resetVerifyOTPError());
}
}}
value={value}
maxLength={4}
testID={'test_enter_otp'}

View File

@@ -7,9 +7,11 @@ import Text from '../../../RN-UI-LIB/src/components/Text';
import {Countdown} from '../../components/countdown';
import {StyleSheet} from 'react-native';
import {COLORS} from '../../../RN-UI-LIB/src/styles/colors';
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
const OtpText = () => {
const [countDownComplete, setCountDownComplete] = useState<boolean>(false);
const [countDownTimeLeft, setCountDownTimeLeft] = useState<number>(30);
const dispatch = useAppDispatch();
const {phoneNumber, verifyOTPError} = useSelector(
@@ -24,35 +26,34 @@ const OtpText = () => {
setCountDownComplete(true)
};
if (verifyOTPError) {
return (
<Text style={styles.errorText}>
{verifyOTPError}{' '}
<Text style={styles.clickableText} onPress={handleResendOTP} testID='resend-otp-text'>
Resend OTP
</Text>
</Text>
);
}
if (countDownComplete) {
return (
<Text light>
Enter 4 digits OTP or{' '}
<Text style={styles.clickableText} onPress={handleResendOTP} testID='resend-otp-text'>
Resend OTP
</Text>
</Text>
);
}
return (
<Text light>
<>
{!countDownComplete && <Text light>
Resend OTP in{' '}
<Countdown
onComplete={handleCountdownComplete}
startFrom={30}
startFrom={countDownTimeLeft}
step={(timeLeft) => setCountDownTimeLeft(timeLeft)}
/>{' '}
second(s)
second{ countDownTimeLeft > 1 && 's' }
</Text>
}
<Text style={GenericStyles.alignCenter} >
{verifyOTPError && (
<Text style={styles.errorText}>
{verifyOTPError}{' '}
</Text>
)}
{
countDownComplete && (
<Text style={styles.clickableText} onPress={handleResendOTP} testID='resend-otp-text'>
Resend OTP
</Text>
)
}
</Text>
</>
);
};

View File

@@ -119,6 +119,12 @@ const TodoList = () => {
const intermediateTodoListLength = intermediateTodoList.length;
const avCases = intermediateTodoList.filter((item) => {
if(caseDetails[item.caseReferenceId]?.caseType === CaseAllocationType.ADDRESS_VERIFICATION_CASE) {
return item;
}
})
return (
<Layout>
<View style={[GenericStyles.fill, styles.todoListContainer]}>
@@ -128,22 +134,27 @@ const TodoList = () => {
} selected`}
onBack={handleBack}
/>
<Banner hideClose style={GenericStyles.m12}>
{ avCases?.length > 0 && <Banner hideClose style={GenericStyles.m12}>
<View style={[GenericStyles.row]}>
<View style={styles.infoIcon}>
<InfoIcon color={COLORS.BACKGROUND.INDIGO_DARK} />
</View>
<View style={{flexBasis: '95%'}}>
<Heading dark type="h5" style={styles.headingText}>
Sharing a message of your visit
<Heading dark type="h5" style={styles.headingText} >
Sharing a message to only onboarding
</Heading>
<Text>
The selected customer{intermediateTodoListLength > 1 ? 's' : ''} will recieve a message
about your visit. Are you sure you want to proceed?
<Heading dark type="h5" style={styles.headingText} >
customers
</Heading>
<Text style={styles.descriptionText} >
The selected onboarding customer{avCases?.length > 1 ? 's' : ''} will
</Text>
<Text style={styles.descriptionText} >
receive a message about your visit
</Text>
</View>
</View>
</Banner>
</Banner>}
<VirtualizedList
style={{ height: 500 }}
data={intermediateTodoList as Array<ICaseItem>}
@@ -213,6 +224,9 @@ const styles = StyleSheet.create({
opacity: 0.6,
bottom: 77,
height: '100%',
},
descriptionText: {
color: '#6770C4'
}
});