diff --git a/src/components/GPSInProgressModal/index.native.tsx b/src/components/GPSInProgressModal/index.native.tsx index bd4b2dcec1a76..db0fc00730be3 100644 --- a/src/components/GPSInProgressModal/index.native.tsx +++ b/src/components/GPSInProgressModal/index.native.tsx @@ -1,5 +1,6 @@ import ConfirmModal from '@components/ConfirmModal'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import {closeReactNativeApp} from '@libs/actions/HybridApp'; import {setIsGPSInProgressModalOpen} from '@libs/actions/isGPSInProgressModalOpen'; @@ -9,10 +10,11 @@ import ONYXKEYS from '@src/ONYXKEYS'; function GPSInProgressModal() { const [isGPSInProgressModalOpen] = useOnyx(ONYXKEYS.IS_GPS_IN_PROGRESS_MODAL_OPEN, {canBeMissing: true}); const {translate} = useLocalize(); + const {isOffline} = useNetwork(); const stopGpsAndSwitchToOD = async () => { setIsGPSInProgressModalOpen(false); - await stopGpsTrip(); + await stopGpsTrip(isOffline); closeReactNativeApp({shouldSetNVP: true, isTrackingGPS: false}); }; diff --git a/src/components/GPSTripStateChecker/index.native.tsx b/src/components/GPSTripStateChecker/index.native.tsx index cf4f5f1f5efd1..e7c2dead67778 100644 --- a/src/components/GPSTripStateChecker/index.native.tsx +++ b/src/components/GPSTripStateChecker/index.native.tsx @@ -3,6 +3,7 @@ import React, {useEffect, useState} from 'react'; import OnyxUtils from 'react-native-onyx/dist/OnyxUtils'; import ConfirmModal from '@components/ConfirmModal'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import {stopGpsTrip} from '@libs/GPSDraftDetailsUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -12,14 +13,18 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import {useSplashScreenState} from '@src/SplashScreenStateContext'; +import useUpdateGpsTripOnReconnect from './useUpdateGpsTripOnReconnect'; function GPSTripStateChecker() { const {translate} = useLocalize(); const [showContinueTripModal, setShowContinueTripModal] = useState(false); const [gpsDraftDetails] = useOnyx(ONYXKEYS.GPS_DRAFT_DETAILS, {canBeMissing: true}); + const {isOffline} = useNetwork(); const {splashScreenState} = useSplashScreenState(); + useUpdateGpsTripOnReconnect(); + useEffect(() => { async function handleGpsTripInProgressOnAppRestart() { const gpsTrip = await OnyxUtils.get(ONYXKEYS.GPS_DRAFT_DETAILS); @@ -72,7 +77,7 @@ function GPSTripStateChecker() { const onViewTrip = () => { setShowContinueTripModal(false); - stopGpsTrip(); + stopGpsTrip(isOffline); navigateToGpsScreen(); }; diff --git a/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts b/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts new file mode 100644 index 0000000000000..9a3d9bcce6719 --- /dev/null +++ b/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts @@ -0,0 +1,44 @@ +import useNetwork from '@hooks/useNetwork'; +import useOnyx from '@hooks/useOnyx'; +import {setEndAddress, setStartAddress} from '@libs/actions/GPSDraftDetails'; +import {addressFromGpsPoint} from '@libs/GPSDraftDetailsUtils'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {GpsDraftDetails} from '@src/types/onyx'; + +function useUpdateGpsTripOnReconnect() { + const [gpsDraftDetails] = useOnyx(ONYXKEYS.GPS_DRAFT_DETAILS, {canBeMissing: true}); + + const updateAddressToHumanReadable = async (gpsPoint: GpsDraftDetails['gpsPoints'][number] | undefined, setAddress: typeof setStartAddress) => { + if (!gpsPoint) { + return; + } + + const address = await addressFromGpsPoint(gpsPoint); + + if (address !== null) { + setAddress({value: address, type: 'address'}); + } + }; + + const updateAddressesToHumanReadable = () => { + if (!gpsDraftDetails) { + return; + } + + const {gpsPoints, startAddress, endAddress} = gpsDraftDetails; + + if (startAddress.type === 'coordinates') { + updateAddressToHumanReadable(gpsPoints.at(0), setStartAddress); + } + + if (endAddress.type === 'coordinates') { + updateAddressToHumanReadable(gpsPoints.at(-1), setEndAddress); + } + }; + + // This is intentional to use async/await pattern for better readability + // eslint-disable-next-line @typescript-eslint/no-misused-promises + useNetwork({onReconnect: updateAddressesToHumanReadable}); +} + +export default useUpdateGpsTripOnReconnect; diff --git a/src/libs/GPSDraftDetailsUtils.ts b/src/libs/GPSDraftDetailsUtils.ts index d75630458ba4c..89c95eae53ca8 100644 --- a/src/libs/GPSDraftDetailsUtils.ts +++ b/src/libs/GPSDraftDetailsUtils.ts @@ -91,7 +91,7 @@ function coordinatesToString(gpsPoint: {lat: number; long: number}): string { return `${gpsPoint.lat},${gpsPoint.long}`; } -async function stopGpsTrip() { +async function stopGpsTrip(isOffline: boolean) { const isBackgroundTaskRunning = await hasStartedLocationUpdatesAsync(BACKGROUND_LOCATION_TRACKING_TASK_NAME); if (isBackgroundTaskRunning) { @@ -108,15 +108,17 @@ async function stopGpsTrip() { return; } - const endAddress = await addressFromGpsPoint(lastPoint); + if (!isOffline) { + const endAddress = await addressFromGpsPoint(lastPoint); - if (endAddress === null) { - const formattedCoordinates = coordinatesToString(lastPoint); - setEndAddress({value: formattedCoordinates, type: 'coordinates'}); - return; + if (endAddress !== null) { + setEndAddress({value: endAddress, type: 'address'}); + return; + } } - setEndAddress({value: endAddress, type: 'address'}); + const formattedCoordinates = coordinatesToString(lastPoint); + setEndAddress({value: formattedCoordinates, type: 'coordinates'}); } export {getGPSRoutes, getGPSWaypoints, stopGpsTrip, getGPSConvertedDistance, getGPSCoordinates, addressFromGpsPoint, coordinatesToString, calculateGPSDistance}; diff --git a/src/pages/iou/request/step/IOURequestStepDistanceGPS/GPSButtons/index.tsx b/src/pages/iou/request/step/IOURequestStepDistanceGPS/GPSButtons/index.tsx index b0c1f106042dd..3f79e8b1bf8e7 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceGPS/GPSButtons/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceGPS/GPSButtons/index.tsx @@ -6,6 +6,7 @@ import ConfirmModal from '@components/ConfirmModal'; import {loadIllustration} from '@components/Icon/IllustrationLoader'; import {useMemoizedLazyAsset} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {initGpsDraft, resetGPSDraftDetails} from '@libs/actions/GPSDraftDetails'; @@ -29,6 +30,7 @@ function GPSButtons({navigateToNextStep, setShouldShowStartError, setShouldShowP const [showStopConfirmation, setShowStopConfirmation] = useState(false); const [showZeroDistanceModal, setShowZeroDistanceModal] = useState(false); const [showDisabledServicesModal, setShowDisabledServicesModal] = useState(false); + const {isOffline} = useNetwork(); const {asset: ReceiptLocationMarker} = useMemoizedLazyAsset(() => loadIllustration('ReceiptLocationMarker')); const [gpsDraftDetails] = useOnyx(ONYXKEYS.GPS_DRAFT_DETAILS, {canBeMissing: true}); @@ -129,7 +131,7 @@ function GPSButtons({navigateToNextStep, setShouldShowStartError, setShouldShowP isVisible={showStopConfirmation} onConfirm={() => { setShowStopConfirmation(false); - stopGpsTrip(); + stopGpsTrip(isOffline); }} onCancel={() => setShowStopConfirmation(false)} confirmText={translate('gps.stopGpsTrackingModal.confirm')} diff --git a/src/setup/backgroundLocationTrackingTask/index.native.ts b/src/setup/backgroundLocationTrackingTask/index.native.ts index 0660d9ff87f72..e5ef120bb35b7 100644 --- a/src/setup/backgroundLocationTrackingTask/index.native.ts +++ b/src/setup/backgroundLocationTrackingTask/index.native.ts @@ -5,6 +5,7 @@ import {addGpsPoints, setStartAddress} from '@libs/actions/GPSDraftDetails'; import {addressFromGpsPoint, coordinatesToString} from '@libs/GPSDraftDetailsUtils'; import {BACKGROUND_LOCATION_TRACKING_TASK_NAME} from '@pages/iou/request/step/IOURequestStepDistanceGPS/const'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {GpsDraftDetails} from '@src/types/onyx'; type BackgroundLocationTrackingTaskData = {locations: LocationObject[]}; @@ -14,25 +15,32 @@ defineTask(BACKGROUND_LOCATION_TRACKING_TASK return; } - const gpsDraftDetails = await OnyxUtils.get(ONYXKEYS.GPS_DRAFT_DETAILS); + const [gpsDraftDetailsPromiseResult, networkPromiseResult] = await Promise.allSettled([OnyxUtils.get(ONYXKEYS.GPS_DRAFT_DETAILS), OnyxUtils.get(ONYXKEYS.NETWORK)]); - const currentPoints = gpsDraftDetails?.gpsPoints ?? []; + const gpsDraftDetails = gpsDraftDetailsPromiseResult.status === 'fulfilled' ? gpsDraftDetailsPromiseResult.value : undefined; + const network = networkPromiseResult.status === 'fulfilled' ? networkPromiseResult.value : undefined; + const isOffline = network?.isOffline ?? false; - if (currentPoints.length === 0) { - const startPoint = data.locations.at(0); - - if (startPoint) { - const address = await addressFromGpsPoint({lat: startPoint.coords.latitude, long: startPoint.coords.longitude}); - - if (address !== null) { - setStartAddress({value: address, type: 'address'}); - } else { - setStartAddress({value: coordinatesToString({lat: startPoint.coords.latitude, long: startPoint.coords.longitude}), type: 'coordinates'}); - } - } - } + updateStartAddress(gpsDraftDetails?.gpsPoints ?? [], data.locations.at(0), isOffline); const newGpsPoints = data.locations.map((location) => ({lat: location.coords.latitude, long: location.coords.longitude})); addGpsPoints(gpsDraftDetails, newGpsPoints); }); + +async function updateStartAddress(currentGpsPoints: GpsDraftDetails['gpsPoints'], startPoint: LocationObject | undefined, isOffline: boolean) { + if (currentGpsPoints.length !== 0 || !startPoint) { + return; + } + + if (!isOffline) { + const address = await addressFromGpsPoint({lat: startPoint.coords.latitude, long: startPoint.coords.longitude}); + + if (address !== null) { + setStartAddress({value: address, type: 'address'}); + return; + } + } + + setStartAddress({value: coordinatesToString({lat: startPoint.coords.latitude, long: startPoint.coords.longitude}), type: 'coordinates'}); +}