feat: allow content commit into message field
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
0c90c8c0e6
commit
f1e92544c9
|
|
@ -1,7 +1,14 @@
|
|||
package chat.revolt.components.chat
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.drawable.ShapeDrawable
|
||||
import android.graphics.drawable.shapes.RectShape
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputConnection
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandIn
|
||||
|
|
@ -46,6 +53,9 @@ import androidx.compose.ui.unit.IntSize
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.inputmethod.EditorInfoCompat
|
||||
import androidx.core.view.inputmethod.InputConnectionCompat
|
||||
import androidx.core.view.setPadding
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import chat.revolt.R
|
||||
|
|
@ -59,6 +69,7 @@ fun NativeMessageField(
|
|||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
onAddAttachment: () -> Unit,
|
||||
onCommitAttachment: (Uri) -> Unit,
|
||||
onPickEmoji: () -> Unit,
|
||||
onSendMessage: () -> Unit,
|
||||
channelType: ChannelType,
|
||||
|
|
@ -86,7 +97,9 @@ fun NativeMessageField(
|
|||
val density = LocalDensity.current
|
||||
|
||||
val selectionColour = MaterialTheme.colorScheme.primary.copy(alpha = 0.2f).toArgb()
|
||||
val cursorColour = MaterialTheme.colorScheme.primary.toArgb()
|
||||
val contentColour = LocalContentColor.current.toArgb()
|
||||
val placeholderColour = LocalContentColor.current.copy(alpha = 0.5f).toArgb()
|
||||
|
||||
LaunchedEffect(editMode) {
|
||||
if (editMode) {
|
||||
|
|
@ -129,13 +142,31 @@ fun NativeMessageField(
|
|||
|
||||
AndroidView(
|
||||
factory = { context ->
|
||||
com.google.android.material.textfield.TextInputEditText(context).apply {
|
||||
object : androidx.appcompat.widget.AppCompatEditText(context) {
|
||||
override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection? {
|
||||
var ic = super.onCreateInputConnection(outAttrs)
|
||||
EditorInfoCompat.setContentMimeTypes(
|
||||
outAttrs,
|
||||
arrayOf("image/*")
|
||||
)
|
||||
val mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this)
|
||||
if (mimeTypes != null) {
|
||||
EditorInfoCompat.setContentMimeTypes(outAttrs, mimeTypes)
|
||||
ic = ic?.let { InputConnectionCompat.createWrapper(this, it, outAttrs) }
|
||||
}
|
||||
return ic
|
||||
}
|
||||
}.apply {
|
||||
background = null
|
||||
textSize = 16f
|
||||
setPadding((density.density * 16.dp.value).toInt())
|
||||
|
||||
// Propagate text changes to parent
|
||||
addTextChangedListener {
|
||||
onValueChange(it.toString())
|
||||
}
|
||||
|
||||
// Hide/show keyboard on focus change and propagate to parent
|
||||
onFocusChangeListener = android.view.View.OnFocusChangeListener { _, hasFocus ->
|
||||
val keyboard =
|
||||
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
|
|
@ -145,23 +176,55 @@ fun NativeMessageField(
|
|||
0
|
||||
)
|
||||
} else {
|
||||
keyboard.hideSoftInputFromWindow(this.getWindowToken(), 0)
|
||||
keyboard.hideSoftInputFromWindow(this.windowToken, 0)
|
||||
}
|
||||
|
||||
onFocusChange(hasFocus)
|
||||
}
|
||||
|
||||
ViewCompat.setOnReceiveContentListener(
|
||||
this,
|
||||
arrayOf("image/*")
|
||||
) { _, payload ->
|
||||
// Check mimetype
|
||||
if (payload.clip.description.hasMimeType("image/*")) {
|
||||
// Get image
|
||||
val item = payload.clip.getItemAt(0)
|
||||
val uri = item.uri
|
||||
|
||||
if (uri == null) {
|
||||
Log.e("MessageField", "Received payload with null uri")
|
||||
return@setOnReceiveContentListener payload
|
||||
}
|
||||
|
||||
onCommitAttachment(uri)
|
||||
|
||||
return@setOnReceiveContentListener null
|
||||
}
|
||||
payload
|
||||
}
|
||||
|
||||
isFocusable = true
|
||||
isFocusableInTouchMode = true
|
||||
|
||||
typeface = ResourcesCompat.getFont(context, R.font.inter)
|
||||
|
||||
// Set colours
|
||||
highlightColor = selectionColour
|
||||
setTextColor(contentColour)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
setTextCursorDrawable(null)
|
||||
}
|
||||
setHintTextColor(ColorStateList.valueOf(placeholderColour))
|
||||
|
||||
this.alpha = 1f
|
||||
// Caret colour and size
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val shapeDrawable = ShapeDrawable(RectShape())
|
||||
shapeDrawable.paint.color = cursorColour
|
||||
val sizeInDp = 1
|
||||
val sizeInPixels = (sizeInDp * resources.displayMetrics.density).toInt()
|
||||
shapeDrawable.intrinsicWidth = sizeInPixels
|
||||
shapeDrawable.intrinsicHeight = sizeInPixels
|
||||
|
||||
setTextCursorDrawable(shapeDrawable)
|
||||
}
|
||||
|
||||
clearFocus = {
|
||||
this.clearFocus()
|
||||
|
|
@ -247,6 +310,7 @@ fun NativeMessageFieldPreview() {
|
|||
value = "Hello world!",
|
||||
onValueChange = {},
|
||||
onAddAttachment = {},
|
||||
onCommitAttachment = {},
|
||||
onPickEmoji = {},
|
||||
onSendMessage = {},
|
||||
channelType = ChannelType.DirectMessage,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
|
|
@ -49,7 +50,9 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -109,9 +112,9 @@ import com.airbnb.lottie.compose.rememberLottieComposition
|
|||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.sentry.Sentry
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
|
|
@ -265,6 +268,8 @@ fun ChatRouterScreen(
|
|||
) {
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val view = LocalView.current
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
val navController = rememberNavController()
|
||||
|
|
@ -317,6 +322,9 @@ fun ChatRouterScreen(
|
|||
.collect { state ->
|
||||
if (state == DrawerValue.Open) {
|
||||
keyboardController?.hide()
|
||||
val keyboard =
|
||||
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
keyboard.hideSoftInputFromWindow(view.windowToken, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -368,7 +376,7 @@ fun ChatRouterScreen(
|
|||
.distinctUntilChanged()
|
||||
.collect { sizeClass ->
|
||||
useTabletAwareUI = sizeClass.widthSizeClass == WindowWidthSizeClass.Expanded &&
|
||||
sizeClass.heightSizeClass != WindowHeightSizeClass.Compact
|
||||
sizeClass.heightSizeClass != WindowHeightSizeClass.Compact
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -847,21 +855,21 @@ fun Sidebar(
|
|||
// - Add the servers that aren't in the ordering to the end of the list.
|
||||
// - Sort the servers that aren't in the ordering by their ID (creation order).
|
||||
(
|
||||
(
|
||||
RevoltAPI.serverCache.values.filter {
|
||||
SyncedSettings.ordering.servers.contains(
|
||||
it.id
|
||||
)
|
||||
}
|
||||
.sortedBy { SyncedSettings.ordering.servers.indexOf(it.id) }
|
||||
) + (
|
||||
RevoltAPI.serverCache.values.filter {
|
||||
!SyncedSettings.ordering.servers.contains(
|
||||
it.id
|
||||
)
|
||||
}.sortedBy { it.id }
|
||||
(
|
||||
RevoltAPI.serverCache.values.filter {
|
||||
SyncedSettings.ordering.servers.contains(
|
||||
it.id
|
||||
)
|
||||
}
|
||||
.sortedBy { SyncedSettings.ordering.servers.indexOf(it.id) }
|
||||
) + (
|
||||
RevoltAPI.serverCache.values.filter {
|
||||
!SyncedSettings.ordering.servers.contains(
|
||||
it.id
|
||||
)
|
||||
}.sortedBy { it.id }
|
||||
)
|
||||
)
|
||||
)
|
||||
.forEach { server ->
|
||||
if (server.id == null || server.name == null) return@forEach
|
||||
|
||||
|
|
|
|||
|
|
@ -503,6 +503,9 @@ fun ChannelScreen(
|
|||
}
|
||||
}
|
||||
},
|
||||
onCommitAttachment = { uri ->
|
||||
processFileUri(uri)
|
||||
},
|
||||
onPickEmoji = {
|
||||
focusManager.clearFocus()
|
||||
if (viewModel.currentBottomPane == BottomPane.EmojiPicker) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue