Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .skills/compose-ui/strings-index.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 4 additions & 28 deletions androidApp/src/fdroid/kotlin/org/meshtastic/app/map/MapView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,6 @@ fun MapView(
// tile-download rectangle machinery) that becomes the draft's bounding box on confirm. Kept distinct from the
// tile-download box (downloadRegionBoundingBox) so the two rectangle modes never collide.
var geofenceBoxDraft by remember { mutableStateOf<Waypoint?>(null) }
// The recipient selected in EditWaypointDialog before "Set area" tore it down — carried across the round trip
// so re-opening the dialog with the drawn box doesn't silently reset the destination back to Broadcast.
var geofenceBoxDraftContactKey by remember { mutableStateOf<String?>(null) }
var geofenceBoxBoundingBox: BoundingBox? by remember { mutableStateOf(null) }

var showDownloadButton: Boolean by remember { mutableStateOf(false) }
Expand Down Expand Up @@ -483,10 +480,8 @@ fun MapView(
waypoint.toGeofence() != null && !mapViewModel.isMyWaypoint(id) -> showGeofenceInfoDialog = waypoint

// edit only when unlocked or lockedTo myNodeNum
waypoint.locked_to in setOf(0, mapViewModel.myNodeNum ?: 0) && isConnected -> {
geofenceBoxDraftContactKey = null
waypoint.locked_to in setOf(0, mapViewModel.myNodeNum ?: 0) && isConnected ->
showEditWaypointDialog = waypoint
}

else -> showDeleteWaypointDialog = waypoint
}
Expand Down Expand Up @@ -588,7 +583,6 @@ fun MapView(
val enabled = isConnected && downloadRegionBoundingBox == null && geofenceBoxDraft == null

if (enabled) {
geofenceBoxDraftContactKey = null
showEditWaypointDialog =
Waypoint(latitude_i = (p.latitude * 1e7).toInt(), longitude_i = (p.longitude * 1e7).toInt())
}
Expand Down Expand Up @@ -900,16 +894,7 @@ fun MapView(
EditWaypointDialog(
waypoint = showEditWaypointDialog ?: return, // Safe call
displayUnits = displayUnits,
nodes = nodes,
ourNode = ourNodeInfo,
channelSet = channelSet,
initialContactKey =
geofenceBoxDraftContactKey
?: showEditWaypointDialog
?.let { wp -> waypoints[wp.id] }
?.let { mapViewModel.waypointContactKey(it) }
?: "0${NodeAddress.ID_BROADCAST}",
onSend = { waypoint, contactKey ->
onSend = { waypoint ->
Logger.d { "User clicked send waypoint ${waypoint.id}" }
showEditWaypointDialog = null

Expand All @@ -927,7 +912,6 @@ fun MapView(
locked_to = newLockedTo,
icon = newIcon,
),
contactKey,
)
},
onDelete = { waypoint ->
Expand All @@ -939,11 +923,10 @@ fun MapView(
Logger.d { "User clicked cancel marker edit dialog" }
showEditWaypointDialog = null
},
onBeginBoxAuthoring = { draft, contactKey ->
onBeginBoxAuthoring = { draft ->
Logger.d { "User began geofence box authoring for waypoint ${draft.id}" }
showEditWaypointDialog = null
geofenceBoxDraft = draft
geofenceBoxDraftContactKey = contactKey
map.generateGeofenceBoxOverlay()
},
)
Expand All @@ -963,7 +946,6 @@ fun MapView(
if (waypoint.locked_to == 0 && isConnected) {
{
showGeofenceInfoDialog = null
geofenceBoxDraftContactKey = null
showEditWaypointDialog = waypoint
}
} else {
Expand Down Expand Up @@ -997,13 +979,7 @@ fun MapView(
TextButton(
onClick = {
Logger.d { "User deleted waypoint ${waypoint.id} for everyone" }
// Route the expiry to wherever the waypoint was originally sent (a DM or a secondary
// channel), not the default broadcast — otherwise a DM'd waypoint's "delete for
// everyone" never reaches its actual recipient.
val originalContactKey =
waypoints[waypoint.id]?.let { mapViewModel.waypointContactKey(it) }
?: "0${NodeAddress.ID_BROADCAST}"
mapViewModel.sendWaypoint(waypoint.copy(expire = 1), originalContactKey)
mapViewModel.sendWaypoint(waypoint.copy(expire = 1))
mapViewModel.deleteWaypoint(waypoint.id)
showDeleteWaypointDialog = null
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import org.meshtastic.core.repository.NotificationPrefs
import org.meshtastic.core.repository.PacketRepository
import org.meshtastic.core.repository.RadioConfigRepository
import org.meshtastic.core.repository.RadioController
import org.meshtastic.core.repository.UiPrefs
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
import org.meshtastic.feature.map.BaseMapViewModel
import java.io.InputStream
Expand All @@ -45,7 +44,6 @@ class MapViewModel(
radioController: RadioController,
radioConfigRepository: RadioConfigRepository,
notificationPrefs: NotificationPrefs,
uiPrefs: UiPrefs,
buildConfigProvider: BuildConfigProvider,
private val mapLayersManager: MapLayersManager,
savedStateHandle: SavedStateHandle,
Expand All @@ -56,7 +54,6 @@ class MapViewModel(
radioController,
radioConfigRepository,
notificationPrefs,
uiPrefs,
) {

private val _selectedWaypointId = MutableStateFlow(savedStateHandle.get<Int>("waypointId"))
Expand Down
34 changes: 5 additions & 29 deletions androidApp/src/google/kotlin/org/meshtastic/app/map/MapView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ import org.meshtastic.app.map.component.WaypointMarkers
import org.meshtastic.app.map.model.NodeClusterItem
import org.meshtastic.core.common.util.nowSeconds
import org.meshtastic.core.model.Node
import org.meshtastic.core.model.NodeAddress
import org.meshtastic.core.model.TracerouteOverlay
import org.meshtastic.core.model.geofence.toGeofence
import org.meshtastic.core.model.util.GeoConstants.DEG_D
Expand Down Expand Up @@ -263,9 +262,6 @@ fun MapView(
// --- Geofence box authoring (Main mode) ---
// When non-null, the user is defining a bounding box for [boxAuthoringDraft] by tapping two corners.
var boxAuthoringDraft by remember { mutableStateOf<Waypoint?>(null) }
// The recipient selected in EditWaypointDialog before box authoring tore it down — carried across the round trip
// so re-opening the dialog with the drawn box doesn't silently reset the destination back to Broadcast.
var boxAuthoringDraftContactKey by remember { mutableStateOf<String?>(null) }
var boxAuthoringFirstCorner by remember { mutableStateOf<LatLng?>(null) }
var boxAuthoringSecondCorner by remember { mutableStateOf<LatLng?>(null) }

Expand Down Expand Up @@ -350,7 +346,6 @@ fun MapView(
DisposableEffect(Unit) { onDispose { fusedLocationClient.removeLocationUpdates(locationCallback) } }

// --- Node & waypoint data ---
val nodes by mapViewModel.nodes.collectAsStateWithLifecycle()
val allNodes by mapViewModel.nodesWithPosition.collectAsStateWithLifecycle(listOf())
val waypoints by mapViewModel.waypoints.collectAsStateWithLifecycle(emptyMap())
val displayableWaypoints = waypoints.values.mapNotNull { it.waypoint }
Expand Down Expand Up @@ -603,7 +598,6 @@ fun MapView(
},
onMapLongClick = { latLng ->
if (isMainMode && isConnected && boxAuthoringDraft == null) {
boxAuthoringDraftContactKey = null
editingWaypoint =
Waypoint(
latitude_i = (latLng.latitude / DEG_D).toInt(),
Expand Down Expand Up @@ -662,10 +656,7 @@ fun MapView(
displayableWaypoints = displayableWaypoints,
myNodeNum = myNodeNum,
isConnected = isConnected,
onEditWaypointRequest = {
boxAuthoringDraftContactKey = null
editingWaypoint = it
},
onEditWaypointRequest = { editingWaypoint = it },
onShowGeofenceInfo = { geofenceInfoWaypoint = it },
selectedWaypointId = selectedWaypointId,
mapLayers = mapLayers,
Expand Down Expand Up @@ -711,41 +702,27 @@ fun MapView(
EditWaypointDialog(
waypoint = waypointToEdit,
displayUnits = displayUnits,
nodes = nodes,
ourNode = ourNodeInfo,
channelSet = channelSet,
initialContactKey =
boxAuthoringDraftContactKey
?: waypoints[waypointToEdit.id]?.let { mapViewModel.waypointContactKey(it) }
?: "0${NodeAddress.ID_BROADCAST}",
onSend = { updatedWp, contactKey ->
onSend = { updatedWp ->
var finalWp = updatedWp
if (updatedWp.id == 0) {
finalWp = finalWp.copy(id = mapViewModel.generatePacketId())
}
if (updatedWp.icon == 0) {
finalWp = finalWp.copy(icon = 0x1F4CD)
}
mapViewModel.sendWaypoint(finalWp, contactKey)
mapViewModel.sendWaypoint(finalWp)
editingWaypoint = null
},
onDelete = { wpToDelete ->
if (wpToDelete.locked_to == 0 && isConnected && wpToDelete.id != 0) {
// Route the expiry to wherever the waypoint was originally sent (a DM or a secondary
// channel), not the default broadcast — otherwise a DM'd waypoint's deletion never
// reaches its actual recipient.
val originalContactKey =
waypoints[wpToDelete.id]?.let { mapViewModel.waypointContactKey(it) }
?: "0${NodeAddress.ID_BROADCAST}"
mapViewModel.sendWaypoint(wpToDelete.copy(expire = 1), originalContactKey)
mapViewModel.sendWaypoint(wpToDelete.copy(expire = 1))
}
mapViewModel.deleteWaypoint(wpToDelete.id)
editingWaypoint = null
},
onDismissRequest = { editingWaypoint = null },
onBeginBoxAuthoring = { draft, contactKey ->
onBeginBoxAuthoring = { draft ->
boxAuthoringDraft = draft
boxAuthoringDraftContactKey = contactKey
boxAuthoringFirstCorner = null
boxAuthoringSecondCorner = null
editingWaypoint = null
Expand All @@ -767,7 +744,6 @@ fun MapView(
if (waypoint.locked_to == 0 && isConnected) {
{
geofenceInfoWaypoint = null
boxAuthoringDraftContactKey = null
editingWaypoint = waypoint
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ class MapViewModel(
radioController,
radioConfigRepository,
notificationPrefs,
uiPrefs,
) {

private val _selectedWaypointId = MutableStateFlow(savedStateHandle.get<Int>("waypointId"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,4 @@ enum class NodeSortOption(val sqlValue: String, val stringRes: StringResource) {
CHANNEL("channel", Res.string.node_sort_channel),
VIA_MQTT("via_mqtt", Res.string.node_sort_via_mqtt),
VIA_FAVORITE("via_favorite", Res.string.node_sort_via_favorite),
;

companion object {
/** Maps a persisted preference ordinal (e.g. `UiPrefs.nodeSort`) back to its option. */
fun fromOrdinal(ordinal: Int): NodeSortOption = entries.getOrElse(ordinal) { VIA_FAVORITE }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,8 @@
*/
package org.meshtastic.core.repository

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import org.meshtastic.core.model.DeviceType
import org.meshtastic.core.model.NodeSortOption

/** Reactive interface for analytics-related preferences. */
interface AnalyticsPrefs {
Expand Down Expand Up @@ -197,10 +194,6 @@ interface UiPrefs {
fun setShouldShowTelemetry(value: Boolean)
}

/** Maps the persisted [UiPrefs.nodeSort] ordinal to its [NodeSortOption], the single source every consumer shares. */
val UiPrefs.nodeSortOption: Flow<NodeSortOption>
get() = nodeSort.map { NodeSortOption.fromOrdinal(it) }

/** Reactive interface for notification preferences. */
interface NotificationPrefs {
val messagesEnabled: StateFlow<Boolean>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1765,14 +1765,10 @@
<string name="wait_for_bluetooth_duration_seconds">Wait for Bluetooth duration</string>
<string name="wake_on_tap_or_motion">Wake on tap or motion</string>
<string name="warning">Warning</string>
<!-- WAYPOINT -->
<string name="waypoint_delete">Delete waypoint?</string>
<string name="waypoint_edit">Edit waypoint</string>
<string name="waypoint_new">New waypoint</string>
<string name="waypoint_received">Received waypoint: %1$s</string>
<string name="waypoint_recipient_broadcast">Broadcast (all nodes)</string>
<string name="waypoint_recipient_channel">Channel %1$d</string>
<string name="waypoint_send_to">Send to</string>
<string name="weight">Weight</string>
<!-- WIFI -->
<string name="wifi_config">WiFi Options</string>
Expand Down
11 changes: 0 additions & 11 deletions feature/map/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,5 @@ kotlin {
implementation(projects.core.ui)
implementation(projects.core.di)
}

getByName("androidHostTest") {
dependencies {
implementation(projects.core.testing)
implementation(libs.kotlinx.coroutines.test)
implementation(libs.compose.multiplatform.ui.test)
// Registers a ComponentActivity in the test manifest so Robolectric's runComposeUiTest has a host
// activity to attach to (feature/map is a library module with no launcher activity of its own).
implementation(libs.androidx.compose.ui.test.manifest)
}
}
}
}
Loading
Loading