@TP-20999 fixed flickering and not loading issues of image | Aman Sethi (#133)

* images fix

* add fast image lib

* prop of loading from hook

* remove care from package.json

* add rn-ui exported image wrapper

* rn ui update
This commit is contained in:
Aman Sethi
2023-03-13 16:49:31 +05:30
committed by GitHub Enterprise
parent 1cc9e8fe72
commit a82a2b35aa
12 changed files with 97 additions and 43 deletions

10
App.tsx
View File

@@ -22,6 +22,8 @@ import { ENV } from './src/constants/config';
import { mockApiServer } from './src/mock-api/server';
import Text from './RN-UI-LIB/src/components/Text';
import { PermissionStatusEnum } from './src/services/geolocation.service';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { LocalStorageKeys } from './src/common/Constants';
Sentry.init({ dsn: SENTRY_DSN });
if (ENV !== 'prod') {
@@ -30,6 +32,7 @@ if (ENV !== 'prod') {
LogBox.ignoreAllLogs();
export let GlobalImageMap: Record<string, string> = {};
const askForPermissions = (setPermissions:React.Dispatch<React.SetStateAction<boolean>> ) => {
if (Platform.OS === 'android') {
@@ -61,6 +64,13 @@ const App = () => {
const [permissions, setPermissions] = React.useState(true);
React.useEffect(()=>{
askForPermissions(setPermissions);
(async() => {
const data = await AsyncStorage.getItem(LocalStorageKeys.GLOBAL_IMAGE_MAP);
if(data) {
const parsedData = JSON.parse(data);
GlobalImageMap = parsedData;
}
})()
}, [])
return (
<Provider store={store}>

View File

@@ -1,4 +1,4 @@
export const BASE_AV_APP_URL = 'https://qa-longhorn-portal.np.navi-tech.in/field-app';
export const BASE_AV_APP_URL = 'https://qa-longhorn-server.np.navi-tech.in/field-app';
export const SENTRY_DSN = 'https://877396e88a2b4f78b911016c64f9121a@glitchtip.cmd.navi-tech.in/155';
export const JANUS_SERVICE_URL = 'https://qa-longhorn-portal.np.navi-tech.in/api/events/json';
export const JANUS_SERVICE_URL = 'https://qa-longhorn-server.np.navi-tech.in/api/events/json';
export const ENV = 'qa';

View File

@@ -46,6 +46,7 @@
"react-native": "0.70.6",
"react-native-code-push": "7.1.0",
"react-native-device-info": "10.3.0",
"react-native-fast-image": "8.6.3",
"react-native-geolocation-service": "5.3.1",
"react-native-image-picker": "4.10.2",
"react-native-pager-view": "6.1.2",

View File

@@ -127,21 +127,23 @@ export const getFilters = () => (dispatch: AppDispatch) => {
});
};
export const getSignedURLs = (urlList: string[]) => (dispatch: AppDispatch) => {
const url = getApiUrl(ApiKeys.IMAGE_SIGNED_URLS);
dispatch(setLoading(true));
return axiosInstance
.post(url, urlList)
export const getSignedApi = async (urlList: string[]): Promise<{imageUrl: string}> => {
const url = getApiUrl(ApiKeys.GET_SIGNED_URL);
const response = await axiosInstance
.post(url, urlList, {
headers: {
donotHandleError: true,
},
})
.then(response => {
if (response?.data) {
return response.data;
return { imageUrl: (Object.values(response?.data)?.[0] as string) ?? '' };
}
throw response;
return {imageUrl: ''};
})
.catch(err => {
logError(err);
})
.finally(() => {
dispatch(setLoading(false));
return {imageUrl: ''};
});
return response;
};

View File

@@ -151,6 +151,7 @@ export const HEADER_SCROLL_DISTANCE = (HEADER_HEIGHT_MAX - HEADER_HEIGHT_MIN) *
export const LocalStorageKeys = {
LOAN_ID_TO_VALUE: 'loanIdToValue',
GLOBAL_IMAGE_MAP: 'globalImageMap',
}
export const SourceTextFocused = new Set([

View File

@@ -21,10 +21,10 @@ export enum ApiKeys {
FEEDBACK,
FILTERS,
JANUS,
IMAGE_SIGNED_URLS,
GENERATE_PAYMENT_LINK,
ADDRESSES_GEOLOCATION,
NEW_ADDRESS,
GET_SIGNED_URL,
}
export const API_URLS: Record<ApiKeys, string> = {} as Record<ApiKeys, string>;
@@ -37,10 +37,10 @@ API_URLS[ApiKeys.LOGOUT] = '/auth/logout';
API_URLS[ApiKeys.FEEDBACK] = '/cases/feedback';
API_URLS[ApiKeys.FILTERS] = '/cases/filters';
API_URLS[ApiKeys.JANUS] = '/events/json';
API_URLS[ApiKeys.IMAGE_SIGNED_URLS] = '/cases/get-signed-urls';
API_URLS[ApiKeys.GENERATE_PAYMENT_LINK] = '/send-payment-link';
API_URLS[ApiKeys.ADDRESSES_GEOLOCATION] = '/addresses-geolocations';
API_URLS[ApiKeys.NEW_ADDRESS] = '/addresses';
API_URLS[ApiKeys.GET_SIGNED_URL] = '/cases/get-signed-urls';
export const API_STATUS_CODE = {
@@ -140,6 +140,9 @@ axiosInstance.interceptors.response.use(
error => {
logError(error as Error, 'From API Helper');
const {config, response} = error;
if(config.headers.donotHandleError) {
return;
}
if (config?.headers) {
const start = response.config.headers['request-start-time'];
const end = Date.now();

View File

@@ -1,25 +1,58 @@
import React, { useEffect, useState } from 'react';
import { setAsyncStorageItem } from './../components/utlis/commonFunctions';
import { CaseDetail } from '../screens/caseDetails/interface';
import { getImage64FromCaseId } from '../components/utlis/customerDbHelper';
import { CaseAllocationType } from '../screens/allCases/interface';
import { LocalStorageKeys } from '../common/Constants';
import { getSignedApi } from '../action/dataActions';
import { getBase64FromUrl } from '../components/utlis/commonFunctions';
import { GlobalImageMap } from '../../App';
const useCustomerImage = (caseDetails: CaseDetail) => {
const [imageUrl, setImageUrl] = useState('');
const [imageUrl, setImageUrl] = useState(
GlobalImageMap?.[caseDetails?.id] ||
caseDetails?.customerInfo?.imageURL ||
caseDetails?.imageUrl,
);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
useEffect(() => {
const imageUrl =
caseDetails?.customerInfo?.imageURL || caseDetails?.imageUrl;
if (imageUrl) {
if (error) {
(async () => {
let image64 = await getImage64FromCaseId(caseDetails?.id);
if (image64) {
// if image exist in WM db, then setting the base64
setImageUrl(image64);
const caseId = caseDetails?.id;
const data = GlobalImageMap?.[caseId];
setLoading(true);
if (data) {
// if image exist in Global, then setting the base64
setImageUrl(data);
setLoading(false);
} else {
setImageUrl(imageUrl);
const imageDocId =
caseDetails?.caseType ===
CaseAllocationType.COLLECTION_CASE ?? false
? caseDetails.documents?.[0]?.referenceId
: caseDetails.customerInfo.customerReferenceId;
const response = await getSignedApi([imageDocId!]);
const url = response?.imageUrl;
if (url) {
const responseImage64 = await getBase64FromUrl(url);
if (responseImage64) {
setImageUrl(responseImage64);
GlobalImageMap[caseId] = responseImage64;
await setAsyncStorageItem(
LocalStorageKeys.GLOBAL_IMAGE_MAP,
GlobalImageMap,
);
}
} else {
setImageUrl('');
}
setLoading(false);
}
})();
}
}, [caseDetails]);
return { imageUrl, setImageUrl };
}, [error]);
return { imageUrl, setImageUrl, setError, loading };
};
export default useCustomerImage;

View File

@@ -3,7 +3,6 @@ import { StyleSheet, View } from 'react-native';
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 { getSignedURLs } from '../../action/dataActions';
import { useAppDispatch, useAppSelector } from '../../hooks';
import useCustomerImage from '../../hooks/useCustomerImage';
import { CaseDetail } from '../caseDetails/interface';
@@ -15,7 +14,6 @@ interface ICaseItemAvatar {
showBorder?: boolean;
}
const MAX_API_CALL = 3;
const RELATIVE_ASYNC_ICON_RIGHT_POSITION = -5;
const RELATIVE_ASYNC_ICON_BOTTOM_POSITION = -5;
@@ -25,24 +23,17 @@ const CaseItemAvatar: React.FC<ICaseItemAvatar> = ({
showBorder = false,
}) => {
const dispatch = useAppDispatch();
const refrenceId = caseData?.caseReferenceId || caseData?.id
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, setImageUrl } = useCustomerImage(caseDetails);
const { imageUrl, setError, loading } = useCustomerImage(caseDetails);
const onError = async () => {
if (apiErrorCount < MAX_API_CALL) {
dispatch(getSignedURLs([imageUrl])).then(resp => {
if (resp?.[imageUrl]) {
setApiErrorCount(apiErrorCount => apiErrorCount + 1);
setImageUrl(resp[imageUrl]);
}
});
}
setError(true);
};
const customerName =
@@ -59,6 +50,7 @@ const CaseItemAvatar: React.FC<ICaseItemAvatar> = ({
dataURI={imageUrl}
onErrorFallback={onError}
style={[showBorder && styles.border]}
loading={loading}
/>
{!isSynced ? (
<View style={styles.unsyncedIcon}>

View File

@@ -8,6 +8,8 @@ import {
TouchableOpacity,
View,
} from 'react-native';
import { GlobalImageMap } from '../../../App';
import RNFastImage from '../../../RN-UI-LIB/src/components/FastImage';
import Heading from '../../../RN-UI-LIB/src/components/Heading';
import LineLoader from '../../../RN-UI-LIB/src/components/suspense_loader/LineLoader';
import SuspenseLoader from '../../../RN-UI-LIB/src/components/suspense_loader/SuspenseLoader';
@@ -43,7 +45,7 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
);
const ANIMATION_DURATION = 500;
const [openImage, setOpenImage] = useState<boolean>(false);
const { imageUrl } = useCustomerImage(caseDetail);
const { imageUrl, setError, loading } = useCustomerImage(caseDetail);
const handleOpenClose = useCallback(() => {
setOpenImage(prev => !prev);
@@ -153,7 +155,7 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
''
)}
<Modal
{ GlobalImageMap[caseDetail?.id] ? <Modal
animationType={'slide'}
visible={openImage}
onRequestClose={handleOpenClose}>
@@ -173,14 +175,15 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
GenericStyles.alignCenter,
GenericStyles.row,
]}>
<Image
<RNFastImage
style={[GenericStyles.fill, styles.imageStyle]}
source={{ uri: imageUrl }}
resizeMode={'stretch'}
onError={() => setError(true)}
/>
</View>
</SafeAreaView>
</Modal>
</Modal> : null}
</View>
);
};

View File

@@ -59,6 +59,7 @@ export interface CustomerInfo {
imageURL: string;
geoLocation: string;
name: string;
imageReferenceId: string;
}
export interface Address {
@@ -165,6 +166,9 @@ export interface CaseDetail {
fatherName?: string;
currentDPD?: number;
loanAccountNumber?: string;
documents?: Array<{
referenceId: string;
}>;
interactionStatus: keyof typeof InteractionStatuses;
}

View File

@@ -7255,6 +7255,11 @@ react-native-device-info@10.3.0:
resolved "https://registry.yarnpkg.com/react-native-device-info/-/react-native-device-info-10.3.0.tgz#6bab64d84d3415dd00cc446c73ec5e2e61fddbe7"
integrity sha512-/ziZN1sA1REbJTv5mQZ4tXggcTvSbct+u5kCaze8BmN//lbxcTvWsU6NQd4IihLt89VkbX+14IGc9sVApSxd/w==
react-native-fast-image@8.6.3:
version "8.6.3"
resolved "https://registry.yarnpkg.com/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz#6edc3f9190092a909d636d93eecbcc54a8822255"
integrity sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==
react-native-geolocation-service@5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/react-native-geolocation-service/-/react-native-geolocation-service-5.3.1.tgz#4ce1017789da6fdfcf7576eb6f59435622af4289"