took master pull
This commit is contained in:
19
App.tsx
19
App.tsx
@@ -18,12 +18,12 @@ import CodePush from 'react-native-code-push';
|
||||
import store, { persistor } from './src/store/store';
|
||||
|
||||
import { navigationRef } from './src/components/utlis/navigationUtlis';
|
||||
import FullScreenLoaderWrapper from './src/common/FullScreenLoaderWrapper';
|
||||
import FullScreenLoader from './RN-UI-LIB/src/components/FullScreenLoader';
|
||||
import { toastConfigs, ToastContainer } from './RN-UI-LIB/src/components/toast';
|
||||
|
||||
import { APM_APP_NAME, APM_BASE_URL, ENV } from './src/constants/config';
|
||||
import { COLORS } from './RN-UI-LIB/src/styles/colors';
|
||||
import { LocalStorageKeys } from './src/common/Constants';
|
||||
import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from './src/common/Constants';
|
||||
import Permissions from './src/screens/permissions/Permissions';
|
||||
import { setJsErrorHandler } from './src/services/exception-handler.service';
|
||||
import SuspenseLoader from './RN-UI-LIB/src/components/suspense_loader/SuspenseLoader';
|
||||
@@ -37,9 +37,13 @@ import {
|
||||
} from './src/components/utlis/PermissionUtils';
|
||||
import usePolling from './src/hooks/usePolling';
|
||||
import { MILLISECONDS_IN_A_SECOND } from './RN-UI-LIB/src/utlis/common';
|
||||
import { setItem } from './src/components/utlis/storageHelper';
|
||||
import { StorageKeys } from './src/types/storageKeys';
|
||||
import dayJs from 'dayjs';
|
||||
import { GlobalImageMap, hydrateGlobalImageMap } from './src/common/CachedImage';
|
||||
import analytics from '@react-native-firebase/analytics';
|
||||
import FullScreenLoader from './RN-UI-LIB/src/components/FullScreenLoader';
|
||||
import handleUpdatedConfigureValuesFromFirebase from './src/services/firebaseFetchAndUpdate.service';
|
||||
import { addClickstreamEvent } from './src/services/clickstreamEventService';
|
||||
|
||||
initSentry();
|
||||
|
||||
@@ -104,6 +108,12 @@ function App() {
|
||||
return route?.name || '';
|
||||
};
|
||||
|
||||
async function setForegroundTimeStampAndClickstream() {
|
||||
const now = dayJs().toString();
|
||||
await setItem(StorageKeys.APP_FOREGROUND_TIMESTAMP, now);
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_APP_FOREGROUND, { now });
|
||||
}
|
||||
|
||||
usePolling(askForPermissions, PERMISSION_CHECK_POLL_INTERVAL);
|
||||
|
||||
initApm({
|
||||
@@ -129,6 +139,9 @@ function App() {
|
||||
setIsGlobalDocumentMapLoaded(true);
|
||||
})();
|
||||
checkCodePushAndSync();
|
||||
handleUpdatedConfigureValuesFromFirebase();
|
||||
setForegroundTimeStampAndClickstream();
|
||||
|
||||
return () => {
|
||||
appStateChange.remove();
|
||||
};
|
||||
|
||||
@@ -131,16 +131,10 @@ def reactNativeArchitectures() {
|
||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||
}
|
||||
|
||||
def VERSION_CODE = 85
|
||||
def VERSION_NAME = "2.4.1"
|
||||
def VERSION_CODE = 86
|
||||
def VERSION_NAME = "2.4.2"
|
||||
|
||||
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
|
||||
|
||||
@@ -47,6 +47,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;
|
||||
}
|
||||
|
||||
|
||||
101
android/app/src/main/java/com/avapp/ScreenshotBlockerModule.java
Normal file
101
android/app/src/main/java/com/avapp/ScreenshotBlockerModule.java
Normal file
@@ -0,0 +1,101 @@
|
||||
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 com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.content.ContentResolver;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
public class ScreenshotBlockerModule extends ReactContextBaseJavaModule {
|
||||
private ReactApplicationContext reactContext;
|
||||
private ContentObserver contentObserver;
|
||||
private boolean isTracking = false;
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void startScreenshotTracking() {
|
||||
if (!isTracking) {
|
||||
isTracking = true;
|
||||
ContentResolver contentResolver = getCurrentActivity().getContentResolver();
|
||||
contentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
|
||||
long lastEventTimestamp = 0;
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
|
||||
if (currentTimeMillis - lastEventTimestamp > 1000) {
|
||||
sendScreenshotEvent();
|
||||
lastEventTimestamp = currentTimeMillis;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
contentResolver.registerContentObserver(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
true,
|
||||
contentObserver
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendScreenshotEvent() {
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putBoolean("isScreenshotDetected", true);
|
||||
reactContext
|
||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("screenshotTaken", params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,24 @@ module.exports = {
|
||||
cwd: 'babelrc',
|
||||
extensions: ['.ts', '.tsx', '.js', '.ios.js', '.android.js'],
|
||||
alias: {
|
||||
'@cuteapp': './app',
|
||||
'@root': './src',
|
||||
'@components': './src/components',
|
||||
'@hooks': './src/hooks',
|
||||
'@actions': './src/action',
|
||||
'@reducers': './src/reducer',
|
||||
'@constants': './src/constants',
|
||||
'@screens': './src/screens',
|
||||
'@services': './src/services',
|
||||
'@types': './src/types',
|
||||
'@common': './src/common',
|
||||
'@assets': './src/assets',
|
||||
'@store': './src/store/store',
|
||||
'@utils': './src/components/utlis',
|
||||
'@rn-ui-lib/components': './RN-UI-LIB/src/components',
|
||||
'@rn-ui-lib/icons': './RN-UI-LIB/src/Icons',
|
||||
'@rn-ui-lib/styles': './RN-UI-LIB/src/styles/index',
|
||||
'@rn-ui-lib/colors': './RN-UI-LIB/src/styles/colors',
|
||||
'@rn-ui-lib/utils': './RN-UI-LIB/src/utlis',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "AV_APP",
|
||||
"version": "2.4.1",
|
||||
"version": "2.4.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android:dev": "yarn move:dev && react-native run-android",
|
||||
@@ -11,7 +11,7 @@
|
||||
"android-field:prod": "yarn move:prod && react-native run-android --variant=fieldAgentsQADebug",
|
||||
"release-field:dev": "yarn move:dev && react-native run-android --variant=fieldAgentsQARelease && cd android && ./gradlew assemblefieldAgentsQARelease",
|
||||
"release-field:qa": "yarn move:qa && react-native run-android --variant=fieldAgentsQARelease && cd android && ./gradlew assemblefieldAgentsQARelease",
|
||||
"release-field:prod": "yarn move:dev && react-native run-android --variant=fieldAgentsProdRelease && cd android && ./gradlew assemblefieldAgentsProdRelease",
|
||||
"release-field:prod": "yarn move:prod && react-native run-android --variant=fieldAgentsProdRelease && cd android && ./gradlew assemblefieldAgentsProdRelease",
|
||||
"android-calling:dev": "yarn move:dev && react-native run-android --variant=callingAgentsQADebug",
|
||||
"android-calling:qa": "yarn move:qa && react-native run-android --variant=callingAgentsQADebug",
|
||||
"android-calling:prod": "yarn move:prod && react-native run-android --variant=callingAgentsQADebug",
|
||||
@@ -49,6 +49,7 @@
|
||||
"@react-native-firebase/database": "16.4.6",
|
||||
"@react-native-firebase/firestore": "16.5.0",
|
||||
"@react-native-firebase/messaging": "17.4.0",
|
||||
"@react-native-firebase/remote-config": "16.4.6",
|
||||
"@react-native-google-signin/google-signin": "9.0.2",
|
||||
"@react-navigation/bottom-tabs": "6.5.5",
|
||||
"@react-navigation/native": "6.1.4",
|
||||
@@ -80,6 +81,7 @@
|
||||
"react-native-gzip": "1.0.0",
|
||||
"react-native-image-picker": "4.10.2",
|
||||
"react-native-pager-view": "6.1.2",
|
||||
"react-native-pdf-renderer": "1.1.1",
|
||||
"react-native-permissions": "3.6.1",
|
||||
"react-native-qrcode-svg": "^6.2.0",
|
||||
"react-native-safe-area-context": "4.4.1",
|
||||
|
||||
19
src/common/AgentActivityConfigurableConstants.ts
Normal file
19
src/common/AgentActivityConfigurableConstants.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
let ACTIVITY_TIME_ON_APP: number = 5; //5 seconds
|
||||
let ACTIVITY_TIME_WINDOW_HIGH: number = 10; //10 minutes
|
||||
let ACTIVITY_TIME_WINDOW_MEDIUM: number = 30; //30 minutes
|
||||
|
||||
export const getActivityTimeOnApp = () => ACTIVITY_TIME_ON_APP;
|
||||
export const getActivityTimeWindowHigh = () => ACTIVITY_TIME_WINDOW_HIGH;
|
||||
export const getActivityTimeWindowMedium = () => ACTIVITY_TIME_WINDOW_MEDIUM;
|
||||
|
||||
export const setActivityTimeOnApp = (activityTimeOnApp: number) => {
|
||||
ACTIVITY_TIME_ON_APP = activityTimeOnApp;
|
||||
};
|
||||
|
||||
export const setActivityTimeWindowHigh = (activityTimeWindowHigh: number) => {
|
||||
ACTIVITY_TIME_WINDOW_HIGH = activityTimeWindowHigh;
|
||||
};
|
||||
|
||||
export const setActivityTimeWindowMedium = (activityTimeWindowMedium: number) => {
|
||||
ACTIVITY_TIME_WINDOW_MEDIUM = activityTimeWindowMedium;
|
||||
};
|
||||
@@ -95,6 +95,10 @@ export const CLICKSTREAM_EVENT_NAMES = {
|
||||
name: 'FA_LOGIN_SCREEN_SEND_OTP_API_FAILED',
|
||||
description: 'Send OTP API failed',
|
||||
},
|
||||
FA_SCREENSHOT_TAKEN: {
|
||||
name: 'FA_SCREENSHOT_TAKEN',
|
||||
description: 'Screenshot atempt detected',
|
||||
},
|
||||
|
||||
// OTP screen
|
||||
AV_OTP_SCREEN_LOAD: { name: 'FA_OTP_SCREEN_LOAD', description: 'OTP screen loaded' },
|
||||
|
||||
@@ -34,8 +34,17 @@ import { setLockData } from '../reducer/userSlice';
|
||||
import { getConfigData } from '../action/configActions';
|
||||
import { AppStates } from '../types/appStates';
|
||||
import { StorageKeys } from '../types/storageKeys';
|
||||
import { AgentActivity } from '../types/agentActivity';
|
||||
import {
|
||||
getActivityTimeOnApp,
|
||||
getActivityTimeWindowMedium,
|
||||
getActivityTimeWindowHigh,
|
||||
} from './AgentActivityConfigurableConstants';
|
||||
import RNFS from 'react-native-fs';
|
||||
import { GlobalImageMap } from './CachedImage';
|
||||
import { get } from 'react-hook-form';
|
||||
import { addClickstreamEvent } from '../services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from './Constants';
|
||||
|
||||
export enum FOREGROUND_TASKS {
|
||||
GEOLOCATION = 'GEOLOCATION',
|
||||
@@ -43,6 +52,7 @@ export enum FOREGROUND_TASKS {
|
||||
DATA_SYNC = 'DATA_SYNC',
|
||||
FIRESTORE_FALLBACK = 'FIRESTORE_FALLBACK',
|
||||
UPDATE_AGENT_ACTIVENESS = 'UPDATE_AGENT_ACTIVENESS',
|
||||
UPDATE_AGENT_ACTIVITY = 'UPDATE_AGENT_ACTIVITY',
|
||||
DELETE_CACHE = 'DELETE_CACHE',
|
||||
}
|
||||
|
||||
@@ -51,7 +61,6 @@ interface ITrackingComponent {
|
||||
}
|
||||
|
||||
let LAST_SYNC_STATUS = 'SKIP';
|
||||
const ACTIVITY_TIME_ON_APP = 5; // 5 seconds
|
||||
const ACTIVITY_TIME_WINDOW = 10; // 10 minutes
|
||||
|
||||
const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
@@ -94,12 +103,16 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
return;
|
||||
}
|
||||
const isActiveOnApp: string | boolean = (await getItem(StorageKeys.IS_USER_ACTIVE)) || false;
|
||||
const userActivityonApp: string =
|
||||
(await getItem(StorageKeys.USER_ACTIVITY_ON_APP)) || AgentActivity.LOW;
|
||||
|
||||
const geolocation: IGeolocationPayload = {
|
||||
latitude: location.latitude,
|
||||
longitude: location.longitude,
|
||||
accuracy: location.accuracy,
|
||||
timestamp: Date.now(),
|
||||
isActiveOnApp: Boolean(isActiveOnApp),
|
||||
userActivityOnApp: String(userActivityonApp),
|
||||
};
|
||||
dispatch(sendLocationAndActivenessToServer([geolocation]));
|
||||
} catch (e: any) {
|
||||
@@ -169,6 +182,7 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
const isForegroundTimeWithInRange =
|
||||
diffBetweenCurrentTimeAndForegroundTime <= ACTIVITY_TIME_WINDOW;
|
||||
const isForegroundTimeAfterBackground = dayJs(foregroundTimestamp).isAfter(backgroundTimestamp);
|
||||
const ACTIVITY_TIME_ON_APP = getActivityTimeOnApp();
|
||||
|
||||
if (isForegroundTimeWithInRange) {
|
||||
if (
|
||||
@@ -184,6 +198,60 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
return;
|
||||
};
|
||||
|
||||
const handleUpdateActivity = async () => {
|
||||
const foregroundTimestamp = await getItem(StorageKeys.APP_FOREGROUND_TIMESTAMP);
|
||||
const backgroundTimestamp = await getItem(StorageKeys.APP_BACKGROUND_TIMESTAMP);
|
||||
const stateSetTimestamp = await getItem(StorageKeys.STATE_SET_TIMESTAMP);
|
||||
|
||||
if (foregroundTimestamp == null) {
|
||||
console.log('fts set after installation');
|
||||
await setItem(StorageKeys.APP_FOREGROUND_TIMESTAMP, dayJs().toString());
|
||||
}
|
||||
|
||||
const foregroundTime = dayJs(foregroundTimestamp);
|
||||
const backgroundTime = dayJs(backgroundTimestamp);
|
||||
const stateSetTime = dayJs(stateSetTimestamp);
|
||||
|
||||
const diffBetweenCurrentTimeAndForegroundTime =
|
||||
dayJs().diff(foregroundTime, 'seconds') < 0 ? 0 : dayJs().diff(foregroundTime, 'seconds');
|
||||
const diffBetweenCurrentTimeAndSetStateTime =
|
||||
dayJs().diff(stateSetTime, 'minutes') < 0 ? 0 : dayJs().diff(stateSetTime, 'minutes');
|
||||
|
||||
const ACTIVITY_TIME_ON_APP = getActivityTimeOnApp();
|
||||
const ACTIVITY_TIME_WINDOW_HIGH = getActivityTimeWindowHigh();
|
||||
const ACTIVITY_TIME_WINDOW_MEDIUM = getActivityTimeWindowMedium();
|
||||
|
||||
const isStateSetTimeWithinHighRange =
|
||||
diffBetweenCurrentTimeAndSetStateTime < ACTIVITY_TIME_WINDOW_HIGH;
|
||||
const isStateSetTimeWithinMediumRange =
|
||||
diffBetweenCurrentTimeAndSetStateTime < ACTIVITY_TIME_WINDOW_MEDIUM;
|
||||
const isForegroundTimeAfterBackground = dayJs(foregroundTimestamp).isAfter(backgroundTimestamp);
|
||||
|
||||
if (AppState.currentState === AppStates.ACTIVE) {
|
||||
if (diffBetweenCurrentTimeAndForegroundTime >= ACTIVITY_TIME_ON_APP) {
|
||||
await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.HIGH);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (isForegroundTimeAfterBackground) {
|
||||
if (diffBetweenCurrentTimeAndForegroundTime >= ACTIVITY_TIME_ON_APP) {
|
||||
await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.HIGH);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
} else if (isStateSetTimeWithinHighRange) {
|
||||
return;
|
||||
} else if (isStateSetTimeWithinMediumRange) {
|
||||
await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.MEDIUM);
|
||||
return;
|
||||
} else {
|
||||
await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.LOW);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteCache = () => {
|
||||
const directoryPath = RNFS.CachesDirectoryPath;
|
||||
const currentDate = new Date().getTime();
|
||||
@@ -239,6 +307,12 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
delay: ACTIVITY_TIME_WINDOW * MILLISECONDS_IN_A_MINUTE, // 10 minutes
|
||||
onLoop: true,
|
||||
},
|
||||
{
|
||||
taskId: FOREGROUND_TASKS.UPDATE_AGENT_ACTIVITY,
|
||||
task: handleUpdateActivity,
|
||||
delay: ACTIVITY_TIME_WINDOW * MILLISECONDS_IN_A_MINUTE, // 10 minutes
|
||||
onLoop: true,
|
||||
},
|
||||
{
|
||||
taskId: FOREGROUND_TASKS.DELETE_CACHE,
|
||||
task: deleteCache,
|
||||
@@ -272,17 +346,39 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
});
|
||||
}
|
||||
|
||||
const userActivityUpdateOnBackground = async () => {
|
||||
const foregroundTimestamp = await getItem(StorageKeys.APP_FOREGROUND_TIMESTAMP);
|
||||
const backgroundTimestamp = await getItem(StorageKeys.APP_BACKGROUND_TIMESTAMP);
|
||||
const foregroundTime = dayJs(foregroundTimestamp);
|
||||
const backgroundTime = dayJs(backgroundTimestamp);
|
||||
const diffBetweenBackgroundAndForegroundTime = dayJs(backgroundTime).diff(
|
||||
foregroundTime,
|
||||
'seconds'
|
||||
);
|
||||
const ACTIVITY_TIME_ON_APP = getActivityTimeOnApp();
|
||||
|
||||
if (diffBetweenBackgroundAndForegroundTime >= ACTIVITY_TIME_ON_APP) {
|
||||
await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.HIGH);
|
||||
await setItem(StorageKeys.STATE_SET_TIMESTAMP, dayJs().toString());
|
||||
return;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
const handleAppStateChange = async (nextAppState: AppStateStatus) => {
|
||||
// App comes to foreground from background
|
||||
const now = dayJs().toString();
|
||||
if (nextAppState === AppStates.ACTIVE) {
|
||||
await setItem(StorageKeys.APP_FOREGROUND_TIMESTAMP, now);
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_APP_FOREGROUND, { now });
|
||||
handleGetCaseSyncStatus();
|
||||
dispatch(getConfigData());
|
||||
CosmosForegroundService.start(tasks);
|
||||
}
|
||||
if (nextAppState === AppStates.BACKGROUND) {
|
||||
await setItem(StorageKeys.APP_BACKGROUND_TIMESTAMP, now);
|
||||
userActivityUpdateOnBackground();
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_APP_BACKGROUND, { now });
|
||||
}
|
||||
appState.current = nextAppState;
|
||||
};
|
||||
@@ -306,12 +402,8 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
|
||||
useEffect(() => {
|
||||
let appStateSubscription: NativeEventSubscription;
|
||||
CosmosForegroundService.isRunning().then((isFGSRunning) => {
|
||||
if (!isFGSRunning) {
|
||||
appStateSubscription = AppState.addEventListener('change', handleAppStateChange);
|
||||
CosmosForegroundService.start(tasks);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
appStateSubscription?.remove();
|
||||
};
|
||||
|
||||
5
src/components/utlis/ScreenshotBlocker.tsx
Normal file
5
src/components/utlis/ScreenshotBlocker.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
const { ScreenshotBlocker } = NativeModules;
|
||||
|
||||
export default ScreenshotBlocker;
|
||||
@@ -13,6 +13,7 @@ export interface IGeolocationPayload {
|
||||
accuracy: number;
|
||||
timestamp: number;
|
||||
isActiveOnApp: boolean;
|
||||
userActivityOnApp: string;
|
||||
}
|
||||
|
||||
export const sendLocationAndActivenessToServer =
|
||||
|
||||
39
src/hooks/useScreenshotTracking.ts
Normal file
39
src/hooks/useScreenshotTracking.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { NativeEventEmitter } from 'react-native';
|
||||
import { useAppSelector } from '.';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '../common/Constants';
|
||||
import { getCurrentScreen } from '../components/utlis/navigationUtlis';
|
||||
import ScreenshotBlocker from '../components/utlis/ScreenshotBlocker';
|
||||
import { addClickstreamEvent } from '../services/clickstreamEventService';
|
||||
|
||||
const useScreenshotTracking = () => {
|
||||
const { user, allCases } = useAppSelector((state) => ({
|
||||
user: state.user,
|
||||
allCases: state.allCases,
|
||||
}));
|
||||
|
||||
const screenshotEventEmitter = useMemo(() => new NativeEventEmitter(ScreenshotBlocker), []);
|
||||
useEffect(() => {
|
||||
ScreenshotBlocker.startScreenshotTracking();
|
||||
|
||||
const screenshotTakenSubscription = screenshotEventEmitter.addListener(
|
||||
'screenshotTaken',
|
||||
(event) => {
|
||||
if (event?.isScreenshotDetected) {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SCREENSHOT_TAKEN, {
|
||||
userId: user?.user?.referenceId ?? '',
|
||||
caseId: allCases.selectedCaseId ?? '',
|
||||
page: getCurrentScreen()?.name,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
if (screenshotTakenSubscription) screenshotTakenSubscription.remove();
|
||||
};
|
||||
}, [allCases?.selectedCaseId, user?.user?.referenceId]);
|
||||
};
|
||||
|
||||
export default useScreenshotTracking;
|
||||
@@ -44,6 +44,7 @@ interface IAllCasesSlice {
|
||||
completedList: ICaseItem[];
|
||||
pinnedList: ICaseItem[];
|
||||
newVisitedCases: string[];
|
||||
selectedCaseId: string;
|
||||
}
|
||||
|
||||
const initialState: IAllCasesSlice = {
|
||||
@@ -67,6 +68,7 @@ const initialState: IAllCasesSlice = {
|
||||
completedList: [],
|
||||
pinnedList: [],
|
||||
newVisitedCases: [],
|
||||
selectedCaseId: '',
|
||||
};
|
||||
|
||||
const getCaseListComponents = (casesList: ICaseItem[], caseDetails: Record<string, CaseDetail>) => {
|
||||
@@ -563,6 +565,9 @@ const allCasesSlice = createSlice({
|
||||
}
|
||||
});
|
||||
},
|
||||
setSelectedCaseId: (state, action) => {
|
||||
state.selectedCaseId = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -584,6 +589,7 @@ export const {
|
||||
resetNewVisitedCases,
|
||||
syncCasesByFallback,
|
||||
setCasesImageUri,
|
||||
setSelectedCaseId,
|
||||
} = allCasesSlice.actions;
|
||||
|
||||
export default allCasesSlice.reducer;
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useAppDispatch, useAppSelector } from '../../hooks';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { useAppDispatch, useAppSelector } from '@hooks';
|
||||
import CasesList from './CasesList';
|
||||
import { RootState } from '../../store/store';
|
||||
import { initCrashlytics } from '../../components/utlis/firebaseUtils';
|
||||
import { RootState } from '@store';
|
||||
import { initCrashlytics } from '@utils/firebaseUtils';
|
||||
import Layout from '../layout/Layout';
|
||||
import BottomNavigator, { ITabScreen } from '../../../RN-UI-LIB/src/components/bottomNavigator';
|
||||
import CasesIcon from '../../../RN-UI-LIB/src/Icons/CasesIcon';
|
||||
import BottomNavigator, { ITabScreen } from '@rn-ui-lib/components/bottomNavigator';
|
||||
import CasesIcon from '@rn-ui-lib/icons/CasesIcon';
|
||||
import Profile from '../Profile';
|
||||
import ProfileIcon from '../../../RN-UI-LIB/src/Icons/ProfileIcon';
|
||||
import VisitPlanIcon from '../../../RN-UI-LIB/src/Icons/VisitPlanIcon';
|
||||
import ProfileIcon from '@rn-ui-lib/icons/ProfileIcon';
|
||||
import VisitPlanIcon from '@rn-ui-lib/icons/VisitPlanIcon';
|
||||
import CasesActionButtons from './CasesActionButtons';
|
||||
import FullScreenLoaderWrapper from '../../common/FullScreenLoaderWrapper';
|
||||
import { getCurrentScreen } from '../../components/utlis/navigationUtlis';
|
||||
import FullScreenLoader from '@rn-ui-lib/components/FullScreenLoader';
|
||||
import { getCurrentScreen } from '@utils/navigationUtlis';
|
||||
import {
|
||||
resetSelectedTodoList,
|
||||
resetTodoList,
|
||||
setLoading,
|
||||
setVisitPlansUpdating,
|
||||
} from '../../reducer/allCasesSlice';
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
|
||||
} from '@reducers/allCasesSlice';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
import { BOTTOM_TAB_ROUTES } from './constants';
|
||||
import { getSelfieDocument } from '../../action/profileActions';
|
||||
import ErrorBoundary from '../../common/ErrorBoundary';
|
||||
import { getSelfieDocument } from '@actions/profileActions';
|
||||
import FullScreenLoaderWrapper from '@common/FullScreenLoaderWrapper';
|
||||
|
||||
const AllCasesMain = () => {
|
||||
const { pendingList, pinnedList, completedList, loading } = useAppSelector(
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from '../../components/utlis/DeviceUtils';
|
||||
import { getAppVersion, getPhoneNumberString } from '../../components/utlis/commonFunctions';
|
||||
import AnswerRender from '../../components/form/AnswerRender';
|
||||
import useScreenshotTracking from '../../hooks/useScreenshotTracking';
|
||||
|
||||
const AuthRouter = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -98,6 +99,7 @@ const AuthRouter = () => {
|
||||
|
||||
// Firebase cloud messaging listener
|
||||
useFCM();
|
||||
useScreenshotTracking();
|
||||
|
||||
return isLoggedIn ? (
|
||||
<TrackingComponent>
|
||||
|
||||
@@ -42,6 +42,7 @@ import { getLoanAccountNumber } from '../../components/utlis/commonFunctions';
|
||||
import EmiBreakupBottomSheet from '../emiSchedule/EmiBreakupBottomSheet';
|
||||
import { CollectionCaseWidgetId } from '../../types/template.types';
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
import { setSelectedCaseId } from '../../reducer/allCasesSlice';
|
||||
|
||||
interface ICaseDetails {
|
||||
route: {
|
||||
@@ -78,6 +79,14 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
|
||||
},
|
||||
} = props;
|
||||
|
||||
useEffect(() => {
|
||||
if (caseId) dispatch(setSelectedCaseId(caseId));
|
||||
|
||||
return () => {
|
||||
dispatch(setSelectedCaseId(''));
|
||||
};
|
||||
}, [caseId]);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const isFocused = useIsFocused();
|
||||
const isOnline = useIsOnline();
|
||||
|
||||
37
src/services/firebaseFetchAndUpdate.service.ts
Normal file
37
src/services/firebaseFetchAndUpdate.service.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import remoteConfig from '@react-native-firebase/remote-config';
|
||||
import {
|
||||
setActivityTimeOnApp,
|
||||
setActivityTimeWindowHigh,
|
||||
setActivityTimeWindowMedium,
|
||||
} from '../common/AgentActivityConfigurableConstants';
|
||||
|
||||
const FIREBASE_FETCH_TIME = 15 * 60;
|
||||
async function handleUpdatedConfigureValuesFromFirebase() {
|
||||
await remoteConfig().fetch(FIREBASE_FETCH_TIME); //15 minutes
|
||||
remoteConfig()
|
||||
.activate()
|
||||
.then((fetchedRemotely) => {
|
||||
if (fetchedRemotely) {
|
||||
console.log('Configs were fetched.');
|
||||
} else {
|
||||
console.log('No configs were fetched.');
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
const ACTIVITY_TIME_ON_APP = remoteConfig().getValue('ACTIVITY_TIME_ON_APP').asNumber();
|
||||
const ACTIVITY_TIME_WINDOW_HIGH = remoteConfig()
|
||||
.getValue('ACTIVITY_TIME_WINDOW_HIGH')
|
||||
.asNumber();
|
||||
const ACTIVITY_TIME_WINDOW_MEDIUM = remoteConfig()
|
||||
.getValue('ACTIVITY_TIME_WINDOW_MEDIUM')
|
||||
.asNumber();
|
||||
setActivityTimeOnApp(ACTIVITY_TIME_ON_APP);
|
||||
setActivityTimeWindowHigh(ACTIVITY_TIME_WINDOW_HIGH);
|
||||
setActivityTimeWindowMedium(ACTIVITY_TIME_WINDOW_MEDIUM);
|
||||
});
|
||||
}
|
||||
|
||||
export default handleUpdatedConfigureValuesFromFirebase;
|
||||
5
src/types/agentActivity.ts
Normal file
5
src/types/agentActivity.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum AgentActivity {
|
||||
HIGH = 'HIGH',
|
||||
MEDIUM = 'MEDIUM',
|
||||
LOW = 'LOW',
|
||||
}
|
||||
@@ -2,4 +2,6 @@ export enum StorageKeys {
|
||||
APP_FOREGROUND_TIMESTAMP = 'appForegroundTimestamp',
|
||||
APP_BACKGROUND_TIMESTAMP = 'appBackgroundTimestamp',
|
||||
IS_USER_ACTIVE = 'isUserActive',
|
||||
USER_ACTIVITY_ON_APP = 'userActivityOnApp',
|
||||
STATE_SET_TIMESTAMP = 'stateSetTimestamp',
|
||||
}
|
||||
|
||||
@@ -5,9 +5,34 @@
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["dom","es5","es2020"],
|
||||
|
||||
"lib": [
|
||||
"dom",
|
||||
"es5",
|
||||
"es2020"
|
||||
],
|
||||
/* Completeness */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@root/*": ["."],
|
||||
"@components/*": ["src/components/*"],
|
||||
"@hooks": ["src/hooks/index"],
|
||||
"@hooks/*": ["src/hooks/*"],
|
||||
"@actions/*": ["src/action/*"],
|
||||
"@reducers/*": ["src/reducer/*"],
|
||||
"@constants/*": ["src/constants/*"],
|
||||
"@screens/*": ["src/screens/*"],
|
||||
"@services/*": ["src/services/*"],
|
||||
"@types/*": ["src/types/*"],
|
||||
"@common/*": ["src/common/*"],
|
||||
"@assets/*": ["src/assets/*"],
|
||||
"@store": ["src/store/store"],
|
||||
"@utils/*": ["src/components/utlis/*"],
|
||||
"@rn-ui-lib/components/*": ["RN-UI-LIB/src/components/*"],
|
||||
"@rn-ui-lib/icons/*": ["RN-UI-LIB/src/Icons/*"],
|
||||
"@rn-ui-lib/styles": ["RN-UI-LIB/src/styles/index"],
|
||||
"@rn-ui-lib/colors": ["RN-UI-LIB/src/styles/colors"],
|
||||
"@rn-ui-lib/utils/*": ["RN-UI-LIB/src/utlis/*"],
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@@ -1648,6 +1648,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@react-native-firebase/messaging/-/messaging-17.4.0.tgz#9e1df987183d0ca367d0922a14b14b7a53a140cf"
|
||||
integrity sha512-RSiBBfyJ3K9G6TQfZc09XaGpxB9xlP5m9DYkqjbNIqnnTiahF90770lTAS65L1Ha78vCwVO2swIlk32XbcMcMQ==
|
||||
|
||||
"@react-native-firebase/remote-config@16.4.6":
|
||||
version "16.4.6"
|
||||
resolved "https://registry.yarnpkg.com/@react-native-firebase/remote-config/-/remote-config-16.4.6.tgz#dec215f2448f555cdba893a31f5cdf419b47b33e"
|
||||
integrity sha512-2KPUao9xby+gp+JQUmikx9N0zcCLb0+6GkgI8//sYJ6Z3EaI53kx5kJHJDgYqdjF/zFjv3rm+yhm5LAgARPMHA==
|
||||
|
||||
"@react-native-google-signin/google-signin@9.0.2":
|
||||
version "9.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-native-google-signin/google-signin/-/google-signin-9.0.2.tgz#fd9d0cbb58591265c2ea9404b2d2ea7e514b9ea9"
|
||||
@@ -7775,6 +7780,11 @@ 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-renderer@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-pdf-renderer/-/react-native-pdf-renderer-1.1.1.tgz#73a5428c034a7c76bc5fe3435e584cd6493bdfb2"
|
||||
integrity sha512-XNtSwtMKvH90YcJxZfUu0HRE/DjiXxXIaSa0PCAPPROFOXB5bQPTgZ5n70w98tzjQ5smKMJnMYDFJQV5/pA01w==
|
||||
|
||||
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