TP-38645 | Show Documents On Customer Profile Screen
This commit is contained in:
7
App.tsx
7
App.tsx
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
AppState,
|
||||
LogBox,
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
import usePolling from './src/hooks/usePolling';
|
||||
import { MILLISECONDS_IN_A_SECOND } from './RN-UI-LIB/src/utlis/common';
|
||||
import analytics from '@react-native-firebase/analytics';
|
||||
import ScreenshotBlocker from './src/components/utlis/ScreenshotBlocker';
|
||||
|
||||
initSentry();
|
||||
|
||||
@@ -112,6 +113,10 @@ function App() {
|
||||
active: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
ScreenshotBlocker.unblockScreenshots();
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
askForPermissions();
|
||||
const appStateChange = AppState.addEventListener('change', async (change) => {
|
||||
|
||||
@@ -135,6 +135,12 @@ def VERSION_CODE = 83
|
||||
def VERSION_NAME = "2.3.10"
|
||||
|
||||
android {
|
||||
packagingOptions {
|
||||
pickFirst 'lib/x86/libc++_shared.so'
|
||||
pickFirst 'lib/x86_64/libc++_shared.so'
|
||||
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
|
||||
pickFirst 'lib/arm64-v8a/libc++_shared.so'
|
||||
}
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
@@ -217,10 +223,18 @@ android {
|
||||
}
|
||||
signingConfigs {
|
||||
debug {
|
||||
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
|
||||
storeFile file(MYAPP_UPLOAD_STORE_FILE)
|
||||
storePassword MYAPP_UPLOAD_STORE_PASSWORD
|
||||
keyAlias MYAPP_UPLOAD_KEY_ALIAS
|
||||
keyPassword MYAPP_UPLOAD_KEY_PASSWORD
|
||||
}
|
||||
else {
|
||||
storeFile file('debug.keystore')
|
||||
storePassword 'android'
|
||||
keyAlias 'androiddebugkey'
|
||||
keyPassword 'android'
|
||||
}
|
||||
}
|
||||
release {
|
||||
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
|
||||
|
||||
@@ -37,6 +37,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
packages.add(new DeviceUtilsModulePackage());
|
||||
packages.add(new ScreenshotBlockerModulePackage());
|
||||
return packages;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.avapp;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.WindowManager;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
public class ScreenshotBlockerModule extends ReactContextBaseJavaModule {
|
||||
private ReactApplicationContext reactContext;
|
||||
|
||||
public ScreenshotBlockerModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.reactContext = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ScreenshotBlocker";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void blockScreenshots() {
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
Activity activity = getCurrentActivity();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (activity != null) {
|
||||
activity.getWindow().setFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void unblockScreenshots() {
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
Activity activity = getCurrentActivity();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (activity != null) {
|
||||
activity.getWindow().clearFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.avapp;
|
||||
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ScreenshotBlockerModulePackage implements ReactPackage {
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(
|
||||
ReactApplicationContext reactContext) {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
|
||||
modules.add(new ScreenshotBlockerModule(reactContext));
|
||||
|
||||
return modules;
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@
|
||||
"react-native-gzip": "1.0.0",
|
||||
"react-native-image-picker": "4.10.2",
|
||||
"react-native-pager-view": "6.1.2",
|
||||
"react-native-pdf": "^6.7.1",
|
||||
"react-native-permissions": "3.6.1",
|
||||
"react-native-qrcode-svg": "^6.2.0",
|
||||
"react-native-safe-area-context": "4.4.1",
|
||||
|
||||
5
src/components/utlis/ScreenshotBlocker.ts
Normal file
5
src/components/utlis/ScreenshotBlocker.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
const { ScreenshotBlocker } = NativeModules;
|
||||
|
||||
export default ScreenshotBlocker;
|
||||
@@ -32,6 +32,7 @@ import Notifications from '../notifications';
|
||||
import RegisterPayments from '../registerPayements/RegisterPayments';
|
||||
import TodoList from '../todoList/TodoList';
|
||||
import UngroupedAddressContainer from '../addressGeolocation/UngroupedAddressContainer';
|
||||
import PDFFullScreen from '../caseDetails/PDFFullScreen';
|
||||
|
||||
const Stack = createNativeStackNavigator();
|
||||
|
||||
@@ -170,6 +171,16 @@ const ProtectedRouter = () => {
|
||||
}}
|
||||
listeners={getScreenFocusListenerObj}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name={'pdfFull'}
|
||||
component={PDFFullScreen}
|
||||
options={{
|
||||
header: () => null,
|
||||
animationDuration: SCREEN_ANIMATION_DURATION,
|
||||
animation: 'none',
|
||||
}}
|
||||
listeners={getScreenFocusListenerObj}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name={PageRouteEnum.PAYMENTS}
|
||||
component={RegisterPayments}
|
||||
|
||||
@@ -41,6 +41,8 @@ import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import { getLoanAccountNumber } from '../../components/utlis/commonFunctions';
|
||||
import EmiBreakupBottomSheet from '../emiSchedule/EmiBreakupBottomSheet';
|
||||
import { CollectionCaseWidgetId } from '../../types/template.types';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
import ScreenshotBlocker from '../../components/utlis/ScreenshotBlocker';
|
||||
|
||||
interface ICaseDetails {
|
||||
route: {
|
||||
@@ -221,6 +223,10 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
|
||||
});
|
||||
};
|
||||
|
||||
useFocusEffect(() => {
|
||||
ScreenshotBlocker.unblockScreenshots();
|
||||
});
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<SafeAreaView style={[GenericStyles.fill, GenericStyles.whiteBackground]}>
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import { ActivityIndicator, Pressable, ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { ScrollView, StyleSheet, View } from 'react-native';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { GenericStyles, SCREEN_WIDTH } from '../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import RNFastImage from '../../../RN-UI-LIB/src/components/FastImage';
|
||||
import NavigationHeader, { Icon } from '../../../RN-UI-LIB/src/components/NavigationHeader';
|
||||
import { CaseDetail, DOCUMENT_TYPE, IDocument } from './interface';
|
||||
import { CaseDetail, DocumentDetail, DOCUMENT_TYPE, IDocument } from './interface';
|
||||
import useIsOnline from '../../hooks/useIsOnline';
|
||||
import useFetchDocument from '../../hooks/useFetchDocument';
|
||||
import {
|
||||
RELATIVE_PATH_PREFIX,
|
||||
checkS3Url,
|
||||
findDocumentByDocumentType,
|
||||
getDocumentList,
|
||||
} from '../../components/utlis/commonFunctions';
|
||||
import { goBack, navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import Text from '../../../RN-UI-LIB/src/components/Text';
|
||||
import { ISignedRequest, getSignedApi } from '../../action/dataActions';
|
||||
import { useTimeout } from 'react-native-toast-message/lib/src/hooks';
|
||||
import { DELAY_FOR_PAINTING_IMAGE } from '../../common/Constants';
|
||||
import { isNullOrEmptyString } from '../../../RN-UI-LIB/src/utlis/common';
|
||||
import { goBack } from '../../components/utlis/navigationUtlis';
|
||||
import SuspenseLoader from '../../../RN-UI-LIB/src/components/suspense_loader/SuspenseLoader';
|
||||
import LineLoader from '../../../RN-UI-LIB/src/components/suspense_loader/LineLoader';
|
||||
import ScreenshotBlocker from '../../components/utlis/ScreenshotBlocker';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
import DocumentImageComponent from './DocumentImageComponent';
|
||||
import DocumentDetails from './DocumentDetails';
|
||||
import { getDocumentDetails } from './utils/documentUtils';
|
||||
|
||||
interface ICustomerProfile {
|
||||
route: {
|
||||
@@ -35,12 +35,9 @@ const CustomerProfile: React.FC<ICustomerProfile> = (props) => {
|
||||
params: { caseDetail },
|
||||
},
|
||||
} = props;
|
||||
const [errorModalImage, setErrorModalImage] = useState<boolean>(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { startTimer, isActive, clearTimer } = useTimeout(() => {
|
||||
setLoading(false);
|
||||
}, DELAY_FOR_PAINTING_IMAGE);
|
||||
const isOnline = useIsOnline();
|
||||
const [isDocumentsLoading, setIsDocumentsLoading] = useState(false);
|
||||
|
||||
const { documentObj, setRetryForUnsignedDocuments } = useFetchDocument(
|
||||
{
|
||||
caseId: caseDetail?.id,
|
||||
@@ -51,45 +48,7 @@ const CustomerProfile: React.FC<ICustomerProfile> = (props) => {
|
||||
false,
|
||||
false
|
||||
);
|
||||
const [vkycUri, setVkycUri] = useState<string>();
|
||||
const [showVkyc, setShowVkyc] = useState<boolean>(false);
|
||||
|
||||
const updateVkycVideo = (uri: string) => {
|
||||
setVkycUri(uri);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (caseDetail) {
|
||||
const docList: IDocument[] = getDocumentList(caseDetail) || [];
|
||||
const vkycDoc = findDocumentByDocumentType(docList, DOCUMENT_TYPE.VKYC_VIDEO);
|
||||
if (!vkycDoc?.referenceId) {
|
||||
return;
|
||||
}
|
||||
setShowVkyc(true);
|
||||
const vkycUri = vkycDoc?.uri;
|
||||
async function checkUrl(url: string) {
|
||||
const result = await checkS3Url(url);
|
||||
if (result) {
|
||||
setVkycUri(url);
|
||||
} else {
|
||||
const imageReferenceId = vkycDoc?.referenceId!!;
|
||||
const signedRequestPayload: ISignedRequest = [
|
||||
{
|
||||
documentReferenceId: imageReferenceId,
|
||||
caseId: caseDetail.id,
|
||||
caseType: caseDetail.caseType,
|
||||
},
|
||||
];
|
||||
const response = await getSignedApi(signedRequestPayload);
|
||||
const url = response?.imageUrl || '';
|
||||
setVkycUri(url);
|
||||
}
|
||||
}
|
||||
if (vkycUri) {
|
||||
checkUrl(vkycUri);
|
||||
}
|
||||
}
|
||||
}, [caseDetail]);
|
||||
const [documentData, setDocumentData] = useState<Array<DocumentDetail>>([]);
|
||||
|
||||
const getImageURI = () => {
|
||||
if (!isOnline) {
|
||||
@@ -106,72 +65,75 @@ const CustomerProfile: React.FC<ICustomerProfile> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleVkycPress = () => {
|
||||
navigateToScreen('vkycFull', {
|
||||
vkycUri,
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
if (caseDetail) {
|
||||
const docList: IDocument[] = getDocumentList(caseDetail) || [];
|
||||
const docPromises: Promise<DocumentDetail | null>[] = [];
|
||||
// TODO: Add Driver License Enum
|
||||
const updatedDocList = [
|
||||
DOCUMENT_TYPE.VKYC_VIDEO,
|
||||
DOCUMENT_TYPE.AADHAR,
|
||||
DOCUMENT_TYPE.PAN,
|
||||
DOCUMENT_TYPE.AADHAR_PHOTO,
|
||||
];
|
||||
updatedDocList.forEach((documentType) => {
|
||||
const document = findDocumentByDocumentType(docList, documentType);
|
||||
if (document) {
|
||||
const docPromise = getDocumentDetails(document, caseDetail.id, caseDetail.caseType);
|
||||
if (docPromise) docPromises.push(docPromise);
|
||||
}
|
||||
});
|
||||
|
||||
const onLoadStart = () => {
|
||||
setLoading(true);
|
||||
setErrorModalImage(false);
|
||||
};
|
||||
const onLoadEnd = () => {
|
||||
// added this to show loader for the image is getting painted on screen
|
||||
startTimer();
|
||||
};
|
||||
setIsDocumentsLoading(true);
|
||||
Promise.all(docPromises)
|
||||
.then((docs: Array<DocumentDetail | null>) => {
|
||||
const documents = docs?.filter(
|
||||
(document: DocumentDetail | null) => document
|
||||
) as Array<DocumentDetail>;
|
||||
setDocumentData(documents);
|
||||
setIsDocumentsLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setIsDocumentsLoading(false);
|
||||
});
|
||||
}
|
||||
}, [caseDetail]);
|
||||
|
||||
const onLoad = () => {
|
||||
clearTimer();
|
||||
setLoading(false);
|
||||
setErrorModalImage(false);
|
||||
};
|
||||
useFocusEffect(() => {
|
||||
ScreenshotBlocker.blockScreenshots();
|
||||
});
|
||||
|
||||
const imageUri = getImageURI();
|
||||
|
||||
return (
|
||||
<SafeAreaView style={[GenericStyles.fill, styles.container, GenericStyles.whiteBackground]}>
|
||||
<NavigationHeader title="Customer profile" onBack={() => goBack()} icon={Icon.close} />
|
||||
<ScrollView>
|
||||
<View style={GenericStyles.centerAlignedRow}>
|
||||
<View style={styles.imageContainer}>
|
||||
<RNFastImage
|
||||
style={styles.imageStyle}
|
||||
source={{ uri: imageUri }}
|
||||
resizeMode="contain"
|
||||
onError={() => {
|
||||
setRetryForUnsignedDocuments([DOCUMENT_TYPE.SELFIE]);
|
||||
setErrorModalImage(true);
|
||||
}}
|
||||
onLoadStart={onLoadStart}
|
||||
onLoad={onLoad}
|
||||
onLoadEnd={onLoadEnd}
|
||||
<DocumentImageComponent
|
||||
docType={DOCUMENT_TYPE.SELFIE}
|
||||
url={imageUri}
|
||||
setRetryForUnsignedDocuments={setRetryForUnsignedDocuments}
|
||||
/>
|
||||
{loading ? (
|
||||
<Text style={[styles.loadingText]} bold>
|
||||
{!isNullOrEmptyString(imageUri) ? 'Loading Image...' : 'No Image Found'}
|
||||
</Text>
|
||||
) : errorModalImage ? (
|
||||
<Text style={[styles.errorText]} bold>
|
||||
Error loading image
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
{showVkyc ? (
|
||||
<View style={styles.vkyc}>
|
||||
<Text bold dark>
|
||||
VKYC video
|
||||
</Text>
|
||||
{vkycUri ? (
|
||||
<Pressable onPress={handleVkycPress}>
|
||||
<Text style={styles.openVideoTxt}>Open video</Text>
|
||||
</Pressable>
|
||||
) : (
|
||||
<ActivityIndicator color={COLORS.BASE.BLUE} />
|
||||
)}
|
||||
</View>
|
||||
) : null}
|
||||
{/* <Accordion title="VKYC Video" content={<VKYCVideo />} /> */}
|
||||
<SuspenseLoader
|
||||
loading={isDocumentsLoading}
|
||||
fallBack={
|
||||
<View style={GenericStyles.ph16}>
|
||||
{[...Array(4).keys()].map(() => (
|
||||
<LineLoader
|
||||
width="100%"
|
||||
height={50}
|
||||
style={[GenericStyles.br6, GenericStyles.mb20]}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
}
|
||||
>
|
||||
<DocumentDetails documentData={documentData} />
|
||||
</SuspenseLoader>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
@@ -190,37 +152,7 @@ const styles = StyleSheet.create({
|
||||
borderWidth: 1,
|
||||
borderColor: COLORS.BORDER.GREY,
|
||||
},
|
||||
imageStyle: {
|
||||
height: '100%',
|
||||
},
|
||||
container: {
|
||||
position: 'relative',
|
||||
},
|
||||
errorText: {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
textAlign: 'center',
|
||||
width: '100%',
|
||||
color: COLORS.TEXT.RED,
|
||||
},
|
||||
loadingText: {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
textAlign: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
vkyc: {
|
||||
marginHorizontal: 16,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 18,
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
borderColor: COLORS.BORDER.GREY,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
openVideoTxt: {
|
||||
color: COLORS.TEXT.BLUE,
|
||||
},
|
||||
});
|
||||
|
||||
131
src/screens/caseDetails/DocumentDetails.tsx
Normal file
131
src/screens/caseDetails/DocumentDetails.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Pressable, StyleSheet, View } from 'react-native';
|
||||
import WebView from 'react-native-webview';
|
||||
import Accordion from '../../../RN-UI-LIB/src/components/accordian/Accordian';
|
||||
import Text from '../../../RN-UI-LIB/src/components/Text';
|
||||
import ArrowSolidDownIcon from '../../../RN-UI-LIB/src/Icons/ArrowSolidDownIcon';
|
||||
import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import { navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import DocumentImageComponent from './DocumentImageComponent';
|
||||
import { DocumentDetail } from './interface';
|
||||
import { vkycHtml } from './vkycTemplate';
|
||||
|
||||
interface DocumentDetails {
|
||||
documentData: Array<DocumentDetail>;
|
||||
}
|
||||
|
||||
const DocumentDetails = (props: DocumentDetails) => {
|
||||
const { documentData } = props;
|
||||
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
||||
|
||||
const handleOpenPdfPress = (url: string) => {
|
||||
// TODO: Dynamic Url
|
||||
navigateToScreen('pdfFull', {
|
||||
pdfUri: 'https://www.africau.edu/images/default/sample.pdf',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={GenericStyles.ph16}>
|
||||
{documentData.map((item: any) =>
|
||||
item.docContentType !== 'pdf' ? (
|
||||
<Accordion
|
||||
accordionStyle={styles.accordionStyles}
|
||||
isActive={isExpanded}
|
||||
touchableDelay={50}
|
||||
touchableOpacity={0.8}
|
||||
accordionHeader={
|
||||
<View style={styles.headerWrapper}>
|
||||
{item?.icon}
|
||||
<Text style={GenericStyles.pl6}>{item?.title}</Text>
|
||||
</View>
|
||||
}
|
||||
customExpandUi={{
|
||||
whenCollapsed: (
|
||||
<View style={styles.accordionExpandBtn}>
|
||||
<ArrowSolidDownIcon />
|
||||
</View>
|
||||
),
|
||||
whenExpanded: (
|
||||
<View style={[styles.accordionExpandBtn, styles.rotateBtn]}>
|
||||
<ArrowSolidDownIcon />
|
||||
</View>
|
||||
),
|
||||
}}
|
||||
onExpanded={(value) => {
|
||||
setIsExpanded(value);
|
||||
}}
|
||||
>
|
||||
<View style={styles.accordionContentWrapper}>
|
||||
{item.docContentType === 'video' ? (
|
||||
<WebView
|
||||
source={{ html: vkycHtml(item.url) }}
|
||||
style={{ flex: 1 }}
|
||||
allowsFullscreenVideo={true}
|
||||
mediaPlaybackRequiresUserAction={false}
|
||||
/>
|
||||
) : (
|
||||
<DocumentImageComponent docType={item.docType} url={item.url} />
|
||||
)}
|
||||
</View>
|
||||
</Accordion>
|
||||
) : (
|
||||
<View
|
||||
style={[
|
||||
styles.accordionStyles,
|
||||
GenericStyles.row,
|
||||
GenericStyles.justifyContentSpaceBetween,
|
||||
{ marginBottom: 12, backgroundColor: COLORS.TEXT.WHITE, paddingHorizontal: 16 },
|
||||
]}
|
||||
>
|
||||
<View style={[styles.headerWrapper, { paddingHorizontal: 0 }]}>
|
||||
{item?.icon}
|
||||
<Text style={GenericStyles.pl6}>{item?.title}</Text>
|
||||
</View>
|
||||
<Pressable onPress={() => handleOpenPdfPress(item.url)}>
|
||||
<Text style={styles.openPdfTxt}>Open PDF</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
openPdfTxt: {
|
||||
color: COLORS.TEXT.BLUE,
|
||||
},
|
||||
accordionExpandBtn: {
|
||||
fontSize: 13,
|
||||
marginTop: 8,
|
||||
fontWeight: '500',
|
||||
lineHeight: 20,
|
||||
color: COLORS.TEXT.BLUE,
|
||||
paddingRight: 16,
|
||||
},
|
||||
accordionContentWrapper: {
|
||||
backgroundColor: '#F7F7F7',
|
||||
borderBottomLeftRadius: 8,
|
||||
borderBottomRightRadius: 8,
|
||||
height: 250,
|
||||
padding: 16,
|
||||
},
|
||||
rotateBtn: { transform: [{ rotateX: '180deg' }], marginTop: 0 },
|
||||
headerWrapper: {
|
||||
...GenericStyles.alignCenter,
|
||||
...GenericStyles.row,
|
||||
...GenericStyles.ph16,
|
||||
...GenericStyles.mb12,
|
||||
},
|
||||
accordionStyles: {
|
||||
...GenericStyles.pt12,
|
||||
...GenericStyles.br8,
|
||||
...getShadowStyle(4),
|
||||
paddingHorizontal: 0,
|
||||
marginBottom: 16,
|
||||
},
|
||||
});
|
||||
|
||||
export default DocumentDetails;
|
||||
116
src/screens/caseDetails/DocumentImageComponent.tsx
Normal file
116
src/screens/caseDetails/DocumentImageComponent.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import React, { useState } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { useTimeout } from 'react-native-toast-message/lib/src/hooks';
|
||||
import RNFastImage from '../../../RN-UI-LIB/src/components/FastImage';
|
||||
import Text from '../../../RN-UI-LIB/src/components/Text';
|
||||
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import { isNullOrEmptyString } from '../../../RN-UI-LIB/src/utlis/common';
|
||||
import { DELAY_FOR_PAINTING_IMAGE } from '../../common/Constants';
|
||||
import { DOCUMENT_TYPE } from './interface';
|
||||
|
||||
interface DocumentImageComponentProps {
|
||||
docType: DOCUMENT_TYPE;
|
||||
url: string;
|
||||
setRetryForUnsignedDocuments?: React.Dispatch<React.SetStateAction<DOCUMENT_TYPE[]>>;
|
||||
}
|
||||
|
||||
const DocumentImageComponent = (props: DocumentImageComponentProps) => {
|
||||
const { docType, url, setRetryForUnsignedDocuments } = props;
|
||||
const [errorModalImage, setErrorModalImage] = useState({
|
||||
[DOCUMENT_TYPE.SELFIE]: false,
|
||||
[DOCUMENT_TYPE.AADHAR]: false,
|
||||
[DOCUMENT_TYPE.PAN]: false,
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState({
|
||||
[DOCUMENT_TYPE.SELFIE]: true,
|
||||
[DOCUMENT_TYPE.AADHAR]: true,
|
||||
[DOCUMENT_TYPE.PAN]: true,
|
||||
});
|
||||
|
||||
const { startTimer, isActive, clearTimer } = useTimeout(() => {
|
||||
setLoading({
|
||||
[DOCUMENT_TYPE.SELFIE]: false,
|
||||
[DOCUMENT_TYPE.AADHAR]: false,
|
||||
[DOCUMENT_TYPE.PAN]: false,
|
||||
});
|
||||
}, DELAY_FOR_PAINTING_IMAGE);
|
||||
|
||||
const onLoadStart = (docType: DOCUMENT_TYPE) => {
|
||||
setLoading({
|
||||
...loading,
|
||||
[docType]: true,
|
||||
});
|
||||
setErrorModalImage({
|
||||
...errorModalImage,
|
||||
[docType]: false,
|
||||
});
|
||||
};
|
||||
const onLoadEnd = () => {
|
||||
// added this to show loader for the image is getting painted on screen
|
||||
startTimer();
|
||||
};
|
||||
|
||||
const onLoad = (docType: DOCUMENT_TYPE) => {
|
||||
clearTimer();
|
||||
setLoading({
|
||||
...loading,
|
||||
[docType]: false,
|
||||
});
|
||||
setErrorModalImage({
|
||||
...errorModalImage,
|
||||
[docType]: false,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<RNFastImage
|
||||
style={styles.imageStyle}
|
||||
source={{ uri: url }}
|
||||
resizeMode="contain"
|
||||
onError={() => {
|
||||
setRetryForUnsignedDocuments?.([docType]);
|
||||
setErrorModalImage({
|
||||
...errorModalImage,
|
||||
[docType]: true,
|
||||
});
|
||||
}}
|
||||
onLoadStart={() => onLoadStart(docType)}
|
||||
onLoad={() => onLoad(docType)}
|
||||
onLoadEnd={onLoadEnd}
|
||||
/>
|
||||
{loading[docType] ? (
|
||||
<Text style={[styles.loadingText]} bold>
|
||||
{!isNullOrEmptyString(url) ? 'Loading Image...' : 'No Image Found'}
|
||||
</Text>
|
||||
) : errorModalImage[docType] ? (
|
||||
<Text style={[styles.errorText]} bold>
|
||||
Error loading image
|
||||
</Text>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
imageStyle: {
|
||||
height: '100%',
|
||||
},
|
||||
errorText: {
|
||||
...GenericStyles.absolute,
|
||||
top: '50%',
|
||||
textAlign: 'center',
|
||||
width: '100%',
|
||||
color: COLORS.TEXT.RED,
|
||||
},
|
||||
loadingText: {
|
||||
...GenericStyles.absolute,
|
||||
top: '50%',
|
||||
textAlign: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
export default DocumentImageComponent;
|
||||
74
src/screens/caseDetails/PDFFullScreen.tsx
Normal file
74
src/screens/caseDetails/PDFFullScreen.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Dimensions, StyleSheet, View } from 'react-native';
|
||||
import React, { useState } from 'react';
|
||||
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
|
||||
import NavigationHeader, { Icon } from '../../../RN-UI-LIB/src/components/NavigationHeader';
|
||||
import { goBack } from '../../components/utlis/navigationUtlis';
|
||||
import Pdf from 'react-native-pdf';
|
||||
import Text from '../../../RN-UI-LIB/src/components/Text';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
|
||||
interface ICustomerProfile {
|
||||
route: {
|
||||
params: {
|
||||
pdfUri: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const PDFFullScreen: React.FC<ICustomerProfile> = (props) => {
|
||||
const {
|
||||
route: {
|
||||
params: { pdfUri },
|
||||
},
|
||||
} = props;
|
||||
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
return (
|
||||
<View style={GenericStyles.fill}>
|
||||
<NavigationHeader onBack={goBack} title="Aadhar PDF viewer" icon={Icon.close} />
|
||||
<View style={styles.container}>
|
||||
<Pdf
|
||||
trustAllCerts={false}
|
||||
source={{ uri: pdfUri }}
|
||||
renderActivityIndicator={() => {
|
||||
return !error ? (
|
||||
<Text>Loading...</Text>
|
||||
) : (
|
||||
<Text style={[styles.errorText]} bold>
|
||||
Error loading pdf
|
||||
</Text>
|
||||
);
|
||||
}}
|
||||
onError={() => {
|
||||
setError(true);
|
||||
}}
|
||||
style={styles.pdf}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default PDFFullScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
margin: 12,
|
||||
},
|
||||
pdf: {
|
||||
flex: 1,
|
||||
width: Dimensions.get('window').width,
|
||||
height: Dimensions.get('window').height,
|
||||
},
|
||||
errorText: {
|
||||
...GenericStyles.absolute,
|
||||
top: '50%',
|
||||
textAlign: 'center',
|
||||
width: '100%',
|
||||
color: COLORS.TEXT.RED,
|
||||
},
|
||||
});
|
||||
@@ -139,6 +139,9 @@ export enum DOCUMENT_TYPE {
|
||||
SELFIE = 'SELFIE',
|
||||
OPTIMIZED_SELFIE = 'OPTIMIZED_SELFIE',
|
||||
VKYC_VIDEO = 'VKYC_VIDEO',
|
||||
PAN = 'PAN',
|
||||
AADHAR = 'AADHAR',
|
||||
AADHAR_PHOTO = 'aadhar_photo',
|
||||
}
|
||||
|
||||
export type TDocumentObj = {
|
||||
@@ -151,6 +154,14 @@ export interface IDocument {
|
||||
type: DOCUMENT_TYPE;
|
||||
}
|
||||
|
||||
export interface DocumentDetail {
|
||||
icon: React.ReactNode;
|
||||
title: string;
|
||||
docType: DOCUMENT_TYPE;
|
||||
docContentType: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export enum FeedbackStatus {
|
||||
ATTEMPTED = 'ATTEMPTED',
|
||||
NOT_ATTEMPTED = 'NOT_ATTEMPTED',
|
||||
|
||||
90
src/screens/caseDetails/utils/documentUtils.tsx
Normal file
90
src/screens/caseDetails/utils/documentUtils.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import ImageIcon from '../../../../RN-UI-LIB/src/Icons/ImageIcon';
|
||||
import PdfIcon from '../../../../RN-UI-LIB/src/Icons/PdfIcon';
|
||||
import VideoIcon from '../../../../RN-UI-LIB/src/Icons/VideoIcon';
|
||||
import { getSignedApi, ISignedRequest } from '../../../action/dataActions';
|
||||
import { checkS3Url } from '../../../components/utlis/commonFunctions';
|
||||
import { CaseAllocationType } from '../../allCases/interface';
|
||||
import { DOCUMENT_TYPE, IDocument } from '../interface';
|
||||
|
||||
async function checkUrlData(
|
||||
url: string,
|
||||
referenceId: string,
|
||||
caseId: string,
|
||||
caseType: CaseAllocationType
|
||||
) {
|
||||
const result = await checkS3Url(url);
|
||||
if (result) {
|
||||
return url;
|
||||
} else {
|
||||
const signedRequestPayload: ISignedRequest = [
|
||||
{
|
||||
documentReferenceId: referenceId!!,
|
||||
caseId: caseId,
|
||||
caseType: caseType,
|
||||
},
|
||||
];
|
||||
const response = await getSignedApi(signedRequestPayload);
|
||||
const url = response?.imageUrl || '';
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
export const getDocumentDetails = async (
|
||||
document: IDocument,
|
||||
caseId: string,
|
||||
caseType: CaseAllocationType
|
||||
) => {
|
||||
if (!document.referenceId) {
|
||||
return null;
|
||||
}
|
||||
if (document?.uri) {
|
||||
try {
|
||||
const imageUrl = await checkUrlData(document.uri, document.referenceId, caseId, caseType);
|
||||
if (!imageUrl) return null;
|
||||
// TODO: Make Dynamic Based on Content type from firestore and add DL Case
|
||||
switch (document.type) {
|
||||
case DOCUMENT_TYPE.VKYC_VIDEO:
|
||||
return {
|
||||
icon: <VideoIcon />,
|
||||
title: 'VKYC video',
|
||||
docType: DOCUMENT_TYPE.VKYC_VIDEO,
|
||||
docContentType: 'video',
|
||||
url: imageUrl,
|
||||
};
|
||||
|
||||
case DOCUMENT_TYPE.PAN:
|
||||
return {
|
||||
icon: <ImageIcon />,
|
||||
title: 'Pan details',
|
||||
docType: DOCUMENT_TYPE.PAN,
|
||||
docContentType: 'image',
|
||||
url: imageUrl,
|
||||
};
|
||||
|
||||
case DOCUMENT_TYPE.AADHAR:
|
||||
return {
|
||||
icon: <PdfIcon />,
|
||||
title: 'Aadhar details',
|
||||
docType: DOCUMENT_TYPE.AADHAR,
|
||||
docContentType: 'pdf',
|
||||
url: imageUrl,
|
||||
};
|
||||
|
||||
case DOCUMENT_TYPE.AADHAR_PHOTO:
|
||||
return {
|
||||
icon: <ImageIcon />,
|
||||
title: 'Aadhar photo',
|
||||
docType: DOCUMENT_TYPE.AADHAR_PHOTO,
|
||||
docContentType: 'image',
|
||||
url: imageUrl,
|
||||
};
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
38
yarn.lock
38
yarn.lock
@@ -1658,16 +1658,16 @@
|
||||
resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e"
|
||||
integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ==
|
||||
|
||||
"@react-native/normalize-color@*", "@react-native/normalize-color@^2.0.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91"
|
||||
integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==
|
||||
|
||||
"@react-native/normalize-color@2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.0.0.tgz#da955909432474a9a0fe1cbffc66576a0447f567"
|
||||
integrity sha512-Wip/xsc5lw8vsBlmY2MO/gFLp3MvuZ2baBZjDeTjjndMgM0h5sxz7AZR62RDPGgstp8Np7JzjvVqVT7tpFZqsw==
|
||||
|
||||
"@react-native/normalize-color@^2.0.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91"
|
||||
integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==
|
||||
|
||||
"@react-native/polyfills@2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa"
|
||||
@@ -3339,6 +3339,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
crypto-js@^3.2.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b"
|
||||
integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==
|
||||
|
||||
css-select@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
|
||||
@@ -3404,7 +3409,7 @@ data-urls@^2.0.0:
|
||||
whatwg-mimetype "^2.3.0"
|
||||
whatwg-url "^8.0.0"
|
||||
|
||||
dayjs@^1.11.9:
|
||||
dayjs@1.11.9:
|
||||
version "1.11.9"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a"
|
||||
integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==
|
||||
@@ -3551,6 +3556,15 @@ depd@2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
||||
|
||||
deprecated-react-native-prop-types@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz#c10c6ee75ff2b6de94bb127f142b814e6e08d9ab"
|
||||
integrity sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==
|
||||
dependencies:
|
||||
"@react-native/normalize-color" "*"
|
||||
invariant "*"
|
||||
prop-types "*"
|
||||
|
||||
destroy@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
|
||||
@@ -5045,7 +5059,7 @@ internal-slot@^1.0.3:
|
||||
has "^1.0.3"
|
||||
side-channel "^1.0.4"
|
||||
|
||||
invariant@2.2.4, invariant@^2.2.2, invariant@^2.2.4:
|
||||
invariant@*, invariant@2.2.4, invariant@^2.2.2, invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
|
||||
@@ -7528,7 +7542,7 @@ prompts@^2.0.1, prompts@^2.4.0:
|
||||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.5"
|
||||
|
||||
prop-types@15.8.1, prop-types@^15.7.2, prop-types@^15.8.0, prop-types@^15.8.1:
|
||||
prop-types@*, 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==
|
||||
@@ -7762,6 +7776,14 @@ react-native-pager-view@6.1.2:
|
||||
resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-6.1.2.tgz#3522079b9a9d6634ca5e8d153bc0b4d660254552"
|
||||
integrity sha512-qs2KSFc+7N7B+UZ6SG2sTvCkppagm5fVyRclv1KFKc7lDtrhXLzN59tXJw575LDP/dRJoXsNwqUAhZJdws6ABQ==
|
||||
|
||||
react-native-pdf@^6.7.1:
|
||||
version "6.7.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-pdf/-/react-native-pdf-6.7.1.tgz#198b8ec3e8f1025ceb43ddfc6bc5b422521c4411"
|
||||
integrity sha512-zszQygtNBYoUfEtP/fV7zhzGeohDlUksh2p3OzshLrxdY9mw7Tm5VXAxYq4d8HsomRJUbFlJ7rHaTU9AQL800g==
|
||||
dependencies:
|
||||
crypto-js "^3.2.0"
|
||||
deprecated-react-native-prop-types "^2.3.0"
|
||||
|
||||
react-native-permissions@3.6.1:
|
||||
version "3.6.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-permissions/-/react-native-permissions-3.6.1.tgz#73adcc1cef8cd57a9ef167b4507405f4ff5749c4"
|
||||
|
||||
Reference in New Issue
Block a user