mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Refactor nav3 architecture and enhance adaptive layouts (#4944)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
3feec759a1
commit
f2d09ff79d
29 changed files with 740 additions and 617 deletions
|
|
@ -19,13 +19,12 @@ package org.meshtastic.feature.intro
|
|||
import android.Manifest
|
||||
import android.os.Build
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import androidx.navigation3.runtime.rememberNavBackStack
|
||||
import androidx.navigation3.ui.NavDisplay
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.PermissionState
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import org.meshtastic.core.ui.component.MeshtasticNavDisplay
|
||||
|
||||
/**
|
||||
* Main application introduction screen. This Composable hosts the navigation flow and hoists the permission states.
|
||||
|
|
@ -58,9 +57,8 @@ fun AppIntroductionScreen(onDone: () -> Unit, viewModel: IntroViewModel) {
|
|||
|
||||
val backStack = rememberNavBackStack(Welcome)
|
||||
|
||||
NavDisplay<NavKey>(
|
||||
MeshtasticNavDisplay(
|
||||
backStack = backStack,
|
||||
onBack = { backStack.removeLastOrNull() },
|
||||
entryProvider =
|
||||
introNavGraph(
|
||||
backStack = backStack,
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ kotlin {
|
|||
implementation(libs.jetbrains.compose.material3.adaptive)
|
||||
implementation(libs.jetbrains.compose.material3.adaptive.layout)
|
||||
implementation(libs.jetbrains.compose.material3.adaptive.navigation)
|
||||
implementation(libs.jetbrains.compose.material3.adaptive.navigation3)
|
||||
}
|
||||
|
||||
androidMain.dependencies { implementation(libs.androidx.work.runtime.ktx) }
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
*/
|
||||
package org.meshtastic.feature.messaging.navigation
|
||||
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
import androidx.compose.material3.adaptive.navigation3.ListDetailSceneStrategy
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
|
|
@ -26,6 +28,7 @@ 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.replaceLast
|
||||
import org.meshtastic.core.ui.component.ScrollToTopEvent
|
||||
import org.meshtastic.feature.messaging.QuickChatScreen
|
||||
import org.meshtastic.feature.messaging.QuickChatViewModel
|
||||
|
|
@ -33,55 +36,54 @@ import org.meshtastic.feature.messaging.ui.contact.AdaptiveContactsScreen
|
|||
import org.meshtastic.feature.messaging.ui.contact.ContactsViewModel
|
||||
import org.meshtastic.feature.messaging.ui.sharing.ShareScreen
|
||||
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Suppress("LongMethod")
|
||||
fun EntryProviderScope<NavKey>.contactsGraph(
|
||||
backStack: NavBackStack<NavKey>,
|
||||
scrollToTopEvents: Flow<ScrollToTopEvent> = MutableSharedFlow(),
|
||||
) {
|
||||
entry<ContactsRoutes.ContactsGraph> {
|
||||
entry<ContactsRoutes.ContactsGraph>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
ContactsEntryContent(backStack = backStack, scrollToTopEvents = scrollToTopEvents)
|
||||
}
|
||||
|
||||
entry<ContactsRoutes.Contacts> {
|
||||
entry<ContactsRoutes.Contacts>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
ContactsEntryContent(backStack = backStack, scrollToTopEvents = scrollToTopEvents)
|
||||
}
|
||||
|
||||
entry<ContactsRoutes.Messages> { args ->
|
||||
ContactsEntryContent(
|
||||
backStack = backStack,
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
initialContactKey = args.contactKey,
|
||||
initialMessage = args.message,
|
||||
entry<ContactsRoutes.Messages>(metadata = { ListDetailSceneStrategy.detailPane() }) { args ->
|
||||
val contactKey = args.contactKey
|
||||
val messageViewModel: org.meshtastic.feature.messaging.MessageViewModel =
|
||||
koinViewModel(key = "messages-$contactKey")
|
||||
messageViewModel.setContactKey(contactKey)
|
||||
|
||||
org.meshtastic.feature.messaging.MessageScreen(
|
||||
contactKey = contactKey,
|
||||
message = args.message,
|
||||
viewModel = messageViewModel,
|
||||
navigateToNodeDetails = { backStack.add(org.meshtastic.core.navigation.NodesRoutes.NodeDetailGraph(it)) },
|
||||
navigateToQuickChatOptions = { backStack.add(org.meshtastic.core.navigation.ContactsRoutes.QuickChat) },
|
||||
onNavigateBack = { backStack.removeLastOrNull() },
|
||||
)
|
||||
}
|
||||
|
||||
entry<ContactsRoutes.Share> { args ->
|
||||
entry<ContactsRoutes.Share>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
val message = args.message
|
||||
val viewModel = koinViewModel<ContactsViewModel>()
|
||||
ShareScreen(
|
||||
viewModel = viewModel,
|
||||
onConfirm = {
|
||||
// Navigation 3 - replace Top with Messages manually, but for now we just pop and add
|
||||
backStack.removeLastOrNull()
|
||||
backStack.add(ContactsRoutes.Messages(it, message))
|
||||
},
|
||||
onConfirm = { contactKey -> backStack.replaceLast(ContactsRoutes.Messages(contactKey, message)) },
|
||||
onNavigateUp = { backStack.removeLastOrNull() },
|
||||
)
|
||||
}
|
||||
|
||||
entry<ContactsRoutes.QuickChat> {
|
||||
entry<ContactsRoutes.QuickChat>(metadata = { ListDetailSceneStrategy.extraPane() }) {
|
||||
val viewModel = koinViewModel<QuickChatViewModel>()
|
||||
QuickChatScreen(viewModel = viewModel, onNavigateUp = { backStack.removeLastOrNull() })
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsEntryContent(
|
||||
backStack: NavBackStack<NavKey>,
|
||||
scrollToTopEvents: Flow<ScrollToTopEvent>,
|
||||
initialContactKey: String? = null,
|
||||
initialMessage: String = "",
|
||||
) {
|
||||
fun ContactsEntryContent(backStack: NavBackStack<NavKey>, scrollToTopEvents: Flow<ScrollToTopEvent>) {
|
||||
val uiViewModel: org.meshtastic.core.ui.viewmodel.UIViewModel = koinViewModel()
|
||||
val sharedContactRequested by uiViewModel.sharedContactRequested.collectAsStateWithLifecycle()
|
||||
val requestChannelSet by uiViewModel.requestChannelSet.collectAsStateWithLifecycle()
|
||||
|
|
@ -90,30 +92,11 @@ fun ContactsEntryContent(
|
|||
AdaptiveContactsScreen(
|
||||
backStack = backStack,
|
||||
contactsViewModel = contactsViewModel,
|
||||
messageViewModel = koinViewModel(), // Ignored by custom detail pane below
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
sharedContactRequested = sharedContactRequested,
|
||||
requestChannelSet = requestChannelSet,
|
||||
onHandleDeepLink = uiViewModel::handleDeepLink,
|
||||
onClearSharedContactRequested = uiViewModel::clearSharedContactRequested,
|
||||
onClearRequestChannelUrl = uiViewModel::clearRequestChannelUrl,
|
||||
initialContactKey = initialContactKey,
|
||||
initialMessage = initialMessage,
|
||||
detailPaneCustom = { contactKey ->
|
||||
val messageViewModel: org.meshtastic.feature.messaging.MessageViewModel =
|
||||
koinViewModel(key = "messages-$contactKey")
|
||||
messageViewModel.setContactKey(contactKey)
|
||||
|
||||
org.meshtastic.feature.messaging.MessageScreen(
|
||||
contactKey = contactKey,
|
||||
message = if (contactKey == initialContactKey) initialMessage else "",
|
||||
viewModel = messageViewModel,
|
||||
navigateToNodeDetails = {
|
||||
backStack.add(org.meshtastic.core.navigation.NodesRoutes.NodeDetailGraph(it))
|
||||
},
|
||||
navigateToQuickChatOptions = { backStack.add(org.meshtastic.core.navigation.ContactsRoutes.QuickChat) },
|
||||
onNavigateBack = { backStack.removeLastOrNull() },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,118 +16,41 @@
|
|||
*/
|
||||
package org.meshtastic.feature.messaging.ui.contact
|
||||
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
|
||||
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.navigation3.runtime.NavBackStack
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
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.resources.Res
|
||||
import org.meshtastic.core.resources.conversations
|
||||
import org.meshtastic.core.ui.component.AdaptiveListDetailScaffold
|
||||
import org.meshtastic.core.ui.component.EmptyDetailPlaceholder
|
||||
import org.meshtastic.core.ui.component.ScrollToTopEvent
|
||||
import org.meshtastic.core.ui.icon.Conversations
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.feature.messaging.MessageScreen
|
||||
import org.meshtastic.feature.messaging.MessageViewModel
|
||||
import org.meshtastic.proto.ChannelSet
|
||||
import org.meshtastic.proto.SharedContact
|
||||
|
||||
@Suppress("LongMethod", "LongParameterList", "CyclomaticComplexMethod")
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Composable
|
||||
fun AdaptiveContactsScreen(
|
||||
backStack: NavBackStack<NavKey>,
|
||||
contactsViewModel: ContactsViewModel,
|
||||
messageViewModel: MessageViewModel,
|
||||
scrollToTopEvents: Flow<ScrollToTopEvent>,
|
||||
sharedContactRequested: SharedContact?,
|
||||
requestChannelSet: ChannelSet?,
|
||||
onHandleDeepLink: (MeshtasticUri, onInvalid: () -> Unit) -> Unit,
|
||||
onClearSharedContactRequested: () -> Unit,
|
||||
onClearRequestChannelUrl: () -> Unit,
|
||||
initialContactKey: String? = null,
|
||||
initialMessage: String = "",
|
||||
detailPaneCustom: @Composable ((contactKey: String) -> Unit)? = null,
|
||||
) {
|
||||
val navigator = rememberListDetailPaneScaffoldNavigator<String>()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val onBackToGraph: () -> Unit = {
|
||||
val currentKey = backStack.lastOrNull()
|
||||
|
||||
if (
|
||||
currentKey is ContactsRoutes.Messages ||
|
||||
currentKey is ContactsRoutes.Contacts ||
|
||||
currentKey is ContactsRoutes.ContactsGraph
|
||||
) {
|
||||
// Check if we navigated here from another screen (e.g., from Nodes or Map)
|
||||
val previousKey = if (backStack.size > 1) backStack[backStack.size - 2] else null
|
||||
val isFromDifferentGraph =
|
||||
previousKey != null &&
|
||||
previousKey !is ContactsRoutes.ContactsGraph &&
|
||||
previousKey !is ContactsRoutes.Contacts &&
|
||||
previousKey !is ContactsRoutes.Messages
|
||||
|
||||
if (isFromDifferentGraph) {
|
||||
// Navigate back via NavController to return to the previous screen (e.g. Node Details)
|
||||
backStack.removeLastOrNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AdaptiveListDetailScaffold(
|
||||
navigator = navigator,
|
||||
ContactsScreen(
|
||||
onNavigateToShare = { backStack.add(ChannelsRoutes.ChannelsGraph) },
|
||||
sharedContactRequested = sharedContactRequested,
|
||||
requestChannelSet = requestChannelSet,
|
||||
onHandleDeepLink = onHandleDeepLink,
|
||||
onClearSharedContactRequested = onClearSharedContactRequested,
|
||||
onClearRequestChannelUrl = onClearRequestChannelUrl,
|
||||
viewModel = contactsViewModel,
|
||||
onClickNodeChip = { backStack.add(NodesRoutes.NodeDetailGraph(it)) },
|
||||
onNavigateToMessages = { contactKey -> backStack.add(ContactsRoutes.Messages(contactKey)) },
|
||||
onNavigateToNodeDetails = { backStack.add(NodesRoutes.NodeDetailGraph(it)) },
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
onBackToGraph = onBackToGraph,
|
||||
onTabPressedEvent = { it is ScrollToTopEvent.ConversationsTabPressed },
|
||||
initialKey = initialContactKey,
|
||||
listPane = { isActive, activeContactKey ->
|
||||
ContactsScreen(
|
||||
onNavigateToShare = { backStack.add(ChannelsRoutes.ChannelsGraph) },
|
||||
sharedContactRequested = sharedContactRequested,
|
||||
requestChannelSet = requestChannelSet,
|
||||
onHandleDeepLink = onHandleDeepLink,
|
||||
onClearSharedContactRequested = onClearSharedContactRequested,
|
||||
onClearRequestChannelUrl = onClearRequestChannelUrl,
|
||||
viewModel = contactsViewModel,
|
||||
onClickNodeChip = { backStack.add(NodesRoutes.NodeDetailGraph(it)) },
|
||||
onNavigateToMessages = { contactKey ->
|
||||
scope.launch { navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, contactKey) }
|
||||
},
|
||||
onNavigateToNodeDetails = { backStack.add(NodesRoutes.NodeDetailGraph(it)) },
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
activeContactKey = activeContactKey,
|
||||
)
|
||||
},
|
||||
detailPane = { contentKey, handleBack ->
|
||||
if (detailPaneCustom != null) {
|
||||
detailPaneCustom(contentKey)
|
||||
} else {
|
||||
MessageScreen(
|
||||
contactKey = contentKey,
|
||||
message = if (contentKey == initialContactKey) initialMessage else "",
|
||||
viewModel = messageViewModel,
|
||||
navigateToNodeDetails = { backStack.add(NodesRoutes.NodeDetailGraph(it)) },
|
||||
navigateToQuickChatOptions = { backStack.add(ContactsRoutes.QuickChat) },
|
||||
onNavigateBack = handleBack,
|
||||
)
|
||||
}
|
||||
},
|
||||
emptyDetailPane = {
|
||||
EmptyDetailPlaceholder(
|
||||
icon = MeshtasticIcons.Conversations,
|
||||
title = stringResource(Res.string.conversations),
|
||||
)
|
||||
},
|
||||
activeContactKey = null,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ kotlin {
|
|||
implementation(libs.jetbrains.compose.material3.adaptive)
|
||||
implementation(libs.jetbrains.compose.material3.adaptive.layout)
|
||||
implementation(libs.jetbrains.compose.material3.adaptive.navigation)
|
||||
implementation(libs.jetbrains.compose.material3.adaptive.navigation3)
|
||||
}
|
||||
|
||||
androidMain.dependencies {
|
||||
|
|
|
|||
|
|
@ -16,93 +16,31 @@
|
|||
*/
|
||||
package org.meshtastic.feature.node.navigation
|
||||
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
|
||||
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.navigation3.runtime.NavBackStack
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import org.meshtastic.core.navigation.ChannelsRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.nodes
|
||||
import org.meshtastic.core.ui.component.AdaptiveListDetailScaffold
|
||||
import org.meshtastic.core.ui.component.EmptyDetailPlaceholder
|
||||
import org.meshtastic.core.ui.component.ScrollToTopEvent
|
||||
import org.meshtastic.core.ui.icon.MeshtasticIcons
|
||||
import org.meshtastic.core.ui.icon.Nodes
|
||||
import org.meshtastic.feature.node.compass.CompassViewModel
|
||||
import org.meshtastic.feature.node.detail.NodeDetailScreen
|
||||
import org.meshtastic.feature.node.detail.NodeDetailViewModel
|
||||
import org.meshtastic.feature.node.list.NodeListScreen
|
||||
import org.meshtastic.feature.node.list.NodeListViewModel
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Composable
|
||||
fun AdaptiveNodeListScreen(
|
||||
backStack: NavBackStack<NavKey>,
|
||||
scrollToTopEvents: Flow<ScrollToTopEvent>,
|
||||
initialNodeId: Int? = null,
|
||||
onNavigate: (Route) -> Unit = {},
|
||||
onNavigateToMessages: (String) -> Unit = {},
|
||||
onHandleDeepLink: (org.meshtastic.core.common.util.MeshtasticUri, onInvalid: () -> Unit) -> Unit = { _, _ -> },
|
||||
) {
|
||||
val nodeListViewModel: NodeListViewModel = koinViewModel()
|
||||
val navigator = rememberListDetailPaneScaffoldNavigator<Int>()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val onBackToGraph: () -> Unit = {
|
||||
val currentKey = backStack.lastOrNull()
|
||||
val isNodesRoute = currentKey is NodesRoutes.Nodes || currentKey is NodesRoutes.NodesGraph
|
||||
val previousKey = if (backStack.size > 1) backStack[backStack.size - 2] else null
|
||||
val isFromDifferentGraph =
|
||||
previousKey != null && previousKey !is NodesRoutes.NodesGraph && previousKey !is NodesRoutes.Nodes
|
||||
|
||||
if (isFromDifferentGraph && !isNodesRoute) {
|
||||
// Navigate back via NavController to return to the previous screen
|
||||
backStack.removeLastOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
AdaptiveListDetailScaffold(
|
||||
navigator = navigator,
|
||||
NodeListScreen(
|
||||
viewModel = nodeListViewModel,
|
||||
navigateToNodeDetails = { nodeId -> backStack.add(NodesRoutes.NodeDetail(nodeId)) },
|
||||
onNavigateToChannels = { backStack.add(ChannelsRoutes.ChannelsGraph) },
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
onBackToGraph = onBackToGraph,
|
||||
onTabPressedEvent = { it is ScrollToTopEvent.NodesTabPressed },
|
||||
initialKey = initialNodeId,
|
||||
listPane = { isActive, activeNodeId ->
|
||||
NodeListScreen(
|
||||
viewModel = nodeListViewModel,
|
||||
navigateToNodeDetails = { nodeId ->
|
||||
scope.launch { navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, nodeId) }
|
||||
},
|
||||
onNavigateToChannels = { backStack.add(ChannelsRoutes.ChannelsGraph) },
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
activeNodeId = activeNodeId,
|
||||
onHandleDeepLink = onHandleDeepLink,
|
||||
)
|
||||
},
|
||||
detailPane = { contentKey, handleBack ->
|
||||
val nodeDetailViewModel: NodeDetailViewModel = koinViewModel()
|
||||
val compassViewModel: CompassViewModel = koinViewModel()
|
||||
NodeDetailScreen(
|
||||
nodeId = contentKey,
|
||||
viewModel = nodeDetailViewModel,
|
||||
compassViewModel = compassViewModel,
|
||||
navigateToMessages = onNavigateToMessages,
|
||||
onNavigate = onNavigate,
|
||||
onNavigateUp = handleBack,
|
||||
)
|
||||
},
|
||||
emptyDetailPane = {
|
||||
EmptyDetailPlaceholder(icon = MeshtasticIcons.Nodes, title = stringResource(Res.string.nodes))
|
||||
},
|
||||
activeNodeId = null,
|
||||
onHandleDeepLink = onHandleDeepLink,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import androidx.compose.material.icons.rounded.People
|
|||
import androidx.compose.material.icons.rounded.PermScanWifi
|
||||
import androidx.compose.material.icons.rounded.Power
|
||||
import androidx.compose.material.icons.rounded.Router
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
import androidx.compose.material3.adaptive.navigation3.ListDetailSceneStrategy
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.navigation3.runtime.EntryProviderScope
|
||||
|
|
@ -51,6 +53,9 @@ import org.meshtastic.core.resources.power
|
|||
import org.meshtastic.core.resources.signal
|
||||
import org.meshtastic.core.resources.traceroute
|
||||
import org.meshtastic.core.ui.component.ScrollToTopEvent
|
||||
import org.meshtastic.feature.node.compass.CompassViewModel
|
||||
import org.meshtastic.feature.node.detail.NodeDetailScreen
|
||||
import org.meshtastic.feature.node.detail.NodeDetailViewModel
|
||||
import org.meshtastic.feature.node.metrics.DeviceMetricsScreen
|
||||
import org.meshtastic.feature.node.metrics.EnvironmentMetricsScreen
|
||||
import org.meshtastic.feature.node.metrics.HostMetricsLogScreen
|
||||
|
|
@ -63,28 +68,25 @@ import org.meshtastic.feature.node.metrics.SignalMetricsScreen
|
|||
import org.meshtastic.feature.node.metrics.TracerouteLogScreen
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Suppress("LongMethod")
|
||||
fun EntryProviderScope<NavKey>.nodesGraph(
|
||||
backStack: NavBackStack<NavKey>,
|
||||
scrollToTopEvents: Flow<ScrollToTopEvent> = MutableSharedFlow(),
|
||||
onHandleDeepLink: (org.meshtastic.core.common.util.MeshtasticUri, onInvalid: () -> Unit) -> Unit = { _, _ -> },
|
||||
) {
|
||||
entry<NodesRoutes.NodesGraph> {
|
||||
entry<NodesRoutes.NodesGraph>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
AdaptiveNodeListScreen(
|
||||
backStack = backStack,
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
onNavigate = { backStack.add(it) },
|
||||
onNavigateToMessages = { backStack.add(ContactsRoutes.Messages(it)) },
|
||||
onHandleDeepLink = onHandleDeepLink,
|
||||
)
|
||||
}
|
||||
|
||||
entry<NodesRoutes.Nodes> {
|
||||
entry<NodesRoutes.Nodes>(metadata = { ListDetailSceneStrategy.listPane() }) {
|
||||
AdaptiveNodeListScreen(
|
||||
backStack = backStack,
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
onNavigate = { backStack.add(it) },
|
||||
onNavigateToMessages = { backStack.add(ContactsRoutes.Messages(it)) },
|
||||
onHandleDeepLink = onHandleDeepLink,
|
||||
)
|
||||
}
|
||||
|
|
@ -92,42 +94,42 @@ fun EntryProviderScope<NavKey>.nodesGraph(
|
|||
nodeDetailGraph(backStack, scrollToTopEvents, onHandleDeepLink)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Suppress("LongMethod")
|
||||
fun EntryProviderScope<NavKey>.nodeDetailGraph(
|
||||
backStack: NavBackStack<NavKey>,
|
||||
scrollToTopEvents: Flow<ScrollToTopEvent>,
|
||||
onHandleDeepLink: (org.meshtastic.core.common.util.MeshtasticUri, onInvalid: () -> Unit) -> Unit = { _, _ -> },
|
||||
) {
|
||||
entry<NodesRoutes.NodeDetailGraph> { args ->
|
||||
entry<NodesRoutes.NodeDetailGraph>(metadata = { ListDetailSceneStrategy.listPane() }) { args ->
|
||||
AdaptiveNodeListScreen(
|
||||
backStack = backStack,
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
initialNodeId = args.destNum,
|
||||
onNavigate = { backStack.add(it) },
|
||||
onNavigateToMessages = { backStack.add(ContactsRoutes.Messages(it)) },
|
||||
onHandleDeepLink = onHandleDeepLink,
|
||||
)
|
||||
}
|
||||
|
||||
entry<NodesRoutes.NodeDetail> { args ->
|
||||
AdaptiveNodeListScreen(
|
||||
backStack = backStack,
|
||||
scrollToTopEvents = scrollToTopEvents,
|
||||
initialNodeId = args.destNum,
|
||||
entry<NodesRoutes.NodeDetail>(metadata = { ListDetailSceneStrategy.detailPane() }) { args ->
|
||||
val nodeDetailViewModel: NodeDetailViewModel = koinViewModel()
|
||||
val compassViewModel: CompassViewModel = koinViewModel()
|
||||
val destNum = args.destNum ?: 0 // Handle nullable destNum if needed
|
||||
NodeDetailScreen(
|
||||
nodeId = destNum,
|
||||
viewModel = nodeDetailViewModel,
|
||||
compassViewModel = compassViewModel,
|
||||
navigateToMessages = { backStack.add(ContactsRoutes.Messages(it)) },
|
||||
onNavigate = { backStack.add(it) },
|
||||
onNavigateToMessages = { backStack.add(ContactsRoutes.Messages(it)) },
|
||||
onHandleDeepLink = onHandleDeepLink,
|
||||
onNavigateUp = { backStack.removeLastOrNull() },
|
||||
)
|
||||
}
|
||||
|
||||
entry<NodeDetailRoutes.NodeMap> { args ->
|
||||
entry<NodeDetailRoutes.NodeMap>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
val mapScreen = org.meshtastic.core.ui.util.LocalNodeMapScreenProvider.current
|
||||
mapScreen(args.destNum) { backStack.removeLastOrNull() }
|
||||
}
|
||||
|
||||
entry<NodeDetailRoutes.TracerouteLog> { args ->
|
||||
val metricsViewModel =
|
||||
koinViewModel<MetricsViewModel>(key = "metrics-${args.destNum}") { parametersOf(args.destNum) }
|
||||
entry<NodeDetailRoutes.TracerouteLog>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
val metricsViewModel = koinViewModel<MetricsViewModel> { parametersOf(args.destNum) }
|
||||
metricsViewModel.setNodeId(args.destNum)
|
||||
|
||||
TracerouteLogScreen(
|
||||
|
|
@ -145,7 +147,7 @@ fun EntryProviderScope<NavKey>.nodeDetailGraph(
|
|||
)
|
||||
}
|
||||
|
||||
entry<NodeDetailRoutes.TracerouteMap> { args ->
|
||||
entry<NodeDetailRoutes.TracerouteMap>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
val tracerouteMapScreen = org.meshtastic.core.ui.util.LocalTracerouteMapScreenProvider.current
|
||||
tracerouteMapScreen(args.destNum, args.requestId, args.logUuid) { backStack.removeLastOrNull() }
|
||||
}
|
||||
|
|
@ -175,14 +177,15 @@ fun EntryProviderScope<NavKey>.nodeDetailGraph(
|
|||
|
||||
fun NavKey.isNodeDetailRoute(): Boolean = NodeDetailRoute.entries.any { this::class == it.routeClass }
|
||||
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
private inline fun <reified R : Route> EntryProviderScope<NavKey>.addNodeDetailScreenComposable(
|
||||
backStack: NavBackStack<NavKey>,
|
||||
routeInfo: NodeDetailRoute,
|
||||
crossinline getDestNum: (R) -> Int,
|
||||
) {
|
||||
entry<R> { args ->
|
||||
entry<R>(metadata = { ListDetailSceneStrategy.extraPane() }) { args ->
|
||||
val destNum = getDestNum(args)
|
||||
val metricsViewModel = koinViewModel<MetricsViewModel>(key = "metrics-$destNum") { parametersOf(destNum) }
|
||||
val metricsViewModel = koinViewModel<MetricsViewModel> { parametersOf(destNum) }
|
||||
metricsViewModel.setNodeId(destNum)
|
||||
|
||||
routeInfo.screenComposable(metricsViewModel) { backStack.removeLastOrNull() }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue