diff --git a/app/androidApp/src/main/kotlin/com/example/aiapp/MainScreen.kt b/app/androidApp/src/main/kotlin/com/example/aiapp/MainScreen.kt index 76e0954..4c65b18 100644 --- a/app/androidApp/src/main/kotlin/com/example/aiapp/MainScreen.kt +++ b/app/androidApp/src/main/kotlin/com/example/aiapp/MainScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.material3.PrimaryTabRow import androidx.compose.material3.Tab import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf @@ -20,6 +21,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.repeatOnLifecycle /** * The app's root: one title, and four views of the backend behind it. @@ -51,6 +55,30 @@ fun MainScreen( var tab by remember { mutableStateOf(MainTab.Sessions) } var refreshToken by remember { mutableIntStateOf(0) } + // Coming back to the app asks again, on whichever tab is showing. + // + // What these four draw is a snapshot of a backend they are not connected to, so it is only as + // fresh as the last answer -- and a *failed* answer is the one that outstays its welcome. A + // phone that was away while the tunnel was down, or that fetched before the network came up, + // came back to "Couldn't reach the server" sitting at the top of a list the server would now + // answer for perfectly well, and nothing took it off until somebody pressed Refresh. A stale + // failure is worse than a stale list: it is a claim about right now. + // + // Through the same token the Refresh button uses, so this is one instruction the tabs already + // understand rather than a second path into each of them -- which is also what makes it cover + // all four rather than the one the report came from. + // + // Not on the first entry: the tab composing already asks, and bumping here would make every + // cold start fetch twice. + val lifecycleOwner = LocalLifecycleOwner.current + LaunchedEffect(lifecycleOwner) { + var opening = true + lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + if (!opening) refreshToken++ + opening = false + } + } + // A tab the app put over the list has to step back to it rather than fall through to the // system default, which closes the app -- that reads as a crash to somebody who only meant to // get back to their sessions. Nested inside AppRoot's handler, so it wins while it is enabled.