diff --git a/app/src/androidTest/java/com/m57/hermescontrol/ui/chat/ChatScreenTest.kt b/app/src/androidTest/java/com/m57/hermescontrol/ui/chat/ChatScreenTest.kt index c0b7731b..505fd402 100644 --- a/app/src/androidTest/java/com/m57/hermescontrol/ui/chat/ChatScreenTest.kt +++ b/app/src/androidTest/java/com/m57/hermescontrol/ui/chat/ChatScreenTest.kt @@ -8,6 +8,7 @@ import androidx.compose.ui.test.performTextInput import androidx.core.content.ContextCompat import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest +import com.m57.hermescontrol.data.model.Attachment import com.m57.hermescontrol.data.ws.ConnectionStatus import com.m57.hermescontrol.ui.common.ActionProgressController import io.mockk.every @@ -82,4 +83,40 @@ class ChatScreenTest { composeTestRule.onNodeWithTag("send_button").assertIsDisplayed() composeTestRule.onNodeWithTag("mic_button").assertIsDisplayed() } + + @Test + fun chatScreen_sendButtonShownForAttachmentWithEmptyInput() { + // Issue #956: an attachment queued with an EMPTY input must still show + // the send button — the old AnimatedContent gate required non-blank + // text even though ChatInputPolicy.canSend already allowed + // attachment-only sends. + val uiState = + ChatUiState( + connectionStatus = ConnectionStatus.CONNECTED, + pendingAttachments = + listOf( + Attachment( + uri = "content://test/photo.jpg", + name = "photo.jpg", + mimeType = "image/jpeg", + ), + ), + ) + val mockViewModel = mockk(relaxed = true) + every { mockViewModel.uiState } returns MutableStateFlow(uiState).asStateFlow() + every { mockViewModel.streamingState } returns MutableStateFlow(StreamingState()).asStateFlow() + every { mockViewModel.actionProgress } returns + ActionProgressController(scope = CoroutineScope(Dispatchers.Main)) + + composeTestRule.setContent { + ChatScreen( + onOpenDrawer = {}, + sessionId = null, + viewModel = mockViewModel, + ) + } + + composeTestRule.onNodeWithTag("chat_input").assertIsDisplayed() + composeTestRule.onNodeWithTag("send_button").assertIsDisplayed() + } } diff --git a/app/src/main/java/com/m57/hermescontrol/ui/chat/components/ChatComposer.kt b/app/src/main/java/com/m57/hermescontrol/ui/chat/components/ChatComposer.kt index 6dcaf178..3e1fa8df 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/chat/components/ChatComposer.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/chat/components/ChatComposer.kt @@ -278,9 +278,12 @@ fun ChatInputBar( innerTextField() } - // Send button INSIDE the field + // Send button INSIDE the field. Shown whenever a send is + // possible — text typed OR an attachment pending (issue + // #956): the old text-only gate hid the button entirely + // for attachment-only sends. AnimatedContent( - targetState = canSend && inputFieldValue.text.isNotBlank(), + targetState = canSend, transitionSpec = { (scaleIn(initialScale = 0.8f) + fadeIn()) .togetherWith(scaleOut(targetScale = 0.8f) + fadeOut())