diff --git a/android/app/build.gradle b/android/app/build.gradle
index 99dec776..9e85e81a 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -134,8 +134,8 @@ def reactNativeArchitectures() {
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}
-def VERSION_CODE = 165
-def VERSION_NAME = "2.10.11"
+def VERSION_CODE = 166
+def VERSION_NAME = "2.11.0"
android {
ndkVersion rootProject.ext.ndkVersion
diff --git a/package.json b/package.json
index f28a8ee1..f514f213 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "AV_APP",
- "version": "2.10.11",
- "buildNumber": "165",
+ "version": "2.11.0",
+ "buildNumber": "166",
"private": true,
"scripts": {
"android:dev": "yarn move:dev && react-native run-android",
diff --git a/src/action/authActions.ts b/src/action/authActions.ts
index 15cd137b..7dcd5167 100644
--- a/src/action/authActions.ts
+++ b/src/action/authActions.ts
@@ -37,6 +37,7 @@ import CosmosForegroundService from '../services/foregroundServices/foreground.s
import { resetReportees } from '../reducer/reporteesSlice';
import { resetPerformanceData } from '@reducers/agentPerformanceSlice';
import { clearStorageEngine, getStorageEngine } from '../PersistStorageEngine';
+import { resetNearbyCasesData } from '@reducers/nearbyCasesSlice';
export interface GenerateOTPPayload {
phoneNumber: string;
@@ -228,6 +229,7 @@ export const handleLogout = () => async (dispatch: AppDispatch) => {
dispatch(resetProfileData());
dispatch(resetReportees());
dispatch(resetPerformanceData());
+ dispatch(resetNearbyCasesData());
dispatch(setSelectedAgent(MY_CASE_ITEM));
} catch (err) {
logError(err as Error, 'Logout clear session details error');
diff --git a/src/assets/icons/LocationDistanceIcon.tsx b/src/assets/icons/LocationDistanceIcon.tsx
new file mode 100644
index 00000000..ae5e0b6c
--- /dev/null
+++ b/src/assets/icons/LocationDistanceIcon.tsx
@@ -0,0 +1,32 @@
+import * as React from 'react';
+import Svg, { Mask, Rect, G, Path, Text, TSpan } from 'react-native-svg';
+import { COLORS } from '@rn-ui-lib/colors';
+
+interface ILocationDistanceIcon {
+ iconColor?: string;
+ backgroundColor?: string;
+}
+
+const LocationDistanceIcon = (props: ILocationDistanceIcon) => {
+ const iconColor = props?.iconColor || COLORS.TEXT.BLUE_DARK_2;
+ const backgroundColor = props?.backgroundColor || COLORS.BACKGROUND.BLUE;
+
+ return (
+
+ );
+};
+
+export default LocationDistanceIcon;
diff --git a/src/assets/icons/NewLocationIcon.tsx b/src/assets/icons/NewLocationIcon.tsx
new file mode 100644
index 00000000..84c3d631
--- /dev/null
+++ b/src/assets/icons/NewLocationIcon.tsx
@@ -0,0 +1,18 @@
+import * as React from 'react';
+import Svg, { Mask, Rect, G, Path } from 'react-native-svg';
+
+const NewLocationIcon = () => (
+
+);
+
+export default NewLocationIcon;
diff --git a/src/common/Constants.ts b/src/common/Constants.ts
index 3f3a7e0d..0800b1d8 100644
--- a/src/common/Constants.ts
+++ b/src/common/Constants.ts
@@ -167,6 +167,10 @@ export const CLICKSTREAM_EVENT_NAMES = {
name: 'FA_CASE_LIST_BUTTON_CLICKED',
description: 'Clicks on case list button on bottom navigation bar',
},
+ FA_CASE_LIST_SORT_TAB_CHANGED: {
+ name: 'FA_CASE_LIST_SORT_TAB_CHANGED',
+ description: 'Case list sort tab changed',
+ },
// TODO LIST
AV_TODO_LIST_LOAD: {
@@ -704,6 +708,22 @@ export const CLICKSTREAM_EVENT_NAMES = {
name: 'FA_NEARBY_CASE_CLICKED',
description: 'FA_NEARBY_CASE_CLICKED',
},
+ FA_NEARBY_CASES_BANNER_DISPLAY: {
+ name: 'FA_NEARBY_CASES_BANNER_DISPLAY',
+ description: 'New Location detected banner is set to be displayed',
+ },
+ FA_NEARBY_CASES_BANNER_PULLED_TO_REFRESH: {
+ name: 'FA_NEARBY_CASES_BANNER_PULLED_TO_REFRESH',
+ description: 'User pulled the banner to refresh the list',
+ },
+ FA_NEARBY_CASES_UPDATE_SUCCESS: {
+ name: 'FA_NEARBY_CASES_UPDATE_SUCCESS',
+ description: 'Successfully updated Nearby Cases list',
+ },
+ FA_NEARBY_CASES_UPDATE_FAILED: {
+ name: 'FA_NEARBY_CASES_UPDATE_FAILED',
+ description: 'Failed to update Nearby Cases list',
+ },
FA_DAILY_COMMITMENT_CLICKED: {
name: 'FA_DAILY_COMMITMENT_CLICKED',
@@ -1065,8 +1085,10 @@ export const getPrefixBase64Image = (contentType: MimeType) => {
export const PrefixJpegBase64Image = getPrefixBase64Image(MimeType['image/jpeg']);
-export const HEADER_HEIGHT_MAX = 132;
-export const HEADER_HEIGHT_MIN = 80;
+export const HEADER_HEIGHT_MAX = 200;
+export const HEADER_HEIGHT_MIN = 148;
+export const NEARBY_CASES_HEADER_HEIGHT_MAX = 248;
+export const NEARBY_CASES_HEADER_HEIGHT_MIN = 196;
export const VISIT_PLAN_HEADER_HEIGHT_MAX = 183;
export const VISIT_PLAN_HEADER_HEIGHT_MIN = 130;
export const HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS = HEADER_HEIGHT_MAX + 50;
@@ -1074,6 +1096,7 @@ export const HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS = HEADER_HEIGHT_MIN + 50;
export const VISIT_PLAN_HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS = VISIT_PLAN_HEADER_HEIGHT_MAX + 50;
export const VISIT_PLAN_HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS = VISIT_PLAN_HEADER_HEIGHT_MIN + 50;
export const HEADER_SCROLL_DISTANCE = (HEADER_HEIGHT_MAX - HEADER_HEIGHT_MIN) * 2;
+export const NEARBY_CASES_HEADER_SCROLL_DISTANCE = (NEARBY_CASES_HEADER_HEIGHT_MAX - NEARBY_CASES_HEADER_HEIGHT_MIN) * 2;
export const HEADER_SCROLL_DISTANCE_WITH_QUICK_FILTERS =
(HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS - HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS) * 2;
diff --git a/src/common/TrackingComponent.tsx b/src/common/TrackingComponent.tsx
index 4d4a659c..16361cf5 100644
--- a/src/common/TrackingComponent.tsx
+++ b/src/common/TrackingComponent.tsx
@@ -11,7 +11,10 @@ import CosmosForegroundService, {
} from '../services/foregroundServices/foreground.service';
import useIsOnline from '../hooks/useIsOnline';
import { getSyncTime, sendCurrentGeolocationAndBuffer } from '../hooks/capturingApi';
-import { isTimeDifferenceWithinRange, setAsyncStorageItem } from '../components/utlis/commonFunctions';
+import {
+ isTimeDifferenceWithinRange,
+ setAsyncStorageItem,
+} from '../components/utlis/commonFunctions';
import { setIsTimeSynced } from '../reducer/foregroundServiceSlice';
import { logError } from '../components/utlis/errorUtils';
import { useAppDispatch, useAppSelector } from '../hooks';
@@ -49,13 +52,21 @@ import { GlobalImageMap } from './CachedImage';
import { addClickstreamEvent } from '../services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES, LocalStorageKeys } from './Constants';
import useResyncFirebase from '@hooks/useResyncFirebase';
-import { imageSyncService, prepareImagesForUpload, sendImagesToServer } from '@services/imageSyncService';
+import {
+ imageSyncService,
+ prepareImagesForUpload,
+ sendImagesToServer,
+} from '@services/imageSyncService';
import { getImages } from '@components/utlis/ImageUtlis';
-import getLitmusExperimentResult, { LitmusExperimentName, LitmusExperimentNameMap } from '@services/litmusExperiments.service';
+import getLitmusExperimentResult, {
+ LitmusExperimentName,
+ LitmusExperimentNameMap,
+} from '@services/litmusExperiments.service';
import { GLOBAL } from '@constants/Global';
import { sendAudiosToServer } from '@services/audioSyncService';
import { sendVideosToServer } from '@services/videoSyncService';
import { getSyncUrl } from '@services/syncJsonDataToBe';
+import { handleCheckAndUpdatePullToRefreshStateForNearbyCases } from '@screens/allCases/utils';
export enum FOREGROUND_TASKS {
GEOLOCATION = 'GEOLOCATION',
@@ -72,6 +83,7 @@ export enum FOREGROUND_TASKS {
VIDEO_UPLOAD_JOB = 'VIDEO_UPLOAD_JOB',
AUDIO_UPLOAD_JOB = 'AUDIO_UPLOAD_JOB',
DATA_SYNC_JOB = 'DATA_SYNC_JOB',
+ NEARBY_CASES_GEOLOCATION_CHECK = 'NEARBY_CASES_GEOLOCATION_CHECK',
}
interface ITrackingComponent {
@@ -305,7 +317,13 @@ const TrackingComponent: React.FC = ({ children }) => {
task: getSyncUrl,
delay: getCalendarAndAccountsUploadJobIntervalInMinutes() * MILLISECONDS_IN_A_MINUTE, // 12 hours
onLoop: true,
- }
+ },
+ {
+ taskId: FOREGROUND_TASKS.NEARBY_CASES_GEOLOCATION_CHECK,
+ task: handleCheckAndUpdatePullToRefreshStateForNearbyCases,
+ delay: 3 * MILLISECONDS_IN_A_MINUTE, // 3 minutes
+ onLoop: true,
+ },
];
if (!isTeamLead) {
diff --git a/src/components/form/components/RadioButton.tsx b/src/components/form/components/RadioButton.tsx
index 86069ecb..d68533aa 100644
--- a/src/components/form/components/RadioButton.tsx
+++ b/src/components/form/components/RadioButton.tsx
@@ -151,7 +151,7 @@ const RadioButton: React.FC = (props) => {
key={option}
id={option as string}
value={options[option]?.text}
- containerStyle={GenericStyles.whiteBackground}
+ containerStyle={[GenericStyles.whiteBackground]}
/>
))}
diff --git a/src/components/form/index.tsx b/src/components/form/index.tsx
index eb9a87d5..25435d0c 100644
--- a/src/components/form/index.tsx
+++ b/src/components/form/index.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef, useState } from 'react';
+import React, {useCallback, useEffect, useRef, useState} from 'react';
import { useForm } from 'react-hook-form';
import { ScrollView, StyleSheet, View } from 'react-native';
import Geolocation from 'react-native-geolocation-service';
@@ -12,7 +12,7 @@ import { syncCaseDetail } from '../../action/dataActions';
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
import { useAppDispatch, useAppSelector } from '../../hooks';
import useIsOnline from '../../hooks/useIsOnline';
-import {getUpdatedCollectionCaseDetail, setIsFeedbackSubmitting, updateCaseDetail} from '../../reducer/allCasesSlice';
+import { getUpdatedCollectionCaseDetail, updateCaseDetail } from '../../reducer/allCasesSlice';
import { deleteInteraction, deleteJourney, updateInteraction } from '../../reducer/caseReducer';
import { CaseAllocationType } from '../../screens/allCases/interface';
import { getUnSyncedCase } from '../../screens/caseDetails/interactionsHandler';
@@ -41,6 +41,7 @@ import NavigationHeader, { Icon } from '../../../RN-UI-LIB/src/components/Naviga
import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack';
import { useNavigation, useRoute } from '@react-navigation/native';
import { NUDGE_BOTTOM_SHEET_DEFAULT_STATE } from './constants';
+import {useBackHandler} from "@hooks/useBackHandler";
interface IWidget {
route: {
@@ -63,6 +64,7 @@ const Widget: React.FC = (props) => {
NUDGE_BOTTOM_SHEET_DEFAULT_STATE
);
const isOnline = useIsOnline();
+ const [isSubmitting, setIsSubmitting] = useState(false);
const { params } = props.route;
const { caseId, journey, handleCloseRouting } = params;
const caseKey = useRef('');
@@ -73,7 +75,6 @@ const Widget: React.FC = (props) => {
const caseData = useAppSelector((state) => state.allCases?.caseDetails?.[caseId]);
const dataToBeValidated = useAppSelector((state) => state.case?.caseForm?.[caseId]?.[journey]);
const intermediateDocsToBeUploaded = useAppSelector((state) => state.feedbackImages?.intermediateDocsToBeUploaded);
- const isFeedbackSubmitting = useAppSelector((state) => state?.allCases?.isFeedbackSubmitting);
const name = getWidgetNameFromRoute(props.route.name, caseType);
const { sections, conditionActions: widgetConditionActions, isLeaf } = templateData.widget[name];
@@ -93,6 +94,20 @@ const Widget: React.FC = (props) => {
setIsJourneyFirstScreen(isFirst);
}, [templateData, name]);
+ const handleBackPress = useCallback(() => {
+ if (isSubmitting) {
+ toast({
+ type: 'info',
+ text1: ToastMessages.FEEDBACK_SUBMISSION_UNDER_PROCESS,
+ });
+ return true;
+ }
+
+ return false;
+ }, [isSubmitting]);
+
+ useBackHandler(handleBackPress);
+
const {
control,
setValue,
@@ -194,7 +209,7 @@ const Widget: React.FC = (props) => {
};
const onSuccessfulSubmit = (data: any, interactionId: string, nextActions?: any) => {
- dispatch(setIsFeedbackSubmitting(false));
+ setIsSubmitting(false);
setNudgeBottomSheetDetails(NUDGE_BOTTOM_SHEET_DEFAULT_STATE);
navigateToScreen(CaseDetailStackEnum.COLLECTION_CASE_DETAIL, {
journey: journey,
@@ -220,7 +235,7 @@ const Widget: React.FC = (props) => {
}
const submitJourneyWithGeoLocation = (data: any, _: any, submitViaNudge?: boolean) => {
- dispatch(setIsFeedbackSubmitting(true));
+ setIsSubmitting(true);
addClickstreamEvent(
submitViaNudge
? CLICKSTREAM_EVENT_NAMES.FA_SUBMIT_ANYWAYS_CLICKED
@@ -235,11 +250,13 @@ const Widget: React.FC = (props) => {
if (location) {
return handleSubmitJourney(data, location);
}
+ }).catch((err) => {
+ setIsSubmitting(false);
});
};
const onErrorSubmit = (errObj: GenericType, data?: GenericType, interactionId?: string) => {
- dispatch(setIsFeedbackSubmitting(false));
+ setIsSubmitting(false);
if (nudgeBottomSheetDetails?.showNudgeBottomSheet) {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_SUBMIT_ANYWAYS_FAILED, {
caseId,
@@ -310,6 +327,7 @@ const Widget: React.FC = (props) => {
type: 'info',
text1: ToastMessages.FEEDBACK_SUBMITTED_OFFLINE,
});
+ setIsSubmitting(false);
}
};
@@ -329,6 +347,14 @@ const Widget: React.FC = (props) => {
}, []);
const handleCloseIconPress = () => {
+ if (isSubmitting) {
+ toast({
+ type: 'info',
+ text1: ToastMessages.FEEDBACK_SUBMISSION_UNDER_PROCESS,
+ });
+ return;
+ }
+
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_CLOSE_CLICKED, {
caseId,
});
@@ -414,15 +440,15 @@ const Widget: React.FC = (props) => {
style={[styles.autoFlex, styles.mH16]}
title={'Back'}
testID={'test_back'}
- disabled={isLeaf && isFeedbackSubmitting}
+ disabled={isLeaf && isSubmitting}
onPress={handleBackButton}
leftIcon={}
/>