From 42672e20853688ce14d8c5ee3871a0d557d4e0fe Mon Sep 17 00:00:00 2001 From: GCyganek Date: Wed, 14 Jan 2026 11:58:22 +0100 Subject: [PATCH 1/7] Handle reconnection while there is an ongoing GPS trip --- .../GPSTripStateChecker/index.native.tsx | 3 + .../useUpdateGpsTripOnReconnect.ts | 57 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts diff --git a/src/components/GPSTripStateChecker/index.native.tsx b/src/components/GPSTripStateChecker/index.native.tsx index 26678e17896b8..8a5799410550c 100644 --- a/src/components/GPSTripStateChecker/index.native.tsx +++ b/src/components/GPSTripStateChecker/index.native.tsx @@ -13,6 +13,7 @@ import coordinatesToString from '@pages/iou/request/step/IOURequestStepDistanceG import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import useUpdateGpsTripOnReconnect from './useUpdateGpsTripOnReconnect'; function GPSTripStateChecker() { const {translate} = useLocalize(); @@ -20,6 +21,8 @@ function GPSTripStateChecker() { const [gpsDraftDetails] = useOnyx(ONYXKEYS.GPS_DRAFT_DETAILS, {canBeMissing: true}); + useUpdateGpsTripOnReconnect(); + useEffect(() => { async function handleGpsTripInProgressOnAppRestart() { const gpsTrip = await OnyxUtils.get(ONYXKEYS.GPS_DRAFT_DETAILS); diff --git a/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts b/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts new file mode 100644 index 0000000000000..303f92b3926ae --- /dev/null +++ b/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts @@ -0,0 +1,57 @@ +import useNetwork from '@hooks/useNetwork'; +import useOnyx from '@hooks/useOnyx'; +import {setEndAddress, setStartAddress} from '@libs/actions/GPSDraftDetails'; +import addressFromGpsPoint from '@pages/iou/request/step/IOURequestStepDistanceGPS/utils/addressFromGpsPoint'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {GpsDraftDetails} from '@src/types/onyx'; + +function useUpdateGpsTripOnReconnect() { + const [gpsDraftDetails] = useOnyx(ONYXKEYS.GPS_DRAFT_DETAILS, {canBeMissing: true}); + + const updateStartAddressToHumanReadable = async (gpsPoints: GpsDraftDetails['gpsPoints']) => { + const firstPoint = gpsPoints.at(0); + if (!firstPoint) { + return; + } + + const startAddress = await addressFromGpsPoint(firstPoint); + + if (startAddress !== null) { + setStartAddress({value: startAddress, type: 'address'}); + } + }; + + const updateEndAddressToHumanReadable = async (gpsPoints: GpsDraftDetails['gpsPoints']) => { + const lastPoint = gpsPoints.at(-1); + if (!lastPoint) { + return; + } + + const endAddress = await addressFromGpsPoint(lastPoint); + + if (endAddress !== null) { + setEndAddress({value: endAddress, type: 'address'}); + } + }; + + const updateAddressesToHumanReadable = () => { + if (!gpsDraftDetails) { + return; + } + + const {gpsPoints, startAddress, endAddress} = gpsDraftDetails; + + if (startAddress.type === 'coordinates') { + updateStartAddressToHumanReadable(gpsPoints); + } + + if (endAddress.type === 'coordinates') { + updateEndAddressToHumanReadable(gpsPoints); + } + }; + + // eslint-disable-next-line @typescript-eslint/no-misused-promises + useNetwork({onReconnect: updateAddressesToHumanReadable}); +} + +export default useUpdateGpsTripOnReconnect; From 7d3c9c22802746948cebd28a1f7412a2213b8128 Mon Sep 17 00:00:00 2001 From: GCyganek Date: Wed, 28 Jan 2026 11:23:52 +0100 Subject: [PATCH 2/7] Fix import --- .../GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts b/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts index 303f92b3926ae..d4fa19bd592d8 100644 --- a/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts +++ b/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts @@ -1,7 +1,7 @@ import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import {setEndAddress, setStartAddress} from '@libs/actions/GPSDraftDetails'; -import addressFromGpsPoint from '@pages/iou/request/step/IOURequestStepDistanceGPS/utils/addressFromGpsPoint'; +import {addressFromGpsPoint} from '@libs/GPSDraftDetailsUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {GpsDraftDetails} from '@src/types/onyx'; From 2f033eaa99c3d1ccc8afe5f0f9888a4850daced2 Mon Sep 17 00:00:00 2001 From: GCyganek Date: Wed, 28 Jan 2026 12:20:50 +0100 Subject: [PATCH 3/7] Do not call reverseGeocodeAsync in offline mode --- .../GPSButtons/index.tsx | 16 +++++--- .../index.native.ts | 38 +++++++++++-------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepDistanceGPS/GPSButtons/index.tsx b/src/pages/iou/request/step/IOURequestStepDistanceGPS/GPSButtons/index.tsx index 161500c21db54..a2c46ec01e692 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, setEndAddress, setIsTracking} 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}); @@ -61,15 +63,17 @@ function GPSButtons({navigateToNextStep, setShouldShowStartError, setShouldShowP 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'}); }; const startGpsTrip = async () => { 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'}); +} From 8bfa9f6bd93fe00ce09e4c4901ab2eb1093c6c17 Mon Sep 17 00:00:00 2001 From: GCyganek Date: Wed, 28 Jan 2026 13:02:18 +0100 Subject: [PATCH 4/7] Rerun Jest tests From 32bad4c21f3898f708fb5d38bd3432a96a67c421 Mon Sep 17 00:00:00 2001 From: GCyganek Date: Wed, 28 Jan 2026 13:34:59 +0100 Subject: [PATCH 5/7] Add explanation and refactor code to avoid repetition --- .../useUpdateGpsTripOnReconnect.ts | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts b/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts index d4fa19bd592d8..affdf4ea967cf 100644 --- a/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts +++ b/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts @@ -8,29 +8,15 @@ import type {GpsDraftDetails} from '@src/types/onyx'; function useUpdateGpsTripOnReconnect() { const [gpsDraftDetails] = useOnyx(ONYXKEYS.GPS_DRAFT_DETAILS, {canBeMissing: true}); - const updateStartAddressToHumanReadable = async (gpsPoints: GpsDraftDetails['gpsPoints']) => { - const firstPoint = gpsPoints.at(0); - if (!firstPoint) { + const updateAddressToHumanReadable = async (gpsPoint: GpsDraftDetails['gpsPoints'][number] | undefined, setAddress: typeof setStartAddress) => { + if (!gpsPoint) { return; } - const startAddress = await addressFromGpsPoint(firstPoint); + const startAddress = await addressFromGpsPoint(gpsPoint); if (startAddress !== null) { - setStartAddress({value: startAddress, type: 'address'}); - } - }; - - const updateEndAddressToHumanReadable = async (gpsPoints: GpsDraftDetails['gpsPoints']) => { - const lastPoint = gpsPoints.at(-1); - if (!lastPoint) { - return; - } - - const endAddress = await addressFromGpsPoint(lastPoint); - - if (endAddress !== null) { - setEndAddress({value: endAddress, type: 'address'}); + setAddress({value: startAddress, type: 'address'}); } }; @@ -42,14 +28,15 @@ function useUpdateGpsTripOnReconnect() { const {gpsPoints, startAddress, endAddress} = gpsDraftDetails; if (startAddress.type === 'coordinates') { - updateStartAddressToHumanReadable(gpsPoints); + updateAddressToHumanReadable(gpsPoints.at(0), setStartAddress); } if (endAddress.type === 'coordinates') { - updateEndAddressToHumanReadable(gpsPoints); + 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}); } From 69104c1a8303c7f2fe875375ed02fd94fff24744 Mon Sep 17 00:00:00 2001 From: GCyganek Date: Thu, 29 Jan 2026 11:24:38 +0100 Subject: [PATCH 6/7] startAddress => address --- .../GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts b/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts index affdf4ea967cf..9a3d9bcce6719 100644 --- a/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts +++ b/src/components/GPSTripStateChecker/useUpdateGpsTripOnReconnect.ts @@ -13,10 +13,10 @@ function useUpdateGpsTripOnReconnect() { return; } - const startAddress = await addressFromGpsPoint(gpsPoint); + const address = await addressFromGpsPoint(gpsPoint); - if (startAddress !== null) { - setAddress({value: startAddress, type: 'address'}); + if (address !== null) { + setAddress({value: address, type: 'address'}); } }; From 2df5c757d76a0683884f11dd927d8b9b3751bff2 Mon Sep 17 00:00:00 2001 From: GCyganek Date: Fri, 6 Feb 2026 11:44:01 +0100 Subject: [PATCH 7/7] Prettier fix --- src/components/GPSTripStateChecker/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/GPSTripStateChecker/index.native.tsx b/src/components/GPSTripStateChecker/index.native.tsx index 1f271fe3a0e91..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,7 +13,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import {useSplashScreenState} from '@src/SplashScreenStateContext'; -import useNetwork from '@hooks/useNetwork'; import useUpdateGpsTripOnReconnect from './useUpdateGpsTripOnReconnect'; function GPSTripStateChecker() {