fix: close message field after closing a keyboard,

change design
This commit is contained in:
2026-04-23 20:35:29 +03:00
parent eae2d4f388
commit 8f6adf0746
@@ -24,17 +24,21 @@ import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import android.widget.TextView import android.widget.TextView
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.isImeVisible
import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@@ -61,12 +65,15 @@ import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
@@ -94,7 +101,6 @@ import de.connect2x.trixnity.core.model.events.m.room.RoomMessageEventContent
import de.connect2x.trixnity.core.model.events.m.room.TopicEventContent import de.connect2x.trixnity.core.model.events.m.room.TopicEventContent
import io.github.rabehx.iconsax.Iconsax import io.github.rabehx.iconsax.Iconsax
import io.github.rabehx.iconsax.filled.Send2 import io.github.rabehx.iconsax.filled.Send2
import io.github.rabehx.iconsax.outline.Send2
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -125,6 +131,7 @@ private const val TIMELINE_BOTTOM_AUTOSCROLL_THRESHOLD = 3
private const val MAX_INITIAL_UNREAD_SEARCH_LIMIT = 500 private const val MAX_INITIAL_UNREAD_SEARCH_LIMIT = 500
private val TIMELINE_AVATAR_SIZE = 32.dp private val TIMELINE_AVATAR_SIZE = 32.dp
private val MESSAGE_FIELD_TOP_PADDING = 8.dp private val MESSAGE_FIELD_TOP_PADDING = 8.dp
private val MESSAGE_FIELD_SIZE = 48.dp
private val simpleHtmlTags = setOf( private val simpleHtmlTags = setOf(
"a", "b", "blockquote", "br", "code", "del", "div", "em", "h1", "h2", "a", "b", "blockquote", "br", "code", "del", "div", "em", "h1", "h2",
@@ -363,17 +370,30 @@ fun Room(modifier: Modifier = Modifier, rid: String) {
} }
} }
Row(Modifier.fillMaxWidth()) { Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField( OutlinedTextField(
modifier = Modifier modifier = Modifier
.clearFocusOnKeyboardDismiss()
.padding(MESSAGE_FIELD_TOP_PADDING) .padding(MESSAGE_FIELD_TOP_PADDING)
.height(MESSAGE_FIELD_SIZE)
.weight(1f), .weight(1f),
shape = CircleShape,
value = message, value = message,
onValueChange = { message = it }, onValueChange = { message = it },
) )
AnimatedVisibility(message.isNotBlank()) {
IconButton( IconButton(
modifier = Modifier
.size(MESSAGE_FIELD_SIZE)
.background(MaterialTheme.colorScheme.primary, CircleShape),
enabled = message.isNotBlank(), enabled = message.isNotBlank(),
content = { Icon(if (message.isBlank()) Iconsax.Outline.Send2 else Iconsax.Filled.Send2, contentDescription = "Send") }, content = {
Icon(Iconsax.Filled.Send2,
contentDescription = "Send",
tint = MaterialTheme.colorScheme.inversePrimary
)
},
onClick = { onClick = {
val payload = message.trim() val payload = message.trim()
if (payload.isBlank()) return@IconButton if (payload.isBlank()) return@IconButton
@@ -388,6 +408,7 @@ fun Room(modifier: Modifier = Modifier, rid: String) {
) )
} }
} }
}
} }
private sealed interface ReadMarkerState { private sealed interface ReadMarkerState {
@@ -1727,3 +1748,28 @@ private fun wrapHtml(
private fun colorToCss(color: Color): String { private fun colorToCss(color: Color): String {
return String.format("#%06X", 0xFFFFFF and color.toArgb()) return String.format("#%06X", 0xFFFFFF and color.toArgb())
} }
@OptIn(ExperimentalLayoutApi::class)
fun Modifier.clearFocusOnKeyboardDismiss(): Modifier = composed {
var isFocused by remember { mutableStateOf(false) }
var keyboardAppearedSinceLastFocused by remember { mutableStateOf(false) }
if (isFocused) {
val imeIsVisible = WindowInsets.isImeVisible
val focusManager = LocalFocusManager.current
LaunchedEffect(imeIsVisible) {
if (imeIsVisible) {
keyboardAppearedSinceLastFocused = true
} else if (keyboardAppearedSinceLastFocused) {
focusManager.clearFocus()
}
}
}
onFocusEvent {
if (isFocused != it.isFocused) {
isFocused = it.isFocused
if (isFocused) {
keyboardAppearedSinceLastFocused = false
}
}
}
}