Skip to content

Commit dc352f4

Browse files
committed
fix: Prevent multiple navigation when button is clicked in fast succession
1 parent 022f462 commit dc352f4

File tree

3 files changed

+43
-7
lines changed

3 files changed

+43
-7
lines changed

app/src/main/java/com/flamyoad/filemanager/ui/FileManagerNavHost.kt

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
package com.flamyoad.filemanager.ui
22

3+
import androidx.compose.animation.AnimatedContentTransitionScope
34
import androidx.compose.animation.EnterTransition
45
import androidx.compose.animation.ExitTransition
6+
import androidx.compose.animation.core.tween
57
import androidx.compose.runtime.Composable
68
import androidx.compose.ui.Modifier
9+
import androidx.lifecycle.Lifecycle
10+
import androidx.lifecycle.compose.dropUnlessResumed
11+
import androidx.navigation.NavController
712
import androidx.navigation.NavHostController
813
import androidx.navigation.compose.NavHost
14+
import com.flamyoad.common.Route
915
import com.flamyoad.explorer_impl.FileListRoute
1016
import com.flamyoad.explorer_impl.HomePageRoute
1117
import com.flamyoad.explorer_impl.fileListRoute
@@ -20,20 +26,49 @@ fun FileManagerNavHost(
2026
NavHost(
2127
navController = navController,
2228
startDestination = HomePageRoute,
23-
enterTransition = { EnterTransition.None },
24-
exitTransition = { ExitTransition.None },
29+
enterTransition = {
30+
slideIntoContainer(
31+
AnimatedContentTransitionScope.SlideDirection.Start,
32+
tween(500)
33+
)
34+
},
35+
exitTransition = {
36+
slideOutOfContainer(
37+
AnimatedContentTransitionScope.SlideDirection.Start,
38+
tween(500)
39+
)
40+
},
2541
modifier = modifier
2642
) {
2743
homePageRoute(
2844
onNavigateToFileList = { directory ->
29-
navController.navigate(FileListRoute(directory))
45+
navController.navigateSafely(FileListRoute(directory))
3046
}
3147
)
3248
fileListRoute(
3349
onNavigateToFileList = { directory ->
34-
navController.navigate(FileListRoute(directory))
50+
navController.navigateSafely(FileListRoute(directory))
3551
}
3652
)
3753
imageViewerRoute()
3854
}
55+
}
56+
57+
// dropUnlessResumed is used to avoid navigating multiple times to the same destination or
58+
// popping the backstack when the destination is already on top.
59+
// https://github.com/seve-andre/jetpack-compose-template/pull/37
60+
//@Composable
61+
//fun NavController.navigateTo(
62+
// route: Route,
63+
//): () -> Unit = dropUnlessResumed { this.navigate(route) }
64+
65+
66+
// https://slack-chats.kotlinlang.org/t/18829110/hey-guys-i-m-trying-to-wrap-my-head-around-the-new-dropunles
67+
// https://github.com/HedvigInsurance/android/blob/develop/app%2Fapp%2Fsrc%2Fmain%2Fkotlin%2Fcom%2Fhedvig%2Fandroid%2Fapp%2Fnavigation%2FRememberNavigator.kt#L20-L28
68+
// Pros: Doesn't require @Composable context
69+
fun NavController.navigateSafely(route: Any) {
70+
val currentBackStackEntry = this.currentBackStackEntry ?: return
71+
if (currentBackStackEntry.lifecycle.currentState == Lifecycle.State.RESUMED) {
72+
this.navigate(route)
73+
}
3974
}

common-ui/src/main/java/com/flamyoad/common_ui/BaseViewModel.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ abstract class BaseViewModel: ViewModel() {
1111

1212
fun <T> Flow<T>.toStateFlow(
1313
initialState: T,
14-
stopTimeoutMillis: Long = 500L
14+
started: SharingStarted = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000L),
1515
): StateFlow<T> {
1616
return stateIn(
1717
scope = this@BaseViewModel.viewModelScope,
18-
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = stopTimeoutMillis),
18+
started = started,
1919
initialValue = initialState,
2020
)
2121
}

explorer-impl/src/main/java/com/flamyoad/explorer_impl/ui/filelist/FileListViewModel.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.flamyoad.file_scanner.DirectoryProvider
88
import com.flamyoad.file_scanner.FileHandle
99
import dagger.hilt.android.lifecycle.HiltViewModel
1010
import kotlinx.coroutines.flow.MutableStateFlow
11+
import kotlinx.coroutines.flow.SharingStarted
1112
import kotlinx.coroutines.flow.StateFlow
1213
import kotlinx.coroutines.flow.catch
1314
import kotlinx.coroutines.flow.filterNotNull
@@ -33,7 +34,7 @@ internal class FileListViewModel @Inject constructor(
3334
.flatMapLatest { directoryProvider.observeDir(it) }
3435
.map<List<File>, UiState<List<File>>> { UiState.Success(it) } // oh no...we shoul;dnt have to
3536
.catch { emit(UiState.Error(it)) }
36-
.toStateFlow(initialState = UiState.Loading)
37+
.toStateFlow(initialState = UiState.Loading, started = SharingStarted.Lazily)
3738

3839
fun initPath(path: File) {
3940
currentPath.update { path }

0 commit comments

Comments
 (0)