Hide the command and mention popups after a message is sent

When a slash command is sent without tapping the autocomplete list, the
command popup stays on screen with its old suggestions. The send path
clears the input field in code, and the popup is hidden only from the
field's text-change handler, which a code-driven clear does not run, so
the show flag is never turned off. The mention popup has the same cause.

CommandProcessor gains clearSuggestions(), which hides both popups and
empties their lists, the same resets the two select functions already do
inline. ChatScreen's send handler calls it where it already resets the
field.

The private chat sheet called updateMentionSuggestions on every keystroke
while rendering its own popups as hidden, so the only effect was on the
main composer behind it: typing @ in the sheet and then sending or
dismissing left a stale mention popup on the main screen. That call is
removed.

Tests in CommandProcessorTest open each popup and assert clearSuggestions
hides it. The unit tests do not drive Compose, so they cover the function
the send handler calls rather than the handler itself.

Full Android unit suite green; lint and the debug build pass.

Fixes #250.
This commit is contained in:
heyaim 2026-07-31 18:05:23 -05:00
parent cb6d68958d
commit 862fd8449d
5 changed files with 50 additions and 2 deletions

View File

@ -386,6 +386,9 @@ fun ChatScreen(viewModel: ChatViewModel) {
if (accepted) {
messageText = TextFieldValue("")
viewModel.setConversationDraft(selectedPrivatePeer, "")
// Clearing the field in code does not run onMessageTextChange,
// so the popups have to be dismissed here.
viewModel.clearSuggestions()
forceScrollToBottom = !forceScrollToBottom
}
}

View File

@ -1341,7 +1341,11 @@ class ChatViewModel(
fun updateCommandSuggestions(input: String) {
commandProcessor.updateCommandSuggestions(input)
}
fun clearSuggestions() {
commandProcessor.clearSuggestions()
}
fun selectCommandSuggestion(suggestion: CommandSuggestion): String {
return commandProcessor.selectCommandSuggestion(suggestion)
}

View File

@ -465,6 +465,18 @@ class CommandProcessor(
messageManager.addMessage(systemMessage)
}
/**
* Dismiss the command and mention popups. The composer clears its field in code
* after a send, which does not run onValueChange, so the popups need an explicit
* clear.
*/
fun clearSuggestions() {
state.setShowCommandSuggestions(false)
state.setCommandSuggestions(emptyList())
state.setShowMentionSuggestions(false)
state.setMentionSuggestions(emptyList())
}
// MARK: - Command Autocomplete
fun updateCommandSuggestions(input: String) {

View File

@ -1849,7 +1849,9 @@ fun PrivateChatSheet(
onMessageTextChange = { newText ->
messageText = newText
viewModel.setConversationDraft(peerID, newText.text)
viewModel.updateMentionSuggestions(newText.text)
// Do not update the shared suggestion state here: this sheet
// renders its own popups as hidden, so an update only leaves
// a stale popup behind for the main composer.
},
onSend = {
if (messageText.text.trim().isNotEmpty()) {

View File

@ -8,6 +8,7 @@ import com.bitchat.android.geohash.GeohashChannelLevel
import com.bitchat.android.mesh.MeshService
import com.bitchat.android.model.BitchatMessage
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertFalse
import junit.framework.TestCase.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@ -161,4 +162,30 @@ class CommandProcessorTest() {
)
assertEquals(0, chatState.getMessagesValue().size)
}
@Test
fun `clearSuggestions hides the command suggestion popup`() {
// Typing "/" opens the command popup.
commandProcessor.updateCommandSuggestions("/")
assertTrue(chatState.getShowCommandSuggestionsValue())
// The send handlers call this after clearing the field in code, which does
// not run the text-change handler that normally hides the popup.
commandProcessor.clearSuggestions()
assertFalse(chatState.getShowCommandSuggestionsValue())
assertTrue(chatState.getCommandSuggestionsValue().isEmpty())
}
@Test
fun `clearSuggestions hides the mention suggestion popup`() {
whenever(meshService.getPeerNicknames()).thenReturn(mapOf("peer-1" to "alice"))
// Typing "@a" opens the mention popup.
commandProcessor.updateMentionSuggestions("@a", meshService, viewModel = null)
assertTrue(chatState.getShowMentionSuggestionsValue())
commandProcessor.clearSuggestions()
assertFalse(chatState.getShowMentionSuggestionsValue())
assertTrue(chatState.getMentionSuggestionsValue().isEmpty())
}
}