From 3652d01ea8f6f90871865cdb9d2854c6ebd1eab6 Mon Sep 17 00:00:00 2001 From: Infi Date: Sun, 1 Oct 2023 00:53:41 +0200 Subject: [PATCH] feat: cursor at end when editing and open keyboard Signed-off-by: Infi --- .../revolt/components/chat/MessageField.kt | 83 ++++++++++--------- .../chat/views/channel/ChannelScreen.kt | 15 +++- .../views/channel/ChannelScreenViewModel.kt | 3 + 3 files changed, 61 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/chat/revolt/components/chat/MessageField.kt b/app/src/main/java/chat/revolt/components/chat/MessageField.kt index 7377cb9f..983f7814 100644 --- a/app/src/main/java/chat/revolt/components/chat/MessageField.kt +++ b/app/src/main/java/chat/revolt/components/chat/MessageField.kt @@ -29,6 +29,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -39,6 +40,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -51,8 +53,8 @@ import chat.revolt.api.schemas.ChannelType @OptIn(ExperimentalMaterial3Api::class) @Composable fun MessageField( - messageContent: String, - onMessageContentChange: (String) -> Unit, + value: TextFieldValue, + onValueChange: (TextFieldValue) -> Unit, onAddAttachment: () -> Unit, onSendMessage: () -> Unit, channelType: ChannelType, @@ -73,16 +75,23 @@ fun MessageField( ChannelType.SavedMessages -> R.string.message_field_placeholder_notes } - val sendButtonVisible = (messageContent.isNotBlank() || forceSendButton) && !disabled + val sendButtonVisible = (value.text.isNotBlank() || forceSendButton) && !disabled + + LaunchedEffect(editMode) { + if (editMode) { + focusRequester.requestFocus() + } else { + focusRequester.freeFocus() + } + } Row( modifier = Modifier .background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)) ) { - BasicTextField( - value = messageContent, - onValueChange = onMessageContentChange, + value = value, + onValueChange = onValueChange, singleLine = false, enabled = !disabled, textStyle = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.onSurface), @@ -97,21 +106,21 @@ fun MessageField( keyboardActions = KeyboardActions.Default, decorationBox = @Composable { innerTextField -> TextFieldDefaults.DecorationBox( - value = messageContent, + value = value.text, innerTextField = innerTextField, enabled = !disabled, singleLine = false, visualTransformation = VisualTransformation.None, interactionSource = remember { MutableInteractionSource() }, placeholder = { - Text( - text = stringResource( - id = placeholderResource, - channelName - ), - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) + Text( + text = stringResource( + id = placeholderResource, + channelName + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) }, colors = TextFieldDefaults.colors( focusedIndicatorColor = Color.Transparent, @@ -127,28 +136,28 @@ fun MessageField( ), contentPadding = PaddingValues(16.dp), leadingIcon = { - Icon( - when { - editMode -> Icons.Default.Close - else -> Icons.Default.Add - }, - tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), - contentDescription = stringResource(id = R.string.add_attachment_alt), - modifier = Modifier - .clip(CircleShape) - .size(32.dp) - .clickable { - when { - editMode -> cancelEdit() - else -> { - focusRequester.freeFocus() // hide keyboard because it's annoying - onAddAttachment() - } + Icon( + when { + editMode -> Icons.Default.Close + else -> Icons.Default.Add + }, + tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), + contentDescription = stringResource(id = R.string.add_attachment_alt), + modifier = Modifier + .clip(CircleShape) + .size(32.dp) + .clickable { + when { + editMode -> cancelEdit() + else -> { + focusRequester.freeFocus() // hide keyboard because it's annoying + onAddAttachment() } } - .padding(4.dp) - .testTag("add_attachment") - ) + } + .padding(4.dp) + .testTag("add_attachment") + ) }, trailingIcon = { AnimatedVisibility(sendButtonVisible, @@ -186,8 +195,8 @@ fun MessageField( @Composable fun MessageFieldPreview() { MessageField( - messageContent = "Hello world!", - onMessageContentChange = {}, + value = TextFieldValue("Hello world!"), + onValueChange = {}, onSendMessage = {}, onAddAttachment = {}, channelType = ChannelType.TextChannel, diff --git a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt index 6c3416de..2283255b 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt @@ -54,6 +54,7 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.documentfile.provider.DocumentFile @@ -173,6 +174,13 @@ fun ChannelScreen( label = "ScrollDownFABPadding" ) + val fieldContent = remember(viewModel.pendingMessageContent, viewModel.textSelection) { + TextFieldValue( + viewModel.pendingMessageContent, + viewModel.textSelection + ) + } + LaunchedEffect(channelId) { viewModel.fetchChannel(channelId) @@ -465,9 +473,10 @@ fun ChannelScreen( ) } else { MessageField( - messageContent = viewModel.pendingMessageContent, - onMessageContentChange = { - viewModel.pendingMessageContent = it + value = fieldContent, + onValueChange = { + viewModel.pendingMessageContent = it.text + viewModel.textSelection = it.selection }, onSendMessage = viewModel::sendPendingMessage, onAddAttachment = { diff --git a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt index c4970a1e..4c55643a 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.runtime.toMutableStateList +import androidx.compose.ui.text.TextRange import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import chat.revolt.R @@ -64,6 +65,7 @@ class ChannelScreenViewModel : ViewModel() { var hasNoMoreMessages by mutableStateOf(false) var pendingMessageContent by mutableStateOf("") + var textSelection by mutableStateOf(TextRange(0)) var pendingReplies = mutableStateListOf() var pendingAttachments = mutableStateListOf() @@ -394,6 +396,7 @@ class ChannelScreenViewModel : ViewModel() { msg.id == it.messageId } ?: return@onEach pendingMessageContent = message.content ?: "" + textSelection = TextRange(message.content?.length ?: 0) } } }.catch {