Skip to content

fix: prevent map viewport race on tab switch with no location#627

Merged
torlando-tech merged 1 commit intomainfrom
fix/map-viewport-race-condition
Mar 8, 2026
Merged

fix: prevent map viewport race on tab switch with no location#627
torlando-tech merged 1 commit intomainfrom
fix/map-viewport-race-condition

Conversation

@torlando-tech
Copy link
Copy Markdown
Owner

Summary

  • Fixes a race condition where switching between map and settings repeatedly (with no GNSS/offline map) causes the map to jump from (0,0) at zoom 2.0 to a full world view at a different zoom level
  • The onCameraIdle listener was saving a fresh MapView's default viewport before the positioning LaunchedEffect could run, overwriting the saved camera position
  • The (0,0) fallback was also being persisted as a "real" saved position, blocking future GPS/default-region centering on tab switches

Changes

  • MapScreen.kt: Add isInitialPositionSet guard that gates onCameraIdle saving until positioning is intentionally set; reset on MapView recreation; clear stale (0,0) from ViewModel
  • MapViewModel.kt: Add clearCameraPosition() to null out the fallback position so it doesn't persist

Test plan

  • With no GNSS and no offline map, open map tab — should show (0,0) at zoom 2.0
  • Switch to settings and back repeatedly (10+ times) — map should stay at (0,0) zoom 2.0, never jump to full world view
  • Enable GNSS, return to map — should center on user location (not stuck at 0,0)
  • With a real location, switch tabs — saved viewport should restore correctly
  • Set an offline map default region, verify it centers correctly on first load

🤖 Generated with Claude Code

…aunchedEffect

When no GNSS and no offline map are available, the map falls back to (0,0)
at zoom 2.0. The onCameraIdle listener would save this as a "real" viewport,
causing it to persist across tab switches. Additionally, when switching tabs
repeatedly, a fresh MapView's default viewport (zoom ~0, full world) could
overwrite the saved position before the positioning LaunchedEffect ran.

Add isInitialPositionSet guard to gate onCameraIdle saving until the
positioning LaunchedEffect has intentionally placed the camera. Clear stale
(0,0) fallback from ViewModel state so it doesn't block future GPS or
default-region centering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sentry
Copy link
Copy Markdown
Contributor

sentry bot commented Mar 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 8, 2026

Greptile Summary

This PR fixes a critical race condition in the map viewport handling where repeatedly switching tabs with no GNSS or offline map caused the viewport to jump from (0,0) zoom 2.0 to a full-world default view. The fix prevents the onCameraIdle listener from persisting stale default viewport data before the positioning logic has a chance to run and establish an intentional camera position.

Key changes:

  • isInitialPositionSet guard added to block premature viewport saves
  • LaunchedEffect(mapLibreMap) resets the guard whenever the MapView is recreated
  • All positioning paths explicitly set the guard to reflect whether a meaningful position was used
  • clearCameraPosition() method clears the (0,0) fallback so it doesn't persist and block future GPS/default-region centering

Testing: The fix correctly handles tab switches, config changes, GNSS availability transitions, and offline map region changes without regressing the viewport behavior.

Confidence Score: 5/5

  • Safe to merge. The fix correctly addresses the race condition with no functional issues or regressions.
  • The PR successfully fixes the described race condition where onCameraIdle could save stale default viewport data before intentional positioning. The implementation correctly gates the save with isInitialPositionSet, resets the guard on MapView recreation via LaunchedEffect, and all positioning paths properly manage the guard state. The clearCameraPosition() addition correctly prevents (0,0) fallback from persisting. Code is straightforward, well-integrated, and handles all documented test scenarios.
  • No files require special attention.

Last reviewed commit: ef405a1

@torlando-tech torlando-tech merged commit b7e9fb7 into main Mar 8, 2026
31 of 36 checks passed
@torlando-tech torlando-tech deleted the fix/map-viewport-race-condition branch March 8, 2026 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant