ubuntudroid
11/01/2023, 3:16 PMubuntudroid
11/01/2023, 3:30 PMCasey Brooks
11/01/2023, 3:34 PMBallastDebuggerClientConnection
instance for both VMs? If not, youāll probably end up with 2 connections instead of 2 VMs in the same connectionCasey Brooks
11/01/2023, 3:37 PMubuntudroid
11/01/2023, 3:42 PMubuntudroid
11/01/2023, 6:43 PMubuntudroid
11/01/2023, 6:52 PMCasey Brooks
11/01/2023, 7:00 PMubuntudroid
11/01/2023, 7:02 PMubuntudroid
11/01/2023, 7:04 PMubuntudroid
11/01/2023, 7:06 PMCasey Brooks
11/01/2023, 7:06 PMCasey Brooks
11/01/2023, 7:07 PMubuntudroid
11/01/2023, 7:07 PMubuntudroid
11/01/2023, 7:07 PMCasey Brooks
11/01/2023, 7:11 PMubuntudroid
11/01/2023, 7:12 PMubuntudroid
11/01/2023, 11:39 PMubuntudroid
11/02/2023, 12:16 AMCasey Brooks
11/02/2023, 5:40 PMBackstackChanged
event does get updated with every change to the Router state, but it is not itself intended to be considered State because itās not actually fully in sync with the state. Only the VMs StateFlow can accurately represent the state.
The order of events using this redirect is causing the issue. Hereās the general sequence:
1. The app loads, and an Input for the initial route is sent
2. The Input gets processed and a new State is generated, which includes that route. Following the state change, a BackstackChanged
event is also sent to the EventHandler.
3. The Compose UI immediately updates, and it loads the screen corresponding to the route.
4. In parallel to the UI, the EventHandler eventually receives the BackstackChanged
event and sends an Input to redirect to the login screen. This redirect is working fine. But notably, this event is handled at some point after the UI state has already changed. As a result, there was a brief time when the router state shows the initial route as the current route, and during that time the screen gets briefly displayed and its VM starts running.
5. That VM sends an Input to initialize itself, which fetches data from a Repository in a sideJob. But since the user is not authenticated, an error is thrown by the repository, because it doesnāt have any user credentials.
6. Shortly after this, the redirect is processed and the user is sent back to the Login screen. This cancels the coroutineScope that the screenās VM was running in, and I believe itās getting cancelled before the VM can notify the debugger that the sideJob failed. Thus, the debugger appears to hang, because it never received a notification that the sideJob completed with an error.
The problem is that the UI is still running and displaying UI without caring whether the user is authenticated or not. What should be done instead is include the authentication/redirection logic directly in the Compose UI, not an EventHandler. This will allow you to actually hide UI that requires auth (and thus prevent those VMs from running at all), while also handling the redirect.Casey Brooks
11/02/2023, 5:40 PMpublic object AuthRequired : RouteAnnotation
enum class Screen(
routeFormat: String,
override val annotations: Set<RouteAnnotation> = emptySet(),
): Route {
AuthenticatedRoute("/pastes", annotations = setOf(AuthRequired)),
Login("/login"),
;
override val matcher: RouteMatcher = RouteMatcher.create(routeFormat)
}
routerState.renderCurrentDestination(
route = { screen ->
val displayRoute = @Composable {
when(screen) {
Screen.Pastes -> {
PastesScreen.Content(router)
}
Screen.Login -> {
LoginScreen.Content(router)
}
}
}
val screenRequiresAuth = AuthRequired in annotations
if(screenRequiresAuth) {
val isAuthenticated = remember(screen) {
userRepository.isAuthenticated
}
if(isAuthenticated) {
displayRoute()
} else {
LaunchedEffect(screen) {
router.trySend(RouterContract.Inputs.GoToDestination(Screen.Login.directions().build()))
}
}
} else {
displayRoute()
}
},
notFound = {
// TODO add "not found" screen
}
)
Casey Brooks
11/02/2023, 5:41 PMubuntudroid
11/02/2023, 6:03 PMCasey Brooks
11/02/2023, 6:37 PMubuntudroid
11/02/2023, 7:03 PM