Fix performance issue by caching typing effect settings in ChatUiState (#329)
* Fix performance issue by caching typing effect settings in ChatUiState - Moved `AuthManager.isTypingEffectEnabled()` and `AuthManager.getTypingEffectDelayMs()` out of `ChatScreen.kt` recomposition loops. - Stored `typingEffectEnabled` and `typingEffectDelayMs` in `ChatUiState`. - Added `refreshSettings()` to `ChatViewModel` to fetch settings upon initialization and on returning to the chat screen (ON_START). - Prevents expensive EncryptedSharedPreferences XML decryption occurring on every message per token stream update. Fixes #269 * Fix performance issue by caching typing effect settings in ChatUiState - Moved `AuthManager.isTypingEffectEnabled()` and `AuthManager.getTypingEffectDelayMs()` out of `ChatScreen.kt` recomposition loops. - Stored `typingEffectEnabled` and `typingEffectDelayMs` in `ChatUiState`. - Added `refreshSettings()` to `ChatViewModel` to fetch settings upon initialization and on returning to the chat screen (ON_START). - Prevents expensive EncryptedSharedPreferences XML decryption occurring on every message per token stream update. - Fixed `ChatViewModelTest` missing mocks for `AuthManager.isTypingEffectEnabled()` and `AuthManager.getTypingEffectDelayMs()`. - Fixed ktlint unused import and max line length violations. Fixes #269
This commit is contained in:
3 files changed
+24
-10
No files matched your search
@@ -104,7 +104,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.m57.hermescontrol.NavigationController
|
||||
import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.data.local.AuthManager
|
||||
import com.m57.hermescontrol.data.ws.ConnectionStatus
|
||||
import com.m57.hermescontrol.notification.NotificationHelper
|
||||
import com.m57.hermescontrol.theme.StatusRed
|
||||
@@ -174,6 +173,7 @@ fun ChatScreen(
|
||||
androidx.lifecycle.Lifecycle.Event.ON_START -> {
|
||||
NotificationHelper.setAppForeground(context, true)
|
||||
NotificationHelper.stop(context)
|
||||
viewModel.refreshSettings()
|
||||
viewModel.refreshCurrentSession()
|
||||
}
|
||||
|
||||
@@ -441,13 +441,15 @@ fun ChatScreen(
|
||||
|
||||
val isLastMessage = index == state.messages.lastIndex
|
||||
val isAssistant = message.role == MessageRole.ASSISTANT
|
||||
val typingEnabled = AuthManager.isTypingEffectEnabled()
|
||||
val typingDelayMs = AuthManager.getTypingEffectDelayMs()
|
||||
|
||||
if (typingEnabled && isLastMessage && isAssistant && lastAnimatedMessageId != message.id) {
|
||||
if (state.typingEffectEnabled &&
|
||||
isLastMessage &&
|
||||
isAssistant &&
|
||||
lastAnimatedMessageId != message.id
|
||||
) {
|
||||
StreamingBubbleWithTypingEffect(
|
||||
streaming = message,
|
||||
typingDelayMs = typingDelayMs,
|
||||
typingDelayMs = state.typingEffectDelayMs,
|
||||
isDark = isDark,
|
||||
onAnimationComplete = {
|
||||
lastAnimatedMessageId = message.id
|
||||
@@ -466,13 +468,10 @@ fun ChatScreen(
|
||||
// Streaming message — rendered separately for O(1) updates
|
||||
state.streamingMessage?.let { streaming ->
|
||||
item(key = "streaming-${streaming.id}") {
|
||||
val typingEnabled = AuthManager.isTypingEffectEnabled()
|
||||
val typingDelayMs = AuthManager.getTypingEffectDelayMs()
|
||||
|
||||
if (typingEnabled && streaming.isStreaming) {
|
||||
if (state.typingEffectEnabled && streaming.isStreaming) {
|
||||
StreamingBubbleWithTypingEffect(
|
||||
streaming = streaming,
|
||||
typingDelayMs = typingDelayMs,
|
||||
typingDelayMs = state.typingEffectDelayMs,
|
||||
isDark = isDark,
|
||||
)
|
||||
} else {
|
||||
|
||||
@@ -57,6 +57,9 @@ data class ChatUiState(
|
||||
val searchQuery: String = "",
|
||||
val searchMatchIndices: List<Int> = emptyList(),
|
||||
val currentSearchMatchIndex: Int = -1,
|
||||
// Cached settings
|
||||
val typingEffectEnabled: Boolean = true,
|
||||
val typingEffectDelayMs: Int = 30,
|
||||
) {
|
||||
/** Convenience — derived from [connectionStatus]. */
|
||||
val isConnected: Boolean get() = connectionStatus == ConnectionStatus.CONNECTED
|
||||
@@ -118,6 +121,7 @@ class ChatViewModel(
|
||||
)
|
||||
|
||||
init {
|
||||
refreshSettings()
|
||||
connectWebSocket()
|
||||
viewModelScope.launch {
|
||||
wsClient.events.collect { event ->
|
||||
@@ -860,6 +864,15 @@ class ChatViewModel(
|
||||
loadSessionMessages(sessionId)
|
||||
}
|
||||
|
||||
fun refreshSettings() {
|
||||
_uiState.update { state ->
|
||||
state.copy(
|
||||
typingEffectEnabled = AuthManager.isTypingEffectEnabled(),
|
||||
typingEffectDelayMs = AuthManager.getTypingEffectDelayMs(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun switchSession(sessionId: String) {
|
||||
if (sessionId == _uiState.value.currentSessionId) return
|
||||
|
||||
|
||||
Reference in new issue
Block a user