From 98976cb3f5ba9054132bca352a876b4c215eaade Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 29 Jul 2026 19:49:43 +0300 Subject: [PATCH] up version --- apps/vbox/client/androidApp/build.gradle.kts | 4 +- .../chat/presentation/ChatScreen.kt | 80 +++++++++++++------ .../kotlin/ru/shadowsparky/ui/BaseScreen.kt | 3 +- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/apps/vbox/client/androidApp/build.gradle.kts b/apps/vbox/client/androidApp/build.gradle.kts index 4dc84a8..ff9fa44 100644 --- a/apps/vbox/client/androidApp/build.gradle.kts +++ b/apps/vbox/client/androidApp/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = namespace minSdk = libs.versions.proj.min.get().toInt() targetSdk = libs.versions.proj.target.get().toInt() - versionCode = 44 - versionName = "1.24" + versionCode = 45 + versionName = "1.24.1" } } diff --git a/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt b/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt index f7b7972..c8a5e68 100644 --- a/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt +++ b/feature/chat/chat-client/src/commonMain/kotlin/ru/shadowsparky/chat/presentation/ChatScreen.kt @@ -41,6 +41,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyEventType +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onKeyEvent +import androidx.compose.ui.input.key.type import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextLinkStyles @@ -127,7 +132,8 @@ private fun ChatContent( ) { val listState = rememberLazyListState() var textInput by remember { mutableStateOf("") } - val requester = remember { FocusRequester() } + val textFieldFocusRequester = remember { FocusRequester() } + val listFocusRequester = remember { FocusRequester() } LaunchedEffect(state.messages.size) { if (state.messages.isNotEmpty()) { @@ -141,6 +147,7 @@ private fun ChatContent( LazyColumn( state = listState, modifier = Modifier.applyBlurSource(blurState) + .focusRequester(listFocusRequester) .weight(1f) .fillMaxWidth(), contentPadding = contentPadding, @@ -170,7 +177,7 @@ private fun ChatContent( OutlinedTextField( value = textInput, onValueChange = { textInput = it }, - modifier = Modifier.focusRequester(requester).fillMaxWidth(), + modifier = Modifier.focusRequester(textFieldFocusRequester).resetFocusOnUpEvent(listFocusRequester).fillMaxWidth(), shape = RoundedCornerShape(24.dp), placeholder = { Text(stringResource(Res.string.chat_input_placeholder)) }, enabled = !state.isSending, @@ -184,7 +191,7 @@ private fun ChatContent( } LaunchedEffect(null) { withFrameNanos {} - requester.requestFocus() + textFieldFocusRequester.requestFocus() } } @@ -199,39 +206,51 @@ private fun MessageBubble( val containerColor = if (isUser) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.surfaceVariant val contentColor = if (isUser) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurfaceVariant val tokens = remember(message.content) { onParseMessage(message.content) } - val annotatedString = buildAnnotatedString { - tokens.forEach { token -> - when (token) { - is TextToken.Plain -> { append(token.text) } - is TextToken.Link -> { - val linkAnnotation = LinkAnnotation.Clickable( - tag = "LINK", - styles = TextLinkStyles( - style = SpanStyle( - color = MaterialTheme.colorScheme.primary, - fontWeight = FontWeight.Bold, - textDecoration = TextDecoration.Underline - ) - ), - linkInteractionListener = { - onClickLink(token.text) - } - ) - withLink(linkAnnotation) { append(token.text) } + val primaryColor = MaterialTheme.colorScheme.primary + val focusBackgroundColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.15f) + val annotatedString = remember(tokens, primaryColor, focusBackgroundColor) { + buildAnnotatedString { + tokens.forEach { token -> + when (token) { + is TextToken.Plain -> append(token.text) + is TextToken.Link -> { + val linkAnnotation = LinkAnnotation.Clickable( + tag = "LINK", + styles = TextLinkStyles( + style = SpanStyle( + color = primaryColor, + fontWeight = FontWeight.Bold, + textDecoration = TextDecoration.Underline + ), + focusedStyle = SpanStyle( + background = focusBackgroundColor, + color = primaryColor + ) + ), + linkInteractionListener = { onClickLink(token.text) } + ) + withLink(linkAnnotation) { append(token.text) } + } } } } } - Box(modifier = Modifier.fillMaxWidth(), contentAlignment = alignment) { + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = alignment + ) { Card( - colors = CardDefaults.cardColors(containerColor = containerColor, contentColor = contentColor), + colors = CardDefaults.cardColors( + containerColor = containerColor, + contentColor = contentColor + ), modifier = Modifier.widthIn(max = 280.dp) ) { Column(modifier = Modifier.padding(12.dp)) { SelectionContainer { Text( text = annotatedString, - style = MaterialTheme.typography.bodyLarge.copy(color = contentColor) + style = MaterialTheme.typography.bodyLarge ) } } @@ -239,3 +258,14 @@ private fun MessageBubble( } } +private fun Modifier.resetFocusOnUpEvent(focusRequester: FocusRequester): Modifier { + return onKeyEvent { + if (it.type == KeyEventType.KeyUp) { + if (it.key == Key.DirectionUp) { + focusRequester.requestFocus() + } + } + true + } +} + diff --git a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/BaseScreen.kt b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/BaseScreen.kt index ccb81af..ba81922 100644 --- a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/BaseScreen.kt +++ b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/BaseScreen.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.material3.ColorScheme import androidx.compose.material3.ExperimentalMaterial3Api @@ -106,7 +105,7 @@ fun AppScaffold( modifier }, snackbarHost = { snackbarState?.let { SnackbarHost(it) } - }, contentWindowInsets = WindowInsets.safeDrawing + } ) } }