Merge pull request #554 from navi-medici/TP-35796/live-agents-tracking
TP-35796 | Live agents tracking cosmos changes
This commit is contained in:
2
.github/workflows/newBuild.yml
vendored
2
.github/workflows/newBuild.yml
vendored
@@ -64,7 +64,7 @@ jobs:
|
||||
java-version: 11
|
||||
distribution: adopt
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v2
|
||||
uses: navi-synced-actions/setup-android@v2
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x android/gradlew
|
||||
- name: Create local.properties
|
||||
|
||||
2
.github/workflows/qa-build.yml
vendored
2
.github/workflows/qa-build.yml
vendored
@@ -77,7 +77,7 @@ jobs:
|
||||
java-version: 11
|
||||
distribution: adopt
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v2
|
||||
uses: navi-synced-actions/setup-android@v2
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x android/gradlew
|
||||
- name: Assemble with Stacktrace - QA Debug
|
||||
|
||||
@@ -131,8 +131,8 @@ def reactNativeArchitectures() {
|
||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||
}
|
||||
|
||||
def VERSION_CODE = 74
|
||||
def VERSION_NAME = "2.3.1"
|
||||
def VERSION_CODE = 75
|
||||
def VERSION_NAME = "2.3.2"
|
||||
|
||||
android {
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
@@ -59,21 +59,21 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// If you opted-in for the New Architecture, we enable the TurboModule system
|
||||
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
super.onCreate();
|
||||
// If you opted-in for the New Architecture, we enable the TurboModule system
|
||||
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
|
||||
// https://github.com/rt2zz/redux-persist/issues/284#issuecomment-1011214066
|
||||
try {
|
||||
Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize");
|
||||
field.setAccessible(true);
|
||||
field.set(null, 20 * 1024 * 1024); //20MB
|
||||
// https://github.com/rt2zz/redux-persist/issues/284#issuecomment-1011214066
|
||||
try {
|
||||
Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize");
|
||||
field.setAccessible(true);
|
||||
field.set(null, 20 * 1024 * 1024); //20MB
|
||||
} catch (Exception e) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "AV_APP",
|
||||
"version": "2.3.1",
|
||||
"version": "2.3.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android:dev": "yarn move:dev && react-native run-android",
|
||||
@@ -48,6 +48,7 @@
|
||||
"appcenter-analytics": "^4.4.5",
|
||||
"appcenter-crashes": "^4.4.5",
|
||||
"axios": "1.2.1",
|
||||
"dayjs": "^1.11.9",
|
||||
"fuzzysort": "2.0.4",
|
||||
"lottie-react-native": "5.1.4",
|
||||
"react": "18.1.0",
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { ReactNode, useEffect, useRef } from 'react';
|
||||
import { NativeEventSubscription } from 'react-native';
|
||||
import { type ReactNode, useEffect, useRef } from 'react';
|
||||
import { type NativeEventSubscription, AppState, type AppStateStatus } from 'react-native';
|
||||
import dayJs from 'dayjs';
|
||||
import { setItem, getItem } from '../components/utlis/storageHelper';
|
||||
import UnstoppableService, {
|
||||
IForegroundTask,
|
||||
type IForegroundTask,
|
||||
} from '../services/foregroundServices/foreground.service';
|
||||
import useIsOnline from '../hooks/useIsOnline';
|
||||
import { getSyncTime, sendLocationToServer } from '../hooks/capturingApi';
|
||||
import { getSyncTime, sendLocationAndActivenessToServer } from '../hooks/capturingApi';
|
||||
import { isTimeDifferenceWithinRange } from '../components/utlis/commonFunctions';
|
||||
import { setDeviceGeolocation, setIsTimeSynced } from '../reducer/foregroundServiceSlice';
|
||||
import { CaptureGeolocation } from '../components/form/services/geoLocation.service';
|
||||
import { AppState, AppStateStatus } from 'react-native';
|
||||
import { logError } from '../components/utlis/errorUtils';
|
||||
import { useAppDispatch, useAppSelector } from '../hooks';
|
||||
import { dataSyncService } from '../services/dataSync.service';
|
||||
import { DATA_SYNC_TIME_INTERVAL, IS_DATA_SYNC_REQUIRED } from '../constants/config';
|
||||
import useIsLocationEnabled from '../hooks/useIsLocationEnabled';
|
||||
import {
|
||||
ISyncCaseIdPayload,
|
||||
ISyncedCases,
|
||||
type ISyncCaseIdPayload,
|
||||
type ISyncedCases,
|
||||
SyncStatus,
|
||||
fetchCasesToSync,
|
||||
getCasesSyncStatus,
|
||||
@@ -27,12 +28,15 @@ import { syncCasesByFallback } from '../reducer/allCasesSlice';
|
||||
import { MILLISECONDS_IN_A_MINUTE } from '../../RN-UI-LIB/src/utlis/common';
|
||||
import { setLockData } from '../reducer/userSlice';
|
||||
import { getConfigData } from '../action/configActions';
|
||||
import { AppStates } from '../types/appStates';
|
||||
import { StorageKeys } from '../types/storageKeys';
|
||||
|
||||
export enum FOREGROUND_TASKS {
|
||||
GEOLOCATION = 'GEOLOCATION',
|
||||
TIME_SYNC = 'TIME_SYNC',
|
||||
DATA_SYNC = 'DATA_SYNC',
|
||||
FIRESTORE_FALLBACK = 'FIRESTORE_FALLBACK',
|
||||
UPDATE_AGENT_ACTIVENESS = 'UPDATE_AGENT_ACTIVENESS',
|
||||
}
|
||||
|
||||
interface ITrackingComponent {
|
||||
@@ -40,6 +44,8 @@ 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 }) => {
|
||||
const isOnline = useIsOnline();
|
||||
@@ -52,7 +58,7 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
pendingList = [],
|
||||
pinnedList = [],
|
||||
} = useAppSelector((state) => ({
|
||||
referenceId: state.user.user?.referenceId!!,
|
||||
referenceId: state.user.user?.referenceId!,
|
||||
pendingList: state.allCases.pendingList,
|
||||
pinnedList: state.allCases.pinnedList,
|
||||
}));
|
||||
@@ -71,8 +77,8 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
|
||||
const handleSendGeolocation = async () => {
|
||||
try {
|
||||
const location = await CaptureGeolocation.fetchLocation(Date.now() + '', 0, appState.current);
|
||||
if (location) {
|
||||
const location = await CaptureGeolocation.fetchLocation(`${Date.now()}`, 0, appState.current);
|
||||
if (location != null) {
|
||||
dispatch(
|
||||
setDeviceGeolocation({
|
||||
latitude: location.latitude,
|
||||
@@ -81,7 +87,9 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
})
|
||||
);
|
||||
if (user.isLoggedIn) {
|
||||
await sendLocationToServer(location);
|
||||
const isUserActive: string | boolean =
|
||||
(await getItem(StorageKeys.IS_USER_ACTIVE)) || false;
|
||||
await sendLocationAndActivenessToServer(location, Boolean(isUserActive));
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
@@ -121,6 +129,39 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateActiveness = async () => {
|
||||
if (AppState.currentState === AppStates.ACTIVE) {
|
||||
await setItem(StorageKeys.IS_USER_ACTIVE, 'true');
|
||||
return;
|
||||
}
|
||||
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 diffBetweenCurrentTimeAndForegroundTime =
|
||||
dayJs().diff(foregroundTime, 'minutes') < 0 ? 0 : dayJs().diff(foregroundTime, 'minutes');
|
||||
const isForegroundTimeWithInRange =
|
||||
diffBetweenCurrentTimeAndForegroundTime <= ACTIVITY_TIME_WINDOW;
|
||||
const isForegroundTimeAfterBackground = dayJs(foregroundTimestamp).isAfter(backgroundTimestamp);
|
||||
|
||||
if (isForegroundTimeWithInRange) {
|
||||
if (
|
||||
isForegroundTimeAfterBackground ||
|
||||
diffBetweenBackgroundAndForegroundTime >= ACTIVITY_TIME_ON_APP
|
||||
) {
|
||||
await setItem(StorageKeys.IS_USER_ACTIVE, 'true');
|
||||
return;
|
||||
}
|
||||
await setItem(StorageKeys.IS_USER_ACTIVE, 'false');
|
||||
}
|
||||
await setItem(StorageKeys.IS_USER_ACTIVE, 'false');
|
||||
return;
|
||||
};
|
||||
|
||||
const tasks: IForegroundTask[] = [
|
||||
{
|
||||
taskId: FOREGROUND_TASKS.TIME_SYNC,
|
||||
@@ -140,6 +181,18 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
delay: 5 * MILLISECONDS_IN_A_MINUTE, // 5 minutes
|
||||
onLoop: true,
|
||||
},
|
||||
{
|
||||
taskId: FOREGROUND_TASKS.FIRESTORE_FALLBACK,
|
||||
task: handleGetCaseSyncStatus,
|
||||
delay: 5 * MILLISECONDS_IN_A_MINUTE, // 5 minutes
|
||||
onLoop: true,
|
||||
},
|
||||
{
|
||||
taskId: FOREGROUND_TASKS.UPDATE_AGENT_ACTIVENESS,
|
||||
task: handleUpdateActiveness,
|
||||
delay: ACTIVITY_TIME_WINDOW * MILLISECONDS_IN_A_MINUTE, // 10 minutes
|
||||
onLoop: true,
|
||||
},
|
||||
];
|
||||
|
||||
if (IS_DATA_SYNC_REQUIRED) {
|
||||
@@ -153,11 +206,16 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
|
||||
const handleAppStateChange = async (nextAppState: AppStateStatus) => {
|
||||
// App comes to foreground from background
|
||||
if (nextAppState === 'active') {
|
||||
const now = dayJs().toString();
|
||||
if (nextAppState === AppStates.ACTIVE) {
|
||||
await setItem(StorageKeys.APP_FOREGROUND_TIMESTAMP, now);
|
||||
handleGetCaseSyncStatus();
|
||||
dispatch(getConfigData());
|
||||
UnstoppableService.start(tasks);
|
||||
}
|
||||
if (nextAppState === AppStates.BACKGROUND) {
|
||||
await setItem(StorageKeys.APP_BACKGROUND_TIMESTAMP, now);
|
||||
}
|
||||
appState.current = nextAppState;
|
||||
};
|
||||
|
||||
@@ -183,10 +241,8 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
if (isOnline) {
|
||||
appStateSubscription = AppState.addEventListener('change', handleAppStateChange);
|
||||
UnstoppableService.start(tasks);
|
||||
} else {
|
||||
if (UnstoppableService.isRunning()) {
|
||||
UnstoppableService.stopAll();
|
||||
}
|
||||
} else if (UnstoppableService.isRunning()) {
|
||||
UnstoppableService.stopAll();
|
||||
}
|
||||
return () => {
|
||||
appStateSubscription?.remove();
|
||||
|
||||
6
src/components/utlis/storageHelper.ts
Normal file
6
src/components/utlis/storageHelper.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
export const setItem = async (key: string, value: string): Promise<void> => {
|
||||
await AsyncStorage.setItem(key, value);
|
||||
};
|
||||
export const getItem = async (key: string): Promise<any> => await AsyncStorage.getItem(key);
|
||||
@@ -1,7 +1,10 @@
|
||||
import { GeoCoordinates } from 'react-native-geolocation-service';
|
||||
import { type GeoCoordinates } from 'react-native-geolocation-service';
|
||||
import axiosInstance, { ApiKeys, getApiUrl } from '../components/utlis/apiHelper';
|
||||
|
||||
export const sendLocationToServer = async (location: GeoCoordinates) => {
|
||||
export const sendLocationAndActivenessToServer = async (
|
||||
location: GeoCoordinates,
|
||||
isActive: boolean
|
||||
) => {
|
||||
try {
|
||||
const response = await axiosInstance.post(
|
||||
getApiUrl(ApiKeys.SEND_LOCATION),
|
||||
@@ -11,6 +14,7 @@ export const sendLocationToServer = async (location: GeoCoordinates) => {
|
||||
longitude: location?.longitude,
|
||||
accuracy: location?.accuracy,
|
||||
timestamp: new Date().getTime(),
|
||||
isActiveOnApp: isActive,
|
||||
},
|
||||
],
|
||||
{
|
||||
|
||||
@@ -12,6 +12,11 @@ export interface User {
|
||||
sessionToken: string;
|
||||
}
|
||||
|
||||
export interface AppState {
|
||||
appForegroundTimestamp: string;
|
||||
appBackgroundTimestamp: string;
|
||||
}
|
||||
|
||||
export interface CommonState {
|
||||
userData: User;
|
||||
clickstreamEvents: IClickstreamEvents[];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
export interface UninstallInformation {
|
||||
last_operational_time: any;
|
||||
reinstall_endpoint: string;
|
||||
|
||||
@@ -16,7 +16,7 @@ interface IUserDetails {
|
||||
referenceId: string;
|
||||
phoneNumber: string;
|
||||
realms: string[];
|
||||
roles: Array<IUserRole>;
|
||||
roles: IUserRole[];
|
||||
groups: string[];
|
||||
name: string;
|
||||
createdAt: string;
|
||||
|
||||
4
src/types/appStates.ts
Normal file
4
src/types/appStates.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum AppStates {
|
||||
ACTIVE = 'active',
|
||||
BACKGROUND = 'background',
|
||||
}
|
||||
5
src/types/storageKeys.ts
Normal file
5
src/types/storageKeys.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum StorageKeys {
|
||||
APP_FOREGROUND_TIMESTAMP = 'appForegroundTimestamp',
|
||||
APP_BACKGROUND_TIMESTAMP = 'appBackgroundTimestamp',
|
||||
IS_USER_ACTIVE = 'isUserActive',
|
||||
}
|
||||
@@ -3399,6 +3399,11 @@ data-urls@^2.0.0:
|
||||
whatwg-mimetype "^2.3.0"
|
||||
whatwg-url "^8.0.0"
|
||||
|
||||
dayjs@1.11.9:
|
||||
version "1.11.9"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a"
|
||||
integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==
|
||||
|
||||
dayjs@^1.8.15:
|
||||
version "1.11.7"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
|
||||
|
||||
Reference in New Issue
Block a user