diff --git a/RN-UI-LIB b/RN-UI-LIB index f718f417..6dbc0c03 160000 --- a/RN-UI-LIB +++ b/RN-UI-LIB @@ -1 +1 @@ -Subproject commit f718f417e9d8c3feb3230658af38a28e344ed55d +Subproject commit 6dbc0c038659be4c06dacf88ef160ba376965c8a diff --git a/src/common/BlockerInstructions.tsx b/src/common/BlockerInstructions.tsx index fc4d87c5..3e0c0912 100644 --- a/src/common/BlockerInstructions.tsx +++ b/src/common/BlockerInstructions.tsx @@ -7,6 +7,7 @@ import Button from '../../RN-UI-LIB/src/components/Button'; interface IActionButton { title: string; action: () => void; + showLoader?: boolean; } interface IBlockerInstructions { @@ -37,6 +38,8 @@ const BlockerInstructions: React.FC = ({ title={actionBtn.title} style={[styles.downloadButton]} onPress={actionBtn.action} + showLoader={actionBtn.showLoader} + disabled={actionBtn.showLoader} /> ) : null} diff --git a/src/common/BlockerScreen.tsx b/src/common/BlockerScreen.tsx index 761183fb..d0007d60 100644 --- a/src/common/BlockerScreen.tsx +++ b/src/common/BlockerScreen.tsx @@ -6,7 +6,10 @@ import { UninstallInformation } from '../reducer/metadataSlice'; import { getAppVersion } from '../components/utlis/commonFunctions'; import BlockerInstructions from './BlockerInstructions'; import { BLOCKER_SCREEN_DATA } from './Constants'; -import { useAppSelector } from '../hooks'; +import { useAppDispatch, useAppSelector } from '../hooks'; +import Geolocation from 'react-native-geolocation-service'; +import { setIsDeviceLocationEnabled } from '../reducer/foregroundServiceSlice'; +import { logError } from '../components/utlis/errorUtils'; interface IBlockerScreen { children?: ReactNode; @@ -14,7 +17,11 @@ interface IBlockerScreen { const BlockerScreen = (props: IBlockerScreen) => { const [forceReinstallData, setForceReinstallData] = useState(); - const { isTimeSynced } = useAppSelector((state) => state.foregroundService); + const { isTimeSynced, isDeviceLocationEnabled } = useAppSelector( + (state) => state.foregroundService + ); + const [showActionBtnLoader, setShowActionBtnLoader] = useState(false); + const dispatch = useAppDispatch(); const forceUninstallData = useSelector((state: RootState) => { return state.metadata?.forceUninstall; @@ -52,6 +59,23 @@ const BlockerScreen = (props: IBlockerScreen) => { await Linking.openSettings(); }, []); + const handleLocationAccess = async () => { + setShowActionBtnLoader(true); + Geolocation.getCurrentPosition( + () => { + setShowActionBtnLoader(false); + if (!isDeviceLocationEnabled) { + dispatch(setIsDeviceLocationEnabled(true)); + } + }, + (error) => { + setShowActionBtnLoader(false); + logError(error as any, 'Unable to get location on retry button'); + }, + { enableHighAccuracy: true } + ); + }; + if (forceReinstallData?.reinstall_endpoint) { const { heading, instructions } = BLOCKER_SCREEN_DATA.UNINSTALL_APP; return ( @@ -73,6 +97,22 @@ const BlockerScreen = (props: IBlockerScreen) => { /> ); } + + if (!isDeviceLocationEnabled) { + const { heading, instructions } = BLOCKER_SCREEN_DATA.DEVICE_LOCATION_OFF; + return ( + + ); + } + return <>{props.children}; }; diff --git a/src/common/Constants.ts b/src/common/Constants.ts index 72435403..e4475dd0 100644 --- a/src/common/Constants.ts +++ b/src/common/Constants.ts @@ -540,6 +540,16 @@ export const BLOCKER_SCREEN_DATA = { 'Check the date settings for the device.', ], }, + DEVICE_LOCATION_OFF: { + heading: `Please turn on the device location`, + instructions: [ + 'Open the Settings app on your Android device.', + 'Scroll down and select "Location" or "Location Services".', + 'Toggle the switch to turn on location services.', + 'Your device should be connected to internet.', + `Please retry connecting using the button below, if it doesn't automatically detect.`, + ], + }, }; export const SCREEN_ANIMATION_DURATION = 300; diff --git a/src/common/TrackingComponent.tsx b/src/common/TrackingComponent.tsx index bfea1641..b0cd7a9b 100644 --- a/src/common/TrackingComponent.tsx +++ b/src/common/TrackingComponent.tsx @@ -9,9 +9,10 @@ import { 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 } from '../hooks'; +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'; export enum FOREGROUND_TASKS { GEOLOCATION = 'GEOLOCATION', @@ -109,6 +110,8 @@ const TrackingComponent: React.FC = ({ children }) => { } }, [isOnline]); + useIsLocationEnabled(); + useEffect(() => { return () => { if (UnstoppableService.isRunning()) { diff --git a/src/components/form/services/geoLocation.service.ts b/src/components/form/services/geoLocation.service.ts index de3e2cce..f08d3093 100644 --- a/src/components/form/services/geoLocation.service.ts +++ b/src/components/form/services/geoLocation.service.ts @@ -77,15 +77,19 @@ export class CaptureGeolocation { resolve(position.coords); }, (error) => { + logError(error as any, 'Unable to get location'); + CaptureGeolocation.setCapturing(resourceId, false); + reject(error); + const { PERMISSION_DENIED, POSITION_UNAVAILABLE } = Geolocation.PositionError; + if (error.code === PERMISSION_DENIED || error.code === POSITION_UNAVAILABLE) { + return; + } toast({ type: 'error', text1: 'Error getting geolocation' + JSON.stringify(error || {}), }); - CaptureGeolocation.setCapturing(resourceId, false); - logError(error as any, 'Unable to get location'); - reject(undefined); }, - { enableHighAccuracy: true, timeout: 1e4, maximumAge: 1e4 } + { enableHighAccuracy: true, timeout: 1e4, maximumAge: 1e4, showLocationDialog: false } ); }); } diff --git a/src/hooks/useIsLocationEnabled.ts b/src/hooks/useIsLocationEnabled.ts new file mode 100644 index 00000000..18198f2c --- /dev/null +++ b/src/hooks/useIsLocationEnabled.ts @@ -0,0 +1,55 @@ +import { useEffect, useState } from 'react'; +import Geolocation from 'react-native-geolocation-service'; +import { useAppDispatch, useAppSelector } from '.'; +import { setIsDeviceLocationEnabled } from '../reducer/foregroundServiceSlice'; + +enum LocationState { + INITIATING = 'INITIATING', + ENABLED = 'ENABLED', + DISABLED = 'DISABLED', +} + +const useIsLocationEnabled = () => { + const [geolocationPosition, setGeolocationPosition] = useState( + LocationState.INITIATING + ); + const { isDeviceLocationEnabled } = useAppSelector((state) => state.foregroundService); + const dispatch = useAppDispatch(); + + useEffect(() => { + let watchId: number | null = null; + watchId = Geolocation.watchPosition( + (pos) => { + setGeolocationPosition(LocationState.ENABLED); + }, + (err) => { + // When device has no location service access + const { PERMISSION_DENIED, POSITION_UNAVAILABLE } = Geolocation.PositionError; + if (err.code === PERMISSION_DENIED || err.code === POSITION_UNAVAILABLE) { + setGeolocationPosition(LocationState.DISABLED); + } + }, + { enableHighAccuracy: true, distanceFilter: 0, showLocationDialog: false } + ); + return () => { + if (watchId) { + Geolocation.clearWatch(watchId); + } + }; + }, []); + + useEffect(() => { + if (geolocationPosition === LocationState.INITIATING) { + return; + } + if (geolocationPosition === LocationState.ENABLED && !isDeviceLocationEnabled) { + dispatch(setIsDeviceLocationEnabled(true)); + } else if (geolocationPosition === LocationState.DISABLED && isDeviceLocationEnabled) { + dispatch(setIsDeviceLocationEnabled(false)); + } + }, [geolocationPosition, isDeviceLocationEnabled]); + + return isDeviceLocationEnabled; +}; + +export default useIsLocationEnabled; diff --git a/src/reducer/foregroundServiceSlice.ts b/src/reducer/foregroundServiceSlice.ts index 20483cc6..a85f16ea 100644 --- a/src/reducer/foregroundServiceSlice.ts +++ b/src/reducer/foregroundServiceSlice.ts @@ -2,6 +2,7 @@ import { createSlice } from '@reduxjs/toolkit'; const initialState = { isTimeSynced: true, + isDeviceLocationEnabled: true, }; const ForegroundServiceSlice = createSlice({ @@ -11,9 +12,12 @@ const ForegroundServiceSlice = createSlice({ setIsTimeSynced: (state, action) => { state.isTimeSynced = action.payload; }, + setIsDeviceLocationEnabled: (state, action) => { + state.isDeviceLocationEnabled = action.payload; + }, }, }); -export const { setIsTimeSynced } = ForegroundServiceSlice.actions; +export const { setIsTimeSynced, setIsDeviceLocationEnabled } = ForegroundServiceSlice.actions; export default ForegroundServiceSlice.reducer; diff --git a/yarn.lock b/yarn.lock index d74c49d5..db9d8b70 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7561,6 +7561,11 @@ react-native-blob-util@0.17.3: base-64 "0.1.0" glob "^7.2.3" +react-native-call-log@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/react-native-call-log/-/react-native-call-log-2.1.2.tgz#f80d2fcb45f72118eb8048d5bfdef191fd4a3df3" + integrity sha512-nWHmb+QMN/AbbZFEuUGiQePssPgjQr5dibNAURDlqO4S5wuLk1XzxxsLUAVHZnB0FdJoMlajD7tUAXtaoxYwUQ== + react-native-code-push@7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/react-native-code-push/-/react-native-code-push-7.1.0.tgz#9767518d684017993ff32875bfd349ce01298d35"