Skip to content

fix(map): allow local deletion of any waypoint, locked or not - #6497

Merged
jamesarich merged 3 commits into
mainfrom
claude/local-waypoint-deletion-6f2624
Jul 28, 2026
Merged

fix(map): allow local deletion of any waypoint, locked or not#6497
jamesarich merged 3 commits into
mainfrom
claude/local-waypoint-deletion-6f2624

Conversation

@jamesarich

@jamesarich jamesarich commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Why

A waypoint locked to another node was permanently stuck on our own map. locked_to is a mesh-wide ownership rule — only the owner may broadcast an edit or removal — but the UI was treating it as a local one too:

  • Google flavor: tapping a waypoint locked to someone else while connected showed a "Locked" toast and offered nothing further. No way to clear the local copy.
  • Both flavors: the foreign-geofence info dialog offered the crossing-alert opt-in and (when unlocked) Edit, but never a delete.

The result was that a stale, mis-placed, or simply unwanted foreign waypoint could never be removed from your own map. Dropping our local database row needs no permission from anyone, so it should always be available.

Local deletion is now unconditional. Only the expire = 1 broadcast stays gated on isModifiableBy(myNodeNum) && isConnected — the mesh-wide contract from #6343/#6348 is unchanged.

🌟 Changes

  • New shared DeleteWaypointDialog (feature/map), built on MeshtasticDialog from core:ui: confirming always drops our local copy, and broadcasting the removal to the mesh is an opt-in checkbox shown only when we may modify the waypoint mesh-wide and are connected. Local deletion is therefore the safe default action.
  • WaypointMarkers (google): the locked-to-someone-else branch now routes to onDeleteWaypointRequest instead of a toast, wired through MainMapContent to a deletingWaypoint dialog in MapView. The edit branch gates on isModifiableBy alone — it previously also opened the editor for a foreign locked waypoint while offline, where no edit could ever be sent.
  • WaypointInfoDialog: gained an onDeleteForMe action, rendered in error color alongside Edit, and offered even when the geofence is locked to its creator. Both flavors pass it.

🧹 Cleanup

  • fdroid's hand-rolled delete AlertDialog is replaced by the shared composable — one copy instead of two, and it no longer bypasses the centralized-dialog rule in .skills/code-review. The now-dead "locked" toast plumbing (rememberCoroutineScope, LocalContext, showToast) and the unused isConnected parameter are gone from WaypointMarkers/MainMapContent.

UI

The delete dialog is a plain Delete / Cancel confirmation, gaining a "Delete for everyone" checkbox only when the removal may also be broadcast. The foreign-geofence info dialog now ends with Delete for me · Close (error-colored delete), with Edit inserted between them when the geofence is unlocked. Both dialog states are pinned by the new screenshot references in this PR.

Testing Performed

  • ./gradlew spotlessApply spotlessCheck detekt assembleDebug test allTests — all green (738 tests).
  • ./gradlew :screenshot-tests:updateDebugScreenshotTest then validateDebugScreenshotTest — green. Four existing WaypointInfoDialog references re-recorded for the new delete action; four new references added for DeleteWaypointDialogLocalOnlyPreview / DeleteWaypointDialogWithBroadcastPreview, which pin both gate states.
  • Rendered goldens inspected by hand to confirm the stacked action column and the info-dialog button row read correctly in light and dark.

Not yet exercised on-device against a real foreign locked waypoint; the gate change is UI-only and the mesh-wide permission path (isModifiableBy) is untouched and still covered by WaypointLockTest.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a dedicated waypoint deletion dialog with an opt-in checkbox to delete for everyone, otherwise deletion happens locally.
    • Enhanced waypoint info to show “Delete for me” alongside edit when applicable, including for locked waypoints owned by others.
    • Updated map deletion handling to route local vs broadcast deletion appropriately.
  • Tests
    • Added screenshot preview test coverage for both deletion dialog variants (local-only and with the broadcast option).

A waypoint locked to another node was permanently stuck on our own map.
The google flavor answered a tap with a "Locked" toast and nothing else,
and the foreign-geofence info dialog offered no removal at all — so a
stale or unwanted foreign waypoint could never be cleared, even though
dropping our local copy needs no mesh-wide permission.

Local deletion is now always available; only the expire=1 broadcast stays
gated on isModifiableBy(myNodeNum) && isConnected.

- new shared DeleteWaypointDialog: "Delete for me" always, "Delete for
  everyone" only when we may modify the waypoint mesh-wide. Replaces the
  hand-rolled fdroid copy so both flavors share one dialog.
- WaypointMarkers routes the locked-to-someone-else branch to the delete
  dialog instead of a toast.
- WaypointInfoDialog gains a local-delete action, offered even when the
  geofence is locked to its creator.

Screenshot references re-recorded; two previews added for the new dialog.
@github-actions github-actions Bot added the bugfix PR tag label Jul 28, 2026
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d26bf47-12ec-4024-be40-4dac64876cdf

📥 Commits

Reviewing files that changed from the base of the PR and between b5fba9f and cc73a9e.

📒 Files selected for processing (1)
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/component/DeleteWaypointDialog.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/component/DeleteWaypointDialog.kt

📝 Walkthrough

Walkthrough

Waypoint deletion now uses a shared confirmation dialog with separate local and mesh-wide actions. Locked waypoint interactions open the dialog, waypoint information dialogs expose local deletion, and preview screenshot coverage covers both permission states.

Changes

Waypoint deletion

Layer / File(s) Summary
Shared deletion dialog contracts
feature/map/.../DeleteWaypointDialog.kt, feature/map/.../WaypointInfoDialog.kt, feature/map/.../*Previews.kt, screenshot-tests/.../MapScreenshotTests.kt
Adds the reusable deletion dialog, local deletion support in waypoint information dialogs, and preview screenshot coverage for local-only and mesh-wide deletion states.
Map deletion request integration
androidApp/src/*/kotlin/org/meshtastic/app/map/MapView.kt, androidApp/src/google/.../WaypointMarkers.kt
Routes locked waypoint selections into deletion state and performs local deletion or expiry broadcasts followed by local removal.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant WaypointMarkers
  participant MainMapContent
  participant MapView
  participant DeleteWaypointDialog
  participant mapViewModel
  WaypointMarkers->>MainMapContent: request deletion for locked waypoint
  MainMapContent->>MapView: store selected waypoint
  MapView->>DeleteWaypointDialog: display deletion choices
  DeleteWaypointDialog->>mapViewModel: deleteWaypoint(id)
  DeleteWaypointDialog->>mapViewModel: sendWaypoint(expire = 1)
  mapViewModel->>mapViewModel: deleteWaypoint(id)
Loading
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: enabling local deletion of waypoints, including locked ones.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@androidApp/src/google/kotlin/org/meshtastic/app/map/component/WaypointMarkers.kt`:
- Around line 105-107: Update the waypoint action branching around
waypoint.isModifiableBy(myNodeNum) so the edit path requires modifiability,
while every non-modifiable waypoint routes to onDeleteWaypointRequest(waypoint)
regardless of connection state. Preserve local deletion as unconditional,
including waypoints locked by another node.

In
`@feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/component/DeleteWaypointDialog.kt`:
- Around line 47-69: Replace the inline AlertDialog in DeleteWaypointDialog with
the shared dialog-host architecture, routing the dialog through
AlertHost(alertManager) or SharedDialogs from core:ui/commonMain. Preserve the
existing delete-for-me, conditional delete-for-everyone, and cancel actions and
their ordering while removing the direct alert-rendering logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b3cfeaac-f38b-4355-82bd-d969665e2a2b

📥 Commits

Reviewing files that changed from the base of the PR and between 4c3196c and 9b1e2cf.

⛔ Files ignored due to path filters (8)
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotDeleteWaypointDialogBoth_Dark_d19fbf1f_0.png is excluded by !**/*.png, !**/*.png
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotDeleteWaypointDialogBoth_Light_b29dc7a7_0.png is excluded by !**/*.png, !**/*.png
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotDeleteWaypointDialogLocalOnly_Dark_d19fbf1f_0.png is excluded by !**/*.png, !**/*.png
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotDeleteWaypointDialogLocalOnly_Light_b29dc7a7_0.png is excluded by !**/*.png, !**/*.png
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotWaypointInfoDialogOptedIn_Dark_d19fbf1f_0.png is excluded by !**/*.png, !**/*.png
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotWaypointInfoDialogOptedIn_Light_b29dc7a7_0.png is excluded by !**/*.png, !**/*.png
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotWaypointInfoDialogReadOnly_Dark_d19fbf1f_0.png is excluded by !**/*.png, !**/*.png
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotWaypointInfoDialogReadOnly_Light_b29dc7a7_0.png is excluded by !**/*.png, !**/*.png
📒 Files selected for processing (8)
  • androidApp/src/fdroid/kotlin/org/meshtastic/app/map/MapView.kt
  • androidApp/src/google/kotlin/org/meshtastic/app/map/MapView.kt
  • androidApp/src/google/kotlin/org/meshtastic/app/map/component/WaypointMarkers.kt
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/component/DeleteWaypointDialog.kt
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/component/DeleteWaypointDialogPreviews.kt
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/component/WaypointInfoDialog.kt
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/component/WaypointInfoDialogPreviews.kt
  • screenshot-tests/src/screenshotTest/kotlin/org/meshtastic/screenshots/feature/MapScreenshotTests.kt

Comment thread androidApp/src/google/kotlin/org/meshtastic/app/map/component/WaypointMarkers.kt Outdated
- WaypointMarkers: the edit branch now requires isModifiableBy alone. The
  old `|| !isConnected` meant a waypoint locked to another node opened the
  (useless) editor while offline and only reached the delete dialog while
  connected. Every non-modifiable waypoint now routes to the delete dialog
  regardless of connectivity, which also drops the isConnected parameter
  from WaypointMarkers and MainMapContent.
- DeleteWaypointDialog: built on the shared MeshtasticDialog instead of an
  inline AlertDialog, per the centralized-dialog rule in .skills/code-review.
  Confirming always deletes locally; the mesh-wide broadcast is now an
  opt-in checkbox shown only when we may modify the waypoint mesh-wide.
  That also replaces the ragged three-button column with a standard
  Delete/Cancel pair.

Screenshot references re-recorded; the Both preview is renamed
WithBroadcast to match the checkbox.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/component/DeleteWaypointDialog.kt`:
- Around line 56-84: Guard the confirmation logic in DeleteWaypointDialog with
canDeleteForEveryone so onDeleteForEveryone() is invoked only while mesh-wide
deletion remains eligible; otherwise call onDeleteForMe(). Optionally key or
reset the remembered alsoDeleteForEveryone state when canDeleteForEveryone
changes so stale selection cannot persist.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d941c5db-a866-42c6-8178-6d06874bc0af

📥 Commits

Reviewing files that changed from the base of the PR and between 9b1e2cf and b5fba9f.

⛔ Files ignored due to path filters (4)
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotDeleteWaypointDialogLocalOnly_Dark_d19fbf1f_0.png is excluded by !**/*.png, !**/*.png
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotDeleteWaypointDialogLocalOnly_Light_b29dc7a7_0.png is excluded by !**/*.png, !**/*.png
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotDeleteWaypointDialogWithBroadcast_Dark_d19fbf1f_0.png is excluded by !**/*.png, !**/*.png
  • screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/MapScreenshotTestsKt/ScreenshotDeleteWaypointDialogWithBroadcast_Light_b29dc7a7_0.png is excluded by !**/*.png, !**/*.png
📒 Files selected for processing (5)
  • androidApp/src/google/kotlin/org/meshtastic/app/map/MapView.kt
  • androidApp/src/google/kotlin/org/meshtastic/app/map/component/WaypointMarkers.kt
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/component/DeleteWaypointDialog.kt
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/component/DeleteWaypointDialogPreviews.kt
  • screenshot-tests/src/screenshotTest/kotlin/org/meshtastic/screenshots/feature/MapScreenshotTests.kt
💤 Files with no reviewable changes (1)
  • androidApp/src/google/kotlin/org/meshtastic/app/map/MapView.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/component/DeleteWaypointDialogPreviews.kt
  • screenshot-tests/src/screenshotTest/kotlin/org/meshtastic/screenshots/feature/MapScreenshotTests.kt

A mid-dialog disconnect hides the "Delete for everyone" checkbox but was
leaving the remembered selection set, so confirming would still broadcast
the removal while no longer eligible. Key the state on canDeleteForEveryone
so it resets with the checkbox, and guard the confirm on eligibility too.
@jamesarich
jamesarich added this pull request to the merge queue Jul 28, 2026
Merged via the queue into main with commit 0c6bf23 Jul 28, 2026
15 checks passed
@jamesarich
jamesarich deleted the claude/local-waypoint-deletion-6f2624 branch July 28, 2026 18:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix PR tag

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant