NTP-38623 | Merge Master

This commit is contained in:
yashmantri
2025-02-24 19:21:09 +05:30
20 changed files with 183 additions and 33 deletions

25
App.tsx
View File

@@ -20,36 +20,36 @@ import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import store, { persistor } from './src/store/store';
import { navigationRef } from '@utils/navigationUtlis';
import FullScreenLoader from './RN-UI-LIB/src/components/FullScreenLoader';
import { toastConfigs, ToastContainer } from './RN-UI-LIB/src/components/toast';
import { navigationRef } from '@utils/navigationUtlis';
import { hydrateGlobalImageMap } from '@common/CachedImage';
import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from '@common/Constants';
import { sendDeviceDetailsToClickstream } from '@components/utlis/commonFunctions';
import { linkingConf } from '@components/utlis/deeplinkingUtils';
import { getBuildFlavour } from '@components/utlis/DeviceUtils';
import { initSentry } from '@components/utlis/sentry';
import { GLOBAL, setGlobalBuildFlavour } from '@constants/Global';
import { AppStates } from '@interfaces/appStates';
import analytics from '@react-native-firebase/analytics';
import dayJs from 'dayjs';
import { COLORS } from '@rn-ui-lib/colors';
import { MILLISECONDS_IN_A_SECOND } from '@rn-ui-lib/utils/common';
import { hydrateGlobalImageMap } from '@common/CachedImage';
import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from '@common/Constants';
import ErrorBoundary from './src/common/ErrorBoundary';
import { type TDocumentObj } from '@screens/caseDetails/interface';
import { addClickstreamEvent } from '@services/clickstreamEventService';
import { setJsErrorHandler } from '@services/exception-handler.service';
import syncSelfCallData from '@services/syncSelfCallData';
import { getPermissionsToRequest } from '@utils/PermissionUtils';
import dayJs from 'dayjs';
import CodePushLoadingModal, { CodePushLoadingModalRef } from './CodePushModal';
import ErrorBoundary from './src/common/ErrorBoundary';
import ScreenshotBlocker from './src/components/utlis/ScreenshotBlocker';
import { setItem } from './src/components/utlis/storageHelper';
import { ENV } from './src/constants/config';
import AuthRouter from './src/screens/auth/AuthRouter';
import { type TDocumentObj } from '@screens/caseDetails/interface';
import Permissions from './src/screens/permissions/Permissions';
import { addClickstreamEvent } from '@services/clickstreamEventService';
import { setJsErrorHandler } from '@services/exception-handler.service';
import fetchUpdatedRemoteConfig from './src/services/firebaseFetchAndUpdate.service';
import { StorageKeys } from './src/types/storageKeys';
import CodePushLoadingModal, { CodePushLoadingModalRef } from './CodePushModal';
import { initSentry } from '@components/utlis/sentry';
import { AppStates } from '@interfaces/appStates';
import syncSelfCallData from '@services/syncSelfCallData';
if (!__DEV__) {
initSentry();
@@ -97,6 +97,7 @@ function App() {
const askForPermissions = async () => {
const permissionsToRequest = await getPermissionsToRequest();
if (Platform.OS === 'android') {
PermissionsAndroid.requestMultiple(permissionsToRequest)
.then(async (result) => {

View File

@@ -3,6 +3,7 @@ package com.avapp.callModule;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.telecom.TelecomManager;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -161,6 +162,28 @@ public class CallModule extends ReactContextBaseJavaModule {
makeCall(activity, recipient, exotelNumber);
}
@ReactMethod
public void getDefaultCallingApp(Promise promise) {
try {
TelecomManager telecomManager = (TelecomManager) reactContext.getSystemService(Context.TELECOM_SERVICE);
if (telecomManager != null) {
String defaultDialer = telecomManager.getDefaultDialerPackage();
if (defaultDialer != null) {
Log.d(TAG, "Default calling app: " + defaultDialer);
promise.resolve(defaultDialer); // Send default dialer package name to JS side
} else {
Log.d(TAG, "No default calling app found.");
promise.resolve(null); // No default dialer app
}
} else {
promise.reject("ERROR", "TelecomManager is not available.");
}
} catch (Exception e) {
Log.e(TAG, "Error retrieving default calling app", e);
promise.reject("ERROR", e);
}
}
private void makeCall(Activity activity, String recipient, String exotelNumber) {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + pendingRecipient));
@@ -176,6 +199,16 @@ public class CallModule extends ReactContextBaseJavaModule {
Log.e(TAG, "No app found to handle the call.");
}
}
@ReactMethod
public void startDialerApp() {
Intent launchIntent = reactContext.getPackageManager().getLaunchIntentForPackage(CALLING_APP_PACKAGE);
if (launchIntent != null) {
Log.d(TAG, "Call initiated successfully.");
reactContext.startActivity(launchIntent);
} else {
Log.e(TAG, "No app found to handle the call.");
}
}
private void promptForInstall(Uri apkUri) {
try {

View File

@@ -1 +1 @@
237
238

View File

@@ -1 +1 @@
2.18.0
2.18.1

View File

@@ -1 +1 @@
306
307

View File

@@ -1 +1 @@
100.2.2
100.2.3

View File

@@ -8,4 +8,5 @@ export const APM_APP_NAME = 'cosmos-app';
export const APM_BASE_URL = 'https://dev-longhorn-portal.np.navi-tech.in/apm-events';
export const GOOGLE_SSO_CLIENT_ID =
'60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com';
export const MS_CLARITY_PROJECT_ID = '';
export const MS_CLARITY_PROJECT_ID = '';
export const COSMOS_DIALER_PACKAGE_NAME = 'org.fossify.phone';

View File

@@ -12,4 +12,5 @@ export const IS_DATA_SYNC_REQUIRED = true;
export const DATA_SYNC_TIME_INTERVAL = 2 * MINUTES_IN_AN_HOUR * MILLISECONDS_IN_A_MINUTE; // 2hr
export const GOOGLE_SSO_CLIENT_ID =
'136591056725-ev8db4hrlud2m23n0o03or3cmmp3a3cq.apps.googleusercontent.com';
export const MS_CLARITY_PROJECT_ID = 'n2nsbu7o78';
export const MS_CLARITY_PROJECT_ID = 'n2nsbu7o78';
export const COSMOS_DIALER_PACKAGE_NAME = 'org.fossify.phone';

View File

@@ -13,3 +13,5 @@ export const DATA_SYNC_TIME_INTERVAL = 2 * MINUTES_IN_AN_HOUR * MILLISECONDS_IN_
export const GOOGLE_SSO_CLIENT_ID =
'60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com';
export const MS_CLARITY_PROJECT_ID = '';
export const COSMOS_DIALER_PACKAGE_NAME = 'org.fossify.phone';

View File

@@ -29,6 +29,7 @@ import {
import AppUpdate from './AppUpdate';
import {
IShouldDialerAppUpdate,
setDefaultDialer,
setShouldDialerAppUpdate,
setShouldUpdate,
} from '@reducers/appUpdateSlice';
@@ -36,6 +37,9 @@ import PostOperativeHours from './PostOperativeHours';
import { isFieldApp } from './utils';
import { AppStates } from '@interfaces/appStates';
import DialerAppUpdate from './DialerAppUpdate';
import { getDefaultCallingApp } from '@components/utlis/callModuleUtils';
import { COSMOS_DIALER_PACKAGE_NAME } from '@constants/config';
import DefaultDialerScreen from './DefaultDialerScreen';
interface IBlockerScreen {
children?: ReactNode;
@@ -60,6 +64,9 @@ const BlockerScreen = (props: IBlockerScreen) => {
);
const shouldDialerAppUpdate =
useAppSelector((state) => state.appUpdate.shouldDialerAppUpdate) || {};
const isCosmosDialerDefault =
useAppSelector((state) => state.appUpdate.isCosmosDialerDefault);
const withinOperativeHours = useAppSelector((state) => state.user?.withinOperativeHours);
const isLoggedIn = useAppSelector((state: RootState) => state.user?.isLoggedIn);
const isFieldAgent =
@@ -155,6 +162,8 @@ const BlockerScreen = (props: IBlockerScreen) => {
});
};
const handleDialerAppUpdate = async () => {
const url = dialerAppState?.currentProdAPK;
if (!shouldDialerAppUpdate.newApkCachedUrl) {
@@ -235,6 +244,8 @@ const BlockerScreen = (props: IBlockerScreen) => {
);
deleteCachedApkFiles(true);
}
const defaultCallingApp = await getDefaultCallingApp()=== COSMOS_DIALER_PACKAGE_NAME;
dispatch(setDefaultDialer(defaultCallingApp));
},
[dialerAppState, shouldDialerAppUpdate, isCosmosDiallerEnabled]
);
@@ -295,6 +306,10 @@ const BlockerScreen = (props: IBlockerScreen) => {
return <DialerAppUpdate onAppUpdate={handleDialerAppUpdate} />;
}
if (!isCosmosDialerDefault && isCosmosDiallerEnabled) {
return <DefaultDialerScreen />
}
if (shouldUpdate.switchToFallback) {
const { heading, instructions } = BLOCKER_SCREEN_DATA.UNINSTALL_APP;
return (

View File

@@ -1484,7 +1484,10 @@ export const CLICKSTREAM_EVENT_NAMES = {
name: 'FA_CALL_VIA_INHOUSE_DIALER_APP_FAILED',
description: 'Call via inhouse dialer app failed',
},
FA_COSMOS_DEFAULT_DIALER: {
name: 'FA_COSMOS_DEFAULT_DIALER',
description: 'Cosmos default dialer',
}
} as const;
export enum MimeType {

View File

@@ -0,0 +1,67 @@
import DialerAppIcon from '@assets/icons/DialerAppIcon';
import { startDialerApp } from '@components/utlis/callModuleUtils';
import { COLORS } from '@rn-ui-lib/colors';
import Button from '@rn-ui-lib/components/Button';
import NavigationHeader from '@rn-ui-lib/components/NavigationHeader';
import Text from '@rn-ui-lib/components/Text';
import { GenericStyles } from '@rn-ui-lib/styles';
import Layout from '@screens/layout/Layout';
import React from 'react';
import { Linking, Pressable, StyleSheet, View } from 'react-native';
const DefaultDialerScreen: React.FC = () => {
const openHelpForm = () => {
Linking.openURL(
'https://docs.google.com/forms/d/e/1FAIpQLSdKtdzB67-yyidd2Gh_52fYsLtL9QeuTmkUb6BZt4fAPJGyOg/viewform?usp=dialog'
);
};
return (
<Layout>
<View style={[GenericStyles.fill]}>
<NavigationHeader
title="Default Dialer"
titleStyle={GenericStyles.pl16}
rightActionable={
<Pressable onPress={openHelpForm}>
<Text style={styles.helpHeading}>Help</Text>
</Pressable>
}
/>
<View style={[GenericStyles.fill, GenericStyles.centerAligned, GenericStyles.ph24]}>
<DialerAppIcon />
<Text dark bold style={styles.heading}>
Cosmos dialer
</Text>
<Text light style={GenericStyles.centerAlignedText}>
Please make the cosmos dialer as default dialer to use cosmos.
</Text>
</View>
<View style={[GenericStyles.elevation10, GenericStyles.p16, GenericStyles.whiteBackground]}>
<Button
title="Open Cosmos dialer"
onPress={()=>startDialerApp()}
style={GenericStyles.w100}
/>
</View>
</View>
</Layout>
);
};
const styles = StyleSheet.create({
heading: {
fontWeight: 'bold',
fontSize: 20,
marginBottom: 16,
},
helpHeading: {
color: COLORS.TEXT.WHITE,
paddingVertical: 16,
paddingHorizontal: 12,
},
});
export default DefaultDialerScreen;

View File

@@ -1,4 +1,4 @@
import React,{ type ReactNode, useEffect, useRef } from 'react';
import React, { type ReactNode, useEffect, useRef } from 'react';
import { AppState, type AppStateStatus } from 'react-native';
import dayJs from 'dayjs';
import RNFS from 'react-native-fs';
@@ -54,6 +54,7 @@ import useFirestoreUpdates from '@hooks/useFirestoreUpdates';
import { handlePostOperativeHourActivity } from '@screens/caseDetails/utils/postOperationalHourActions';
import { setPostOperationalHourRestrictions } from '@reducers/postOperationalHourRestrictionsSlice';
import { isCallingApp } from './utils';
import { getDefaultCallingApp } from '@components/utlis/callModuleUtils';
export enum FOREGROUND_TASKS {
GEOLOCATION = 'GEOLOCATION',
@@ -106,6 +107,13 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
const resyncFirebase = useResyncFirebase();
const handleUpdateActivity = async () => {
const isDialerEnabled = store?.getState()?.user?.featureFlags?.isCosmosDiallerEnabled;
if (isDialerEnabled) {
const defaultDialer = await getDefaultCallingApp();
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_COSMOS_DEFAULT_DIALER, {
defaultDialer
}, true);
}
const foregroundTimestamp = await getItem(StorageKeys.APP_FOREGROUND_TIMESTAMP);
const backgroundTimestamp = await getItem(StorageKeys.APP_BACKGROUND_TIMESTAMP);
const stateSetTimestamp = await getItem(StorageKeys.STATE_SET_TIMESTAMP);
@@ -204,18 +212,18 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
const allPermissionsGranted = permissionsToRequest?.length === 0;
return allPermissionsGranted;
};
const appVersion = getAppVersion();
const taskSyncToLonghorn = async () => {
const allPermissionsGranted = await checkPermissions();
const agentId= store.getState().user.user?.referenceId!;
if(!agentId) {
const allPermissionsGranted = await checkPermissions();
const agentId = store.getState().user.user?.referenceId!;
if (!agentId) {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_COSMOS_SYNC_TO_LONGHORN_ERROR);
return;
}
await syncToLonghorn({allPermissionsGranted, agentId, appVersion, isSyncToastEnabled: false});
await syncToLonghorn({ allPermissionsGranted, agentId, appVersion, isSyncToastEnabled: false });
};
const tasks: IForegroundTask[] = [
@@ -294,7 +302,7 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
},
];
if(isCallingApp()) {
if (isCallingApp()) {
tasks.push({
taskId: FOREGROUND_TASKS.COSMOS_SYNC_WITH_LONGHORN,
task: taskSyncToLonghorn,

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useMemo, useState } from 'react';
import { View } from 'react-native';
import { StyleSheet, View } from 'react-native';
import { Control, Controller, UseFormSetValue } from 'react-hook-form';
import { useSelector } from 'react-redux';
@@ -147,7 +147,7 @@ const RadioButton: React.FC<IRadioButton> = (props) => {
key={option}
id={option as string}
value={options[option]?.text}
containerStyle={[GenericStyles.whiteBackground]}
containerStyle={[GenericStyles.whiteBackground, styles.mr6]}
/>
))}
</RadioGroup>
@@ -186,4 +186,10 @@ const RadioButton: React.FC<IRadioButton> = (props) => {
);
};
const styles = StyleSheet.create({
mr6: {
marginRight: 6,
},
});
export default RadioButton;

View File

@@ -8,3 +8,9 @@ export const launchCallIntent = (number: string, exotelnumber: string, filePatch
export const isDialerAppValidApk = (filePath: string): Promise<boolean> => {
return CallModule.isDialerAppValidApkFile(filePath);
}
export const getDefaultCallingApp = (): Promise<string> => {
return CallModule.getDefaultCallingApp();
}
export const startDialerApp = (): void => CallModule.startDialerApp();

View File

@@ -13,3 +13,5 @@ export const DATA_SYNC_TIME_INTERVAL = 2 * MINUTES_IN_AN_HOUR * MILLISECONDS_IN_
export const GOOGLE_SSO_CLIENT_ID =
'60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com';
export const MS_CLARITY_PROJECT_ID = '';
export const COSMOS_DIALER_PACKAGE_NAME = 'org.fossify.phone';

View File

@@ -12,6 +12,7 @@ interface IAppUpdateSlice {
switchToFallback: boolean;
};
shouldDialerAppUpdate: IShouldDialerAppUpdate;
isCosmosDialerDefault: boolean;
}
const initialState: IAppUpdateSlice = {
@@ -24,6 +25,7 @@ const initialState: IAppUpdateSlice = {
switchToFallback: false,
isAppInstalled: false,
},
isCosmosDialerDefault: false,
};
const AppUpdateSlice = createSlice({
@@ -36,9 +38,12 @@ const AppUpdateSlice = createSlice({
setShouldDialerAppUpdate: (state, action) => {
state.shouldDialerAppUpdate = action.payload;
},
setDefaultDialer: (state, action) => {
state.isCosmosDialerDefault = action.payload;
}
},
});
export const { setShouldUpdate, setShouldDialerAppUpdate } = AppUpdateSlice.actions;
export const { setShouldUpdate, setShouldDialerAppUpdate, setDefaultDialer } = AppUpdateSlice.actions;
export default AppUpdateSlice.reducer;

View File

@@ -35,7 +35,7 @@ const SkipTracingAddressContainer: React.FC<IAddressContainer> = ({
]}
>
<CustomLocationIcon />
<Text style={[styles.textContainer, styles.noSkipTracingAddressText]}>No skip tracing found</Text>
<Text style={[styles.textContainer, styles.noSkipTracingAddressText]}>No customer outreach address found</Text>
</View>
);
}

View File

@@ -15,7 +15,7 @@ export const PRIMARY_SOURCE_MAPPING = {
[PrimarySourcesType.AVS_V1]: 'AVS_V1',
[PrimarySourcesType.KARZA]: 'Karza',
[PrimarySourcesType['Field App']]: 'COSMOS',
[PrimarySourcesType.SKIP_TRACING]: 'Skip Tracing',
[PrimarySourcesType.SKIP_TRACING]: 'Customer outreach',
[PrimarySourcesType.ACCOUNT_AGGREGATOR]: 'Account Aggregator',
};
@@ -30,7 +30,7 @@ export const ADDRESSES_TABS = [
},
{
key: 'skipTracing',
label: 'Skip Tracing',
label: 'Customer Outreach',
},
];

View File

@@ -430,7 +430,7 @@ export interface INearbyCaseItemObj extends CaseDetail {
export enum GeolocationSourceMap {
VKYC = 'VKYC',
DATA_SUTRAM = 'Skip Tracing',
DATA_SUTRAM = 'Customer outreach',
}
export interface Tab {