feat: cursor at end when editing and open keyboard
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
5a6a25c113
commit
3652d01ea8
|
|
@ -29,6 +29,7 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextFieldDefaults
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
import androidx.compose.material3.surfaceColorAtElevation
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
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.graphics.SolidColor
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.res.stringResource
|
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.input.VisualTransformation
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
|
@ -51,8 +53,8 @@ import chat.revolt.api.schemas.ChannelType
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MessageField(
|
fun MessageField(
|
||||||
messageContent: String,
|
value: TextFieldValue,
|
||||||
onMessageContentChange: (String) -> Unit,
|
onValueChange: (TextFieldValue) -> Unit,
|
||||||
onAddAttachment: () -> Unit,
|
onAddAttachment: () -> Unit,
|
||||||
onSendMessage: () -> Unit,
|
onSendMessage: () -> Unit,
|
||||||
channelType: ChannelType,
|
channelType: ChannelType,
|
||||||
|
|
@ -73,16 +75,23 @@ fun MessageField(
|
||||||
ChannelType.SavedMessages -> R.string.message_field_placeholder_notes
|
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(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp))
|
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp))
|
||||||
) {
|
) {
|
||||||
|
|
||||||
BasicTextField(
|
BasicTextField(
|
||||||
value = messageContent,
|
value = value,
|
||||||
onValueChange = onMessageContentChange,
|
onValueChange = onValueChange,
|
||||||
singleLine = false,
|
singleLine = false,
|
||||||
enabled = !disabled,
|
enabled = !disabled,
|
||||||
textStyle = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.onSurface),
|
textStyle = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.onSurface),
|
||||||
|
|
@ -97,21 +106,21 @@ fun MessageField(
|
||||||
keyboardActions = KeyboardActions.Default,
|
keyboardActions = KeyboardActions.Default,
|
||||||
decorationBox = @Composable { innerTextField ->
|
decorationBox = @Composable { innerTextField ->
|
||||||
TextFieldDefaults.DecorationBox(
|
TextFieldDefaults.DecorationBox(
|
||||||
value = messageContent,
|
value = value.text,
|
||||||
innerTextField = innerTextField,
|
innerTextField = innerTextField,
|
||||||
enabled = !disabled,
|
enabled = !disabled,
|
||||||
singleLine = false,
|
singleLine = false,
|
||||||
visualTransformation = VisualTransformation.None,
|
visualTransformation = VisualTransformation.None,
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
placeholder = {
|
placeholder = {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(
|
text = stringResource(
|
||||||
id = placeholderResource,
|
id = placeholderResource,
|
||||||
channelName
|
channelName
|
||||||
),
|
),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
colors = TextFieldDefaults.colors(
|
colors = TextFieldDefaults.colors(
|
||||||
focusedIndicatorColor = Color.Transparent,
|
focusedIndicatorColor = Color.Transparent,
|
||||||
|
|
@ -127,28 +136,28 @@ fun MessageField(
|
||||||
),
|
),
|
||||||
contentPadding = PaddingValues(16.dp),
|
contentPadding = PaddingValues(16.dp),
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
Icon(
|
Icon(
|
||||||
when {
|
when {
|
||||||
editMode -> Icons.Default.Close
|
editMode -> Icons.Default.Close
|
||||||
else -> Icons.Default.Add
|
else -> Icons.Default.Add
|
||||||
},
|
},
|
||||||
tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f),
|
tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f),
|
||||||
contentDescription = stringResource(id = R.string.add_attachment_alt),
|
contentDescription = stringResource(id = R.string.add_attachment_alt),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.size(32.dp)
|
.size(32.dp)
|
||||||
.clickable {
|
.clickable {
|
||||||
when {
|
when {
|
||||||
editMode -> cancelEdit()
|
editMode -> cancelEdit()
|
||||||
else -> {
|
else -> {
|
||||||
focusRequester.freeFocus() // hide keyboard because it's annoying
|
focusRequester.freeFocus() // hide keyboard because it's annoying
|
||||||
onAddAttachment()
|
onAddAttachment()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(4.dp)
|
}
|
||||||
.testTag("add_attachment")
|
.padding(4.dp)
|
||||||
)
|
.testTag("add_attachment")
|
||||||
|
)
|
||||||
},
|
},
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
AnimatedVisibility(sendButtonVisible,
|
AnimatedVisibility(sendButtonVisible,
|
||||||
|
|
@ -186,8 +195,8 @@ fun MessageField(
|
||||||
@Composable
|
@Composable
|
||||||
fun MessageFieldPreview() {
|
fun MessageFieldPreview() {
|
||||||
MessageField(
|
MessageField(
|
||||||
messageContent = "Hello world!",
|
value = TextFieldValue("Hello world!"),
|
||||||
onMessageContentChange = {},
|
onValueChange = {},
|
||||||
onSendMessage = {},
|
onSendMessage = {},
|
||||||
onAddAttachment = {},
|
onAddAttachment = {},
|
||||||
channelType = ChannelType.TextChannel,
|
channelType = ChannelType.TextChannel,
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
|
@ -173,6 +174,13 @@ fun ChannelScreen(
|
||||||
label = "ScrollDownFABPadding"
|
label = "ScrollDownFABPadding"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val fieldContent = remember(viewModel.pendingMessageContent, viewModel.textSelection) {
|
||||||
|
TextFieldValue(
|
||||||
|
viewModel.pendingMessageContent,
|
||||||
|
viewModel.textSelection
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(channelId) {
|
LaunchedEffect(channelId) {
|
||||||
viewModel.fetchChannel(channelId)
|
viewModel.fetchChannel(channelId)
|
||||||
|
|
||||||
|
|
@ -465,9 +473,10 @@ fun ChannelScreen(
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
MessageField(
|
MessageField(
|
||||||
messageContent = viewModel.pendingMessageContent,
|
value = fieldContent,
|
||||||
onMessageContentChange = {
|
onValueChange = {
|
||||||
viewModel.pendingMessageContent = it
|
viewModel.pendingMessageContent = it.text
|
||||||
|
viewModel.textSelection = it.selection
|
||||||
},
|
},
|
||||||
onSendMessage = viewModel::sendPendingMessage,
|
onSendMessage = viewModel::sendPendingMessage,
|
||||||
onAddAttachment = {
|
onAddAttachment = {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.toMutableStateList
|
import androidx.compose.runtime.toMutableStateList
|
||||||
|
import androidx.compose.ui.text.TextRange
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import chat.revolt.R
|
import chat.revolt.R
|
||||||
|
|
@ -64,6 +65,7 @@ class ChannelScreenViewModel : ViewModel() {
|
||||||
var hasNoMoreMessages by mutableStateOf(false)
|
var hasNoMoreMessages by mutableStateOf(false)
|
||||||
|
|
||||||
var pendingMessageContent by mutableStateOf("")
|
var pendingMessageContent by mutableStateOf("")
|
||||||
|
var textSelection by mutableStateOf(TextRange(0))
|
||||||
var pendingReplies = mutableStateListOf<SendMessageReply>()
|
var pendingReplies = mutableStateListOf<SendMessageReply>()
|
||||||
var pendingAttachments = mutableStateListOf<FileArgs>()
|
var pendingAttachments = mutableStateListOf<FileArgs>()
|
||||||
|
|
||||||
|
|
@ -394,6 +396,7 @@ class ChannelScreenViewModel : ViewModel() {
|
||||||
msg.id == it.messageId
|
msg.id == it.messageId
|
||||||
} ?: return@onEach
|
} ?: return@onEach
|
||||||
pendingMessageContent = message.content ?: ""
|
pendingMessageContent = message.content ?: ""
|
||||||
|
textSelection = TextRange(message.content?.length ?: 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.catch {
|
}.catch {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue