feat: Save unsent chat message as draft (#5686)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: James Rich <james.a.rich@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Copilot
2026-05-31 12:31:30 -05:00
committed by GitHub
parent fb942c518e
commit a36b60e551
2 changed files with 22 additions and 2 deletions
@@ -51,6 +51,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
@@ -138,12 +139,17 @@ fun MessageScreen(
var showDeleteDialog by rememberSaveable { mutableStateOf(false) }
var sharedContact by rememberSaveable { mutableStateOf<Node?>(null) }
val selectedMessageIds = rememberSaveable { mutableStateOf(emptySet<Long>()) }
val messageInputState = rememberTextFieldState(message)
val messageInputState = rememberTextFieldState(message.ifEmpty { viewModel.draftMessage.value })
val showQuickChat by viewModel.showQuickChat.collectAsStateWithLifecycle()
val filteredCount by viewModel.filteredCount.collectAsStateWithLifecycle()
val showFiltered by viewModel.showFiltered.collectAsStateWithLifecycle()
val filteringDisabled = contactSettings[contactKey]?.filteringDisabled ?: false
// Sync text field changes back to ViewModel draft
LaunchedEffect(messageInputState) {
snapshotFlow { messageInputState.text.toString() }.collect { text -> viewModel.setDraftMessage(text) }
}
// Prevent the message TextField from stealing focus when the screen opens
LaunchedEffect(contactKey) { focusManager.clearFocus() }
@@ -233,6 +239,7 @@ fun MessageScreen(
viewModel.sendMessage(event.text, contactKey, event.replyingToPacketId)
if (event.replyingToPacketId != null) replyingToPacketId = null
messageInputState.clearText()
viewModel.clearDraftMessage()
}
is MessageScreenEvent.SendReaction ->
@@ -55,7 +55,7 @@ import org.meshtastic.proto.ChannelSet
@Suppress("LongParameterList", "TooManyFunctions")
@KoinViewModel
class MessageViewModel(
savedStateHandle: SavedStateHandle,
private val savedStateHandle: SavedStateHandle,
private val nodeRepository: NodeRepository,
radioConfigRepository: RadioConfigRepository,
quickChatActionRepository: QuickChatActionRepository,
@@ -70,6 +70,19 @@ class MessageViewModel(
private val _title = MutableStateFlow("")
val title: StateFlow<String> = _title.asStateFlow()
private val _draftMessage = MutableStateFlow(savedStateHandle.get<String>("draftMessage") ?: "")
val draftMessage: StateFlow<String> = _draftMessage.asStateFlow()
fun setDraftMessage(text: String) {
_draftMessage.value = text
savedStateHandle["draftMessage"] = text
}
fun clearDraftMessage() {
_draftMessage.value = ""
savedStateHandle["draftMessage"] = ""
}
val ourNodeInfo = nodeRepository.ourNodeInfo
val connectionState = serviceRepository.connectionState