diff --git a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt index c0a9a245..7c76affb 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatScreen.kt @@ -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 { diff --git a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt index fedb1bcd..ecd49836 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt @@ -57,6 +57,9 @@ data class ChatUiState( val searchQuery: String = "", val searchMatchIndices: List = 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 diff --git a/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt b/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt index fc311cf8..3c070465 100644 --- a/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt +++ b/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt @@ -64,6 +64,8 @@ class ChatViewModelTest { mockConnectionStatus.value = ConnectionStatus.DISCONNECTED every { AuthManager.getToken() } returns "test-token" + every { AuthManager.isTypingEffectEnabled() } returns true + every { AuthManager.getTypingEffectDelayMs() } returns 30 every { HermesWsClient.events } returns mockEventsFlow every { HermesWsClient.connectionStatus } returns mockConnectionStatus every { HermesWsClient.connect() } returns Unit