fix: preserve bottom-tab state during tab switching

Restore top-level tab destinations instead of recreating them on each tap, and avoid immediately resubscribing inbox streams when users bounce between tabs. This reduces navigation jank and repeated hangs on slower devices.
This commit is contained in:
Barry Deen
2026-05-04 07:44:39 -04:00
parent b028007a7c
commit a0ec123e0c
+20 -5
View File
@@ -1,5 +1,6 @@
package com.wisp.app package com.wisp.app
import android.os.SystemClock
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -26,6 +27,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavType import androidx.navigation.NavType
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
@@ -527,6 +529,16 @@ fun WispNavHost(
} }
} }
var lastInboxRefreshElapsedMs by rememberSaveable { mutableLongStateOf(0L) }
fun refreshInboxSubscriptionsIfStale() {
val now = SystemClock.elapsedRealtime()
// Inbox subscriptions already stay live in the background; avoid re-sending the
// same REQs every time the user bounces between top-level tabs.
if (now - lastInboxRefreshElapsedMs < 30_000L) return
lastInboxRefreshElapsedMs = now
feedViewModel.refreshDmsAndNotifications()
}
// Crash report dialog — check on launch if a crash log exists // Crash report dialog — check on launch if a crash log exists
var showCrashDialog by remember { mutableStateOf(CrashHandler.hasCrashLog(context)) } var showCrashDialog by remember { mutableStateOf(CrashHandler.hasCrashLog(context)) }
if (showCrashDialog) { if (showCrashDialog) {
@@ -607,8 +619,11 @@ fun WispNavHost(
} else { } else {
if (tab == BottomTab.WALLET) walletViewModel.navigateHome() if (tab == BottomTab.WALLET) walletViewModel.navigateHome()
navController.navigate(tab.route) { navController.navigate(tab.route) {
popUpTo(Routes.FEED) { inclusive = false } popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true launchSingleTop = true
restoreState = true
} }
} }
} }
@@ -1271,7 +1286,7 @@ fun WispNavHost(
composable(Routes.DM_LIST) { composable(Routes.DM_LIST) {
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
feedViewModel.refreshDmsAndNotifications() refreshInboxSubscriptionsIfStale()
// Decrypt pending gift wraps when signer is available // Decrypt pending gift wraps when signer is available
activeSigner?.let { dmListViewModel.decryptPending(it) } activeSigner?.let { dmListViewModel.decryptPending(it) }
dmListViewModel.markDmsRead() dmListViewModel.markDmsRead()
@@ -1331,7 +1346,7 @@ fun WispNavHost(
val dmConvoViewModel: DmConversationViewModel = viewModel() val dmConvoViewModel: DmConversationViewModel = viewModel()
val userPubkey = feedViewModel.getUserPubkey() val userPubkey = feedViewModel.getUserPubkey()
LaunchedEffect(pubkey) { LaunchedEffect(pubkey) {
feedViewModel.refreshDmsAndNotifications() refreshInboxSubscriptionsIfStale()
dmConvoViewModel.init( dmConvoViewModel.init(
peerPubkeyHex = pubkey, peerPubkeyHex = pubkey,
dmRepository = feedViewModel.dmRepo, dmRepository = feedViewModel.dmRepo,
@@ -1413,7 +1428,7 @@ fun WispNavHost(
val dmConvoViewModel: DmConversationViewModel = viewModel() val dmConvoViewModel: DmConversationViewModel = viewModel()
val userPubkey = feedViewModel.getUserPubkey() val userPubkey = feedViewModel.getUserPubkey()
LaunchedEffect(convKey) { LaunchedEffect(convKey) {
feedViewModel.refreshDmsAndNotifications() refreshInboxSubscriptionsIfStale()
dmConvoViewModel.init( dmConvoViewModel.init(
peerPubkeyHex = participantList.firstOrNull() ?: "", peerPubkeyHex = participantList.firstOrNull() ?: "",
dmRepository = feedViewModel.dmRepo, dmRepository = feedViewModel.dmRepo,
@@ -3034,7 +3049,7 @@ fun WispNavHost(
onDispose { feedViewModel.notifRepo.isViewing = false } onDispose { feedViewModel.notifRepo.isViewing = false }
} }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
feedViewModel.refreshDmsAndNotifications() refreshInboxSubscriptionsIfStale()
notificationsViewModel.markRead() notificationsViewModel.markRead()
} }