Decouple NodeMapScreen from MetricsViewModel (#3323)

Co-authored-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
Phil Oliver 2025-10-04 09:17:00 -04:00 committed by GitHub
parent 8b4397a825
commit ff95bc5311
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 87 additions and 47 deletions

View file

@ -24,9 +24,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.viewinterop.AndroidView
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.MetricsViewModel
import org.meshtastic.feature.map.addCopyright
import org.meshtastic.feature.map.addPolyline
import org.meshtastic.feature.map.addPositionMarkers
@ -39,20 +37,16 @@ import org.osmdroid.util.GeoPoint
private const val DEG_D = 1e-7
@Composable
fun NodeMapScreen(
metricsViewModel: MetricsViewModel = hiltViewModel(),
nodeMapViewModel: NodeMapViewModel = hiltViewModel(),
onNavigateUp: () -> Unit,
) {
fun NodeMapScreen(nodeMapViewModel: NodeMapViewModel, onNavigateUp: () -> Unit) {
val density = LocalDensity.current
val state by metricsViewModel.state.collectAsStateWithLifecycle()
val geoPoints = state.positionLogs.map { GeoPoint(it.latitudeI * DEG_D, it.longitudeI * DEG_D) }
val positionLogs by nodeMapViewModel.positionLogs.collectAsStateWithLifecycle()
val geoPoints = positionLogs.map { GeoPoint(it.latitudeI * DEG_D, it.longitudeI * DEG_D) }
val cameraView = remember { BoundingBox.fromGeoPoints(geoPoints) }
val mapView =
rememberMapViewWithLifecycle(
applicationId = nodeMapViewModel.applicationId,
box = cameraView,
tileSource = metricsViewModel.tileSource,
tileSource = nodeMapViewModel.tileSource,
)
AndroidView(
@ -64,7 +58,7 @@ fun NodeMapScreen(
map.addScaleBarOverlay(density)
map.addPolyline(density, geoPoints) {}
map.addPositionMarkers(state.positionLogs) {}
map.addPositionMarkers(positionLogs) {}
},
)
}

View file

@ -21,32 +21,24 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.MetricsViewModel
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.feature.map.MapView
import org.meshtastic.feature.map.node.NodeMapViewModel
@Composable
fun NodeMapScreen(
metricsViewModel: MetricsViewModel = hiltViewModel(),
nodeMapViewModel: NodeMapViewModel = hiltViewModel(),
onNavigateUp: () -> Unit,
) {
val state by metricsViewModel.state.collectAsState()
val positions = state.positionLogs
val destNum = state.node?.num
val ourNodeInfo by nodeMapViewModel.ourNodeInfo.collectAsStateWithLifecycle()
fun NodeMapScreen(nodeMapViewModel: NodeMapViewModel, onNavigateUp: () -> Unit) {
val node by nodeMapViewModel.node.collectAsStateWithLifecycle()
val positions by nodeMapViewModel.positionLogs.collectAsStateWithLifecycle()
val destNum = node?.num
Scaffold(
topBar = {
MainAppBar(
title = state.node?.user?.longName ?: "",
ourNode = ourNodeInfo,
title = node?.user?.longName ?: "",
ourNode = null,
showNodeChip = false,
canNavigateUp = true,
onNavigateUp = onNavigateUp,

View file

@ -62,6 +62,7 @@ import org.meshtastic.core.model.DataPacket
import org.meshtastic.core.model.DeviceHardware
import org.meshtastic.core.navigation.NodesRoutes
import org.meshtastic.core.prefs.map.MapPrefs
import org.meshtastic.core.proto.toPosition
import org.meshtastic.core.service.ServiceAction
import org.meshtastic.core.service.ServiceRepository
import org.meshtastic.core.strings.R
@ -190,12 +191,6 @@ enum class TimeFrame(val seconds: Long, @StringRes val strRes: Int) {
private fun MeshPacket.hasValidSignal(): Boolean =
rxTime > 0 && (rxSnr != 0f && rxRssi != 0) && (hopStart > 0 && hopStart - hopLimit == 0)
private fun MeshPacket.toPosition(): Position? = if (!decoded.wantResponse) {
runCatching { Position.parseFrom(decoded.payload) }.getOrNull()
} else {
null
}
@Suppress("LongParameterList")
@HiltViewModel
class MetricsViewModel

View file

@ -57,6 +57,7 @@ import org.meshtastic.core.navigation.NodeDetailRoutes
import org.meshtastic.core.navigation.NodesRoutes
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.strings.R
import org.meshtastic.feature.map.node.NodeMapViewModel
fun NavGraphBuilder.nodesGraph(navController: NavHostController) {
navigation<NodesRoutes.NodesGraph>(startDestination = NodesRoutes.Nodes) {
@ -90,6 +91,21 @@ fun NavGraphBuilder.nodeDetailGraph(navController: NavHostController) {
)
}
composable<NodeDetailRoutes.NodeMap>(
deepLinks =
listOf(
navDeepLink<NodeDetailRoutes.NodeMap>(basePath = "$DEEP_LINK_BASE_URI/node/{destNum}/node_map"),
navDeepLink<NodeDetailRoutes.NodeMap>(basePath = "$DEEP_LINK_BASE_URI/node/node_map"),
),
) { backStackEntry ->
val parentGraphBackStackEntry =
remember(backStackEntry) { navController.getBackStackEntry(NodesRoutes.NodeDetailGraph::class) }
NodeMapScreen(
hiltViewModel<NodeMapViewModel>(parentGraphBackStackEntry),
onNavigateUp = navController::navigateUp,
)
}
NodeDetailRoute.entries.forEach { entry ->
when (entry.route) {
is NodeDetailRoutes.DeviceMetrics ->
@ -98,12 +114,6 @@ fun NavGraphBuilder.nodeDetailGraph(navController: NavHostController) {
entry,
entry.screenComposable,
)
is NodeDetailRoutes.NodeMap ->
addNodeDetailScreenComposable<NodeDetailRoutes.NodeMap>(
navController,
entry,
entry.screenComposable,
)
is NodeDetailRoutes.PositionLog ->
addNodeDetailScreenComposable<NodeDetailRoutes.PositionLog>(
navController,
@ -199,12 +209,6 @@ enum class NodeDetailRoute(
Icons.Default.Router,
{ metricsVM, onNavigateUp -> DeviceMetricsScreen(metricsVM, onNavigateUp) },
),
NODE_MAP(
R.string.node_map,
NodeDetailRoutes.NodeMap,
Icons.Default.LocationOn,
{ metricsVM, onNavigateUp -> NodeMapScreen(metricsVM, onNavigateUp = onNavigateUp) },
),
POSITION_LOG(
R.string.position_log,
NodeDetailRoutes.PositionLog,