refactor: organize navigation (#2042)

This commit is contained in:
James Rich 2025-06-06 20:45:26 +00:00 committed by GitHub
parent c757224269
commit 2a05fc072d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 306 additions and 265 deletions

View file

@ -77,8 +77,10 @@ import com.geeksville.mesh.model.DeviceVersion
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.navigation.ChannelsRoutes
import com.geeksville.mesh.navigation.ConnectionsRoutes
import com.geeksville.mesh.navigation.ContactsRoutes
import com.geeksville.mesh.navigation.NavGraph
import com.geeksville.mesh.navigation.NodesRoutes
import com.geeksville.mesh.navigation.RadioConfigRoutes
import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.navigation.showLongNameTitle
import com.geeksville.mesh.service.MeshService
@ -89,7 +91,7 @@ import com.geeksville.mesh.ui.common.components.SimpleAlertDialog
import com.geeksville.mesh.ui.debug.DebugMenuActions
enum class TopLevelDestination(@StringRes val label: Int, val icon: ImageVector, val route: Route) {
Contacts(R.string.contacts, Icons.AutoMirrored.TwoTone.Chat, Route.Contacts),
Contacts(R.string.contacts, Icons.AutoMirrored.TwoTone.Chat, ContactsRoutes.Contacts),
Nodes(R.string.nodes, Icons.TwoTone.People, NodesRoutes.Nodes),
Map(R.string.map, Icons.TwoTone.Map, Route.Map),
Channels(R.string.channels, Icons.TwoTone.Contactless, ChannelsRoutes.Channels),
@ -167,8 +169,8 @@ fun MainScreen(
) { action ->
when (action) {
MainMenuAction.DEBUG -> navController.navigate(Route.DebugPanel)
MainMenuAction.RADIO_CONFIG -> navController.navigate(Route.RadioConfig())
MainMenuAction.QUICK_CHAT -> navController.navigate(Route.QuickChat)
MainMenuAction.RADIO_CONFIG -> navController.navigate(RadioConfigRoutes.RadioConfig())
MainMenuAction.QUICK_CHAT -> navController.navigate(ContactsRoutes.QuickChat)
else -> onAction(action)
}
}
@ -271,7 +273,7 @@ private fun MainAppBar(
val canNavigateBack = navController.previousBackStackEntry != null
val isTopLevelRoute = currentDestination?.isTopLevel() == true
val navigateUp: () -> Unit = navController::navigateUp
if (currentDestination?.hasRoute<Route.Messages>() == true) {
if (currentDestination?.hasRoute<ContactsRoutes.Messages>() == true) {
return
}
TopAppBar(
@ -288,12 +290,12 @@ private fun MainAppBar(
stringResource(id = R.string.debug_panel),
)
currentDestination.hasRoute<Route.QuickChat>() ->
currentDestination.hasRoute<ContactsRoutes.QuickChat>() ->
Text(
stringResource(id = R.string.quick_chat),
)
currentDestination.hasRoute<Route.Share>() ->
currentDestination.hasRoute<ContactsRoutes.Share>() ->
Text(
stringResource(id = R.string.share_to),
)

View file

@ -96,6 +96,7 @@ import com.geeksville.mesh.model.NO_DEVICE_SELECTED
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.navigation.ConfigRoute
import com.geeksville.mesh.navigation.RadioConfigRoutes
import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.navigation.getNavRouteFrom
import com.geeksville.mesh.repository.network.NetworkRepository
@ -158,7 +159,9 @@ fun ConnectionsScreen(
getNavRouteFrom(radioConfigState.route)?.let { route ->
isWaiting = false
radioConfigViewModel.clearPacketResponse()
onConfigNavigate(route)
if (route == RadioConfigRoutes.LoRa) {
onConfigNavigate(RadioConfigRoutes.LoRa)
}
}
},
)

View file

@ -66,7 +66,7 @@ import java.util.concurrent.TimeUnit
@Composable
fun ContactsScreen(
uiViewModel: UIViewModel = hiltViewModel(),
onNavigate: (String) -> Unit = {}
onNavigateToMessages: (String) -> Unit = {}
) {
var showMuteDialog by remember { mutableStateOf(false) }
var showDeleteDialog by remember { mutableStateOf(false) }
@ -96,7 +96,7 @@ fun ContactsScreen(
}
} else {
// If not in selection mode, navigate to messages
onNavigate(contact.contactKey)
onNavigateToMessages(contact.contactKey)
}
}

View file

@ -119,6 +119,8 @@ import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.model.isUnmessageableRole
import com.geeksville.mesh.navigation.NodeDetailRoutes
import com.geeksville.mesh.navigation.RadioConfigRoutes
import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.service.ServiceAction
import com.geeksville.mesh.ui.common.components.PreferenceCategory
@ -143,14 +145,14 @@ private enum class LogsType(
val icon: ImageVector,
val route: Route
) {
DEVICE(R.string.device_metrics_log, Icons.Default.ChargingStation, Route.DeviceMetrics),
NODE_MAP(R.string.node_map, Icons.Default.Map, Route.NodeMap),
POSITIONS(R.string.position_log, Icons.Default.LocationOn, Route.PositionLog),
ENVIRONMENT(R.string.env_metrics_log, Icons.Default.Thermostat, Route.EnvironmentMetrics),
SIGNAL(R.string.sig_metrics_log, Icons.Default.SignalCellularAlt, Route.SignalMetrics),
POWER(R.string.power_metrics_log, Icons.Default.Power, Route.PowerMetrics),
TRACEROUTE(R.string.traceroute_log, Icons.Default.Route, Route.TracerouteLog),
HOST(R.string.host_metrics_log, Icons.Default.Memory, Route.HostMetricsLog),
DEVICE(R.string.device_metrics_log, Icons.Default.ChargingStation, NodeDetailRoutes.DeviceMetrics),
NODE_MAP(R.string.node_map, Icons.Default.Map, NodeDetailRoutes.NodeMap),
POSITIONS(R.string.position_log, Icons.Default.LocationOn, NodeDetailRoutes.PositionLog),
ENVIRONMENT(R.string.env_metrics_log, Icons.Default.Thermostat, NodeDetailRoutes.EnvironmentMetrics),
SIGNAL(R.string.sig_metrics_log, Icons.Default.SignalCellularAlt, NodeDetailRoutes.SignalMetrics),
POWER(R.string.power_metrics_log, Icons.Default.Power, NodeDetailRoutes.PowerMetrics),
TRACEROUTE(R.string.traceroute_log, Icons.Default.Route, NodeDetailRoutes.TracerouteLog),
HOST(R.string.host_metrics_log, Icons.Default.Memory, NodeDetailRoutes.HostMetricsLog),
}
@Suppress("LongMethod")
@ -327,7 +329,7 @@ private fun NodeDetailList(
icon = Icons.Default.Settings,
enabled = true
) {
onAction(Route.RadioConfig(node.num))
onAction(RadioConfigRoutes.RadioConfig(node.num))
}
}
}

View file

@ -21,6 +21,7 @@ import android.app.Application
import android.net.Uri
import android.os.RemoteException
import androidx.annotation.StringRes
import androidx.core.net.toUri
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@ -44,12 +45,12 @@ import com.geeksville.mesh.model.getChannelList
import com.geeksville.mesh.model.getStringResFrom
import com.geeksville.mesh.model.toChannelSet
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
import com.geeksville.mesh.service.MeshService.ConnectionState
import com.geeksville.mesh.navigation.AdminRoute
import com.geeksville.mesh.navigation.ConfigRoute
import com.geeksville.mesh.navigation.ModuleRoute
import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.navigation.RadioConfigRoutes
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
import com.geeksville.mesh.service.MeshService.ConnectionState
import com.geeksville.mesh.util.UiText
import com.google.protobuf.MessageLite
import dagger.hilt.android.lifecycle.HiltViewModel
@ -93,7 +94,7 @@ class RadioConfigViewModel @Inject constructor(
) : ViewModel(), Logging {
private val meshService: IMeshService? get() = radioConfigRepository.meshService
private val destNum = savedStateHandle.toRoute<Route.RadioConfig>().destNum
private val destNum = savedStateHandle.toRoute<RadioConfigRoutes.RadioConfig>().destNum
private val _destNode = MutableStateFlow<Node?>(null)
val destNode: StateFlow<Node?> get() = _destNode
@ -214,7 +215,7 @@ class RadioConfigViewModel @Inject constructor(
}
private fun setChannels(channelUrl: String) = viewModelScope.launch {
val new = Uri.parse(channelUrl).toChannelSet()
val new = channelUrl.toUri().toChannelSet()
val old = radioConfigRepository.channelSetFlow.firstOrNull() ?: return@launch
updateChannels(new.settingsList, old.settingsList)
}
@ -568,9 +569,11 @@ class RadioConfigViewModel @Inject constructor(
// Stop once we get to the first disabled entry
if (response.role != ChannelProtos.Channel.Role.DISABLED) {
_radioConfigState.update { state ->
state.copy(channelList = state.channelList.toMutableList().apply {
add(response.index, response.settings)
})
state.copy(
channelList = state.channelList.toMutableList().apply {
add(response.index, response.settings)
}
)
}
incrementCompleted()
if (response.index + 1 < maxChannels && route == ConfigRoute.CHANNELS.name) {