feat: cursor at end when editing and open keyboard

Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
Infi 2023-10-01 00:53:41 +02:00
parent 5a6a25c113
commit 3652d01ea8
3 changed files with 61 additions and 40 deletions

View File

@ -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,

View File

@ -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 = {

View File

@ -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<SendMessageReply>()
var pendingAttachments = mutableStateListOf<FileArgs>()
@ -394,6 +396,7 @@ class ChannelScreenViewModel : ViewModel() {
msg.id == it.messageId
} ?: return@onEach
pendingMessageContent = message.content ?: ""
textSelection = TextRange(message.content?.length ?: 0)
}
}
}.catch {