Add MapScreen (#3142)

This commit is contained in:
Phil Oliver 2025-09-18 12:31:17 -04:00 committed by GitHub
parent f2d29d4582
commit 8b34a69d62
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 97 additions and 46 deletions

View file

@ -17,58 +17,24 @@
package com.geeksville.mesh.navigation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import com.geeksville.mesh.R
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.common.components.MainAppBar
import com.geeksville.mesh.ui.map.MapView
import com.geeksville.mesh.ui.node.components.NodeMenuAction
import com.geeksville.mesh.ui.map.MapScreen
fun NavGraphBuilder.mapGraph(navController: NavHostController, uiViewModel: UIViewModel) {
composable<MapRoutes.Map>(deepLinks = listOf(navDeepLink<MapRoutes.Map>(basePath = "$DEEP_LINK_BASE_URI/map"))) {
val ourNodeInfo by uiViewModel.ourNodeInfo.collectAsStateWithLifecycle()
val isConnected by uiViewModel.isConnectedStateFlow.collectAsStateWithLifecycle()
Scaffold(
topBar = {
MainAppBar(
title = stringResource(R.string.map),
ourNode = ourNodeInfo,
isConnected = isConnected,
showNodeChip = ourNodeInfo != null && isConnected,
canNavigateUp = false,
onNavigateUp = {},
actions = {},
onAction = { action ->
when (action) {
is NodeMenuAction.MoreDetails -> {
navController.navigate(NodesRoutes.NodeDetailGraph(action.node.num)) {
launchSingleTop = true
restoreState = true
}
}
else -> {}
}
},
)
MapScreen(
uiViewModel = uiViewModel,
onClickNodeChip = {
navController.navigate(NodesRoutes.NodeDetailGraph(it)) {
launchSingleTop = true
restoreState = true
}
},
) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) {
MapView(
uiViewModel = uiViewModel,
navigateToNodeDetails = { navController.navigate(NodesRoutes.NodeDetailGraph(it)) },
)
}
}
navigateToNodeDetails = { navController.navigate(NodesRoutes.NodeDetailGraph(it)) },
)
}
}

View file

@ -24,6 +24,7 @@ import com.geeksville.mesh.database.NodeRepository
import com.geeksville.mesh.database.PacketRepository
import com.geeksville.mesh.database.entity.Packet
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@ -37,6 +38,7 @@ abstract class BaseMapViewModel(
protected val mapPrefs: MapPrefs,
nodeRepository: NodeRepository,
packetRepository: PacketRepository,
radioConfigRepository: RadioConfigRepository,
) : ViewModel() {
val nodes: StateFlow<List<Node>> =
@ -67,6 +69,13 @@ abstract class BaseMapViewModel(
private val showPrecisionCircleOnMap = MutableStateFlow(mapPrefs.showPrecisionCircleOnMap)
val ourNodeInfo: StateFlow<Node?> = nodeRepository.ourNodeInfo
val isConnected =
radioConfigRepository.connectionState
.map { it.isConnected() }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), false)
fun toggleOnlyFavorites() {
val current = showOnlyFavorites.value
mapPrefs.showOnlyFavorites = !current

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.map
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.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.R
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.common.components.MainAppBar
import com.geeksville.mesh.ui.node.components.NodeMenuAction
@Composable
fun MapScreen(
onClickNodeChip: (Int) -> Unit,
navigateToNodeDetails: (Int) -> Unit,
uiViewModel: UIViewModel,
modifier: Modifier = Modifier,
mapViewModel: MapViewModel = hiltViewModel(),
) {
val ourNodeInfo by mapViewModel.ourNodeInfo.collectAsStateWithLifecycle()
val isConnected by mapViewModel.isConnected.collectAsStateWithLifecycle()
@Suppress("ViewModelForwarding")
Scaffold(
modifier = modifier,
topBar = {
MainAppBar(
title = stringResource(R.string.map),
ourNode = ourNodeInfo,
isConnected = isConnected,
showNodeChip = ourNodeInfo != null && isConnected,
canNavigateUp = false,
onNavigateUp = {},
actions = {},
onAction = { action ->
when (action) {
is NodeMenuAction.MoreDetails -> onClickNodeChip(action.node.num)
else -> {}
}
},
)
},
) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) {
MapView(
uiViewModel = uiViewModel,
mapViewModel = mapViewModel,
navigateToNodeDetails = navigateToNodeDetails,
)
}
}
}