Merge branch 'master' of https://github.com/navi-medici/address-verification-app into TP-38645
This commit is contained in:
@@ -313,7 +313,7 @@ dependencies {
|
||||
|
||||
implementation "com.github.anrwatchdog:anrwatchdog:1.4.0"
|
||||
|
||||
implementation 'com.navi.medici:alfred:v1.0.1'
|
||||
implementation 'com.navi.medici:alfred:v1.0.2'
|
||||
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
<queries>
|
||||
<package android:name="com.whatsapp" />
|
||||
<package android:name="com.whatsapp.w4b" />
|
||||
</queries>
|
||||
<uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
|
||||
<application
|
||||
|
||||
@@ -21,6 +21,9 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.uimanager.NativeViewHierarchyManager;
|
||||
import com.facebook.react.uimanager.UIBlock;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.navi.alfred.AlfredManager;
|
||||
|
||||
import android.content.pm.PackageInfo;
|
||||
@@ -28,6 +31,7 @@ import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Base64;
|
||||
|
||||
import android.os.Looper;
|
||||
@@ -39,6 +43,7 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@@ -71,7 +76,7 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule {
|
||||
@Override
|
||||
public void onActivityResult(Activity activity, int i, int i1, @Nullable Intent intent) {
|
||||
if(i1 != RESULT_CANCELED) {
|
||||
if (i == WHATSAPP_SHARE_REQUEST_CODE) {
|
||||
if (i == WHATSAPP_SHARE_REQUEST_CODE && (imageFile!=null)) {
|
||||
new File(Uri.fromFile(imageFile).getPath()).delete();
|
||||
}
|
||||
}
|
||||
@@ -117,6 +122,10 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule {
|
||||
promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void sendBottomSheetOpenSignal(Boolean isBottomSheetOpen) {
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setUserId(String userId) {
|
||||
@@ -154,16 +163,29 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void sendBottomSheetOpenSignal(Boolean isBottomSheetOpen) {
|
||||
if (isBottomSheetOpen) {
|
||||
View bottomSheetScreen = LayoutInflater.from(RNContext).inflate(R.layout.bottom_sheet_screen, null);
|
||||
AlfredManager.INSTANCE.measureInflatedView(bottomSheetScreen, 1080, 540);
|
||||
AlfredManager.INSTANCE.setCosmosBottomSheet(bottomSheetScreen);
|
||||
} else {
|
||||
AlfredManager.INSTANCE.setCosmosBottomSheet(null);
|
||||
public void setBottomSheetView(Integer refID) {
|
||||
if (refID != null) {
|
||||
UIManagerModule uiManagerModule = RNContext.getNativeModule(UIManagerModule.class);
|
||||
if (uiManagerModule != null) {
|
||||
try {
|
||||
uiManagerModule.addUIBlock(nativeViewHierarchyManager -> {
|
||||
Log.d("Alfred", "setBottomSheetView nativeViewHierarchyManager:" + nativeViewHierarchyManager);
|
||||
View view = nativeViewHierarchyManager.resolveView(refID);
|
||||
Log.d("Alfred", "setBottomSheetView view:" + view);
|
||||
AlfredManager.INSTANCE.setBottomSheetView(view);
|
||||
});
|
||||
} catch (Exception error) {
|
||||
Log.d("Alfred", "setBottomSheetView error:" + error);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void clearBottomSheet() {
|
||||
AlfredManager.INSTANCE.clearBottomSheetView();
|
||||
}
|
||||
|
||||
private static File convertBase64ToFile(Context context,String base64Data) {
|
||||
try {
|
||||
byte[] decodedBytes = Base64.decode(base64Data, Base64.DEFAULT);
|
||||
@@ -180,50 +202,87 @@ public class DeviceUtilsModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isWhatsAppInstalled() {
|
||||
public ArrayList<String> isWhatsAppInstalled() {
|
||||
PackageManager packageManager = RNContext.getPackageManager();
|
||||
List<PackageInfo> packages = packageManager.getInstalledPackages(PackageManager.GET_META_DATA);
|
||||
ArrayList<String> appsInstalled = new ArrayList<String>();
|
||||
|
||||
for (PackageInfo packageInfo : packages) {
|
||||
String packageName = packageInfo.packageName;
|
||||
if(packageName.equals("com.whatsapp")){
|
||||
return true;
|
||||
if(packageName.equals("com.whatsapp") || packageName.equals("com.whatsapp.w4b")){
|
||||
appsInstalled.add(packageName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return appsInstalled;
|
||||
}
|
||||
|
||||
public Intent getWhatsappShareIntent(String message, String imageUrl, String mimeType, String packageName) {
|
||||
Intent sendIntent = new Intent();
|
||||
sendIntent.setAction(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, message);
|
||||
|
||||
if (imageUrl.equals("")) {
|
||||
sendIntent.setType("text/plain");
|
||||
sendIntent.setPackage(packageName);
|
||||
} else {
|
||||
sendIntent.setType(mimeType);
|
||||
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
imageFile = convertBase64ToFile(getReactApplicationContext(), imageUrl);
|
||||
Uri fileUri = FileProvider.getUriForFile(getReactApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", new File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
imageFile.getName()
|
||||
)
|
||||
);
|
||||
sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
||||
sendIntent.setPackage(packageName);
|
||||
}
|
||||
|
||||
return sendIntent;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void sendFeedbackToWhatsapp(String message, String imageUrl, String mimeType, Promise promise) {
|
||||
try{
|
||||
if(!isWhatsAppInstalled()){
|
||||
ArrayList<String> appsInstalled = isWhatsAppInstalled();
|
||||
int numberOfAppsInstalled = appsInstalled.size();
|
||||
|
||||
if(numberOfAppsInstalled == 0){
|
||||
promise.reject("errorCode", "1");
|
||||
return;
|
||||
}
|
||||
|
||||
Intent sendIntent = new Intent();
|
||||
sendIntent.setAction(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, message);
|
||||
|
||||
if(imageUrl.equals("")) {
|
||||
sendIntent.setType("text/plain");
|
||||
sendIntent.setPackage("com.whatsapp");
|
||||
getCurrentActivity().startActivity(sendIntent);
|
||||
} else {
|
||||
sendIntent.setType(mimeType);
|
||||
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
imageFile = convertBase64ToFile(getReactApplicationContext(), imageUrl);
|
||||
Uri fileUri = FileProvider.getUriForFile(getReactApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", new File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
imageFile.getName()
|
||||
)
|
||||
);
|
||||
sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
||||
sendIntent.setPackage("com.whatsapp");
|
||||
getCurrentActivity().startActivityForResult(sendIntent, WHATSAPP_SHARE_REQUEST_CODE);
|
||||
else if(numberOfAppsInstalled == 1) {
|
||||
String packageName = appsInstalled.get(0);
|
||||
Intent sendIntent = getWhatsappShareIntent(message, imageUrl, mimeType, packageName);
|
||||
if(getCurrentActivity()!=null) {
|
||||
getCurrentActivity().startActivityForResult(sendIntent, WHATSAPP_SHARE_REQUEST_CODE);
|
||||
}
|
||||
promise.resolve(true);
|
||||
return;
|
||||
}
|
||||
|
||||
else {
|
||||
String packageName1 = appsInstalled.get(0);
|
||||
String packageName2 = appsInstalled.get(1);
|
||||
//Firing two intents, one for WhatsApp, another for WhatsApp business
|
||||
Intent sendIntent1 = getWhatsappShareIntent(message, imageUrl, mimeType, packageName1);
|
||||
Intent sendIntent2 = getWhatsappShareIntent(message, imageUrl, mimeType, packageName2);
|
||||
ArrayList<Intent> appIntents = new ArrayList<>();
|
||||
appIntents.add(sendIntent1);
|
||||
appIntents.add(sendIntent2);
|
||||
|
||||
Intent defaultIntent = new Intent(android.content.Intent.ACTION_SEND);
|
||||
defaultIntent.setType("text/plain");
|
||||
defaultIntent.putExtra(android.content.Intent.EXTRA_TEXT, "Sharing to WhatsApp");
|
||||
|
||||
Intent chooserIntent = Intent.createChooser(defaultIntent, "Share via");
|
||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, appIntents.toArray(new Parcelable[appIntents.size()]));
|
||||
if (getCurrentActivity() != null) {
|
||||
getCurrentActivity().startActivityForResult(chooserIntent, WHATSAPP_SHARE_REQUEST_CODE);
|
||||
}
|
||||
promise.resolve(true);
|
||||
return;
|
||||
}
|
||||
promise.resolve(true);
|
||||
return;
|
||||
|
||||
} catch (Error e){
|
||||
promise.reject("errorCode","2");
|
||||
|
||||
@@ -16,7 +16,7 @@ module.exports = {
|
||||
'@constants': './src/constants',
|
||||
'@screens': './src/screens',
|
||||
'@services': './src/services',
|
||||
'@types': './src/types',
|
||||
'@interfaces': './src/types',
|
||||
'@common': './src/common',
|
||||
'@assets': './src/assets',
|
||||
'@store': './src/store/store',
|
||||
|
||||
@@ -2,24 +2,28 @@ import React from 'react';
|
||||
import BottomSheet, {
|
||||
BottomSheetProps,
|
||||
} from '../../RN-UI-LIB/src/components/bottom_sheet/BottomSheet';
|
||||
import { sendBottomSheetOpenSignal } from '../components/utlis/DeviceUtils';
|
||||
import { NativeSyntheticEvent } from 'react-native';
|
||||
import { clearBottomSheet, setBottomSheetView } from '../components/utlis/DeviceUtils';
|
||||
|
||||
interface IBottomSheetWrapperProps extends BottomSheetProps {}
|
||||
|
||||
const BottomSheetWrapper: React.FC<IBottomSheetWrapperProps> = (props) => {
|
||||
const { children, onShow, onSwipeDownClose, onClose, ...restProps } = props;
|
||||
const onCloseHandler = () => {
|
||||
sendBottomSheetOpenSignal(false);
|
||||
clearBottomSheet();
|
||||
if (typeof onClose === 'function') onClose();
|
||||
};
|
||||
|
||||
const onShowHandler = (event: NativeSyntheticEvent<any>) => {
|
||||
sendBottomSheetOpenSignal(true);
|
||||
if (typeof onShow === 'function') onShow(event);
|
||||
const onAnimationEndHandler = (id: number | null) => {
|
||||
if (!id) return;
|
||||
setBottomSheetView(id);
|
||||
};
|
||||
return (
|
||||
<BottomSheet onShow={onShowHandler} onClose={onCloseHandler} {...restProps}>
|
||||
<BottomSheet
|
||||
onShow={onShow}
|
||||
onClose={onCloseHandler}
|
||||
onBottomSheetAnimationEnd={onAnimationEndHandler}
|
||||
{...restProps}
|
||||
>
|
||||
{children}
|
||||
</BottomSheet>
|
||||
);
|
||||
|
||||
@@ -638,6 +638,23 @@ export const CLICKSTREAM_EVENT_NAMES = {
|
||||
name: 'FA_PERFORMANCE_DASHBOARD_BROKEN_PTP_CASES_LOAD',
|
||||
description: 'Performance Dashboard Broken PTP Cases Load',
|
||||
},
|
||||
// Nearby Cases
|
||||
FA_NEARBY_CASES_BUTTON_CLICKED: {
|
||||
name: 'FA_NEARBY_CASES_BUTTON_CLICKED',
|
||||
description: 'FA_NEARBY_CASES_BUTTON_CLICKED',
|
||||
},
|
||||
FA_NEARBY_CASES_SCREEN_LOADED: {
|
||||
name: 'FA_NEARBY_CASES_SCREEN_LOADED',
|
||||
description: 'FA_NEARBY_CASES_SCREEN_LOADED',
|
||||
},
|
||||
FA_NEARBY_CASES_SCREEN_CLOSED: {
|
||||
name: 'FA_NEARBY_CASES_SCREEN_CLOSED',
|
||||
description: 'FA_NEARBY_CASES_SCREEN_CLOSED',
|
||||
},
|
||||
FA_NEARBY_CASE_CLICKED: {
|
||||
name: 'FA_NEARBY_CASE_CLICKED',
|
||||
description: 'FA_NEARBY_CASE_CLICKED',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export enum MimeType {
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
import React from 'react';
|
||||
import Dropdown, { IDropdown } from '../../RN-UI-LIB/src/components/dropdown/Dropdown';
|
||||
import { sendBottomSheetOpenSignal } from '../components/utlis/DeviceUtils';
|
||||
import {
|
||||
clearBottomSheet,
|
||||
sendBottomSheetOpenSignal,
|
||||
setBottomSheetView,
|
||||
} from '../components/utlis/DeviceUtils';
|
||||
|
||||
const DropDownWrapper: React.FC<IDropdown> = (props) => {
|
||||
const { onShow, onClose, children, ...remainingProps } = props;
|
||||
const onShowHandler = () => {
|
||||
if (typeof onShow === 'function') onShow();
|
||||
sendBottomSheetOpenSignal(true);
|
||||
};
|
||||
const { onShow, onClose, onAnimationEnd, children, ...remainingProps } = props;
|
||||
|
||||
const onCloseHandler = () => {
|
||||
if (typeof onClose === 'function') onClose();
|
||||
sendBottomSheetOpenSignal(false);
|
||||
clearBottomSheet();
|
||||
};
|
||||
|
||||
const onAnimationEndHandler = (id: number | null) => {
|
||||
if (!id) return;
|
||||
console.log('dropdown opened', id);
|
||||
setBottomSheetView(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dropdown onShow={onShowHandler} onClose={onCloseHandler} {...remainingProps}>
|
||||
<Dropdown onClose={onCloseHandler} onAnimationEnd={onAnimationEndHandler} {...remainingProps}>
|
||||
{children}
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
45
src/common/ModalWrapperForAlfredV2.tsx
Normal file
45
src/common/ModalWrapperForAlfredV2.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Modal, NativeSyntheticEvent, View, findNodeHandle } from 'react-native';
|
||||
import { IModalWrapper } from '../../RN-UI-LIB/src/components/modalWrapper/ModalWrapper';
|
||||
import {
|
||||
clearBottomSheet,
|
||||
sendBottomSheetOpenSignal,
|
||||
setBottomSheetView,
|
||||
} from '../components/utlis/DeviceUtils';
|
||||
import { GenericStyles } from '@rn-ui-lib/styles';
|
||||
|
||||
const ModalWrapperForAlfredV2: React.FC<IModalWrapper> = ({ children, ...props }) => {
|
||||
const { onRequestClose, onShow, visible } = props;
|
||||
const modalRef = React.useRef<View>(null);
|
||||
const lastSent = React.useRef(visible);
|
||||
const onRequestCloseHandler = (event: NativeSyntheticEvent<any>) => {
|
||||
if (typeof onRequestClose === 'function') onRequestClose(event);
|
||||
clearBottomSheet();
|
||||
};
|
||||
const onShowHandler = (event: NativeSyntheticEvent<any>) => {
|
||||
if (typeof onShow === 'function') onShow(event);
|
||||
const nodeId = findNodeHandle(modalRef.current);
|
||||
lastSent.current = true;
|
||||
setBottomSheetView(nodeId);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
transparent={false}
|
||||
onShow={onShowHandler}
|
||||
animationType="none"
|
||||
onRequestClose={onRequestCloseHandler}
|
||||
{...props}
|
||||
>
|
||||
<View
|
||||
ref={modalRef}
|
||||
collapsable={false}
|
||||
style={[GenericStyles.fill, GenericStyles.whiteBackground]}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalWrapperForAlfredV2;
|
||||
@@ -16,13 +16,13 @@ export const getVisitedWidgetsNodeList = (
|
||||
let visitedWidgetsNodeList: string[] = [startingWidgetName];
|
||||
|
||||
let nextScreenName = '';
|
||||
const MAX_WIDGET_SIZE = Object.keys(templateData.widget).length;
|
||||
const MAX_WIDGET_SIZE = Object.keys(templateData?.widget || {}).length;
|
||||
|
||||
let iteration = 0;
|
||||
while (nextScreenName !== CommonCaseWidgetId.END && iteration++ <= MAX_WIDGET_SIZE) {
|
||||
const currentScreenName = visitedWidgetsNodeList[visitedWidgetsNodeList.length - 1];
|
||||
const currentScreenName = visitedWidgetsNodeList?.[visitedWidgetsNodeList?.length - 1];
|
||||
nextScreenName = getNextWidget(
|
||||
templateData.widget[currentScreenName].conditionActions,
|
||||
templateData?.widget?.[currentScreenName]?.conditionActions,
|
||||
formWidgetContext
|
||||
);
|
||||
visitedWidgetsNodeList.push(nextScreenName);
|
||||
|
||||
@@ -32,9 +32,14 @@ const FiltersContainer: React.FC<FilterContainerProps> = (props) => {
|
||||
React.useState<Record<string, any>>(selectedFilters);
|
||||
const filterGroupKeys = Object.keys(filters);
|
||||
const filterKeys: Record<string, string[]> = {};
|
||||
|
||||
filterGroupKeys.forEach((filterGroupKey) => {
|
||||
filterKeys[filterGroupKey] = Object.keys(filters[filterGroupKey].filters);
|
||||
const visibleFilters = Object.keys(filters?.[filterGroupKey]?.filters || {}).filter((key) => {
|
||||
if (filters?.[filterGroupKey]?.filters?.[key]?.visible) return key;
|
||||
});
|
||||
filterKeys?.[filterGroupKey] = visibleFilters;
|
||||
});
|
||||
|
||||
const [selectedFilterKey, setSelectedFilterKey] = React.useState<ISelectedFilterKey>({
|
||||
filterGroup: (filterGroupKeys && filterGroupKeys.length > 0 && filterGroupKeys[0]) || '',
|
||||
filterKey:
|
||||
@@ -43,6 +48,7 @@ const FiltersContainer: React.FC<FilterContainerProps> = (props) => {
|
||||
filterKeys &&
|
||||
filterKeys[filterGroupKeys[0]][0]) ||
|
||||
'',
|
||||
isSearchable: false,
|
||||
});
|
||||
const [filterSearchString, setFilterSearchString] = React.useState<string>('');
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -133,19 +139,21 @@ const FiltersContainer: React.FC<FilterContainerProps> = (props) => {
|
||||
<ScrollView>
|
||||
{filterGroupKeys.map((filterGroupKey) => (
|
||||
<>
|
||||
<View
|
||||
style={[
|
||||
styles.silverBackground,
|
||||
GenericStyles.row,
|
||||
GenericStyles.alignCenter,
|
||||
GenericStyles.spaceBetween,
|
||||
styles.p8,
|
||||
]}
|
||||
>
|
||||
<Heading type="h5" bold dark>
|
||||
{filters[filterGroupKey].headerText}
|
||||
</Heading>
|
||||
</View>
|
||||
{filterGroupKeys.length > 1 && (
|
||||
<View
|
||||
style={[
|
||||
styles.silverBackground,
|
||||
GenericStyles.row,
|
||||
GenericStyles.alignCenter,
|
||||
GenericStyles.spaceBetween,
|
||||
styles.p8,
|
||||
]}
|
||||
>
|
||||
<Heading type="h5" bold dark>
|
||||
{filters?.[filterGroupKey]?.headerText}
|
||||
</Heading>
|
||||
</View>
|
||||
)}
|
||||
<View style={[styles.p8]}>
|
||||
{filterKeys[filterGroupKey].map((filterKey) =>
|
||||
filters[filterGroupKey].filters[filterKey]?.visible !== false ? (
|
||||
@@ -163,6 +171,8 @@ const FiltersContainer: React.FC<FilterContainerProps> = (props) => {
|
||||
setSelectedFilterKey({
|
||||
filterGroup: filterGroupKey,
|
||||
filterKey,
|
||||
isSearchable:
|
||||
filters?.[filterGroupKey]?.filters?.[filterKey]?.searchEnabled,
|
||||
});
|
||||
setFilterSearchString('');
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FILTERS_TAB_CLICKED, {
|
||||
@@ -210,15 +220,17 @@ const FiltersContainer: React.FC<FilterContainerProps> = (props) => {
|
||||
<View style={[styles.p8, styles.filterColumn]}>
|
||||
{selectedFilterKey && (
|
||||
<>
|
||||
<View style={[GenericStyles.pt16, styles.ph7]}>
|
||||
<TextInput
|
||||
LeftComponent={<SearchIcon />}
|
||||
placeholder="Search..."
|
||||
defaultValue={filterSearchString}
|
||||
onChangeText={handleOptionFilterChange}
|
||||
testID="test_search"
|
||||
/>
|
||||
</View>
|
||||
{selectedFilterKey.isSearchable && (
|
||||
<View style={[GenericStyles.pt16, styles.ph7]}>
|
||||
<TextInput
|
||||
LeftComponent={<SearchIcon />}
|
||||
placeholder="Search..."
|
||||
defaultValue={filterSearchString}
|
||||
onChangeText={handleOptionFilterChange}
|
||||
testID="test_search"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<FilterOptions
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
filterSearchString={filterSearchString}
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface FilterContainerProps {
|
||||
export interface ISelectedFilterKey {
|
||||
filterGroup: string;
|
||||
filterKey: string;
|
||||
isSearchable: boolean;
|
||||
}
|
||||
|
||||
export interface IFilterOptionsProps {
|
||||
|
||||
@@ -29,6 +29,10 @@ export const alfredSetUserId = (userId: string) => DeviceUtilsModule.setUserId(u
|
||||
export const sendBottomSheetOpenSignal = (e: boolean) =>
|
||||
DeviceUtilsModule.sendBottomSheetOpenSignal(e);
|
||||
|
||||
export const setBottomSheetView = (id: number | null) => DeviceUtilsModule.setBottomSheetView(id);
|
||||
|
||||
export const clearBottomSheet = () => DeviceUtilsModule.clearBottomSheet();
|
||||
|
||||
export const alfredSetEmailId = (emailId: string) => DeviceUtilsModule.setEmailId(emailId);
|
||||
|
||||
// sends feedback data to whatsapp.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Permission, PermissionsAndroid, Platform } from 'react-native';
|
||||
import { AppState, Permission, PermissionsAndroid, Platform } from 'react-native';
|
||||
import { PermissionsToCheck } from '../../common/Constants';
|
||||
import { checkNotifications } from 'react-native-permissions';
|
||||
import CosmosForegroundService from '../../services/foregroundServices/foreground.service';
|
||||
import { AppStates } from '@types/appStates';
|
||||
|
||||
let isNotificationPermissionEnabled = true;
|
||||
|
||||
@@ -32,6 +33,7 @@ export const getPermissionsToRequest = async () => {
|
||||
if (permission === PermissionsAndroid.PERMISSIONS.POST_NOTIFICATION) {
|
||||
const notificationPermission = await checkNotifications();
|
||||
const notificationStatus = notificationPermission.status === 'granted';
|
||||
isNotificationPermissionEnabled = notificationStatus;
|
||||
if (!notificationStatus) {
|
||||
permissionsToRequest.push(permission);
|
||||
} else {
|
||||
@@ -43,7 +45,6 @@ export const getPermissionsToRequest = async () => {
|
||||
CosmosForegroundService.update();
|
||||
}
|
||||
}
|
||||
isNotificationPermissionEnabled = notificationStatus;
|
||||
continue;
|
||||
}
|
||||
const granted = await PermissionsAndroid.check(permission);
|
||||
|
||||
@@ -353,7 +353,7 @@ export function getDistanceFromLatLonInKm(
|
||||
latLong1: IGeolocationCoordinate,
|
||||
latLong2: IGeolocationCoordinate
|
||||
) {
|
||||
if (!latLong1.latitude || !latLong1.longitude || !latLong2.latitude || !latLong2.longitude)
|
||||
if (!latLong1?.latitude || !latLong1?.longitude || !latLong2?.latitude || !latLong2?.longitude)
|
||||
return NaN;
|
||||
|
||||
const EARTH_RADIUS = 6371;
|
||||
@@ -368,10 +368,3 @@ export function getDistanceFromLatLonInKm(
|
||||
const distance = 2 * Math.atan2(Math.sqrt(intermediateResult), Math.sqrt(1 - intermediateResult));
|
||||
return EARTH_RADIUS * distance;
|
||||
}
|
||||
|
||||
export function insertCommasinAmount(amount: number | undefined) {
|
||||
const reversedAmount = amount?.toString().split('').reverse().join('');
|
||||
const groups = reversedAmount?.match(/.{1,3}/g);
|
||||
const result = groups?.join(',').split('').reverse().join('');
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ export const initCrashlytics = async (userState: IUserSlice) => {
|
||||
if (!userState) return;
|
||||
|
||||
await Promise.all([
|
||||
crashlytics().setUserId(userState.user?.emailId as string),
|
||||
crashlytics().setUserId((userState.user?.emailId || '') as string),
|
||||
crashlytics().setAttributes({
|
||||
deviceId: userState.deviceId,
|
||||
phoneNumber: userState.user?.phoneNumber as string,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
|
||||
const useRefresh = (refreshAction: () => void) => {
|
||||
const useRefresh = (refreshAction: () => void, refreshTime = 2000) => {
|
||||
const [refreshing, setRefreshing] = React.useState(false);
|
||||
|
||||
const onRefresh = () => {
|
||||
setRefreshing(true);
|
||||
refreshAction();
|
||||
setTimeout(() => setRefreshing(false), 2000);
|
||||
setTimeout(() => setRefreshing(false), refreshTime);
|
||||
};
|
||||
|
||||
return { refreshing, onRefresh };
|
||||
|
||||
@@ -73,16 +73,15 @@ function AddressItem({
|
||||
|
||||
let relativeDistanceBwLatLong = 0;
|
||||
|
||||
if (isGroupedAddress) {
|
||||
const addressGeolocationCoordinated: IGeolocationCoordinate = {
|
||||
latitude: addressItem.latitude,
|
||||
longitude: addressItem.longitude,
|
||||
};
|
||||
relativeDistanceBwLatLong = getDistanceFromLatLonInKm(
|
||||
currentGeolocationCoordinates,
|
||||
addressGeolocationCoordinated
|
||||
);
|
||||
}
|
||||
const addressGeolocationCoordinated: IGeolocationCoordinate = {
|
||||
latitude: addressItem.latitude,
|
||||
longitude: addressItem.longitude,
|
||||
};
|
||||
relativeDistanceBwLatLong = getDistanceFromLatLonInKm(
|
||||
currentGeolocationCoordinates,
|
||||
addressGeolocationCoordinated
|
||||
);
|
||||
|
||||
const handleAddFeedback = () => {
|
||||
if (prefilledAddressScreenTemplate != null) {
|
||||
const addressKey = '{{addressReferenceId}}';
|
||||
@@ -138,22 +137,24 @@ function AddressItem({
|
||||
<View
|
||||
style={[styles.container, GenericStyles.columnDirection, containerStyle, , { flex: 1 }]}
|
||||
>
|
||||
<View style={[styles.container, GenericStyles.row, { alignItems: 'center' }]}>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
style={[styles.textContainer, styles.cardBoldTitle, { fontWeight: 'bold' }]}
|
||||
>
|
||||
{sanitizeString([addressItem?.pinCode, addressItem?.city].filter(Boolean).join(', '))}
|
||||
</Text>
|
||||
<Text numberOfLines={1} ellipsizeMode="tail" style={[GenericStyles.ml4]}>
|
||||
{showRelativeDistance && relativeDistanceBwLatLong ? (
|
||||
<>({relativeDistanceFormatter(relativeDistanceBwLatLong)} km away)</>
|
||||
) : (
|
||||
'--'
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
{addressItem?.pinCode || addressItem?.city || relativeDistanceBwLatLong ? (
|
||||
<View style={[styles.container, GenericStyles.row, { alignItems: 'center' }]}>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
style={[styles.textContainer, styles.cardBoldTitle, { fontWeight: 'bold' }]}
|
||||
>
|
||||
{sanitizeString(
|
||||
[addressItem?.pinCode ?? '', addressItem?.city ?? ''].filter(Boolean).join(', ')
|
||||
)}
|
||||
</Text>
|
||||
<Text numberOfLines={1} ellipsizeMode="tail" style={[GenericStyles.ml4]}>
|
||||
{showRelativeDistance && relativeDistanceBwLatLong ? (
|
||||
<>({relativeDistanceFormatter(relativeDistanceBwLatLong)} km away)</>
|
||||
) : null}
|
||||
</Text>
|
||||
</View>
|
||||
) : null}
|
||||
{lastFeedbackForAddress?.feedbackPresent ? (
|
||||
<View style={[styles.container, { marginVertical: 8 }]}>
|
||||
<View>
|
||||
|
||||
@@ -29,6 +29,7 @@ import { type GenericFunctionArgs } from '../../common/GenericTypes';
|
||||
import { toast } from '../../../RN-UI-LIB/src/components/toast';
|
||||
import { ToastMessages } from '../allCases/constants';
|
||||
import AddressSource from './AddressSource';
|
||||
import relativeDistanceFormatter from './utils/relativeDistanceFormatter';
|
||||
|
||||
interface IAddressItem {
|
||||
addressItem: IAddress;
|
||||
@@ -167,8 +168,10 @@ function SimilarAddressItem({
|
||||
{showRelativeDistance && relativeDistanceBwLatLong ? (
|
||||
<>
|
||||
<Text style={GenericStyles.tiny}> ● </Text>
|
||||
{!isNaN(relativeDistanceBwLatLong) ? relativeDistanceBwLatLong.toFixed(2) : '--'} km
|
||||
away
|
||||
{!isNaN(relativeDistanceBwLatLong)
|
||||
? relativeDistanceFormatter(relativeDistanceBwLatLong)
|
||||
: '--'}{' '}
|
||||
km away
|
||||
</>
|
||||
) : null}
|
||||
{showSource ? <AddressSource addressItem={addressItem} /> : null}
|
||||
|
||||
@@ -4,10 +4,10 @@ const relativeDistanceFormatter = (relativeDistance: number) => {
|
||||
return '--';
|
||||
}
|
||||
if (relativeDistance >= MAXIMUM_DISTANCE_WITH_DECIMAL) {
|
||||
return Math.round(relativeDistance, 0);
|
||||
return Math.round(relativeDistance);
|
||||
}
|
||||
|
||||
return relativeDistance.toFixed(2);
|
||||
return relativeDistance.toFixed(1);
|
||||
};
|
||||
|
||||
export default relativeDistanceFormatter;
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Text, View, ViewProps, StyleSheet } from 'react-native';
|
||||
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
|
||||
import { Text, View, ViewProps, StyleSheet, Pressable } from 'react-native';
|
||||
import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
|
||||
import { CaseTypes, ICaseItemCaseDetailObj } from './interface';
|
||||
import ListItem from './ListItem';
|
||||
import Button from '../../../RN-UI-LIB/src/components/Button';
|
||||
import { navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import { useAppSelector } from '../../hooks';
|
||||
import { FeedbackStatus } from '../caseDetails/interface';
|
||||
import { PageRouteEnum } from '@screens/auth/ProtectedRouter';
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
import LocationIcon from '@assets/icons/LocationIcon';
|
||||
import ArrowRightOutlineIcon from '@rn-ui-lib/icons/ArrowRightOutlineIcon';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
|
||||
interface ICaseItemProps extends ViewProps {
|
||||
caseDetailObj: ICaseItemCaseDetailObj;
|
||||
@@ -16,6 +22,7 @@ interface ICaseItemProps extends ViewProps {
|
||||
shouldBatchAvatar?: boolean;
|
||||
allCasesView?: boolean;
|
||||
isAgentDashboard?: boolean;
|
||||
nearbyCaseView?: boolean;
|
||||
}
|
||||
|
||||
const CaseItem: React.FC<ICaseItemProps> = ({
|
||||
@@ -26,9 +33,10 @@ const CaseItem: React.FC<ICaseItemProps> = ({
|
||||
shouldBatchAvatar = false,
|
||||
allCasesView = false,
|
||||
isAgentDashboard = false,
|
||||
nearbyCaseView = false,
|
||||
...restProps
|
||||
}) => {
|
||||
const { ADD_VISIT_PLAN, ATTEMPTED_CASES } = CaseTypes;
|
||||
const { ADD_VISIT_PLAN, ATTEMPTED_CASES, NEARBY_CASES } = CaseTypes;
|
||||
const { attemptedCount, totalPinnedCount } = useAppSelector((state) => ({
|
||||
totalPinnedCount: state.allCases.pinnedList.length,
|
||||
attemptedCount: state.allCases.pinnedList.filter(
|
||||
@@ -42,6 +50,11 @@ const CaseItem: React.FC<ICaseItemProps> = ({
|
||||
navigateToScreen('Cases');
|
||||
};
|
||||
|
||||
const navigateToNearbyCases = () => {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_NEARBY_CASES_BUTTON_CLICKED);
|
||||
navigateToScreen(PageRouteEnum.NEARBY_CASES);
|
||||
};
|
||||
|
||||
const getCaseItemCaseDetailObj = useMemo((): ICaseItemCaseDetailObj => {
|
||||
return caseDetailObj;
|
||||
}, [
|
||||
@@ -81,6 +94,19 @@ const CaseItem: React.FC<ICaseItemProps> = ({
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
case NEARBY_CASES: {
|
||||
return (
|
||||
<Pressable onPress={navigateToNearbyCases}>
|
||||
<View style={styles.nearByCasesContainer}>
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
|
||||
<LocationIcon />
|
||||
<Text style={styles.nearByCasesText}>View nearby cases</Text>
|
||||
</View>
|
||||
<ArrowRightOutlineIcon fillColor={COLORS.BACKGROUND.LIGHT} />
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
default:
|
||||
return (
|
||||
<View {...restProps}>
|
||||
@@ -91,6 +117,7 @@ const CaseItem: React.FC<ICaseItemProps> = ({
|
||||
isTodoItem={isTodoItem}
|
||||
allCasesView={allCasesView}
|
||||
isAgentDashboard={isAgentDashboard}
|
||||
nearbyCaseView={nearbyCaseView}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
@@ -103,5 +130,21 @@ const styles = StyleSheet.create({
|
||||
paddingBottom: 6,
|
||||
paddingHorizontal: 2,
|
||||
},
|
||||
nearByCasesContainer: {
|
||||
...GenericStyles.row,
|
||||
...GenericStyles.alignCenter,
|
||||
...GenericStyles.spaceBetween,
|
||||
...getShadowStyle(2),
|
||||
...GenericStyles.ph12,
|
||||
...GenericStyles.pv12,
|
||||
...GenericStyles.br8,
|
||||
...GenericStyles.mt16,
|
||||
...GenericStyles.mb12,
|
||||
...GenericStyles.whiteBackground,
|
||||
},
|
||||
nearByCasesText: {
|
||||
...GenericStyles.pl4,
|
||||
color: COLORS.TEXT.DARK,
|
||||
},
|
||||
});
|
||||
export default CaseItem;
|
||||
|
||||
@@ -56,6 +56,7 @@ import BottomSheetWrapper from '../../common/BottomSheetWrapper';
|
||||
import { toast } from '../../../RN-UI-LIB/src/components/toast';
|
||||
import { setFilteredListToast } from '../../reducer/allCasesSlice';
|
||||
import { getFilterCount, getSelectedFilters } from '../Dashboard/utils';
|
||||
import ModalWrapperForAlfredV2 from '@common/ModalWrapperForAlfredV2';
|
||||
|
||||
export const getItem = (item: Array<ICaseItem>, index: number) => item[index];
|
||||
export const ESTIMATED_ITEM_SIZE = 250; // Average height of List item
|
||||
@@ -210,6 +211,8 @@ const CasesList: React.FC<ICasesList> = ({
|
||||
|
||||
const filteredCasesListWithCTA = useMemo(() => {
|
||||
if (!isVisitPlan) {
|
||||
if (allCasesView && filteredCasesList?.length)
|
||||
return [ListHeaderItems.NEARBY_CASES as ICaseItem, ...filteredCasesList];
|
||||
return [...filteredCasesList];
|
||||
}
|
||||
if (isLockedVisitPlanStatus) {
|
||||
@@ -354,7 +357,7 @@ const CasesList: React.FC<ICasesList> = ({
|
||||
<View style={GenericStyles.ph12}>{listEmptyComponent}</View>
|
||||
)}
|
||||
</View>
|
||||
<ModalWrapperForAlfred
|
||||
<ModalWrapperForAlfredV2
|
||||
animationType="slide"
|
||||
animated
|
||||
onRequestClose={() => {
|
||||
@@ -370,7 +373,7 @@ const CasesList: React.FC<ICasesList> = ({
|
||||
isVisitPlan={isVisitPlan}
|
||||
isAgentDashboard={isAgentDashboard}
|
||||
/>
|
||||
</ModalWrapperForAlfred>
|
||||
</ModalWrapperForAlfredV2>
|
||||
<BottomSheetWrapper
|
||||
HeaderNode={() => (
|
||||
<View style={[...row, GenericStyles.ph16]}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
|
||||
import React from 'react';
|
||||
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
|
||||
import Heading from '../../../RN-UI-LIB/src/components/Heading';
|
||||
@@ -22,6 +22,8 @@ interface IEmptyList {
|
||||
isFilterApplied?: boolean;
|
||||
setShowAgentSelectionBottomSheet?: (val: boolean) => void;
|
||||
isAgentDashboard?: boolean;
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
isNearByCase?: boolean;
|
||||
}
|
||||
|
||||
const EmptyList: React.FC<IEmptyList> = (props) => {
|
||||
@@ -31,6 +33,8 @@ const EmptyList: React.FC<IEmptyList> = (props) => {
|
||||
isFilterApplied,
|
||||
setShowAgentSelectionBottomSheet,
|
||||
isAgentDashboard,
|
||||
containerStyle,
|
||||
isNearByCase,
|
||||
} = props;
|
||||
const {
|
||||
isLockedVisitPlanStatus,
|
||||
@@ -85,6 +89,9 @@ const EmptyList: React.FC<IEmptyList> = (props) => {
|
||||
return EmptyListMessages.NO_ACTIVE_ALLOCATIONS_SELECTED_AGENT;
|
||||
}
|
||||
|
||||
if (isNearByCase) {
|
||||
return EmptyListMessages.NO_NEARBY_CASES_FOUND;
|
||||
}
|
||||
return EmptyListMessages.NO_PENDING_CASES;
|
||||
};
|
||||
|
||||
@@ -130,7 +137,7 @@ const EmptyList: React.FC<IEmptyList> = (props) => {
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={[GenericStyles.w100, styles.centerAbsolute]}>
|
||||
<View style={[GenericStyles.w100, styles.centerAbsolute, containerStyle]}>
|
||||
{renderIcon()}
|
||||
<View style={[GenericStyles.mt12, styles.text]}>
|
||||
<Heading
|
||||
|
||||
@@ -33,6 +33,7 @@ import { toast } from '../../../RN-UI-LIB/src/components/toast';
|
||||
import { COMPLETED_STATUSES, ToastMessages } from './constants';
|
||||
import { VisitPlanStatus } from '../../reducer/userSlice';
|
||||
import { PaymentStatus } from '../caseDetails/interface';
|
||||
import relativeDistanceFormatter from '@screens/addressGeolocation/utils/relativeDistanceFormatter';
|
||||
|
||||
interface IListItem {
|
||||
caseListItemDetailObj: ICaseItemCaseDetailObj;
|
||||
@@ -41,6 +42,7 @@ interface IListItem {
|
||||
shouldBatchAvatar?: boolean;
|
||||
allCasesView?: boolean;
|
||||
isAgentDashboard?: boolean;
|
||||
nearbyCaseView?: boolean;
|
||||
}
|
||||
|
||||
const paymentStatusMapping: Record<
|
||||
@@ -63,7 +65,7 @@ const ListItem: React.FC<IListItem> = (props) => {
|
||||
isTodoItem,
|
||||
shouldBatchAvatar,
|
||||
allCasesView,
|
||||
isAgentDashboard,
|
||||
nearbyCaseView,
|
||||
} = props;
|
||||
const {
|
||||
id: caseId,
|
||||
@@ -79,6 +81,7 @@ const ListItem: React.FC<IListItem> = (props) => {
|
||||
interactionStatus,
|
||||
caseVerdict,
|
||||
totalOverdueAmount,
|
||||
distanceInKm,
|
||||
} = caseListItemDetailObj;
|
||||
|
||||
const isCollectionCaseType = caseType === CaseAllocationType.COLLECTION_CASE;
|
||||
@@ -128,6 +131,11 @@ const ListItem: React.FC<IListItem> = (props) => {
|
||||
screen: getCurrentScreen().name === 'Profile' ? 'Completed Cases' : getCurrentScreen().name, // todo: need to update use router
|
||||
caseType,
|
||||
});
|
||||
if (nearbyCaseView) {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_NEARBY_CASE_CLICKED, {
|
||||
caseId,
|
||||
});
|
||||
}
|
||||
if (isCollectionCaseType) {
|
||||
navigateToScreen('collectionCaseDetail', { caseId });
|
||||
} else {
|
||||
@@ -179,7 +187,11 @@ const ListItem: React.FC<IListItem> = (props) => {
|
||||
const caseCompleted = COMPLETED_STATUSES.includes(caseStatus);
|
||||
|
||||
const showVisitPlanBtn =
|
||||
!(caseCompleted || isCaseItemPinnedMainView) && !isTodoItem && !isCompleted && !isTeamLead;
|
||||
!(caseCompleted || isCaseItemPinnedMainView) &&
|
||||
!isTodoItem &&
|
||||
!isCompleted &&
|
||||
!isTeamLead &&
|
||||
!nearbyCaseView;
|
||||
|
||||
return (
|
||||
<Pressable onPress={handleCaseClick}>
|
||||
@@ -211,6 +223,13 @@ const ListItem: React.FC<IListItem> = (props) => {
|
||||
<Text style={[GenericStyles.fontSize12, styles.visitPlanText]}>In visit plan</Text>
|
||||
</View>
|
||||
)}
|
||||
{nearbyCaseView && distanceInKm && (
|
||||
<View style={[GenericStyles.absolute, styles.distanceContainer]}>
|
||||
<Text style={[GenericStyles.fontSize12, styles.distanceText]}>
|
||||
{relativeDistanceFormatter(distanceInKm)} km away
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={[styles.caseItemInfo]}>
|
||||
<View style={styles.tag}>
|
||||
{isCollectionCaseType ? (
|
||||
@@ -334,6 +353,17 @@ const styles = StyleSheet.create({
|
||||
visitPlanText: {
|
||||
color: COLORS.TEXT.BLUE,
|
||||
},
|
||||
distanceContainer: {
|
||||
right: 0,
|
||||
top: 0,
|
||||
padding: 8,
|
||||
backgroundColor: COLORS.BACKGROUND.SILVER,
|
||||
borderBottomLeftRadius: 4,
|
||||
borderTopRightRadius: 4,
|
||||
},
|
||||
distanceText: {
|
||||
color: COLORS.TEXT.BLACK,
|
||||
},
|
||||
});
|
||||
|
||||
export default memo(ListItem);
|
||||
|
||||
100
src/screens/allCases/NearbyCases.tsx
Normal file
100
src/screens/allCases/NearbyCases.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { goBack } from '@components/utlis/navigationUtlis';
|
||||
import { useAppSelector } from '@hooks';
|
||||
import useRefresh from '@hooks/useRefresh';
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
import NavigationHeader from '@rn-ui-lib/components/NavigationHeader';
|
||||
import { GenericStyles } from '@rn-ui-lib/styles';
|
||||
import { INearbyCaseItemObj } from '@screens/caseDetails/interface';
|
||||
import { FlashList } from '@shopify/flash-list';
|
||||
import { ListRenderItemInfo, RefreshControl, StyleSheet, View } from 'react-native';
|
||||
import CaseItem from './CaseItem';
|
||||
import { ESTIMATED_ITEM_SIZE, ESTIMATED_LIST_SIZE } from './CasesList';
|
||||
import EmptyList from './EmptyList';
|
||||
import { CaseTypes, ICaseItem } from './interface';
|
||||
import { getNearByCases } from './utils';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
|
||||
const NearbyCases = () => {
|
||||
const { deviceGeolocationCoordinate, caseDetails, pendingList, pinnedList } = useAppSelector(
|
||||
(state) => ({
|
||||
deviceGeolocationCoordinate: state.foregroundService?.deviceGeolocationCoordinate,
|
||||
caseDetails: state.allCases?.caseDetails,
|
||||
pendingList: state.allCases?.pendingList,
|
||||
pinnedList: state.allCases?.pinnedList,
|
||||
})
|
||||
);
|
||||
const [caseData, setCaseData] = useState<Array<INearbyCaseItemObj>>([]);
|
||||
const isFocused = useIsFocused();
|
||||
|
||||
const handlePullToRefresh = () => {
|
||||
const data = getNearByCases(
|
||||
[...pinnedList, ...pendingList],
|
||||
caseDetails,
|
||||
deviceGeolocationCoordinate
|
||||
);
|
||||
setCaseData(data);
|
||||
};
|
||||
|
||||
const { refreshing, onRefresh } = useRefresh(handlePullToRefresh, 1000);
|
||||
|
||||
useEffect(() => {
|
||||
if (isFocused) {
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
return () => {
|
||||
isFocused && addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_NEARBY_CASES_SCREEN_CLOSED);
|
||||
};
|
||||
}, [isFocused]);
|
||||
|
||||
const renderListItem = (row: ListRenderItemInfo<ICaseItem>) => {
|
||||
const caseDetailItem = row.item as INearbyCaseItemObj;
|
||||
const { type } = row.item;
|
||||
return (
|
||||
<CaseItem
|
||||
key={caseDetailItem?.distanceInKm}
|
||||
renderingType={type}
|
||||
caseDetailObj={caseDetailItem}
|
||||
shouldBatchAvatar={true}
|
||||
testID={`case-${type === CaseTypes.TODO ? 'todo' : ''}-${row.index}`}
|
||||
nearbyCaseView
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={[GenericStyles.fill, GenericStyles.relative]}>
|
||||
<NavigationHeader title="Nearby Cases" onBack={goBack} />
|
||||
<View style={GenericStyles.fill}>
|
||||
{caseData.length ? (
|
||||
<FlashList
|
||||
data={caseData}
|
||||
keyboardShouldPersistTaps={'handled'}
|
||||
scrollEventThrottle={16}
|
||||
renderItem={renderListItem}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={refreshing} onRefresh={handlePullToRefresh} />
|
||||
}
|
||||
contentContainerStyle={GenericStyles.p12}
|
||||
estimatedItemSize={ESTIMATED_ITEM_SIZE}
|
||||
estimatedListSize={ESTIMATED_LIST_SIZE}
|
||||
/>
|
||||
) : (
|
||||
<View style={GenericStyles.ph12}>
|
||||
<EmptyList containerStyle={styles.pt0} isNearByCase />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
pt0: {
|
||||
paddingTop: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export default NearbyCases;
|
||||
@@ -27,6 +27,10 @@ export const ListHeaderItems = {
|
||||
type: CaseTypes.ATTEMPTED_CASES,
|
||||
caseReferenceId: '-5',
|
||||
},
|
||||
NEARBY_CASES: {
|
||||
type: CaseTypes.NEARBY_CASES,
|
||||
caseReferenceId: '-6',
|
||||
},
|
||||
};
|
||||
|
||||
export const LIST_HEADER_ITEMS = [
|
||||
@@ -49,6 +53,7 @@ export const EmptyListMessages = {
|
||||
NO_ACTIVE_ALLOCATIONS_SELECTED_AGENT: 'Selected agent does not have any active allocations',
|
||||
SELECT_AGENT: 'Select an agent to view cases',
|
||||
SELECT_AGENT_SELECTED_AGENT: 'Select another agent to view cases',
|
||||
NO_NEARBY_CASES_FOUND: 'No nearby cases found',
|
||||
};
|
||||
|
||||
export const ToastMessages = {
|
||||
@@ -88,3 +93,5 @@ export enum BOTTOM_TAB_ROUTES {
|
||||
Profile = 'Profile',
|
||||
Dashboard = 'Dashboard',
|
||||
}
|
||||
|
||||
export const NEARBY_CASES_COUNT = 10;
|
||||
|
||||
@@ -24,6 +24,7 @@ export enum CaseTypes {
|
||||
BANNER,
|
||||
ADD_VISIT_PLAN,
|
||||
ATTEMPTED_CASES,
|
||||
NEARBY_CASES,
|
||||
}
|
||||
|
||||
export enum caseVerdict {
|
||||
@@ -204,6 +205,7 @@ export interface IFilterMeta {
|
||||
fieldsToCompare: IFieldToCompare[];
|
||||
operator: CONDITIONAL_OPERATORS;
|
||||
visible?: boolean;
|
||||
searchEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface IQuickFilter extends IFilterMeta, Option {}
|
||||
@@ -322,6 +324,7 @@ export interface ICaseItemAvatarCaseDetailObj extends IFetchDocumentCaseDetailOb
|
||||
|
||||
export interface ICaseItemCaseDetailObj extends CaseDetail {
|
||||
isIntermediateOrSelectedTodoCaseItem?: boolean;
|
||||
distanceInKm?: number;
|
||||
}
|
||||
|
||||
export interface ISectionListData {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { CaseDetail, FeedbackStatus } from '../caseDetails/interface';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
import { getDistanceFromLatLonInKm } from '@components/utlis/commonFunctions';
|
||||
import { IGeoLocation } from '@interfaces/addressGeolocation.types';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import { Address, CaseDetail, FeedbackStatus, INearbyCaseItemObj } from '../caseDetails/interface';
|
||||
import { NEARBY_CASES_COUNT } from './constants';
|
||||
import { ICaseItem, IReportee, ISectionListData } from './interface';
|
||||
|
||||
export const getAttemptedList = (
|
||||
@@ -59,3 +64,50 @@ export const sectionListTranformData = (agentList: IReportee[]): ISectionListDat
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getAddressLocation = (addresses: Address[] | undefined) => {
|
||||
if (!addresses?.length) return null;
|
||||
|
||||
for (const address of addresses) {
|
||||
if (address?.location?.latitude && address?.location?.longitude) {
|
||||
return address.location;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getNearByCases = (
|
||||
casesList: Array<ICaseItem>,
|
||||
caseDetails: Record<string, CaseDetail>,
|
||||
deviceGeolocationCoordinate: IGeoLocation
|
||||
) => {
|
||||
let caseDetailsData: Array<INearbyCaseItemObj> = [];
|
||||
let caseIds: Array<string> = [];
|
||||
casesList?.forEach((pinnedId) => {
|
||||
const caseDetail = caseDetails?.[pinnedId.caseReferenceId];
|
||||
const addressLocation = getAddressLocation(caseDetail?.addresses);
|
||||
|
||||
if (addressLocation) {
|
||||
const distanceInKm = getDistanceFromLatLonInKm(addressLocation, deviceGeolocationCoordinate);
|
||||
|
||||
if (distanceInKm) {
|
||||
caseIds.push(caseDetail.caseReferenceId);
|
||||
caseDetailsData.push({
|
||||
...caseDetail,
|
||||
distanceInKm: distanceInKm,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
caseDetailsData?.sort(
|
||||
(a: INearbyCaseItemObj, b: INearbyCaseItemObj) => a.distanceInKm - b.distanceInKm
|
||||
);
|
||||
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_NEARBY_CASES_SCREEN_LOADED, {
|
||||
userGeolocation: deviceGeolocationCoordinate,
|
||||
caseIds: caseIds.slice(0, NEARBY_CASES_COUNT),
|
||||
});
|
||||
|
||||
return caseDetailsData?.slice(0, NEARBY_CASES_COUNT);
|
||||
};
|
||||
|
||||
@@ -40,6 +40,7 @@ import { setDeviceGeolocation } from '@reducers/foregroundServiceSlice';
|
||||
import FullScreenLoader from '../../../RN-UI-LIB/src/components/FullScreenLoader';
|
||||
import PDFFullScreen from '../caseDetails/PDFFullScreen';
|
||||
import ImageViewer from '../caseDetails/ImageViewer';
|
||||
import NearbyCases from '@screens/allCases/NearbyCases';
|
||||
|
||||
const Stack = createNativeStackNavigator();
|
||||
|
||||
@@ -54,6 +55,7 @@ export enum PageRouteEnum {
|
||||
CASH_COLLECTED = 'cashCollected',
|
||||
DASHBOARD_MAIN = 'dashboardMain',
|
||||
FILTERED_CASES = 'filteredCases',
|
||||
NEARBY_CASES = 'nearbyCases',
|
||||
GEOLOCATION_OLD_FEEDBACKS = 'geolocationOldFeedbacks',
|
||||
}
|
||||
|
||||
@@ -193,6 +195,16 @@ const ProtectedRouter = () => {
|
||||
}}
|
||||
listeners={getScreenFocusListenerObj}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name={PageRouteEnum.NEARBY_CASES}
|
||||
component={NearbyCases}
|
||||
options={{
|
||||
header: () => null,
|
||||
animationDuration: SCREEN_ANIMATION_DURATION,
|
||||
animation: 'slide_from_right',
|
||||
}}
|
||||
listeners={getScreenFocusListenerObj}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name={'vkycFull'}
|
||||
component={VKYCFullScreen}
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
} from '../../../../RN-UI-LIB/src/utlis/dates';
|
||||
import { CaseDetail, Address as IAddress, IGeolocation, VisitType } from '../interface';
|
||||
import {
|
||||
debounce,
|
||||
getGoogleMapUrl,
|
||||
insertCommasinAmount,
|
||||
sanitizeString,
|
||||
} from '../../../components/utlis/commonFunctions';
|
||||
import {
|
||||
@@ -33,6 +33,7 @@ import { useAppSelector } from '../../../hooks';
|
||||
import { toast } from '../../../../RN-UI-LIB/src/components/toast';
|
||||
import { ToastMessages } from '../../allCases/constants';
|
||||
import { sendFeedbackToWhatsapp } from '../../../components/utlis/DeviceUtils';
|
||||
import { getSanitizedCommaAmount } from '@rn-ui-lib/utils/amount';
|
||||
|
||||
interface IFeedbackDetailItem {
|
||||
feedbackItem: IFeedback;
|
||||
@@ -71,122 +72,133 @@ function getLocationLink(latitude: string, longitude: string): string {
|
||||
return link;
|
||||
}
|
||||
|
||||
const sendToWhatsappNative = (
|
||||
message: string,
|
||||
imageUrl: string,
|
||||
mimeType: string,
|
||||
caseDetails: CaseDetail,
|
||||
agentId: string
|
||||
) => {
|
||||
sendFeedbackToWhatsapp(message, imageUrl, mimeType)
|
||||
.then((res: boolean) => {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SHARE_SUCCESSFUL, {
|
||||
caseId: caseDetails?.id,
|
||||
agentId: agentId,
|
||||
});
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
if (err.message === '1') {
|
||||
toast({
|
||||
text1: ToastMessages.WHATSAPP_NOT_INSTALLED,
|
||||
type: 'error',
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
text1: ToastMessages.WHATSAPP_FEEDBACK_SHARE_FAILURE,
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const sendToWhatsapp = (feedbackItem: IFeedback, caseDetails: CaseDetail, agentId: string) => {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SHARE_FEEDBACK_CLICKED, {
|
||||
caseId: caseDetails?.id,
|
||||
agentId: agentId,
|
||||
});
|
||||
|
||||
var message = `*Visit Feedback* for ${sanitizeString(caseDetails?.customerName)}
|
||||
_${sanitizeString(dateFormat(new Date(feedbackItem?.createdAt), 'DD MMM, YYYY | HH:mm a.'))}_\n
|
||||
*LAN*: ${sanitizeString(caseDetails?.loanAccountNumber)}
|
||||
*DPD Bucket*: ${sanitizeString(caseDetails?.dpdBucket)}
|
||||
*EMI Amount*: ₹${insertCommasinAmount(caseDetails?.outstandingEmiDetails?.[0]?.emiAmount)}\n
|
||||
*Disposition*: ${sanitizeString(feedbackItem?.interactionStatus)}`;
|
||||
|
||||
const ptpDate = feedbackItem?.answerViews.filter((answer) => answer.questionName === 'PTP Date');
|
||||
if (ptpDate.length > 0) {
|
||||
message +=
|
||||
' for ' + sanitizeString(dateFormat(new Date(ptpDate[0].inputDate), 'DD MMM, YYYY')) + '\n\n';
|
||||
} else {
|
||||
message += '\n\n';
|
||||
}
|
||||
|
||||
message += '*Remarks*: ';
|
||||
const answerList = feedbackItem?.answerViews?.filter(
|
||||
(answer) => answer.questionName === 'Comments'
|
||||
);
|
||||
if (answerList.length > 0) {
|
||||
message += sanitizeString(answerList[0]?.inputText) + '\n\n';
|
||||
} else {
|
||||
message += 'N.A.\n\n';
|
||||
}
|
||||
|
||||
{
|
||||
feedbackItem?.metadata?.interactionLatitude &&
|
||||
feedbackItem?.metadata?.interactionLongitude &&
|
||||
FIELD_FEEDBACKS.includes(feedbackItem?.type)
|
||||
? (message +=
|
||||
'*Location of feedback*: ' +
|
||||
sanitizeString(
|
||||
getLocationLink(
|
||||
feedbackItem?.metadata?.interactionLatitude,
|
||||
feedbackItem?.metadata?.interactionLongitude
|
||||
)
|
||||
) +
|
||||
'\n\n')
|
||||
: null;
|
||||
}
|
||||
|
||||
const imagesList = feedbackItem?.answerViews.filter(
|
||||
(answer) => answer.questionTag === OPTION_TAG.IMAGE_UPLOAD
|
||||
);
|
||||
var imageUrl = '';
|
||||
const mimeType = 'image/*';
|
||||
if (imagesList.length > 0) {
|
||||
var imageUri = '';
|
||||
imageUri = imagesList[0]?.inputText ? imagesList[0].inputText : '';
|
||||
let imagePath = '';
|
||||
ReactNativeBlobUtil.config({
|
||||
fileCache: true,
|
||||
})
|
||||
.fetch('GET', imageUri)
|
||||
.then((resp: any) => {
|
||||
if (resp.info().status !== 200) {
|
||||
return '';
|
||||
} else {
|
||||
imagePath = resp.path();
|
||||
return resp.readFile('base64');
|
||||
}
|
||||
})
|
||||
.then((base64Data: any) => {
|
||||
imageUrl = base64Data;
|
||||
sendToWhatsappNative(message, imageUrl, mimeType, caseDetails, agentId);
|
||||
ReactNativeBlobUtil.fs.unlink(imagePath);
|
||||
});
|
||||
} else {
|
||||
sendToWhatsappNative(message, imageUrl, mimeType, caseDetails, agentId);
|
||||
}
|
||||
};
|
||||
|
||||
const FeedbackDetailItem = ({
|
||||
feedbackItem,
|
||||
isExpanded,
|
||||
caseId,
|
||||
hideAddress,
|
||||
}: IFeedbackDetailItem) => {
|
||||
const isGeolocation = feedbackItem?.source?.sourceType === VisitType.GEOLOCATION;
|
||||
const caseDetails = useAppSelector((state) => state.allCases.caseDetails[caseId]);
|
||||
const { agentId } = useAppSelector((state) => ({ agentId: state.user.user?.referenceId!! }));
|
||||
const [isWhastappSendLoading, setIsWhatsappSendLoading] = useState(false);
|
||||
const isGeolocation = feedbackItem?.source?.sourceType === VisitType.GEOLOCATION;
|
||||
|
||||
const sendToWhatsappNative = (
|
||||
message: string,
|
||||
imageUrl: string,
|
||||
mimeType: string,
|
||||
caseDetails: CaseDetail,
|
||||
agentId: string
|
||||
) => {
|
||||
setIsWhatsappSendLoading(true);
|
||||
sendFeedbackToWhatsapp(message, imageUrl, mimeType)
|
||||
.then((res: boolean) => {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SHARE_SUCCESSFUL, {
|
||||
caseId: caseDetails?.id,
|
||||
agentId: agentId,
|
||||
});
|
||||
setIsWhatsappSendLoading(false);
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
setIsWhatsappSendLoading(false);
|
||||
if (err.message === '1') {
|
||||
toast({
|
||||
text1: ToastMessages.WHATSAPP_NOT_INSTALLED,
|
||||
type: 'error',
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
text1: ToastMessages.WHATSAPP_FEEDBACK_SHARE_FAILURE,
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const sendToWhatsapp = (feedbackItem: IFeedback, caseDetails: CaseDetail, agentId: string) => {
|
||||
setIsWhatsappSendLoading(true);
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SHARE_FEEDBACK_CLICKED, {
|
||||
caseId: caseDetails?.id,
|
||||
agentId: agentId,
|
||||
});
|
||||
|
||||
let message = `*Visit Feedback* for ${sanitizeString(caseDetails?.customerName)}
|
||||
_${sanitizeString(dateFormat(new Date(feedbackItem?.createdAt), 'DD MMM, YYYY | HH:mm a.'))}_\n
|
||||
*LAN*: ${sanitizeString(caseDetails?.loanAccountNumber)}
|
||||
*DPD Bucket*: ${sanitizeString(caseDetails?.dpdBucket)}
|
||||
*EMI Amount*: ₹${getSanitizedCommaAmount(caseDetails?.currentOutstandingEmi)}\n
|
||||
*Disposition*: ${sanitizeString(feedbackItem?.interactionStatus)}`;
|
||||
|
||||
const ptpDate = feedbackItem?.answerViews.filter(
|
||||
(answer) => answer.questionName === 'PTP Date'
|
||||
);
|
||||
if (ptpDate.length > 0) {
|
||||
message +=
|
||||
' for ' +
|
||||
sanitizeString(dateFormat(new Date(ptpDate[0].inputDate), 'DD MMM, YYYY')) +
|
||||
'\n\n';
|
||||
} else {
|
||||
message += '\n\n';
|
||||
}
|
||||
|
||||
message += '*Remarks*: ';
|
||||
const answerList = feedbackItem?.answerViews?.filter(
|
||||
(answer) => answer.questionName === 'Comments'
|
||||
);
|
||||
if (answerList.length > 0) {
|
||||
message += sanitizeString(answerList[0]?.inputText) + '\n\n';
|
||||
} else {
|
||||
message += 'N.A.\n\n';
|
||||
}
|
||||
|
||||
{
|
||||
feedbackItem?.metadata?.interactionLatitude &&
|
||||
feedbackItem?.metadata?.interactionLongitude &&
|
||||
FIELD_FEEDBACKS.includes(feedbackItem?.type)
|
||||
? (message +=
|
||||
'*Location of feedback*: ' +
|
||||
sanitizeString(
|
||||
getLocationLink(
|
||||
feedbackItem?.metadata?.interactionLatitude,
|
||||
feedbackItem?.metadata?.interactionLongitude
|
||||
)
|
||||
) +
|
||||
'\n\n')
|
||||
: null;
|
||||
}
|
||||
|
||||
const imagesList = feedbackItem?.answerViews.filter(
|
||||
(answer) => answer.questionTag === OPTION_TAG.IMAGE_UPLOAD
|
||||
);
|
||||
let imageUrl = '';
|
||||
const mimeType = 'image/*';
|
||||
if (imagesList.length > 0) {
|
||||
let imageUri = '';
|
||||
imageUri = imagesList[0]?.inputText ? imagesList[0].inputText : '';
|
||||
let imagePath = '';
|
||||
ReactNativeBlobUtil.config({
|
||||
fileCache: true,
|
||||
})
|
||||
.fetch('GET', imageUri)
|
||||
.then((resp: any) => {
|
||||
if (resp.info().status !== 200) {
|
||||
return '';
|
||||
} else {
|
||||
imagePath = resp.path();
|
||||
return resp.readFile('base64');
|
||||
}
|
||||
})
|
||||
.then((base64Data: any) => {
|
||||
imageUrl = base64Data;
|
||||
sendToWhatsappNative(message, imageUrl, mimeType, caseDetails, agentId);
|
||||
ReactNativeBlobUtil.fs.unlink(imagePath);
|
||||
});
|
||||
} else {
|
||||
sendToWhatsappNative(message, imageUrl, mimeType, caseDetails, agentId);
|
||||
}
|
||||
};
|
||||
|
||||
const throttledSendToWhatsapp = React.useRef(debounce(sendToWhatsapp, 500));
|
||||
return (
|
||||
<View style={[styles.addressItem]}>
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
|
||||
@@ -273,8 +285,11 @@ const FeedbackDetailItem = ({
|
||||
) : null}
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={() => sendToWhatsapp(feedbackItem, caseDetails, agentId)}
|
||||
onPress={() => {
|
||||
throttledSendToWhatsapp.current(feedbackItem, caseDetails, agentId);
|
||||
}}
|
||||
style={[GenericStyles.row, styles.BtnPadding]}
|
||||
disabled={isWhastappSendLoading}
|
||||
>
|
||||
<IconLabel
|
||||
text="Share"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { IGeolocationCoordinate } from '@interfaces/addressGeolocation.types';
|
||||
import {
|
||||
CaseAllocationType,
|
||||
CaseStatuses,
|
||||
@@ -80,6 +81,7 @@ export interface Address {
|
||||
permanent: boolean;
|
||||
zipCode?: any;
|
||||
addressQualityStatus?: any;
|
||||
location: IGeolocationCoordinate;
|
||||
}
|
||||
export interface Metadata {
|
||||
'@class': string;
|
||||
@@ -347,3 +349,7 @@ export enum PhoneNumberSource {
|
||||
CRIF = 'CRIF',
|
||||
CIBIL_CU = 'CIBIL_CU',
|
||||
}
|
||||
|
||||
export interface INearbyCaseItemObj extends CaseDetail {
|
||||
distanceInKm: number;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import ForegroundService from '@supersami/rn-foreground-service';
|
||||
import { logError } from '../../components/utlis/errorUtils';
|
||||
import { GLOBAL } from '../../constants/Global';
|
||||
import { AppState } from 'react-native';
|
||||
import { AppStates } from '@types/appStates';
|
||||
|
||||
export interface IForegroundTask {
|
||||
task: () => void;
|
||||
@@ -34,6 +36,9 @@ class CosmosForegroundService {
|
||||
private constructor() {}
|
||||
|
||||
static async start(tasks?: IForegroundTask[]) {
|
||||
if (AppState.currentState !== AppStates.ACTIVE) {
|
||||
return;
|
||||
}
|
||||
if (GLOBAL.IS_IMPERSONATED) {
|
||||
return;
|
||||
}
|
||||
@@ -60,6 +65,9 @@ class CosmosForegroundService {
|
||||
}
|
||||
|
||||
static async update() {
|
||||
if (AppState.currentState !== AppStates.ACTIVE) {
|
||||
return;
|
||||
}
|
||||
if (GLOBAL.IS_IMPERSONATED) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@constants/*": ["src/constants/*"],
|
||||
"@screens/*": ["src/screens/*"],
|
||||
"@services/*": ["src/services/*"],
|
||||
"@types/*": ["src/types/*"],
|
||||
"@interfaces/*": ["src/types/*"],
|
||||
"@common/*": ["src/common/*"],
|
||||
"@assets/*": ["src/assets/*"],
|
||||
"@store": ["src/store/store"],
|
||||
|
||||
Reference in New Issue
Block a user