From acf7aea098e00184aeaad8fe41d125d22864cfa9 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Sat, 14 Mar 2026 08:43:25 -0500 Subject: [PATCH] feat(desktop): add enter-to-send functionality in messaging (#4793) --- .../ui/messaging/DesktopMessageContent.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/desktop/src/main/kotlin/org/meshtastic/desktop/ui/messaging/DesktopMessageContent.kt b/desktop/src/main/kotlin/org/meshtastic/desktop/ui/messaging/DesktopMessageContent.kt index e71352880..8a2b50a3a 100644 --- a/desktop/src/main/kotlin/org/meshtastic/desktop/ui/messaging/DesktopMessageContent.kt +++ b/desktop/src/main/kotlin/org/meshtastic/desktop/ui/messaging/DesktopMessageContent.kt @@ -37,6 +37,12 @@ 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 +import androidx.compose.ui.input.key.isShiftPressed +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onPreviewKeyEvent +import androidx.compose.ui.input.key.type import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -58,6 +64,7 @@ import org.meshtastic.core.ui.util.createClipEntry import org.meshtastic.feature.messaging.MessageViewModel import org.meshtastic.feature.messaging.component.ActionModeTopBar import org.meshtastic.feature.messaging.component.DeleteMessageDialog +import org.meshtastic.feature.messaging.component.MESSAGE_CHARACTER_LIMIT_BYTES import org.meshtastic.feature.messaging.component.MessageInput import org.meshtastic.feature.messaging.component.MessageItem import org.meshtastic.feature.messaging.component.MessageMenuAction @@ -301,6 +308,24 @@ fun DesktopMessageContent( }, isEnabled = connectionState.isConnected(), isHomoglyphEncodingEnabled = homoglyphEncodingEnabled, + modifier = + Modifier.onPreviewKeyEvent { event -> + if (event.type == KeyEventType.KeyDown && event.key == Key.Enter && !event.isShiftPressed) { + val currentByteLength = messageText.encodeToByteArray().size + val isOverLimit = currentByteLength > MESSAGE_CHARACTER_LIMIT_BYTES + val trimmed = messageText.trim() + if (trimmed.isNotEmpty() && connectionState.isConnected() && !isOverLimit) { + viewModel.sendMessage(trimmed, contactKey, replyingToPacketId) + if (replyingToPacketId != null) replyingToPacketId = null + messageText = "" + return@onPreviewKeyEvent true + } + // If over limit or empty, we still consume Enter to prevent newlines if the user + // intended to send, but only if they are not holding shift. + if (!event.isShiftPressed) return@onPreviewKeyEvent true + } + false + }, ) } },