mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
ci: cache Robolectric SDK jars to prevent flaky SocketException failures (#5045)
This commit is contained in:
parent
93e0b9ca57
commit
1390a3cd4f
34 changed files with 374 additions and 426 deletions
9
.github/actions/gradle-setup/action.yml
vendored
9
.github/actions/gradle-setup/action.yml
vendored
|
|
@ -27,6 +27,15 @@ runs:
|
|||
distribution: ${{ inputs.jdk_distribution }}
|
||||
token: ${{ github.token }}
|
||||
|
||||
# Robolectric downloads instrumented SDK jars from Maven Central at test time.
|
||||
# Cache them to avoid flaky SocketException failures on CI runners.
|
||||
# Update the key when bumping robolectric version in libs.versions.toml or sdk in robolectric.properties.
|
||||
- name: Cache Robolectric SDK jars
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.m2/repository/org/robolectric
|
||||
key: robolectric-4.16.1-sdk34
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Meshtastic-Android is a Kotlin Multiplatform (KMP) application for off-grid, dec
|
|||
| `core:data` | Core manager implementations and data orchestration. |
|
||||
| `core:network` | KMP networking layer using Ktor, MQTT abstractions, and shared transport (`StreamFrameCodec`, `TcpTransport`, `SerialTransport`, `BleRadioInterface`). |
|
||||
| `core:di` | Common DI qualifiers and dispatchers. |
|
||||
| `core:navigation` | Shared navigation keys/routes for Navigation 3, `DeepLinkRouter` for typed backstack synthesis, and `MeshtasticNavSavedStateConfig` for backstack persistence. |
|
||||
| `core:navigation` | Shared navigation keys/routes for Navigation 3 using `@Serializable sealed interface` hierarchies per feature domain (e.g., `SettingsRoute`, `NodesRoute`). `DeepLinkRouter` for typed backstack synthesis, and `MeshtasticNavSavedStateConfig` with `subclassesOfSealed()` for automatic polymorphic backstack persistence — new routes are registered at compile time. |
|
||||
| `core:ui` | Shared Compose UI components (`MeshtasticAppShell`, `MeshtasticNavDisplay`, `MeshtasticNavigationSuite`, `AlertHost`, `SharedDialogs`, `PlaceholderScreen`, `MainAppBar`, dialogs, preferences) and platform abstractions. |
|
||||
| `core:service` | KMP service layer; Android bindings stay in `androidMain`. |
|
||||
| `core:api` | Public AIDL/API integration module for external clients. |
|
||||
|
|
@ -189,6 +189,7 @@ Always run commands in the following order to ensure reliability. Do not attempt
|
|||
- **`maxParallelForks` CI logic:** ProjectExtensions.kt line ~79 checks `project.findProperty("ci") == "true"` and uses full available processors in CI (4 forks on std runners) vs. half locally. All CI invocations pass `-Pci=true` to enable this.
|
||||
- **Detekt report formats:** Detekt.kt line ~44 checks `project.findProperty("ci") == "true"` and disables html, txt, md reports in CI; only xml + sarif are required for GitHub reporting.
|
||||
- **KMP Smoke Compile:** Use `./gradlew kmpSmokeCompile` instead of listing individual module compile tasks. The `kmpSmokeCompile` lifecycle task (registered in `RootConventionPlugin`) auto-discovers all KMP modules and depends on their `compileKotlinJvm` + `compileKotlinIosSimulatorArm64` tasks.
|
||||
- **Robolectric SDK caching:** The `gradle-setup` composite action caches `~/.m2/repository/org/robolectric` to prevent flaky `SocketException` failures when Robolectric downloads instrumented SDK jars. The cache key is `robolectric-{version}-sdk{level}` — update it when bumping the Robolectric version in `libs.versions.toml` or the SDK level in `robolectric.properties` / `@Config(sdk = ...)`.
|
||||
- **`mavenLocal()` gated:** The `mavenLocal()` repository is disabled by default to prevent CI cache poisoning. For local JitPack testing, pass `-PuseMavenLocal` to Gradle.
|
||||
- **Terminal Pagers:** When running shell commands like `git diff` or `git log`, ALWAYS use `--no-pager` (e.g., `git --no-pager diff`) to prevent the agent from getting stuck in an interactive prompt.
|
||||
- **Text Search:** Prefer using `rg` (ripgrep) over `grep` or `find` for fast text searching across the codebase.
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import co.touchlab.kermit.Logger
|
|||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.meshtastic.app.BuildConfig
|
||||
import org.meshtastic.core.model.ConnectionState
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
import org.meshtastic.core.navigation.rememberMultiBackstack
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.app_too_old
|
||||
|
|
@ -53,7 +53,7 @@ import org.meshtastic.feature.wifiprovision.navigation.wifiProvisionGraph
|
|||
@Composable
|
||||
fun MainScreen() {
|
||||
val viewModel: UIViewModel = koinViewModel()
|
||||
val multiBackstack = rememberMultiBackstack(NodesRoutes.NodesGraph)
|
||||
val multiBackstack = rememberMultiBackstack(NodesRoute.NodesGraph)
|
||||
val backStack = multiBackstack.activeBackStack
|
||||
|
||||
AndroidAppVersionCheck(viewModel)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import kotlinx.coroutines.flow.emptyFlow
|
|||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
import org.meshtastic.feature.connections.navigation.connectionsGraph
|
||||
import org.meshtastic.feature.firmware.navigation.firmwareGraph
|
||||
import org.meshtastic.feature.map.navigation.mapGraph
|
||||
|
|
@ -44,7 +44,7 @@ class NavigationAssemblyTest {
|
|||
@Test
|
||||
fun verifyNavigationGraphsAssembleWithoutCrashing() {
|
||||
composeTestRule.setContent {
|
||||
val backStack = rememberNavBackStack(NodesRoutes.NodesGraph)
|
||||
val backStack = rememberNavBackStack(NodesRoute.NodesGraph)
|
||||
entryProvider<NavKey> {
|
||||
contactsGraph(backStack, emptyFlow())
|
||||
nodesGraph(backStack = backStack, scrollToTopEvents = emptyFlow())
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Contains serializable `NavKey` route classes/objects used by shared feature grap
|
|||
Parses Meshtastic deep-link URIs and synthesizes a typed backstack (for example `/nodes/1234/device-metrics`).
|
||||
|
||||
### 3. `NavigationConfig.kt`
|
||||
Defines `MeshtasticNavSavedStateConfig` so Navigation 3 backstacks can be persisted/restored safely.
|
||||
Defines `MeshtasticNavSavedStateConfig` using sealed interface hierarchies so Navigation 3 backstacks can be persisted/restored safely — new routes are auto-registered at compile time.
|
||||
|
||||
## Features
|
||||
- **Type-Safety**: Uses serializable `NavKey` routes instead of ad-hoc string routes.
|
||||
|
|
@ -25,10 +25,10 @@ Feature modules depend on this module to define their entry points and navigate
|
|||
```kotlin
|
||||
import androidx.navigation3.runtime.NavBackStack
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
|
||||
fun openNodeDetail(backStack: NavBackStack<NavKey>, destNum: Int) {
|
||||
backStack.add(NodesRoutes.NodeDetail(destNum))
|
||||
backStack.add(NodesRoute.NodeDetail(destNum))
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -59,11 +59,11 @@ object DeepLinkRouter {
|
|||
"messages",
|
||||
"quickchat",
|
||||
-> routeContacts(uri, pathSegments)
|
||||
"connections" -> listOf(ConnectionsRoutes.ConnectionsGraph)
|
||||
"connections" -> listOf(ConnectionsRoute.ConnectionsGraph)
|
||||
"map" -> routeMap(uri, pathSegments)
|
||||
"nodes" -> routeNodes(uri, pathSegments)
|
||||
"settings" -> routeSettings(pathSegments)
|
||||
"channels" -> listOf(ChannelsRoutes.ChannelsGraph)
|
||||
"channels" -> listOf(ChannelsRoute.ChannelsGraph)
|
||||
"firmware" -> routeFirmware(pathSegments)
|
||||
"wifi-provision" -> routeWifiProvision(uri)
|
||||
else -> {
|
||||
|
|
@ -78,31 +78,31 @@ object DeepLinkRouter {
|
|||
return when (firstSegment) {
|
||||
"share" -> {
|
||||
val message = uri.getQueryParameter("message") ?: ""
|
||||
listOf(ContactsRoutes.ContactsGraph, ContactsRoutes.Share(message))
|
||||
listOf(ContactsRoute.ContactsGraph, ContactsRoute.Share(message))
|
||||
}
|
||||
"quickchat" -> {
|
||||
listOf(ContactsRoutes.ContactsGraph, ContactsRoutes.QuickChat)
|
||||
listOf(ContactsRoute.ContactsGraph, ContactsRoute.QuickChat)
|
||||
}
|
||||
"messages" -> {
|
||||
val contactKey = if (segments.size > 1) segments[1] else uri.getQueryParameter("contactKey") ?: ""
|
||||
val message = uri.getQueryParameter("message") ?: ""
|
||||
if (contactKey.isNotBlank()) {
|
||||
listOf(
|
||||
ContactsRoutes.ContactsGraph,
|
||||
ContactsRoutes.Messages(contactKey = contactKey, message = message),
|
||||
ContactsRoute.ContactsGraph,
|
||||
ContactsRoute.Messages(contactKey = contactKey, message = message),
|
||||
)
|
||||
} else {
|
||||
listOf(ContactsRoutes.ContactsGraph)
|
||||
listOf(ContactsRoute.ContactsGraph)
|
||||
}
|
||||
}
|
||||
else -> listOf(ContactsRoutes.ContactsGraph)
|
||||
else -> listOf(ContactsRoute.ContactsGraph)
|
||||
}
|
||||
}
|
||||
|
||||
private fun routeMap(uri: CommonUri, segments: List<String>): List<NavKey> {
|
||||
val waypointIdStr = if (segments.size > 1) segments[1] else uri.getQueryParameter("waypointId")
|
||||
val waypointId = waypointIdStr?.toIntOrNull()
|
||||
return listOf(MapRoutes.Map(waypointId))
|
||||
return listOf(MapRoute.Map(waypointId))
|
||||
}
|
||||
|
||||
private fun routeNodes(uri: CommonUri, segments: List<String>): List<NavKey> {
|
||||
|
|
@ -110,17 +110,17 @@ object DeepLinkRouter {
|
|||
val destNum = destNumStr?.toIntOrNull()
|
||||
|
||||
return if (destNum == null) {
|
||||
listOf(NodesRoutes.NodesGraph)
|
||||
listOf(NodesRoute.NodesGraph)
|
||||
} else if (segments.size > 2) {
|
||||
val subRouteStr = segments[2].lowercase()
|
||||
val detailRouteFn = nodeDetailSubRoutes[subRouteStr]
|
||||
if (detailRouteFn != null) {
|
||||
listOf(NodesRoutes.NodesGraph, NodesRoutes.NodeDetailGraph(destNum), detailRouteFn(destNum))
|
||||
listOf(NodesRoute.NodesGraph, NodesRoute.NodeDetailGraph(destNum), detailRouteFn(destNum))
|
||||
} else {
|
||||
listOf(NodesRoutes.NodesGraph, NodesRoutes.NodeDetail(destNum))
|
||||
listOf(NodesRoute.NodesGraph, NodesRoute.NodeDetail(destNum))
|
||||
}
|
||||
} else {
|
||||
listOf(NodesRoutes.NodesGraph, NodesRoutes.NodeDetail(destNum))
|
||||
listOf(NodesRoute.NodesGraph, NodesRoute.NodeDetail(destNum))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -142,79 +142,79 @@ object DeepLinkRouter {
|
|||
}
|
||||
|
||||
if (subRouteStr == null) {
|
||||
return listOf(SettingsRoutes.SettingsGraph(destNum))
|
||||
return listOf(SettingsRoute.SettingsGraph(destNum))
|
||||
}
|
||||
|
||||
val subRoute = settingsSubRoutes[subRouteStr]
|
||||
return if (subRoute != null) {
|
||||
listOf(SettingsRoutes.SettingsGraph(destNum), subRoute)
|
||||
listOf(SettingsRoute.SettingsGraph(destNum), subRoute)
|
||||
} else {
|
||||
listOf(SettingsRoutes.SettingsGraph(destNum))
|
||||
listOf(SettingsRoute.SettingsGraph(destNum))
|
||||
}
|
||||
}
|
||||
|
||||
private fun routeWifiProvision(uri: CommonUri): List<NavKey> {
|
||||
val address = uri.getQueryParameter("address")
|
||||
return listOf(WifiProvisionRoutes.WifiProvision(address))
|
||||
return listOf(WifiProvisionRoute.WifiProvision(address))
|
||||
}
|
||||
|
||||
private fun routeFirmware(segments: List<String>): List<NavKey> {
|
||||
val update = if (segments.size > 1) segments[1].lowercase() == "update" else false
|
||||
return if (update) {
|
||||
listOf(FirmwareRoutes.FirmwareGraph, FirmwareRoutes.FirmwareUpdate)
|
||||
listOf(FirmwareRoute.FirmwareGraph, FirmwareRoute.FirmwareUpdate)
|
||||
} else {
|
||||
listOf(FirmwareRoutes.FirmwareGraph)
|
||||
listOf(FirmwareRoute.FirmwareGraph)
|
||||
}
|
||||
}
|
||||
|
||||
private val settingsSubRoutes: Map<String, Route> =
|
||||
mapOf(
|
||||
"device-config" to SettingsRoutes.DeviceConfiguration,
|
||||
"module-config" to SettingsRoutes.ModuleConfiguration,
|
||||
"admin" to SettingsRoutes.Administration,
|
||||
"user" to SettingsRoutes.User,
|
||||
"channel" to SettingsRoutes.ChannelConfig,
|
||||
"device" to SettingsRoutes.Device,
|
||||
"position" to SettingsRoutes.Position,
|
||||
"power" to SettingsRoutes.Power,
|
||||
"network" to SettingsRoutes.Network,
|
||||
"display" to SettingsRoutes.Display,
|
||||
"lora" to SettingsRoutes.LoRa,
|
||||
"bluetooth" to SettingsRoutes.Bluetooth,
|
||||
"security" to SettingsRoutes.Security,
|
||||
"mqtt" to SettingsRoutes.MQTT,
|
||||
"serial" to SettingsRoutes.Serial,
|
||||
"ext-notification" to SettingsRoutes.ExtNotification,
|
||||
"store-forward" to SettingsRoutes.StoreForward,
|
||||
"range-test" to SettingsRoutes.RangeTest,
|
||||
"telemetry" to SettingsRoutes.Telemetry,
|
||||
"canned-message" to SettingsRoutes.CannedMessage,
|
||||
"audio" to SettingsRoutes.Audio,
|
||||
"remote-hardware" to SettingsRoutes.RemoteHardware,
|
||||
"neighbor-info" to SettingsRoutes.NeighborInfo,
|
||||
"ambient-lighting" to SettingsRoutes.AmbientLighting,
|
||||
"detection-sensor" to SettingsRoutes.DetectionSensor,
|
||||
"paxcounter" to SettingsRoutes.Paxcounter,
|
||||
"status-message" to SettingsRoutes.StatusMessage,
|
||||
"traffic-management" to SettingsRoutes.TrafficManagement,
|
||||
"tak" to SettingsRoutes.TAK,
|
||||
"clean-node-db" to SettingsRoutes.CleanNodeDb,
|
||||
"debug-panel" to SettingsRoutes.DebugPanel,
|
||||
"about" to SettingsRoutes.About,
|
||||
"filter-settings" to SettingsRoutes.FilterSettings,
|
||||
"device-config" to SettingsRoute.DeviceConfiguration,
|
||||
"module-config" to SettingsRoute.ModuleConfiguration,
|
||||
"admin" to SettingsRoute.Administration,
|
||||
"user" to SettingsRoute.User,
|
||||
"channel" to SettingsRoute.ChannelConfig,
|
||||
"device" to SettingsRoute.Device,
|
||||
"position" to SettingsRoute.Position,
|
||||
"power" to SettingsRoute.Power,
|
||||
"network" to SettingsRoute.Network,
|
||||
"display" to SettingsRoute.Display,
|
||||
"lora" to SettingsRoute.LoRa,
|
||||
"bluetooth" to SettingsRoute.Bluetooth,
|
||||
"security" to SettingsRoute.Security,
|
||||
"mqtt" to SettingsRoute.MQTT,
|
||||
"serial" to SettingsRoute.Serial,
|
||||
"ext-notification" to SettingsRoute.ExtNotification,
|
||||
"store-forward" to SettingsRoute.StoreForward,
|
||||
"range-test" to SettingsRoute.RangeTest,
|
||||
"telemetry" to SettingsRoute.Telemetry,
|
||||
"canned-message" to SettingsRoute.CannedMessage,
|
||||
"audio" to SettingsRoute.Audio,
|
||||
"remote-hardware" to SettingsRoute.RemoteHardware,
|
||||
"neighbor-info" to SettingsRoute.NeighborInfo,
|
||||
"ambient-lighting" to SettingsRoute.AmbientLighting,
|
||||
"detection-sensor" to SettingsRoute.DetectionSensor,
|
||||
"paxcounter" to SettingsRoute.Paxcounter,
|
||||
"status-message" to SettingsRoute.StatusMessage,
|
||||
"traffic-management" to SettingsRoute.TrafficManagement,
|
||||
"tak" to SettingsRoute.TAK,
|
||||
"clean-node-db" to SettingsRoute.CleanNodeDb,
|
||||
"debug-panel" to SettingsRoute.DebugPanel,
|
||||
"about" to SettingsRoute.About,
|
||||
"filter-settings" to SettingsRoute.FilterSettings,
|
||||
)
|
||||
|
||||
private val nodeDetailSubRoutes: Map<String, (Int) -> Route> =
|
||||
mapOf(
|
||||
"device-metrics" to { destNum -> NodeDetailRoutes.DeviceMetrics(destNum) },
|
||||
"map" to { destNum -> NodeDetailRoutes.NodeMap(destNum) },
|
||||
"position" to { destNum -> NodeDetailRoutes.PositionLog(destNum) },
|
||||
"environment" to { destNum -> NodeDetailRoutes.EnvironmentMetrics(destNum) },
|
||||
"signal" to { destNum -> NodeDetailRoutes.SignalMetrics(destNum) },
|
||||
"power" to { destNum -> NodeDetailRoutes.PowerMetrics(destNum) },
|
||||
"traceroute" to { destNum -> NodeDetailRoutes.TracerouteLog(destNum) },
|
||||
"host-metrics" to { destNum -> NodeDetailRoutes.HostMetricsLog(destNum) },
|
||||
"pax" to { destNum -> NodeDetailRoutes.PaxMetrics(destNum) },
|
||||
"neighbors" to { destNum -> NodeDetailRoutes.NeighborInfoLog(destNum) },
|
||||
"device-metrics" to { destNum -> NodeDetailRoute.DeviceMetrics(destNum) },
|
||||
"map" to { destNum -> NodeDetailRoute.NodeMap(destNum) },
|
||||
"position" to { destNum -> NodeDetailRoute.PositionLog(destNum) },
|
||||
"environment" to { destNum -> NodeDetailRoute.EnvironmentMetrics(destNum) },
|
||||
"signal" to { destNum -> NodeDetailRoute.SignalMetrics(destNum) },
|
||||
"power" to { destNum -> NodeDetailRoute.PowerMetrics(destNum) },
|
||||
"traceroute" to { destNum -> NodeDetailRoute.TracerouteLog(destNum) },
|
||||
"host-metrics" to { destNum -> NodeDetailRoute.HostMetricsLog(destNum) },
|
||||
"pax" to { destNum -> NodeDetailRoute.PaxMetrics(destNum) },
|
||||
"neighbors" to { destNum -> NodeDetailRoute.NeighborInfoLog(destNum) },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,99 +18,28 @@ package org.meshtastic.core.navigation
|
|||
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import androidx.savedstate.serialization.SavedStateConfiguration
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.polymorphic
|
||||
import kotlinx.serialization.modules.subclassesOfSealed
|
||||
|
||||
/**
|
||||
* Shared polymorphic serialization configuration for Navigation 3 saved-state support. Registers all route types used
|
||||
* across Android and Desktop navigation graphs.
|
||||
* Shared polymorphic serialization configuration for Navigation 3 saved-state support. Uses sealed interface
|
||||
* hierarchies so that new routes are automatically registered at compile time — no manual `subclass()` calls needed.
|
||||
*/
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
val MeshtasticNavSavedStateConfig = SavedStateConfiguration {
|
||||
serializersModule = SerializersModule {
|
||||
polymorphic(NavKey::class) {
|
||||
// Nodes
|
||||
subclass(NodesRoutes.NodesGraph::class, NodesRoutes.NodesGraph.serializer())
|
||||
subclass(NodesRoutes.Nodes::class, NodesRoutes.Nodes.serializer())
|
||||
subclass(NodesRoutes.NodeDetailGraph::class, NodesRoutes.NodeDetailGraph.serializer())
|
||||
subclass(NodesRoutes.NodeDetail::class, NodesRoutes.NodeDetail.serializer())
|
||||
|
||||
// Node detail sub-screens
|
||||
subclass(NodeDetailRoutes.DeviceMetrics::class, NodeDetailRoutes.DeviceMetrics.serializer())
|
||||
subclass(NodeDetailRoutes.NodeMap::class, NodeDetailRoutes.NodeMap.serializer())
|
||||
subclass(NodeDetailRoutes.PositionLog::class, NodeDetailRoutes.PositionLog.serializer())
|
||||
subclass(NodeDetailRoutes.EnvironmentMetrics::class, NodeDetailRoutes.EnvironmentMetrics.serializer())
|
||||
subclass(NodeDetailRoutes.SignalMetrics::class, NodeDetailRoutes.SignalMetrics.serializer())
|
||||
subclass(NodeDetailRoutes.PowerMetrics::class, NodeDetailRoutes.PowerMetrics.serializer())
|
||||
subclass(NodeDetailRoutes.TracerouteLog::class, NodeDetailRoutes.TracerouteLog.serializer())
|
||||
subclass(NodeDetailRoutes.TracerouteMap::class, NodeDetailRoutes.TracerouteMap.serializer())
|
||||
subclass(NodeDetailRoutes.HostMetricsLog::class, NodeDetailRoutes.HostMetricsLog.serializer())
|
||||
subclass(NodeDetailRoutes.PaxMetrics::class, NodeDetailRoutes.PaxMetrics.serializer())
|
||||
subclass(NodeDetailRoutes.NeighborInfoLog::class, NodeDetailRoutes.NeighborInfoLog.serializer())
|
||||
|
||||
// Conversations
|
||||
subclass(ContactsRoutes.ContactsGraph::class, ContactsRoutes.ContactsGraph.serializer())
|
||||
subclass(ContactsRoutes.Contacts::class, ContactsRoutes.Contacts.serializer())
|
||||
subclass(ContactsRoutes.Messages::class, ContactsRoutes.Messages.serializer())
|
||||
subclass(ContactsRoutes.Share::class, ContactsRoutes.Share.serializer())
|
||||
subclass(ContactsRoutes.QuickChat::class, ContactsRoutes.QuickChat.serializer())
|
||||
|
||||
// Map
|
||||
subclass(MapRoutes.Map::class, MapRoutes.Map.serializer())
|
||||
|
||||
// Firmware
|
||||
subclass(FirmwareRoutes.FirmwareGraph::class, FirmwareRoutes.FirmwareGraph.serializer())
|
||||
subclass(FirmwareRoutes.FirmwareUpdate::class, FirmwareRoutes.FirmwareUpdate.serializer())
|
||||
|
||||
// Settings
|
||||
subclass(SettingsRoutes.SettingsGraph::class, SettingsRoutes.SettingsGraph.serializer())
|
||||
subclass(SettingsRoutes.Settings::class, SettingsRoutes.Settings.serializer())
|
||||
subclass(SettingsRoutes.DeviceConfiguration::class, SettingsRoutes.DeviceConfiguration.serializer())
|
||||
subclass(SettingsRoutes.ModuleConfiguration::class, SettingsRoutes.ModuleConfiguration.serializer())
|
||||
subclass(SettingsRoutes.Administration::class, SettingsRoutes.Administration.serializer())
|
||||
|
||||
// Settings - Config routes
|
||||
subclass(SettingsRoutes.User::class, SettingsRoutes.User.serializer())
|
||||
subclass(SettingsRoutes.ChannelConfig::class, SettingsRoutes.ChannelConfig.serializer())
|
||||
subclass(SettingsRoutes.Device::class, SettingsRoutes.Device.serializer())
|
||||
subclass(SettingsRoutes.Position::class, SettingsRoutes.Position.serializer())
|
||||
subclass(SettingsRoutes.Power::class, SettingsRoutes.Power.serializer())
|
||||
subclass(SettingsRoutes.Network::class, SettingsRoutes.Network.serializer())
|
||||
subclass(SettingsRoutes.Display::class, SettingsRoutes.Display.serializer())
|
||||
subclass(SettingsRoutes.LoRa::class, SettingsRoutes.LoRa.serializer())
|
||||
subclass(SettingsRoutes.Bluetooth::class, SettingsRoutes.Bluetooth.serializer())
|
||||
subclass(SettingsRoutes.Security::class, SettingsRoutes.Security.serializer())
|
||||
|
||||
// Settings - Module routes
|
||||
subclass(SettingsRoutes.MQTT::class, SettingsRoutes.MQTT.serializer())
|
||||
subclass(SettingsRoutes.Serial::class, SettingsRoutes.Serial.serializer())
|
||||
subclass(SettingsRoutes.ExtNotification::class, SettingsRoutes.ExtNotification.serializer())
|
||||
subclass(SettingsRoutes.StoreForward::class, SettingsRoutes.StoreForward.serializer())
|
||||
subclass(SettingsRoutes.RangeTest::class, SettingsRoutes.RangeTest.serializer())
|
||||
subclass(SettingsRoutes.Telemetry::class, SettingsRoutes.Telemetry.serializer())
|
||||
subclass(SettingsRoutes.CannedMessage::class, SettingsRoutes.CannedMessage.serializer())
|
||||
subclass(SettingsRoutes.Audio::class, SettingsRoutes.Audio.serializer())
|
||||
subclass(SettingsRoutes.RemoteHardware::class, SettingsRoutes.RemoteHardware.serializer())
|
||||
subclass(SettingsRoutes.NeighborInfo::class, SettingsRoutes.NeighborInfo.serializer())
|
||||
subclass(SettingsRoutes.AmbientLighting::class, SettingsRoutes.AmbientLighting.serializer())
|
||||
subclass(SettingsRoutes.DetectionSensor::class, SettingsRoutes.DetectionSensor.serializer())
|
||||
subclass(SettingsRoutes.Paxcounter::class, SettingsRoutes.Paxcounter.serializer())
|
||||
subclass(SettingsRoutes.StatusMessage::class, SettingsRoutes.StatusMessage.serializer())
|
||||
subclass(SettingsRoutes.TrafficManagement::class, SettingsRoutes.TrafficManagement.serializer())
|
||||
subclass(SettingsRoutes.TAK::class, SettingsRoutes.TAK.serializer())
|
||||
|
||||
// Settings - Advanced routes
|
||||
subclass(SettingsRoutes.CleanNodeDb::class, SettingsRoutes.CleanNodeDb.serializer())
|
||||
subclass(SettingsRoutes.DebugPanel::class, SettingsRoutes.DebugPanel.serializer())
|
||||
subclass(SettingsRoutes.About::class, SettingsRoutes.About.serializer())
|
||||
subclass(SettingsRoutes.FilterSettings::class, SettingsRoutes.FilterSettings.serializer())
|
||||
|
||||
// Channels
|
||||
subclass(ChannelsRoutes.ChannelsGraph::class, ChannelsRoutes.ChannelsGraph.serializer())
|
||||
subclass(ChannelsRoutes.Channels::class, ChannelsRoutes.Channels.serializer())
|
||||
|
||||
// Connections
|
||||
subclass(ConnectionsRoutes.ConnectionsGraph::class, ConnectionsRoutes.ConnectionsGraph.serializer())
|
||||
subclass(ConnectionsRoutes.Connections::class, ConnectionsRoutes.Connections.serializer())
|
||||
subclassesOfSealed<ChannelsRoute>()
|
||||
subclassesOfSealed<ConnectionsRoute>()
|
||||
subclassesOfSealed<ContactsRoute>()
|
||||
subclassesOfSealed<MapRoute>()
|
||||
subclassesOfSealed<NodesRoute>()
|
||||
subclassesOfSealed<NodeDetailRoute>()
|
||||
subclassesOfSealed<SettingsRoute>()
|
||||
subclassesOfSealed<FirmwareRoute>()
|
||||
subclassesOfSealed<WifiProvisionRoute>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,160 +25,169 @@ interface Route : NavKey
|
|||
|
||||
interface Graph : Route
|
||||
|
||||
object ChannelsRoutes {
|
||||
@Serializable data object ChannelsGraph : Graph
|
||||
@Serializable
|
||||
sealed interface ChannelsRoute : Route {
|
||||
@Serializable data object ChannelsGraph : ChannelsRoute, Graph
|
||||
|
||||
@Serializable data object Channels : Route
|
||||
@Serializable data object Channels : ChannelsRoute
|
||||
}
|
||||
|
||||
object ConnectionsRoutes {
|
||||
@Serializable data object ConnectionsGraph : Graph
|
||||
@Serializable
|
||||
sealed interface ConnectionsRoute : Route {
|
||||
@Serializable data object ConnectionsGraph : ConnectionsRoute, Graph
|
||||
|
||||
@Serializable data object Connections : Route
|
||||
@Serializable data object Connections : ConnectionsRoute
|
||||
}
|
||||
|
||||
object ContactsRoutes {
|
||||
@Serializable data object ContactsGraph : Graph
|
||||
@Serializable
|
||||
sealed interface ContactsRoute : Route {
|
||||
@Serializable data object ContactsGraph : ContactsRoute, Graph
|
||||
|
||||
@Serializable data object Contacts : Route
|
||||
@Serializable data object Contacts : ContactsRoute
|
||||
|
||||
@Serializable data class Messages(val contactKey: String, val message: String = "") : Route
|
||||
@Serializable data class Messages(val contactKey: String, val message: String = "") : ContactsRoute
|
||||
|
||||
@Serializable data class Share(val message: String) : Route
|
||||
@Serializable data class Share(val message: String) : ContactsRoute
|
||||
|
||||
@Serializable data object QuickChat : Route
|
||||
@Serializable data object QuickChat : ContactsRoute
|
||||
}
|
||||
|
||||
object MapRoutes {
|
||||
@Serializable data class Map(val waypointId: Int? = null) : Route
|
||||
@Serializable
|
||||
sealed interface MapRoute : Route {
|
||||
@Serializable data class Map(val waypointId: Int? = null) : MapRoute
|
||||
}
|
||||
|
||||
object NodesRoutes {
|
||||
@Serializable data object NodesGraph : Graph
|
||||
@Serializable
|
||||
sealed interface NodesRoute : Route {
|
||||
@Serializable data object NodesGraph : NodesRoute, Graph
|
||||
|
||||
@Serializable data object Nodes : Route
|
||||
@Serializable data object Nodes : NodesRoute
|
||||
|
||||
@Serializable data class NodeDetailGraph(val destNum: Int? = null) : Graph
|
||||
@Serializable data class NodeDetailGraph(val destNum: Int? = null) : NodesRoute, Graph
|
||||
|
||||
@Serializable data class NodeDetail(val destNum: Int? = null) : Route
|
||||
@Serializable data class NodeDetail(val destNum: Int? = null) : NodesRoute
|
||||
}
|
||||
|
||||
object NodeDetailRoutes {
|
||||
@Serializable data class DeviceMetrics(val destNum: Int) : Route
|
||||
@Serializable
|
||||
sealed interface NodeDetailRoute : Route {
|
||||
@Serializable data class DeviceMetrics(val destNum: Int) : NodeDetailRoute
|
||||
|
||||
@Serializable data class NodeMap(val destNum: Int) : Route
|
||||
@Serializable data class NodeMap(val destNum: Int) : NodeDetailRoute
|
||||
|
||||
@Serializable data class PositionLog(val destNum: Int) : Route
|
||||
@Serializable data class PositionLog(val destNum: Int) : NodeDetailRoute
|
||||
|
||||
@Serializable data class EnvironmentMetrics(val destNum: Int) : Route
|
||||
@Serializable data class EnvironmentMetrics(val destNum: Int) : NodeDetailRoute
|
||||
|
||||
@Serializable data class SignalMetrics(val destNum: Int) : Route
|
||||
@Serializable data class SignalMetrics(val destNum: Int) : NodeDetailRoute
|
||||
|
||||
@Serializable data class PowerMetrics(val destNum: Int) : Route
|
||||
@Serializable data class PowerMetrics(val destNum: Int) : NodeDetailRoute
|
||||
|
||||
@Serializable data class TracerouteLog(val destNum: Int) : Route
|
||||
@Serializable data class TracerouteLog(val destNum: Int) : NodeDetailRoute
|
||||
|
||||
@Serializable data class TracerouteMap(val destNum: Int, val requestId: Int, val logUuid: String? = null) : Route
|
||||
@Serializable data class TracerouteMap(val destNum: Int, val requestId: Int, val logUuid: String? = null) : NodeDetailRoute
|
||||
|
||||
@Serializable data class HostMetricsLog(val destNum: Int) : Route
|
||||
@Serializable data class HostMetricsLog(val destNum: Int) : NodeDetailRoute
|
||||
|
||||
@Serializable data class PaxMetrics(val destNum: Int) : Route
|
||||
@Serializable data class PaxMetrics(val destNum: Int) : NodeDetailRoute
|
||||
|
||||
@Serializable data class NeighborInfoLog(val destNum: Int) : Route
|
||||
@Serializable data class NeighborInfoLog(val destNum: Int) : NodeDetailRoute
|
||||
}
|
||||
|
||||
object SettingsRoutes {
|
||||
@Serializable data class SettingsGraph(val destNum: Int? = null) : Graph
|
||||
@Serializable
|
||||
sealed interface SettingsRoute : Route {
|
||||
@Serializable data class SettingsGraph(val destNum: Int? = null) : SettingsRoute, Graph
|
||||
|
||||
@Serializable data class Settings(val destNum: Int? = null) : Route
|
||||
@Serializable data class Settings(val destNum: Int? = null) : SettingsRoute
|
||||
|
||||
@Serializable data object DeviceConfiguration : Route
|
||||
@Serializable data object DeviceConfiguration : SettingsRoute
|
||||
|
||||
@Serializable data object ModuleConfiguration : Route
|
||||
@Serializable data object ModuleConfiguration : SettingsRoute
|
||||
|
||||
@Serializable data object Administration : Route
|
||||
@Serializable data object Administration : SettingsRoute
|
||||
|
||||
// region radio Config Routes
|
||||
|
||||
@Serializable data object User : Route
|
||||
@Serializable data object User : SettingsRoute
|
||||
|
||||
@Serializable data object ChannelConfig : Route
|
||||
@Serializable data object ChannelConfig : SettingsRoute
|
||||
|
||||
@Serializable data object Device : Route
|
||||
@Serializable data object Device : SettingsRoute
|
||||
|
||||
@Serializable data object Position : Route
|
||||
@Serializable data object Position : SettingsRoute
|
||||
|
||||
@Serializable data object Power : Route
|
||||
@Serializable data object Power : SettingsRoute
|
||||
|
||||
@Serializable data object Network : Route
|
||||
@Serializable data object Network : SettingsRoute
|
||||
|
||||
@Serializable data object Display : Route
|
||||
@Serializable data object Display : SettingsRoute
|
||||
|
||||
@Serializable data object LoRa : Route
|
||||
@Serializable data object LoRa : SettingsRoute
|
||||
|
||||
@Serializable data object Bluetooth : Route
|
||||
@Serializable data object Bluetooth : SettingsRoute
|
||||
|
||||
@Serializable data object Security : Route
|
||||
@Serializable data object Security : SettingsRoute
|
||||
|
||||
// endregion
|
||||
|
||||
// region module config routes
|
||||
|
||||
@Serializable data object MQTT : Route
|
||||
@Serializable data object MQTT : SettingsRoute
|
||||
|
||||
@Serializable data object Serial : Route
|
||||
@Serializable data object Serial : SettingsRoute
|
||||
|
||||
@Serializable data object ExtNotification : Route
|
||||
@Serializable data object ExtNotification : SettingsRoute
|
||||
|
||||
@Serializable data object StoreForward : Route
|
||||
@Serializable data object StoreForward : SettingsRoute
|
||||
|
||||
@Serializable data object RangeTest : Route
|
||||
@Serializable data object RangeTest : SettingsRoute
|
||||
|
||||
@Serializable data object Telemetry : Route
|
||||
@Serializable data object Telemetry : SettingsRoute
|
||||
|
||||
@Serializable data object CannedMessage : Route
|
||||
@Serializable data object CannedMessage : SettingsRoute
|
||||
|
||||
@Serializable data object Audio : Route
|
||||
@Serializable data object Audio : SettingsRoute
|
||||
|
||||
@Serializable data object RemoteHardware : Route
|
||||
@Serializable data object RemoteHardware : SettingsRoute
|
||||
|
||||
@Serializable data object NeighborInfo : Route
|
||||
@Serializable data object NeighborInfo : SettingsRoute
|
||||
|
||||
@Serializable data object AmbientLighting : Route
|
||||
@Serializable data object AmbientLighting : SettingsRoute
|
||||
|
||||
@Serializable data object DetectionSensor : Route
|
||||
@Serializable data object DetectionSensor : SettingsRoute
|
||||
|
||||
@Serializable data object Paxcounter : Route
|
||||
@Serializable data object Paxcounter : SettingsRoute
|
||||
|
||||
@Serializable data object StatusMessage : Route
|
||||
@Serializable data object StatusMessage : SettingsRoute
|
||||
|
||||
@Serializable data object TrafficManagement : Route
|
||||
@Serializable data object TrafficManagement : SettingsRoute
|
||||
|
||||
@Serializable data object TAK : Route
|
||||
@Serializable data object TAK : SettingsRoute
|
||||
|
||||
// endregion
|
||||
|
||||
// region advanced config routes
|
||||
|
||||
@Serializable data object CleanNodeDb : Route
|
||||
@Serializable data object CleanNodeDb : SettingsRoute
|
||||
|
||||
@Serializable data object DebugPanel : Route
|
||||
@Serializable data object DebugPanel : SettingsRoute
|
||||
|
||||
@Serializable data object About : Route
|
||||
@Serializable data object About : SettingsRoute
|
||||
|
||||
@Serializable data object FilterSettings : Route
|
||||
@Serializable data object FilterSettings : SettingsRoute
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
object FirmwareRoutes {
|
||||
@Serializable data object FirmwareGraph : Graph
|
||||
@Serializable
|
||||
sealed interface FirmwareRoute : Route {
|
||||
@Serializable data object FirmwareGraph : FirmwareRoute, Graph
|
||||
|
||||
@Serializable data object FirmwareUpdate : Route
|
||||
@Serializable data object FirmwareUpdate : FirmwareRoute
|
||||
}
|
||||
|
||||
object WifiProvisionRoutes {
|
||||
@Serializable data object WifiProvisionGraph : Graph
|
||||
@Serializable
|
||||
sealed interface WifiProvisionRoute : Route {
|
||||
@Serializable data object WifiProvisionGraph : WifiProvisionRoute, Graph
|
||||
|
||||
@Serializable data class WifiProvision(val address: String? = null) : Route
|
||||
@Serializable data class WifiProvision(val address: String? = null) : WifiProvisionRoute
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,11 +32,11 @@ import org.meshtastic.core.resources.nodes
|
|||
* and Desktop navigation shells.
|
||||
*/
|
||||
enum class TopLevelDestination(val label: StringResource, val route: Route) {
|
||||
Conversations(Res.string.conversations, ContactsRoutes.ContactsGraph),
|
||||
Nodes(Res.string.nodes, NodesRoutes.NodesGraph),
|
||||
Map(Res.string.map, MapRoutes.Map()),
|
||||
Settings(Res.string.bottom_nav_settings, SettingsRoutes.SettingsGraph()),
|
||||
Connections(Res.string.connections, ConnectionsRoutes.ConnectionsGraph),
|
||||
Conversations(Res.string.conversations, ContactsRoute.ContactsGraph),
|
||||
Nodes(Res.string.nodes, NodesRoute.NodesGraph),
|
||||
Map(Res.string.map, MapRoute.Map()),
|
||||
Settings(Res.string.bottom_nav_settings, SettingsRoute.SettingsGraph()),
|
||||
Connections(Res.string.connections, ConnectionsRoute.ConnectionsGraph),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class MultiBackstackTest {
|
|||
val multiBackstack = MultiBackstack(startTab)
|
||||
|
||||
val nodesStack =
|
||||
NavBackStack<NavKey>().apply { addAll(listOf(TopLevelDestination.Nodes.route, NodesRoutes.Nodes)) }
|
||||
NavBackStack<NavKey>().apply { addAll(listOf(TopLevelDestination.Nodes.route, NodesRoute.Nodes)) }
|
||||
val mapStack = NavBackStack<NavKey>().apply { addAll(listOf(TopLevelDestination.Map.route)) }
|
||||
|
||||
multiBackstack.backStacks =
|
||||
|
|
@ -51,7 +51,7 @@ class MultiBackstackTest {
|
|||
val multiBackstack = MultiBackstack(startTab)
|
||||
|
||||
val nodesStack =
|
||||
NavBackStack<NavKey>().apply { addAll(listOf(TopLevelDestination.Nodes.route, NodesRoutes.Nodes)) }
|
||||
NavBackStack<NavKey>().apply { addAll(listOf(TopLevelDestination.Nodes.route, NodesRoute.Nodes)) }
|
||||
multiBackstack.backStacks = mapOf(TopLevelDestination.Nodes.route to nodesStack)
|
||||
|
||||
assertEquals(2, multiBackstack.activeBackStack.size)
|
||||
|
|
@ -68,7 +68,7 @@ class MultiBackstackTest {
|
|||
val multiBackstack = MultiBackstack(startTab)
|
||||
|
||||
val nodesStack =
|
||||
NavBackStack<NavKey>().apply { addAll(listOf(TopLevelDestination.Nodes.route, NodesRoutes.Nodes)) }
|
||||
NavBackStack<NavKey>().apply { addAll(listOf(TopLevelDestination.Nodes.route, NodesRoute.Nodes)) }
|
||||
multiBackstack.backStacks = mapOf(TopLevelDestination.Nodes.route to nodesStack)
|
||||
|
||||
multiBackstack.goBack()
|
||||
|
|
@ -104,11 +104,11 @@ class MultiBackstackTest {
|
|||
val settingsStack = NavBackStack<NavKey>().apply { addAll(listOf(TopLevelDestination.Settings.route)) }
|
||||
multiBackstack.backStacks = mapOf(TopLevelDestination.Settings.route to settingsStack)
|
||||
|
||||
val deepLinkPath = listOf(TopLevelDestination.Settings.route, SettingsRoutes.About)
|
||||
val deepLinkPath = listOf(TopLevelDestination.Settings.route, SettingsRoute.About)
|
||||
multiBackstack.handleDeepLink(deepLinkPath)
|
||||
|
||||
assertEquals(TopLevelDestination.Settings.route, multiBackstack.currentTabRoute)
|
||||
assertEquals(2, multiBackstack.activeBackStack.size)
|
||||
assertEquals(SettingsRoutes.About, multiBackstack.activeBackStack.last())
|
||||
assertEquals(SettingsRoute.About, multiBackstack.activeBackStack.last())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import org.meshtastic.core.navigation.MultiBackstack
|
||||
import org.meshtastic.core.navigation.NodeDetailRoutes
|
||||
import org.meshtastic.core.navigation.NodeDetailRoute
|
||||
import org.meshtastic.core.ui.viewmodel.UIViewModel
|
||||
|
||||
/**
|
||||
|
|
@ -44,7 +44,7 @@ fun MeshtasticAppShell(
|
|||
uiViewModel = uiViewModel,
|
||||
onNavigateToTracerouteMap = { destNum, requestId, logUuid ->
|
||||
multiBackstack.activeBackStack.add(
|
||||
NodeDetailRoutes.TracerouteMap(destNum = destNum, requestId = requestId, logUuid = logUuid),
|
||||
NodeDetailRoute.TracerouteMap(destNum = destNum, requestId = requestId, logUuid = logUuid),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -50,9 +50,9 @@ import org.jetbrains.compose.resources.stringResource
|
|||
import org.jetbrains.compose.resources.vectorResource
|
||||
import org.meshtastic.core.model.ConnectionState
|
||||
import org.meshtastic.core.model.DeviceType
|
||||
import org.meshtastic.core.navigation.ContactsRoutes
|
||||
import org.meshtastic.core.navigation.ContactsRoute
|
||||
import org.meshtastic.core.navigation.MultiBackstack
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
import org.meshtastic.core.navigation.TopLevelDestination
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.connected
|
||||
|
|
@ -141,7 +141,7 @@ private fun handleNavigation(
|
|||
val currentKey = multiBackstack.activeBackStack.lastOrNull()
|
||||
when (destination) {
|
||||
TopLevelDestination.Nodes -> {
|
||||
val onNodesList = currentKey is NodesRoutes.NodesGraph || currentKey is NodesRoutes.Nodes
|
||||
val onNodesList = currentKey is NodesRoute.NodesGraph || currentKey is NodesRoute.Nodes
|
||||
if (!onNodesList) {
|
||||
multiBackstack.navigateTopLevel(destination.route)
|
||||
} else {
|
||||
|
|
@ -150,7 +150,7 @@ private fun handleNavigation(
|
|||
}
|
||||
TopLevelDestination.Conversations -> {
|
||||
val onConversationsList =
|
||||
currentKey is ContactsRoutes.ContactsGraph || currentKey is ContactsRoutes.Contacts
|
||||
currentKey is ContactsRoute.ContactsGraph || currentKey is ContactsRoute.Contacts
|
||||
if (!onConversationsList) {
|
||||
multiBackstack.navigateTopLevel(destination.route)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ import org.jetbrains.skia.Image
|
|||
import org.koin.core.context.startKoin
|
||||
import org.meshtastic.core.common.util.MeshtasticUri
|
||||
import org.meshtastic.core.database.desktopDataDir
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
import org.meshtastic.core.navigation.TopLevelDestination
|
||||
import org.meshtastic.core.navigation.rememberMultiBackstack
|
||||
import org.meshtastic.core.repository.UiPrefs
|
||||
|
|
@ -241,7 +241,7 @@ fun main(args: Array<String>) = application(exitProcessOnExit = false) {
|
|||
true
|
||||
}
|
||||
event.key == Key.Slash -> {
|
||||
backStack.add(SettingsRoutes.About)
|
||||
backStack.add(SettingsRoute.About)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@
|
|||
*/
|
||||
package org.meshtastic.desktop.ui
|
||||
|
||||
import org.meshtastic.core.navigation.ConnectionsRoutes
|
||||
import org.meshtastic.core.navigation.ContactsRoutes
|
||||
import org.meshtastic.core.navigation.FirmwareRoutes
|
||||
import org.meshtastic.core.navigation.MapRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.ConnectionsRoute
|
||||
import org.meshtastic.core.navigation.ContactsRoute
|
||||
import org.meshtastic.core.navigation.FirmwareRoute
|
||||
import org.meshtastic.core.navigation.MapRoute
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
import org.meshtastic.core.navigation.TopLevelDestination
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.test.Test
|
||||
|
|
@ -41,11 +41,11 @@ class DesktopTopLevelDestinationParityTest {
|
|||
|
||||
val androidParityRoutes: Set<KClass<out Route>> =
|
||||
setOf(
|
||||
ContactsRoutes.ContactsGraph::class,
|
||||
NodesRoutes.NodesGraph::class,
|
||||
MapRoutes.Map::class,
|
||||
SettingsRoutes.SettingsGraph::class,
|
||||
ConnectionsRoutes.ConnectionsGraph::class,
|
||||
ContactsRoute.ContactsGraph::class,
|
||||
NodesRoute.NodesGraph::class,
|
||||
MapRoute.Map::class,
|
||||
SettingsRoute.SettingsGraph::class,
|
||||
ConnectionsRoute.ConnectionsGraph::class,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
|
|
@ -60,7 +60,7 @@ class DesktopTopLevelDestinationParityTest {
|
|||
val desktopRoutes: Set<KClass<out Route>> = TopLevelDestination.entries.map { it.route::class }.toSet()
|
||||
|
||||
assertFalse(
|
||||
actual = desktopRoutes.contains(FirmwareRoutes.FirmwareGraph::class),
|
||||
actual = desktopRoutes.contains(FirmwareRoute.FirmwareGraph::class),
|
||||
message = "Firmware must stay in-flow and not appear in the desktop top-level rail",
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,30 +20,30 @@ import androidx.navigation3.runtime.EntryProviderScope
|
|||
import androidx.navigation3.runtime.NavBackStack
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.meshtastic.core.navigation.ConnectionsRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.ConnectionsRoute
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
import org.meshtastic.feature.connections.ScannerViewModel
|
||||
import org.meshtastic.feature.connections.ui.ConnectionsScreen
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
|
||||
/** Navigation graph for for the top level ConnectionsScreen - [ConnectionsRoutes.Connections]. */
|
||||
/** Navigation graph for for the top level ConnectionsScreen - [ConnectionsRoute.Connections]. */
|
||||
fun EntryProviderScope<NavKey>.connectionsGraph(backStack: NavBackStack<NavKey>) {
|
||||
entry<ConnectionsRoutes.ConnectionsGraph> {
|
||||
entry<ConnectionsRoute.ConnectionsGraph> {
|
||||
ConnectionsScreen(
|
||||
scanModel = koinViewModel<ScannerViewModel>(),
|
||||
radioConfigViewModel = koinViewModel<RadioConfigViewModel>(),
|
||||
onClickNodeChip = { backStack.add(NodesRoutes.NodeDetail(it)) },
|
||||
onNavigateToNodeDetails = { backStack.add(NodesRoutes.NodeDetail(it)) },
|
||||
onClickNodeChip = { backStack.add(NodesRoute.NodeDetail(it)) },
|
||||
onNavigateToNodeDetails = { backStack.add(NodesRoute.NodeDetail(it)) },
|
||||
onConfigNavigate = { route -> backStack.add(route) },
|
||||
)
|
||||
}
|
||||
|
||||
entry<ConnectionsRoutes.Connections> {
|
||||
entry<ConnectionsRoute.Connections> {
|
||||
ConnectionsScreen(
|
||||
scanModel = koinViewModel<ScannerViewModel>(),
|
||||
radioConfigViewModel = koinViewModel<RadioConfigViewModel>(),
|
||||
onClickNodeChip = { backStack.add(NodesRoutes.NodeDetail(it)) },
|
||||
onNavigateToNodeDetails = { backStack.add(NodesRoutes.NodeDetail(it)) },
|
||||
onClickNodeChip = { backStack.add(NodesRoute.NodeDetail(it)) },
|
||||
onNavigateToNodeDetails = { backStack.add(NodesRoute.NodeDetail(it)) },
|
||||
onConfigNavigate = { route -> backStack.add(route) },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ import org.koin.compose.viewmodel.koinViewModel
|
|||
import org.meshtastic.core.model.ConnectionState
|
||||
import org.meshtastic.core.model.DeviceType
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.connected
|
||||
import org.meshtastic.core.resources.connected_device
|
||||
|
|
@ -125,8 +125,8 @@ fun ConnectionsScreen(
|
|||
getNavRouteFrom(radioConfigState.route)?.let { route ->
|
||||
isWaiting = false
|
||||
radioConfigViewModel.clearPacketResponse()
|
||||
if (route == SettingsRoutes.LoRa) {
|
||||
onConfigNavigate(SettingsRoutes.LoRa)
|
||||
if (route == SettingsRoute.LoRa) {
|
||||
onConfigNavigate(SettingsRoute.LoRa)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ import androidx.navigation3.runtime.EntryProviderScope
|
|||
import androidx.navigation3.runtime.NavBackStack
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.meshtastic.core.navigation.FirmwareRoutes
|
||||
import org.meshtastic.core.navigation.FirmwareRoute
|
||||
import org.meshtastic.feature.firmware.FirmwareUpdateScreen
|
||||
import org.meshtastic.feature.firmware.FirmwareUpdateViewModel
|
||||
|
||||
/** Registers the firmware update screen entries into the Navigation 3 entry provider. */
|
||||
fun EntryProviderScope<NavKey>.firmwareGraph(backStack: NavBackStack<NavKey>) {
|
||||
entry<FirmwareRoutes.FirmwareGraph> { FirmwareScreen(onNavigateUp = { backStack.removeLastOrNull() }) }
|
||||
entry<FirmwareRoutes.FirmwareUpdate> { FirmwareScreen(onNavigateUp = { backStack.removeLastOrNull() }) }
|
||||
entry<FirmwareRoute.FirmwareGraph> { FirmwareScreen(onNavigateUp = { backStack.removeLastOrNull() }) }
|
||||
entry<FirmwareRoute.FirmwareUpdate> { FirmwareScreen(onNavigateUp = { backStack.removeLastOrNull() }) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -19,15 +19,15 @@ package org.meshtastic.feature.map.navigation
|
|||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import androidx.navigation3.runtime.NavBackStack
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import org.meshtastic.core.navigation.MapRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.MapRoute
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
|
||||
fun EntryProviderScope<NavKey>.mapGraph(backStack: NavBackStack<NavKey>) {
|
||||
entry<MapRoutes.Map> { args ->
|
||||
entry<MapRoute.Map> { args ->
|
||||
val mapScreen = org.meshtastic.core.ui.util.LocalMapMainScreenProvider.current
|
||||
mapScreen(
|
||||
{ backStack.add(NodesRoutes.NodeDetail(it)) }, // onClickNodeChip
|
||||
{ backStack.add(NodesRoutes.NodeDetail(it)) }, // navigateToNodeDetails
|
||||
{ backStack.add(NodesRoute.NodeDetail(it)) }, // onClickNodeChip
|
||||
{ backStack.add(NodesRoute.NodeDetail(it)) }, // navigateToNodeDetails
|
||||
args.waypointId,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import androidx.navigation3.runtime.NavKey
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.meshtastic.core.navigation.ContactsRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.ContactsRoute
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
import org.meshtastic.core.navigation.replaceLast
|
||||
import org.meshtastic.core.ui.component.ScrollToTopEvent
|
||||
import org.meshtastic.feature.messaging.QuickChatScreen
|
||||
|
|
@ -43,15 +43,15 @@ fun EntryProviderScope<NavKey>.contactsGraph(
|
|||
backStack: NavBackStack<NavKey>,
|
||||
scrollToTopEvents: Flow<ScrollToTopEvent> = MutableSharedFlow(),
|
||||
) {
|
||||
entry<ContactsRoutes.ContactsGraph>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
entry<ContactsRoute.ContactsGraph>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
ContactsEntryContent(backStack = backStack, scrollToTopEvents = scrollToTopEvents)
|
||||
}
|
||||
|
||||
entry<ContactsRoutes.Contacts>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
entry<ContactsRoute.Contacts>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
ContactsEntryContent(backStack = backStack, scrollToTopEvents = scrollToTopEvents)
|
||||
}
|
||||
|
||||
entry<ContactsRoutes.Messages>(metadata = { ListDetailSceneStrategy.detailPane() }) { args ->
|
||||
entry<ContactsRoute.Messages>(metadata = { ListDetailSceneStrategy.detailPane() }) { args ->
|
||||
val contactKey = args.contactKey
|
||||
val messageViewModel: org.meshtastic.feature.messaging.MessageViewModel =
|
||||
koinViewModel(key = "messages-$contactKey")
|
||||
|
|
@ -61,23 +61,23 @@ fun EntryProviderScope<NavKey>.contactsGraph(
|
|||
contactKey = contactKey,
|
||||
message = args.message,
|
||||
viewModel = messageViewModel,
|
||||
navigateToNodeDetails = { backStack.add(NodesRoutes.NodeDetail(it)) },
|
||||
navigateToQuickChatOptions = { backStack.add(org.meshtastic.core.navigation.ContactsRoutes.QuickChat) },
|
||||
navigateToNodeDetails = { backStack.add(NodesRoute.NodeDetail(it)) },
|
||||
navigateToQuickChatOptions = { backStack.add(org.meshtastic.core.navigation.ContactsRoute.QuickChat) },
|
||||
onNavigateBack = { backStack.removeLastOrNull() },
|
||||
)
|
||||
}
|
||||
|
||||
entry<ContactsRoutes.Share>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
entry<ContactsRoute.Share>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
val message = args.message
|
||||
val viewModel = koinViewModel<ContactsViewModel>()
|
||||
ShareScreen(
|
||||
viewModel = viewModel,
|
||||
onConfirm = { contactKey -> backStack.replaceLast(ContactsRoutes.Messages(contactKey, message)) },
|
||||
onConfirm = { contactKey -> backStack.replaceLast(ContactsRoute.Messages(contactKey, message)) },
|
||||
onNavigateUp = { backStack.removeLastOrNull() },
|
||||
)
|
||||
}
|
||||
|
||||
entry<ContactsRoutes.QuickChat>(metadata = { ListDetailSceneStrategy.extraPane() }) {
|
||||
entry<ContactsRoute.QuickChat>(metadata = { ListDetailSceneStrategy.extraPane() }) {
|
||||
val viewModel = koinViewModel<QuickChatViewModel>()
|
||||
QuickChatScreen(viewModel = viewModel, onNavigateUp = { backStack.removeLastOrNull() })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import androidx.navigation3.runtime.NavBackStack
|
|||
import androidx.navigation3.runtime.NavKey
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.meshtastic.core.common.util.MeshtasticUri
|
||||
import org.meshtastic.core.navigation.ChannelsRoutes
|
||||
import org.meshtastic.core.navigation.ContactsRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.ChannelsRoute
|
||||
import org.meshtastic.core.navigation.ContactsRoute
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
import org.meshtastic.core.ui.component.ScrollToTopEvent
|
||||
import org.meshtastic.proto.ChannelSet
|
||||
import org.meshtastic.proto.SharedContact
|
||||
|
|
@ -40,16 +40,16 @@ fun AdaptiveContactsScreen(
|
|||
onClearRequestChannelUrl: () -> Unit,
|
||||
) {
|
||||
ContactsScreen(
|
||||
onNavigateToShare = { backStack.add(ChannelsRoutes.ChannelsGraph) },
|
||||
onNavigateToShare = { backStack.add(ChannelsRoute.ChannelsGraph) },
|
||||
sharedContactRequested = sharedContactRequested,
|
||||
requestChannelSet = requestChannelSet,
|
||||
onHandleDeepLink = onHandleDeepLink,
|
||||
onClearSharedContactRequested = onClearSharedContactRequested,
|
||||
onClearRequestChannelUrl = onClearRequestChannelUrl,
|
||||
viewModel = contactsViewModel,
|
||||
onClickNodeChip = { backStack.add(NodesRoutes.NodeDetail(it)) },
|
||||
onNavigateToMessages = { contactKey -> backStack.add(ContactsRoutes.Messages(contactKey)) },
|
||||
onNavigateToNodeDetails = { backStack.add(NodesRoutes.NodeDetail(it)) },
|
||||
onClickNodeChip = { backStack.add(NodesRoute.NodeDetail(it)) },
|
||||
onNavigateToMessages = { contactKey -> backStack.add(ContactsRoute.Messages(contactKey)) },
|
||||
onNavigateToNodeDetails = { backStack.add(NodesRoute.NodeDetail(it)) },
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
activeContactKey = null,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import org.meshtastic.core.database.entity.asDeviceVersion
|
|||
import org.meshtastic.core.model.DeviceVersion
|
||||
import org.meshtastic.core.model.Node
|
||||
import org.meshtastic.core.model.service.ServiceAction
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.administration
|
||||
import org.meshtastic.core.resources.firmware
|
||||
|
|
@ -77,7 +77,7 @@ fun AdministrationSection(
|
|||
leadingIcon = MeshtasticIcons.Settings,
|
||||
enabled = metricsState.isLocal || node.metadata != null,
|
||||
) {
|
||||
onAction(NodeDetailAction.Navigate(SettingsRoutes.Settings(node.num)))
|
||||
onAction(NodeDetailAction.Navigate(SettingsRoute.Settings(node.num)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package org.meshtastic.feature.node.model
|
|||
|
||||
import org.jetbrains.compose.resources.DrawableResource
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.meshtastic.core.navigation.NodeDetailRoutes
|
||||
import org.meshtastic.core.navigation.NodeDetailRoute
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.device_metrics_log
|
||||
|
|
@ -43,14 +43,14 @@ import org.meshtastic.core.resources.signal_quality
|
|||
import org.meshtastic.core.resources.traceroute_log
|
||||
|
||||
enum class LogsType(val titleRes: StringResource, val icon: DrawableResource, val routeFactory: (Int) -> Route) {
|
||||
DEVICE(Res.string.device_metrics_log, Res.drawable.ic_charging_station, { NodeDetailRoutes.DeviceMetrics(it) }),
|
||||
NODE_MAP(Res.string.node_map, Res.drawable.ic_map, { NodeDetailRoutes.NodeMap(it) }),
|
||||
POSITIONS(Res.string.position_log, Res.drawable.ic_location_on, { NodeDetailRoutes.PositionLog(it) }),
|
||||
ENVIRONMENT(Res.string.env_metrics_log, Res.drawable.ic_thermostat, { NodeDetailRoutes.EnvironmentMetrics(it) }),
|
||||
SIGNAL(Res.string.signal_quality, Res.drawable.ic_signal_cellular_alt, { NodeDetailRoutes.SignalMetrics(it) }),
|
||||
POWER(Res.string.power_metrics_log, Res.drawable.ic_power, { NodeDetailRoutes.PowerMetrics(it) }),
|
||||
TRACEROUTE(Res.string.traceroute_log, Res.drawable.ic_route, { NodeDetailRoutes.TracerouteLog(it) }),
|
||||
NEIGHBOR_INFO(Res.string.neighbor_info, Res.drawable.ic_groups, { NodeDetailRoutes.NeighborInfoLog(it) }),
|
||||
HOST(Res.string.host_metrics_log, Res.drawable.ic_memory, { NodeDetailRoutes.HostMetricsLog(it) }),
|
||||
PAX(Res.string.pax_metrics_log, Res.drawable.ic_people, { NodeDetailRoutes.PaxMetrics(it) }),
|
||||
DEVICE(Res.string.device_metrics_log, Res.drawable.ic_charging_station, { NodeDetailRoute.DeviceMetrics(it) }),
|
||||
NODE_MAP(Res.string.node_map, Res.drawable.ic_map, { NodeDetailRoute.NodeMap(it) }),
|
||||
POSITIONS(Res.string.position_log, Res.drawable.ic_location_on, { NodeDetailRoute.PositionLog(it) }),
|
||||
ENVIRONMENT(Res.string.env_metrics_log, Res.drawable.ic_thermostat, { NodeDetailRoute.EnvironmentMetrics(it) }),
|
||||
SIGNAL(Res.string.signal_quality, Res.drawable.ic_signal_cellular_alt, { NodeDetailRoute.SignalMetrics(it) }),
|
||||
POWER(Res.string.power_metrics_log, Res.drawable.ic_power, { NodeDetailRoute.PowerMetrics(it) }),
|
||||
TRACEROUTE(Res.string.traceroute_log, Res.drawable.ic_route, { NodeDetailRoute.TracerouteLog(it) }),
|
||||
NEIGHBOR_INFO(Res.string.neighbor_info, Res.drawable.ic_groups, { NodeDetailRoute.NeighborInfoLog(it) }),
|
||||
HOST(Res.string.host_metrics_log, Res.drawable.ic_memory, { NodeDetailRoute.HostMetricsLog(it) }),
|
||||
PAX(Res.string.pax_metrics_log, Res.drawable.ic_people, { NodeDetailRoute.PaxMetrics(it) }),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import androidx.navigation3.runtime.NavBackStack
|
|||
import androidx.navigation3.runtime.NavKey
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.meshtastic.core.navigation.ChannelsRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.ChannelsRoute
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
import org.meshtastic.core.ui.component.ScrollToTopEvent
|
||||
import org.meshtastic.feature.node.list.NodeListScreen
|
||||
import org.meshtastic.feature.node.list.NodeListViewModel
|
||||
|
|
@ -37,8 +37,8 @@ fun AdaptiveNodeListScreen(
|
|||
|
||||
NodeListScreen(
|
||||
viewModel = nodeListViewModel,
|
||||
navigateToNodeDetails = { nodeId -> backStack.add(NodesRoutes.NodeDetail(nodeId)) },
|
||||
onNavigateToChannels = { backStack.add(ChannelsRoutes.ChannelsGraph) },
|
||||
navigateToNodeDetails = { nodeId -> backStack.add(NodesRoute.NodeDetail(nodeId)) },
|
||||
onNavigateToChannels = { backStack.add(ChannelsRoute.ChannelsGraph) },
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
activeNodeId = null,
|
||||
onHandleDeepLink = onHandleDeepLink,
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ import org.jetbrains.compose.resources.DrawableResource
|
|||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.meshtastic.core.navigation.ContactsRoutes
|
||||
import org.meshtastic.core.navigation.NodeDetailRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.ContactsRoute
|
||||
import org.meshtastic.core.navigation.NodeDetailRoute
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.device
|
||||
|
|
@ -74,7 +74,7 @@ fun EntryProviderScope<NavKey>.nodesGraph(
|
|||
scrollToTopEvents: Flow<ScrollToTopEvent> = MutableSharedFlow(),
|
||||
onHandleDeepLink: (org.meshtastic.core.common.util.MeshtasticUri, onInvalid: () -> Unit) -> Unit = { _, _ -> },
|
||||
) {
|
||||
entry<NodesRoutes.NodesGraph>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
entry<NodesRoute.NodesGraph>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
AdaptiveNodeListScreen(
|
||||
backStack = backStack,
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
|
|
@ -82,7 +82,7 @@ fun EntryProviderScope<NavKey>.nodesGraph(
|
|||
)
|
||||
}
|
||||
|
||||
entry<NodesRoutes.Nodes>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
entry<NodesRoute.Nodes>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
AdaptiveNodeListScreen(
|
||||
backStack = backStack,
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
|
|
@ -100,7 +100,7 @@ fun EntryProviderScope<NavKey>.nodeDetailGraph(
|
|||
scrollToTopEvents: Flow<ScrollToTopEvent>,
|
||||
onHandleDeepLink: (org.meshtastic.core.common.util.MeshtasticUri, onInvalid: () -> Unit) -> Unit = { _, _ -> },
|
||||
) {
|
||||
entry<NodesRoutes.NodeDetailGraph>(metadata = { ListDetailSceneStrategy.listPane() }) { args ->
|
||||
entry<NodesRoute.NodeDetailGraph>(metadata = { ListDetailSceneStrategy.listPane() }) { args ->
|
||||
AdaptiveNodeListScreen(
|
||||
backStack = backStack,
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
|
|
@ -108,7 +108,7 @@ fun EntryProviderScope<NavKey>.nodeDetailGraph(
|
|||
)
|
||||
}
|
||||
|
||||
entry<NodesRoutes.NodeDetail>(metadata = { ListDetailSceneStrategy.detailPane() }) { args ->
|
||||
entry<NodesRoute.NodeDetail>(metadata = { ListDetailSceneStrategy.detailPane() }) { args ->
|
||||
val nodeDetailViewModel: NodeDetailViewModel = koinViewModel()
|
||||
val compassViewModel: CompassViewModel = koinViewModel()
|
||||
val destNum = args.destNum ?: 0 // Handle nullable destNum if needed
|
||||
|
|
@ -116,18 +116,18 @@ fun EntryProviderScope<NavKey>.nodeDetailGraph(
|
|||
nodeId = destNum,
|
||||
viewModel = nodeDetailViewModel,
|
||||
compassViewModel = compassViewModel,
|
||||
navigateToMessages = { backStack.add(ContactsRoutes.Messages(it)) },
|
||||
navigateToMessages = { backStack.add(ContactsRoute.Messages(it)) },
|
||||
onNavigate = { backStack.add(it) },
|
||||
onNavigateUp = { backStack.removeLastOrNull() },
|
||||
)
|
||||
}
|
||||
|
||||
entry<NodeDetailRoutes.NodeMap>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
entry<NodeDetailRoute.NodeMap>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
val mapScreen = org.meshtastic.core.ui.util.LocalNodeMapScreenProvider.current
|
||||
mapScreen(args.destNum) { backStack.removeLastOrNull() }
|
||||
}
|
||||
|
||||
entry<NodeDetailRoutes.TracerouteLog>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
entry<NodeDetailRoute.TracerouteLog>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
val metricsViewModel = koinViewModel<MetricsViewModel> { parametersOf(args.destNum) }
|
||||
metricsViewModel.setNodeId(args.destNum)
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ fun EntryProviderScope<NavKey>.nodeDetailGraph(
|
|||
onNavigateUp = { backStack.removeLastOrNull() },
|
||||
onViewOnMap = { requestId, responseLogUuid ->
|
||||
backStack.add(
|
||||
NodeDetailRoutes.TracerouteMap(
|
||||
NodeDetailRoute.TracerouteMap(
|
||||
destNum = args.destNum,
|
||||
requestId = requestId,
|
||||
logUuid = responseLogUuid,
|
||||
|
|
@ -146,40 +146,40 @@ fun EntryProviderScope<NavKey>.nodeDetailGraph(
|
|||
)
|
||||
}
|
||||
|
||||
entry<NodeDetailRoutes.TracerouteMap>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
entry<NodeDetailRoute.TracerouteMap>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
val tracerouteMapScreen = org.meshtastic.core.ui.util.LocalTracerouteMapScreenProvider.current
|
||||
tracerouteMapScreen(args.destNum, args.requestId, args.logUuid) { backStack.removeLastOrNull() }
|
||||
}
|
||||
|
||||
NodeDetailRoute.entries.forEach { routeInfo ->
|
||||
NodeDetailScreen.entries.forEach { routeInfo ->
|
||||
when (routeInfo.routeClass) {
|
||||
NodeDetailRoutes.DeviceMetrics::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoutes.DeviceMetrics>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoutes.PositionLog::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoutes.PositionLog>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoutes.EnvironmentMetrics::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoutes.EnvironmentMetrics>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoutes.SignalMetrics::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoutes.SignalMetrics>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoutes.PowerMetrics::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoutes.PowerMetrics>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoutes.HostMetricsLog::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoutes.HostMetricsLog>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoutes.PaxMetrics::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoutes.PaxMetrics>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoutes.NeighborInfoLog::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoutes.NeighborInfoLog>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoute.DeviceMetrics::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoute.DeviceMetrics>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoute.PositionLog::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoute.PositionLog>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoute.EnvironmentMetrics::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoute.EnvironmentMetrics>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoute.SignalMetrics::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoute.SignalMetrics>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoute.PowerMetrics::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoute.PowerMetrics>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoute.HostMetricsLog::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoute.HostMetricsLog>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoute.PaxMetrics::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoute.PaxMetrics>(backStack, routeInfo) { it.destNum }
|
||||
NodeDetailRoute.NeighborInfoLog::class ->
|
||||
addNodeDetailScreenComposable<NodeDetailRoute.NeighborInfoLog>(backStack, routeInfo) { it.destNum }
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun NavKey.isNodeDetailRoute(): Boolean = NodeDetailRoute.entries.any { this::class == it.routeClass }
|
||||
fun NavKey.isNodeDetailRoute(): Boolean = NodeDetailScreen.entries.any { this::class == it.routeClass }
|
||||
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
private inline fun <reified R : Route> EntryProviderScope<NavKey>.addNodeDetailScreenComposable(
|
||||
backStack: NavBackStack<NavKey>,
|
||||
routeInfo: NodeDetailRoute,
|
||||
routeInfo: NodeDetailScreen,
|
||||
crossinline getDestNum: (R) -> Int,
|
||||
) {
|
||||
entry<R>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
|
|
@ -192,7 +192,7 @@ private inline fun <reified R : Route> EntryProviderScope<NavKey>.addNodeDetailS
|
|||
}
|
||||
|
||||
/** Expect declaration for the platform-specific traceroute map screen. */
|
||||
enum class NodeDetailRoute(
|
||||
enum class NodeDetailScreen(
|
||||
val title: StringResource,
|
||||
val routeClass: KClass<out Route>,
|
||||
val icon: DrawableResource? = null,
|
||||
|
|
@ -200,55 +200,55 @@ enum class NodeDetailRoute(
|
|||
) {
|
||||
DEVICE(
|
||||
Res.string.device,
|
||||
NodeDetailRoutes.DeviceMetrics::class,
|
||||
NodeDetailRoute.DeviceMetrics::class,
|
||||
Res.drawable.ic_router,
|
||||
{ metricsVM, onNavigateUp -> DeviceMetricsScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
POSITION_LOG(
|
||||
Res.string.position_log,
|
||||
NodeDetailRoutes.PositionLog::class,
|
||||
NodeDetailRoute.PositionLog::class,
|
||||
Res.drawable.ic_location_on,
|
||||
{ metricsVM, onNavigateUp -> PositionLogScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
ENVIRONMENT(
|
||||
Res.string.environment,
|
||||
NodeDetailRoutes.EnvironmentMetrics::class,
|
||||
NodeDetailRoute.EnvironmentMetrics::class,
|
||||
Res.drawable.ic_light_mode,
|
||||
{ metricsVM, onNavigateUp -> EnvironmentMetricsScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
SIGNAL(
|
||||
Res.string.signal,
|
||||
NodeDetailRoutes.SignalMetrics::class,
|
||||
NodeDetailRoute.SignalMetrics::class,
|
||||
Res.drawable.ic_cell_tower,
|
||||
{ metricsVM, onNavigateUp -> SignalMetricsScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
TRACEROUTE(
|
||||
Res.string.traceroute,
|
||||
NodeDetailRoutes.TracerouteLog::class,
|
||||
NodeDetailRoute.TracerouteLog::class,
|
||||
Res.drawable.ic_perm_scan_wifi,
|
||||
{ metricsVM, onNavigateUp -> TracerouteLogScreen(viewModel = metricsVM, onNavigateUp = onNavigateUp) },
|
||||
),
|
||||
NEIGHBOR_INFO(
|
||||
Res.string.neighbor_info,
|
||||
NodeDetailRoutes.NeighborInfoLog::class,
|
||||
NodeDetailRoute.NeighborInfoLog::class,
|
||||
Res.drawable.ic_groups,
|
||||
{ metricsVM, onNavigateUp -> NeighborInfoLogScreen(viewModel = metricsVM, onNavigateUp = onNavigateUp) },
|
||||
),
|
||||
POWER(
|
||||
Res.string.power,
|
||||
NodeDetailRoutes.PowerMetrics::class,
|
||||
NodeDetailRoute.PowerMetrics::class,
|
||||
Res.drawable.ic_power,
|
||||
{ metricsVM, onNavigateUp -> PowerMetricsScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
HOST(
|
||||
Res.string.host,
|
||||
NodeDetailRoutes.HostMetricsLog::class,
|
||||
NodeDetailRoute.HostMetricsLog::class,
|
||||
Res.drawable.ic_memory,
|
||||
{ metricsVM, onNavigateUp -> HostMetricsLogScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
PAX(
|
||||
Res.string.pax,
|
||||
NodeDetailRoutes.PaxMetrics::class,
|
||||
NodeDetailRoute.PaxMetrics::class,
|
||||
Res.drawable.ic_people,
|
||||
{ metricsVM, onNavigateUp -> PaxMetricsScreen(metricsVM, onNavigateUp) },
|
||||
),
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ import org.meshtastic.core.common.util.toDate
|
|||
import org.meshtastic.core.common.util.toInstant
|
||||
import org.meshtastic.core.common.util.toMeshtasticUri
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.WifiProvisionRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
import org.meshtastic.core.navigation.WifiProvisionRoute
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.bottom_nav_settings
|
||||
import org.meshtastic.core.resources.export_configuration
|
||||
|
|
@ -233,7 +233,7 @@ fun SettingsScreen(
|
|||
|
||||
ExpressiveSection(title = stringResource(Res.string.wifi_devices)) {
|
||||
ListItem(text = stringResource(Res.string.wifi_devices), leadingIcon = MeshtasticIcons.Wifi) {
|
||||
onNavigate(WifiProvisionRoutes.WifiProvision())
|
||||
onNavigate(WifiProvisionRoute.WifiProvision())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -249,7 +249,7 @@ fun SettingsScreen(
|
|||
excludedModulesUnlocked = excludedModulesUnlocked,
|
||||
onUnlockExcludedModules = { settingsViewModel.unlockExcludedModules() },
|
||||
onShowAppIntro = { settingsViewModel.showAppIntro() },
|
||||
onNavigateToAbout = { onNavigate(SettingsRoutes.About) },
|
||||
onNavigateToAbout = { onNavigate(SettingsRoute.About) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
package org.meshtastic.feature.settings.navigation
|
||||
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
|
||||
actual fun getAboutLibrariesJson(): String =
|
||||
SettingsRoutes::class.java.getResource("/aboutlibraries.json")?.readText() ?: ""
|
||||
SettingsRoute::class.java.getResource("/aboutlibraries.json")?.readText() ?: ""
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ package org.meshtastic.feature.settings.navigation
|
|||
import org.jetbrains.compose.resources.DrawableResource
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.bluetooth
|
||||
import org.meshtastic.core.resources.channels
|
||||
|
|
@ -50,43 +50,43 @@ enum class ConfigRoute(
|
|||
val icon: DrawableResource? = null,
|
||||
val type: Int = 0,
|
||||
) {
|
||||
USER(Res.string.user, SettingsRoutes.User, Res.drawable.ic_person, 0),
|
||||
CHANNELS(Res.string.channels, SettingsRoutes.ChannelConfig, Res.drawable.ic_list, 0),
|
||||
USER(Res.string.user, SettingsRoute.User, Res.drawable.ic_person, 0),
|
||||
CHANNELS(Res.string.channels, SettingsRoute.ChannelConfig, Res.drawable.ic_list, 0),
|
||||
DEVICE(
|
||||
Res.string.device,
|
||||
SettingsRoutes.Device,
|
||||
SettingsRoute.Device,
|
||||
Res.drawable.ic_router,
|
||||
AdminMessage.ConfigType.DEVICE_CONFIG.value,
|
||||
),
|
||||
POSITION(
|
||||
Res.string.position,
|
||||
SettingsRoutes.Position,
|
||||
SettingsRoute.Position,
|
||||
Res.drawable.ic_location_on,
|
||||
AdminMessage.ConfigType.POSITION_CONFIG.value,
|
||||
),
|
||||
POWER(Res.string.power, SettingsRoutes.Power, Res.drawable.ic_power, AdminMessage.ConfigType.POWER_CONFIG.value),
|
||||
POWER(Res.string.power, SettingsRoute.Power, Res.drawable.ic_power, AdminMessage.ConfigType.POWER_CONFIG.value),
|
||||
NETWORK(
|
||||
Res.string.network,
|
||||
SettingsRoutes.Network,
|
||||
SettingsRoute.Network,
|
||||
Res.drawable.ic_wifi,
|
||||
AdminMessage.ConfigType.NETWORK_CONFIG.value,
|
||||
),
|
||||
DISPLAY(
|
||||
Res.string.display,
|
||||
SettingsRoutes.Display,
|
||||
SettingsRoute.Display,
|
||||
Res.drawable.ic_display_settings,
|
||||
AdminMessage.ConfigType.DISPLAY_CONFIG.value,
|
||||
),
|
||||
LORA(Res.string.lora, SettingsRoutes.LoRa, Res.drawable.ic_cell_tower, AdminMessage.ConfigType.LORA_CONFIG.value),
|
||||
LORA(Res.string.lora, SettingsRoute.LoRa, Res.drawable.ic_cell_tower, AdminMessage.ConfigType.LORA_CONFIG.value),
|
||||
BLUETOOTH(
|
||||
Res.string.bluetooth,
|
||||
SettingsRoutes.Bluetooth,
|
||||
SettingsRoute.Bluetooth,
|
||||
Res.drawable.ic_bluetooth,
|
||||
AdminMessage.ConfigType.BLUETOOTH_CONFIG.value,
|
||||
),
|
||||
SECURITY(
|
||||
Res.string.security,
|
||||
SettingsRoutes.Security,
|
||||
SettingsRoute.Security,
|
||||
Res.drawable.ic_security,
|
||||
AdminMessage.ConfigType.SECURITY_CONFIG.value,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import org.jetbrains.compose.resources.DrawableResource
|
|||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.meshtastic.core.model.Capabilities
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.ambient_lighting
|
||||
import org.meshtastic.core.resources.audio
|
||||
|
|
@ -64,96 +64,96 @@ enum class ModuleRoute(
|
|||
val isSupported: (Capabilities) -> Boolean = { true },
|
||||
val isApplicable: (Config.DeviceConfig.Role?) -> Boolean = { true },
|
||||
) {
|
||||
MQTT(Res.string.mqtt, SettingsRoutes.MQTT, Res.drawable.ic_cloud, AdminMessage.ModuleConfigType.MQTT_CONFIG.value),
|
||||
MQTT(Res.string.mqtt, SettingsRoute.MQTT, Res.drawable.ic_cloud, AdminMessage.ModuleConfigType.MQTT_CONFIG.value),
|
||||
SERIAL(
|
||||
Res.string.serial,
|
||||
SettingsRoutes.Serial,
|
||||
SettingsRoute.Serial,
|
||||
Res.drawable.ic_usb,
|
||||
AdminMessage.ModuleConfigType.SERIAL_CONFIG.value,
|
||||
),
|
||||
EXT_NOTIFICATION(
|
||||
Res.string.external_notification,
|
||||
SettingsRoutes.ExtNotification,
|
||||
SettingsRoute.ExtNotification,
|
||||
Res.drawable.ic_notifications,
|
||||
AdminMessage.ModuleConfigType.EXTNOTIF_CONFIG.value,
|
||||
),
|
||||
STORE_FORWARD(
|
||||
Res.string.store_forward,
|
||||
SettingsRoutes.StoreForward,
|
||||
SettingsRoute.StoreForward,
|
||||
Res.drawable.ic_terminal,
|
||||
AdminMessage.ModuleConfigType.STOREFORWARD_CONFIG.value,
|
||||
),
|
||||
RANGE_TEST(
|
||||
Res.string.range_test,
|
||||
SettingsRoutes.RangeTest,
|
||||
SettingsRoute.RangeTest,
|
||||
Res.drawable.ic_speed,
|
||||
AdminMessage.ModuleConfigType.RANGETEST_CONFIG.value,
|
||||
),
|
||||
TELEMETRY(
|
||||
Res.string.telemetry,
|
||||
SettingsRoutes.Telemetry,
|
||||
SettingsRoute.Telemetry,
|
||||
Res.drawable.ic_data_usage,
|
||||
AdminMessage.ModuleConfigType.TELEMETRY_CONFIG.value,
|
||||
),
|
||||
CANNED_MESSAGE(
|
||||
Res.string.canned_message,
|
||||
SettingsRoutes.CannedMessage,
|
||||
SettingsRoute.CannedMessage,
|
||||
Res.drawable.ic_message,
|
||||
AdminMessage.ModuleConfigType.CANNEDMSG_CONFIG.value,
|
||||
),
|
||||
AUDIO(
|
||||
Res.string.audio,
|
||||
SettingsRoutes.Audio,
|
||||
SettingsRoute.Audio,
|
||||
Res.drawable.ic_volume_up,
|
||||
AdminMessage.ModuleConfigType.AUDIO_CONFIG.value,
|
||||
),
|
||||
REMOTE_HARDWARE(
|
||||
Res.string.remote_hardware,
|
||||
SettingsRoutes.RemoteHardware,
|
||||
SettingsRoute.RemoteHardware,
|
||||
Res.drawable.ic_settings_remote,
|
||||
AdminMessage.ModuleConfigType.REMOTEHARDWARE_CONFIG.value,
|
||||
),
|
||||
NEIGHBOR_INFO(
|
||||
Res.string.neighbor_info,
|
||||
SettingsRoutes.NeighborInfo,
|
||||
SettingsRoute.NeighborInfo,
|
||||
Res.drawable.ic_people,
|
||||
AdminMessage.ModuleConfigType.NEIGHBORINFO_CONFIG.value,
|
||||
),
|
||||
AMBIENT_LIGHTING(
|
||||
Res.string.ambient_lighting,
|
||||
SettingsRoutes.AmbientLighting,
|
||||
SettingsRoute.AmbientLighting,
|
||||
Res.drawable.ic_light_mode,
|
||||
AdminMessage.ModuleConfigType.AMBIENTLIGHTING_CONFIG.value,
|
||||
),
|
||||
DETECTION_SENSOR(
|
||||
Res.string.detection_sensor,
|
||||
SettingsRoutes.DetectionSensor,
|
||||
SettingsRoute.DetectionSensor,
|
||||
Res.drawable.ic_sensors,
|
||||
AdminMessage.ModuleConfigType.DETECTIONSENSOR_CONFIG.value,
|
||||
),
|
||||
PAXCOUNTER(
|
||||
Res.string.paxcounter,
|
||||
SettingsRoutes.Paxcounter,
|
||||
SettingsRoute.Paxcounter,
|
||||
Res.drawable.ic_perm_scan_wifi,
|
||||
AdminMessage.ModuleConfigType.PAXCOUNTER_CONFIG.value,
|
||||
),
|
||||
STATUS_MESSAGE(
|
||||
Res.string.status_message,
|
||||
SettingsRoutes.StatusMessage,
|
||||
SettingsRoute.StatusMessage,
|
||||
Res.drawable.ic_message,
|
||||
AdminMessage.ModuleConfigType.STATUSMESSAGE_CONFIG.value,
|
||||
isSupported = { it.supportsStatusMessage },
|
||||
),
|
||||
TRAFFIC_MANAGEMENT(
|
||||
Res.string.traffic_management,
|
||||
SettingsRoutes.TrafficManagement,
|
||||
SettingsRoute.TrafficManagement,
|
||||
Res.drawable.ic_alt_route,
|
||||
AdminMessage.ModuleConfigType.TRAFFICMANAGEMENT_CONFIG.value,
|
||||
isSupported = { it.supportsTrafficManagementConfig },
|
||||
),
|
||||
TAK(
|
||||
Res.string.tak,
|
||||
SettingsRoutes.TAK,
|
||||
SettingsRoute.TAK,
|
||||
Res.drawable.ic_people,
|
||||
AdminMessage.ModuleConfigType.TAK_CONFIG.value,
|
||||
isSupported = { it.supportsTakConfig },
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ import androidx.navigation3.runtime.EntryProviderScope
|
|||
import androidx.navigation3.runtime.NavBackStack
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoute
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
import org.meshtastic.feature.settings.AboutScreen
|
||||
import org.meshtastic.feature.settings.AdministrationScreen
|
||||
import org.meshtastic.feature.settings.DeviceConfigurationScreen
|
||||
|
|
@ -74,10 +74,10 @@ fun getRadioConfigViewModel(backStack: NavBackStack<NavKey>): RadioConfigViewMod
|
|||
val viewModel = koinViewModel<RadioConfigViewModel>()
|
||||
val destNum =
|
||||
remember(backStack.toList()) {
|
||||
backStack.lastOrNull { it is SettingsRoutes.Settings }?.let { (it as SettingsRoutes.Settings).destNum }
|
||||
backStack.lastOrNull { it is SettingsRoute.Settings }?.let { (it as SettingsRoute.Settings).destNum }
|
||||
?: backStack
|
||||
.lastOrNull { it is SettingsRoutes.SettingsGraph }
|
||||
?.let { (it as SettingsRoutes.SettingsGraph).destNum }
|
||||
.lastOrNull { it is SettingsRoute.SettingsGraph }
|
||||
?.let { (it as SettingsRoute.SettingsGraph).destNum }
|
||||
}
|
||||
SideEffect { viewModel.initDestNum(destNum) }
|
||||
return viewModel
|
||||
|
|
@ -85,25 +85,25 @@ fun getRadioConfigViewModel(backStack: NavBackStack<NavKey>): RadioConfigViewMod
|
|||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
fun EntryProviderScope<NavKey>.settingsGraph(backStack: NavBackStack<NavKey>) {
|
||||
entry<SettingsRoutes.SettingsGraph> {
|
||||
entry<SettingsRoute.SettingsGraph> {
|
||||
SettingsMainScreen(
|
||||
settingsViewModel = koinViewModel(),
|
||||
radioConfigViewModel = getRadioConfigViewModel(backStack),
|
||||
onClickNodeChip = { backStack.add(NodesRoutes.NodeDetail(it)) },
|
||||
onClickNodeChip = { backStack.add(NodesRoute.NodeDetail(it)) },
|
||||
onNavigate = { backStack.add(it) },
|
||||
)
|
||||
}
|
||||
|
||||
entry<SettingsRoutes.Settings> {
|
||||
entry<SettingsRoute.Settings> {
|
||||
SettingsMainScreen(
|
||||
settingsViewModel = koinViewModel(),
|
||||
radioConfigViewModel = getRadioConfigViewModel(backStack),
|
||||
onClickNodeChip = { backStack.add(NodesRoutes.NodeDetail(it)) },
|
||||
onClickNodeChip = { backStack.add(NodesRoute.NodeDetail(it)) },
|
||||
onNavigate = { backStack.add(it) },
|
||||
)
|
||||
}
|
||||
|
||||
entry<SettingsRoutes.DeviceConfiguration> {
|
||||
entry<SettingsRoute.DeviceConfiguration> {
|
||||
DeviceConfigurationScreen(
|
||||
viewModel = getRadioConfigViewModel(backStack),
|
||||
onBack = { backStack.removeLastOrNull() },
|
||||
|
|
@ -111,7 +111,7 @@ fun EntryProviderScope<NavKey>.settingsGraph(backStack: NavBackStack<NavKey>) {
|
|||
)
|
||||
}
|
||||
|
||||
entry<SettingsRoutes.ModuleConfiguration> {
|
||||
entry<SettingsRoute.ModuleConfiguration> {
|
||||
val settingsViewModel: SettingsViewModel = koinViewModel()
|
||||
val excludedModulesUnlocked by settingsViewModel.excludedModulesUnlocked.collectAsStateWithLifecycle()
|
||||
ModuleConfigurationScreen(
|
||||
|
|
@ -122,11 +122,11 @@ fun EntryProviderScope<NavKey>.settingsGraph(backStack: NavBackStack<NavKey>) {
|
|||
)
|
||||
}
|
||||
|
||||
entry<SettingsRoutes.Administration> {
|
||||
entry<SettingsRoute.Administration> {
|
||||
AdministrationScreen(viewModel = getRadioConfigViewModel(backStack), onBack = { backStack.removeLastOrNull() })
|
||||
}
|
||||
|
||||
entry<SettingsRoutes.CleanNodeDb> {
|
||||
entry<SettingsRoute.CleanNodeDb> {
|
||||
val viewModel: CleanNodeDatabaseViewModel = koinViewModel()
|
||||
CleanNodeDatabaseScreen(viewModel = viewModel)
|
||||
}
|
||||
|
|
@ -185,16 +185,16 @@ fun EntryProviderScope<NavKey>.settingsGraph(backStack: NavBackStack<NavKey>) {
|
|||
}
|
||||
}
|
||||
|
||||
entry<SettingsRoutes.DebugPanel> {
|
||||
entry<SettingsRoute.DebugPanel> {
|
||||
val viewModel: DebugViewModel = koinViewModel()
|
||||
DebugScreen(viewModel = viewModel, onNavigateUp = { backStack.removeLastOrNull() })
|
||||
}
|
||||
|
||||
entry<SettingsRoutes.About> {
|
||||
entry<SettingsRoute.About> {
|
||||
AboutScreen(onNavigateUp = { backStack.removeLastOrNull() }, jsonProvider = { getAboutLibrariesJson() })
|
||||
}
|
||||
|
||||
entry<SettingsRoutes.FilterSettings> {
|
||||
entry<SettingsRoute.FilterSettings> {
|
||||
val viewModel: FilterSettingsViewModel = koinViewModel()
|
||||
FilterSettingsScreen(viewModel = viewModel, onBack = { backStack.removeLastOrNull() })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ import org.jetbrains.compose.resources.DrawableResource
|
|||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.jetbrains.compose.resources.vectorResource
|
||||
import org.meshtastic.core.navigation.FirmwareRoutes
|
||||
import org.meshtastic.core.navigation.FirmwareRoute
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.administration
|
||||
import org.meshtastic.core.resources.advanced_title
|
||||
|
|
@ -125,7 +125,7 @@ private fun DeviceConfigSection(isManaged: Boolean, enabled: Boolean, onNavigate
|
|||
trailingIcon = MeshtasticIcons.KeyboardArrowRight,
|
||||
enabled = enabled,
|
||||
) {
|
||||
onNavigate(SettingsRoutes.DeviceConfiguration)
|
||||
onNavigate(SettingsRoute.DeviceConfiguration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -142,7 +142,7 @@ private fun ModuleSettingsSection(isManaged: Boolean, enabled: Boolean, onNaviga
|
|||
trailingIcon = MeshtasticIcons.KeyboardArrowRight,
|
||||
enabled = enabled,
|
||||
) {
|
||||
onNavigate(SettingsRoutes.ModuleConfiguration)
|
||||
onNavigate(SettingsRoute.ModuleConfiguration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -181,7 +181,7 @@ private fun AdministrationSection(enabled: Boolean, onNavigate: (Route) -> Unit)
|
|||
trailingIconTint = MaterialTheme.colorScheme.error,
|
||||
enabled = enabled,
|
||||
) {
|
||||
onNavigate(SettingsRoutes.Administration)
|
||||
onNavigate(SettingsRoute.Administration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -198,7 +198,7 @@ private fun AdvancedSection(isManaged: Boolean, isOtaCapable: Boolean, enabled:
|
|||
text = stringResource(Res.string.firmware_update_title),
|
||||
leadingIcon = MeshtasticIcons.SystemUpdate,
|
||||
enabled = enabled,
|
||||
onClick = { onNavigate(FirmwareRoutes.FirmwareUpdate) },
|
||||
onClick = { onNavigate(FirmwareRoute.FirmwareUpdate) },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -206,14 +206,14 @@ private fun AdvancedSection(isManaged: Boolean, isOtaCapable: Boolean, enabled:
|
|||
text = stringResource(Res.string.clean_node_database_title),
|
||||
leadingIcon = MeshtasticIcons.CleaningServices,
|
||||
enabled = enabled,
|
||||
onClick = { onNavigate(SettingsRoutes.CleanNodeDb) },
|
||||
onClick = { onNavigate(SettingsRoute.CleanNodeDb) },
|
||||
)
|
||||
|
||||
ListItem(
|
||||
text = stringResource(Res.string.debug_panel),
|
||||
leadingIcon = MeshtasticIcons.BugReport,
|
||||
enabled = enabled,
|
||||
onClick = { onNavigate(SettingsRoutes.DebugPanel) },
|
||||
onClick = { onNavigate(SettingsRoute.DebugPanel) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ import androidx.navigation3.runtime.EntryProviderScope
|
|||
import androidx.navigation3.runtime.NavBackStack
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.meshtastic.core.navigation.ChannelsRoutes
|
||||
import org.meshtastic.core.navigation.ChannelsRoute
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
|
||||
/** Navigation graph for for the top level ChannelScreen - [ChannelsRoutes.Channels]. */
|
||||
/** Navigation graph for for the top level ChannelScreen - [ChannelsRoute.Channels]. */
|
||||
fun EntryProviderScope<NavKey>.channelsGraph(backStack: NavBackStack<NavKey>) {
|
||||
entry<ChannelsRoutes.ChannelsGraph> {
|
||||
entry<ChannelsRoute.ChannelsGraph> {
|
||||
ChannelScreen(
|
||||
radioConfigViewModel = koinViewModel<RadioConfigViewModel>(),
|
||||
onNavigate = { route -> backStack.add(route) },
|
||||
|
|
@ -33,7 +33,7 @@ fun EntryProviderScope<NavKey>.channelsGraph(backStack: NavBackStack<NavKey>) {
|
|||
)
|
||||
}
|
||||
|
||||
entry<ChannelsRoutes.Channels> {
|
||||
entry<ChannelsRoute.Channels> {
|
||||
ChannelScreen(
|
||||
radioConfigViewModel = koinViewModel<RadioConfigViewModel>(),
|
||||
onNavigate = { route -> backStack.add(route) },
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ import kotlinx.coroutines.launch
|
|||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.database.DatabaseConstants
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.WifiProvisionRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
import org.meshtastic.core.navigation.WifiProvisionRoute
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.acknowledgements
|
||||
import org.meshtastic.core.resources.app_settings
|
||||
|
|
@ -202,7 +202,7 @@ fun DesktopSettingsScreen(
|
|||
|
||||
ExpressiveSection(title = stringResource(Res.string.wifi_devices)) {
|
||||
ListItem(text = stringResource(Res.string.wifi_devices), leadingIcon = MeshtasticIcons.Wifi) {
|
||||
onNavigate(WifiProvisionRoutes.WifiProvision())
|
||||
onNavigate(WifiProvisionRoute.WifiProvision())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -219,7 +219,7 @@ fun DesktopSettingsScreen(
|
|||
appVersionName = settingsViewModel.appVersionName,
|
||||
excludedModulesUnlocked = excludedModulesUnlocked,
|
||||
onUnlockExcludedModules = { settingsViewModel.unlockExcludedModules() },
|
||||
onNavigateToAbout = { onNavigate(SettingsRoutes.About) },
|
||||
onNavigateToAbout = { onNavigate(SettingsRoute.About) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
package org.meshtastic.feature.settings.navigation
|
||||
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
|
||||
actual fun getAboutLibrariesJson(): String =
|
||||
SettingsRoutes::class.java.getResource("/aboutlibraries.json")?.readText() ?: ""
|
||||
SettingsRoute::class.java.getResource("/aboutlibraries.json")?.readText() ?: ""
|
||||
|
|
|
|||
|
|
@ -19,21 +19,21 @@ package org.meshtastic.feature.wifiprovision.navigation
|
|||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
import androidx.navigation3.runtime.NavBackStack
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import org.meshtastic.core.navigation.WifiProvisionRoutes
|
||||
import org.meshtastic.core.navigation.WifiProvisionRoute
|
||||
import org.meshtastic.feature.wifiprovision.ui.WifiProvisionScreen
|
||||
|
||||
/**
|
||||
* Registers the WiFi provisioning graph entries into the host navigation provider.
|
||||
*
|
||||
* Both the graph sentinel ([WifiProvisionRoutes.WifiProvisionGraph]) and the primary screen
|
||||
* ([WifiProvisionRoutes.WifiProvision]) navigate to the same composable so that the feature can be reached via either a
|
||||
* Both the graph sentinel ([WifiProvisionRoute.WifiProvisionGraph]) and the primary screen
|
||||
* ([WifiProvisionRoute.WifiProvision]) navigate to the same composable so that the feature can be reached via either a
|
||||
* top-level push or a deep-link graph push.
|
||||
*/
|
||||
fun EntryProviderScope<NavKey>.wifiProvisionGraph(backStack: NavBackStack<NavKey>) {
|
||||
entry<WifiProvisionRoutes.WifiProvisionGraph> {
|
||||
entry<WifiProvisionRoute.WifiProvisionGraph> {
|
||||
WifiProvisionScreen(onNavigateUp = { backStack.removeLastOrNull() })
|
||||
}
|
||||
entry<WifiProvisionRoutes.WifiProvision> { key ->
|
||||
entry<WifiProvisionRoute.WifiProvision> { key ->
|
||||
WifiProvisionScreen(onNavigateUp = { backStack.removeLastOrNull() }, address = key.address)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue