From 03ed85decd2eacc908a0daeb3103f5c2b9be2085 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Wed, 10 Dec 2025 09:09:47 -0600 Subject: [PATCH] feat(navigation): Navigate back to list view on tab reselection (#3948) Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> --- .../java/com/geeksville/mesh/model/UIState.kt | 12 +++++++----- .../mesh/ui/contact/AdaptiveContactsScreen.kt | 16 ++++++++++++++++ .../mesh/ui/node/AdaptiveNodeListScreen.kt | 15 +++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index da04eecd4..bdf9be6f5 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -28,19 +28,20 @@ import androidx.navigation.NavHostController import com.geeksville.mesh.repository.radio.MeshActivity import com.geeksville.mesh.repository.radio.RadioInterfaceService import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.shareIn import org.jetbrains.compose.resources.getString import org.meshtastic.core.analytics.platform.PlatformAnalytics @@ -131,11 +132,12 @@ constructor( val meshActivity: SharedFlow = radioInterfaceService.meshActivity.shareIn(viewModelScope, SharingStarted.Eagerly, 0) - private val scrollToTopEventChannel = Channel(capacity = Channel.CONFLATED) - val scrollToTopEventFlow: Flow = scrollToTopEventChannel.receiveAsFlow() + private val _scrollToTopEventFlow = + MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + val scrollToTopEventFlow: Flow = _scrollToTopEventFlow.asSharedFlow() fun emitScrollToTopEvent(event: ScrollToTopEvent) { - scrollToTopEventChannel.trySend(event) + _scrollToTopEventFlow.tryEmit(event) } data class AlertData( diff --git a/app/src/main/java/com/geeksville/mesh/ui/contact/AdaptiveContactsScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/contact/AdaptiveContactsScreen.kt index 03e984917..9996ec1e1 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/contact/AdaptiveContactsScreen.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/contact/AdaptiveContactsScreen.kt @@ -77,6 +77,22 @@ fun AdaptiveContactsScreen( navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, initialContactKey) } } + + LaunchedEffect(scrollToTopEvents) { + scrollToTopEvents.collect { event -> + if ( + event is ScrollToTopEvent.ConversationsTabPressed && + navigator.currentDestination?.pane == ListDetailPaneScaffoldRole.Detail + ) { + if (navigator.canNavigateBack(backNavigationBehavior)) { + navigator.navigateBack(backNavigationBehavior) + } else { + navigator.navigateTo(ListDetailPaneScaffoldRole.List) + } + } + } + } + ListDetailPaneScaffold( directive = navigator.scaffoldDirective, value = navigator.scaffoldValue, diff --git a/app/src/main/java/com/geeksville/mesh/ui/node/AdaptiveNodeListScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/node/AdaptiveNodeListScreen.kt index f4db4c311..5b76a9425 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/node/AdaptiveNodeListScreen.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/node/AdaptiveNodeListScreen.kt @@ -77,6 +77,21 @@ fun AdaptiveNodeListScreen( } } + LaunchedEffect(scrollToTopEvents) { + scrollToTopEvents.collect { event -> + if ( + event is ScrollToTopEvent.NodesTabPressed && + navigator.currentDestination?.pane == ListDetailPaneScaffoldRole.Detail + ) { + if (navigator.canNavigateBack(backNavigationBehavior)) { + navigator.navigateBack(backNavigationBehavior) + } else { + navigator.navigateTo(ListDetailPaneScaffoldRole.List) + } + } + } + } + ListDetailPaneScaffold( directive = navigator.scaffoldDirective, value = navigator.scaffoldValue,