fix(markers): render locally-dropped markers on the 2D engine (#77)#91
Merged
Conversation
Root cause (TacticalMap.kt, line 383 — now removed): the contact push
was gated behind `map.style != null` inside an async `getMapAsync`
callback launched from a `DisposableEffect(mapView, contacts)`. During
the initial style-load window (100–2 000 ms) that guard evaluates false
and the update is silently dropped. The one-shot style-ready callback at
line ~156 was meant to recover via `currentContacts`, but it only fires
once — any marker ingested after that first push and before the next
Compose-driven `contacts` key change was irretrievably lost.
Cesium has never had this bug: `AndroidView(update = { pushEntities() })`
re-pushes on every recomposition using `rememberUpdatedState`, so the
update retries automatically on the next frame.
Fix (TacticalMap.kt, line 557):
1. Move the contact push into `AndroidView.update { }` so it fires on
every recomposition that delivers a changed contacts list — identical
cadence to the Cesium path. The removed `DisposableEffect(mapView,
contacts)` block is no longer needed.
2. Replace `if (map.style != null)` with `map.getStyle { }` (the
queuing variant): when the style is still loading the callback is
queued and fires as soon as the style is ready, so a marker dropped
during the style-load window is never silently discarded.
3. Read `currentContacts` (rememberUpdatedState) inside the callback
instead of the DisposableEffect-time snapshot — the async getStyle
callback always sees the latest list, closing the snapshot-staleness
gap that existed under concurrent rapid ingest.
No other files changed. On-device verification rides with the next
closed-track build.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jfuginay
added a commit
that referenced
this pull request
Jun 11, 2026
- versionName 0.35.3 -> 0.35.4, versionCode 91 -> 92 - adds #88 (compass clears top bar, #81), #90 (drawings survive style reloads, #80), #91 (locally-dropped markers on 2D, #77) on top of 0.35.3's #75/#78 — all five from the r/ATAK tester thread, same day - supersedes the never-uploaded 0.35.3 vc91 AAB Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
File:
app/src/main/kotlin/soy/engindearing/omnitak/mobile/ui/components/TacticalMap.kt, line 383 (removed)The contact push inside
TacticalMapwas wired as:During the initial style-load window (100–2 000 ms),
map.style != nullis false. The update is silently dropped. The style-ready callback at line ~156 was meant to recover viacurrentContacts(rememberUpdatedState), but it fires only once. Any marker ingested after that first push and before the next Compose-triggeredcontactskey change was irretrievably lost — showing up on the Cesium 3D Globe (whoseAndroidView.update { pushEntities() }retries on every recomposition) but never on 2D MapLibre.What changed
TacticalMap.kt— removedDisposableEffect(mapView, contacts)and changed the terminalAndroidViewfromfactory-only tofactory + update:Three deliberate changes vs the removed effect:
AndroidView.update { }fires on every recomposition that receives a new contacts list — identical cadence to the Cesium engine. The update retries on the next frame instead of being lost.map.getStyle { }(queuing variant) instead ofif (map.style != null): when the style is still loading the callback queues and fires when ready, closing the style-load race entirely.currentContacts(rememberUpdatedState) inside the callback instead of the DisposableEffect-time snapshot — the async callback always sees the latest list.ContactLayerFeatureTest.kt(new) — pure JVM test for the data-layer preconditions: every locally-dropped marker type (point-dropper, coordinate-entry, FEMA palette) produces finite lat/lon and a valid CSS hex color string, coveringargbToHexedge cases and the NaN-guard contract thatContactLayer.updatedepends on.Test evidence
New test class:
ContactLayerFeatureTest(14 tests).On-device verification rides with the next closed-track build.