@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:
committed by
GitHub Enterprise
parent
1cc9e8fe72
commit
a82a2b35aa
10
App.tsx
10
App.tsx
@@ -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}>
|
||||
|
||||
Submodule RN-UI-LIB updated: b4b0ea26db...6a2d7bb1a9
@@ -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';
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user