perf(ui): suspend polling outside visible screens (#806)
This commit is contained in:
@@ -66,7 +66,10 @@ import androidx.compose.ui.text.input.TextFieldValue
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.m57.hermescontrol.NavigationController
|
import com.m57.hermescontrol.NavigationController
|
||||||
import com.m57.hermescontrol.R
|
import com.m57.hermescontrol.R
|
||||||
@@ -102,7 +105,7 @@ import java.text.SimpleDateFormat
|
|||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
private const val SESSION_SYNC_INTERVAL_MS = 5_000L
|
private const val SESSION_SYNC_INTERVAL_MS = 30_000L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chat screen — the primary conversation surface of Hermes Control.
|
* Chat screen — the primary conversation surface of Hermes Control.
|
||||||
@@ -126,6 +129,7 @@ fun ChatScreen(
|
|||||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
val streamingState by viewModel.streamingState.collectAsStateWithLifecycle()
|
val streamingState by viewModel.streamingState.collectAsStateWithLifecycle()
|
||||||
val credentialWarning by HermesWsClient.credentialWarning.collectAsStateWithLifecycle()
|
val credentialWarning by HermesWsClient.credentialWarning.collectAsStateWithLifecycle()
|
||||||
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val scrollScope = rememberCoroutineScope()
|
val scrollScope = rememberCoroutineScope()
|
||||||
val scrollController = rememberChatScrollController(listState, scrollScope)
|
val scrollController = rememberChatScrollController(listState, scrollScope)
|
||||||
@@ -133,15 +137,17 @@ fun ChatScreen(
|
|||||||
var showContextSheet by remember { mutableStateOf(false) }
|
var showContextSheet by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
// Periodic session sync while connected.
|
// Periodic session sync while connected.
|
||||||
LaunchedEffect(state.currentSessionId, state.connectionStatus) {
|
LaunchedEffect(lifecycleOwner, state.currentSessionId, state.connectionStatus) {
|
||||||
if (state.currentSessionId != null && state.connectionStatus == ConnectionStatus.CONNECTED) {
|
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
viewModel.syncCurrentSession()
|
if (state.currentSessionId != null && state.connectionStatus == ConnectionStatus.CONNECTED) {
|
||||||
viewModel.fetchContextUsage()
|
viewModel.syncCurrentSession()
|
||||||
}
|
viewModel.fetchContextUsage()
|
||||||
while (state.currentSessionId != null && state.connectionStatus == ConnectionStatus.CONNECTED) {
|
}
|
||||||
delay(SESSION_SYNC_INTERVAL_MS)
|
while (state.currentSessionId != null && state.connectionStatus == ConnectionStatus.CONNECTED) {
|
||||||
viewModel.syncCurrentSession()
|
delay(SESSION_SYNC_INTERVAL_MS)
|
||||||
viewModel.fetchContextUsage()
|
viewModel.syncCurrentSession()
|
||||||
|
viewModel.fetchContextUsage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2238,7 +2238,7 @@ class ChatViewModel(
|
|||||||
* Both calls are independent and best-effort: a failure on one must not
|
* Both calls are independent and best-effort: a failure on one must not
|
||||||
* wipe the other's already-shown value, and neither blocks the chat. The
|
* wipe the other's already-shown value, and neither blocks the chat. The
|
||||||
* two fetches are launched separately so a slow/erroring one can't starve
|
* two fetches are launched separately so a slow/erroring one can't starve
|
||||||
* the other. Polled from [syncCurrentSession] via the 5s loop and re-fired
|
* the other. Polled from [syncCurrentSession] via the 30s loop and re-fired
|
||||||
* on model switch (the denominator changes).
|
* on model switch (the denominator changes).
|
||||||
*/
|
*/
|
||||||
fun fetchContextUsage() {
|
fun fetchContextUsage() {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import androidx.compose.material3.IconButton
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -45,6 +44,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.compose.LifecycleStartEffect
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.m57.hermescontrol.R
|
import com.m57.hermescontrol.R
|
||||||
@@ -120,9 +120,9 @@ fun LogsScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Auto-refresh logs via polling while the screen is visible
|
// Auto-refresh logs via polling while the screen is visible
|
||||||
DisposableEffect(Unit) {
|
LifecycleStartEffect(viewModel) {
|
||||||
viewModel.startAutoRefresh()
|
viewModel.startAutoRefresh()
|
||||||
onDispose { viewModel.stopAutoRefresh() }
|
onStopOrDispose { viewModel.stopAutoRefresh() }
|
||||||
}
|
}
|
||||||
|
|
||||||
ToastEffect(toastMessage = state.toastMessage, onClearToast = viewModel::clearToast)
|
ToastEffect(toastMessage = state.toastMessage, onClearToast = viewModel::clearToast)
|
||||||
|
|||||||
Reference in New Issue
Block a user