[GPS] Pause/Resume functionality#88609
Conversation
|
Hey, I noticed you changed If you want to automatically generate translations for other locales, an Expensify employee will have to:
Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running: npx ts-node ./scripts/generateTranslations.ts --helpTypically, you'd want to translate only what you changed by running |
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
@Expensify/design the backend is not merged yet and we need to wait for it to be able to create receipts with split GPS trips, but here you can see how it will look like: Screen.Recording.2026-04-23.at.18.00.07.movOf course map styles (icons and route) will be updated soon in a separate PR |
We have it from the beginning, but I can delete it if you prefer, of course |
|
Yeah now that we have a super simple way to resume if you accidentally stopped, I think we don't need it personally. |
|
Will do! |
Totally agree! |
|
Agree with removing the modal. That was there for safety before the pause. Can we change the wording here a bit. The primary button for in progress should say: And then ones paused can we say That is if @Expensify/design team is okay with that ofc. |
I like this change, will wait for confirmation before I change it here |
|
I don't feel strongly. Part of me thinks it won't be common for people to pause and resume, so having the only word to "finish" your tracking be "Pause" feels a bit odd to me. I think I would personally just keep Stop but let's see how the others feel! |
|
No strong feelings from me - when I first read your updated copy I thought "Oh yeah I like that!" but then when I read Shawn's comment I did kinda feel like oh yeah, stop is more clear/expected than pause for ending my tracking. I do kinda like I also don't really use any GPS tracking apps or workflows, so I'm not familiar with what the most common patterns and verbiage are. Happy to lean on those with more experience/context! |
|
Alright, let's leave it as is then. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 68184edd30
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: de02cb9ad7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
Let me check that address issue. We should be showing coordinates only if fetching human readable address from coordinates fails or user's offline EDIT: Should be fixed now |
Tunnels are problematic for GPS tracking as if satellite is not reachable and there is no cell service then there is no way to get location updates. If this is a real problem for us then I could do some research, but I don't have a solution at the moment unfortunately. This actually reminds me that @JmillsExpensify is working on the option to choose alternative routes, so maybe using it user would have a way to choose the same route, but with the fixed tunnel part? |
|
How often are we adding a geolocation and spot on the polyline? Obviously I'm assuming we don't wanna add a mapping solution, but I did find it a bit jarring that, when driving, it took quite a few meters before the trip length grew. Maybe it's fine though |
We have 0.1 mi distance interval. It can be changed, but we decided to stick with it, because the longer the trip, the more points we save and we have a limit in the backend of how many points can be used to create a receipt and if we have too many points, then we have to use an algorithm which discards every n-th point to go down to the limit and makes the trip lose precision anyway Anyway, we could try reducing the distance interval, which will make the trip updates more frequent and the only downside would be that the map with the route on the receipt generated in the backend will be less precise than what the user sees on the map while the trip is in progress and before it's saved as an expense faster (less distance needed to reach the limit when we have to use the algorithm that discards points). |
|
on it now |
| import type {GPSPoint} from '@src/types/onyx/GpsDraftDetails'; | ||
|
|
||
| function useUpdateGpsTripOnReconnect() { | ||
| const [gpsDraftDetails] = useOnyx(ONYXKEYS.GPS_DRAFT_DETAILS); |
There was a problem hiding this comment.
I think we can remove it entirely, then use OnyxUtils.get inside updateAddressesToHumanReadable. If we still need gpsDraftDetails reactive value, we can pass it from its parent
| import type {Coordinate} from './MapViewTypes'; | ||
|
|
||
| function isSingleSegmentRoute(directionCoordinates: Coordinate[] | Coordinate[][]): directionCoordinates is Coordinate[] { | ||
| return getArrayDepth(directionCoordinates) === 2; |
There was a problem hiding this comment.
let's use is2dArray, or we can use is2dArray directly, no need to create isSingleSegmentRoute
There was a problem hiding this comment.
I can use is2dArray in this function, but I'd leave the isSingleSegmentRoute, as it makes the code more readable IMO. Without it it can be confusing, as looking at the type Coordinate[] you'd guess it's 1d array, but it's actually a 2d array because Coordinate is [number, number], so using isSingleSegmentRoute immediately tells us that we're checking if we're using a segmented trip (3d array) or a simple trip without segments (2d array)
|
In useGPSWaypointMarkers.tsx |
|
The rest looks good |
Applied changes according to the review |
|
on it now |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-05-07.at.23.38.23.movAndroid: mWeb ChromeiOS: HybridAppScreen.Recording.2026-05-07.at.23.28.58.moviOS: mWeb SafariMacOS: Chrome / Safari |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 @puneetlath has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/puneetlath in version: 9.3.69-0 🚀
Bundle Size Analysis (Sentry): |
|
Yes, help site changes are required. The GPS tracking flow changed significantly in this PR — stop is now immediate (no confirmation dialog), Resume enables multi-segment trips, Save replaces Next, and there's a new discard button. I've created a draft PR with the necessary updates to two help site articles: Draft PR: #90014 Files updated:
Changes made:
|
|
🚀 Deployed to production by https://github.com/Beamanator in version: 9.3.69-18 🚀
|


Explanation of Change
gpsPointsinGpsDraftDetailschanged from 1D array to 2D array of segments. Each paused-and-resumed section of a trip becomes a separate segment.startAddressandendAddresstop-level fields removed from GpsDraftDetails. Address is now stored directly on eachGPSPointas an optional address field, so each segment carries its own start/end address.resumeGpsTripaction adds a new empty segment togpsPointsand setsisTracking: true. The background location task automatically resolves the start address for the new segment when the first point arrives.DiscardGPSTripButton) shown only in the stopped state.directionCoordinatesinMapViewPropsandDirectionPropsupdated to acceptCoordinate[] | Coordinate[][].ConvertGpsPointsTo2DArraymigration wraps any old existinggpsPointsinto an additional array to make it 2D and migratesstartAddress/endAddressvalues into the address field on the first and last points respectively.Fixed Issues
$ #86026
PROPOSAL: N/A
Tests
Discard button:
Multi-segment trip:
Tracking distance...and there is a new separate segment of the trip on the mapRepeat, but create selfDM expense this time
Single segment trip:
Repeat, but create selfDM expense this time
Offline tests
QA Steps
Same as tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Screen.Recording.2026-05-04.at.15.48.17.mov
Screen.Recording.2026-05-04.at.15.49.15.mov
Screen.Recording.2026-05-04.at.17.35.53.mov
Screen.Recording.2026-05-04.at.17.37.12.mov
iOS: Native
Screen.Recording.2026-05-04.at.17.54.18.mov
Screen.Recording.2026-05-04.at.17.55.40.mov
Screen.Recording.2026-05-04.at.17.56.22.mov
Screen.Recording.2026-05-04.at.17.57.37.mov
Screen.Recording.2026-05-04.at.17.57.37.mov