Improve UI

Signed-off-by: denis0001-dev <denis0001.dev@ya.ru>
This commit is contained in:
2025-09-13 18:56:07 +03:00
Unverified
parent 8d2703a77e
commit be169d0fe9
5 changed files with 147 additions and 100 deletions
+1
View File
@@ -29,6 +29,7 @@
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="NestedLambdaShadowedImplicitParameter" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" /> <option name="previewFile" value="true" />
+1 -1
View File
@@ -13,7 +13,7 @@ plugins {
kotlin { kotlin {
compilerOptions { compilerOptions {
jvmTarget.set(JvmTarget.JVM_11) jvmTarget.set(JvmTarget.JVM_11)
freeCompilerArgs.add("-Xsuppress-warning=NOTHING_TO_INLINE") freeCompilerArgs.add("-Xwarning-level=NOTHING_TO_INLINE:disabled")
} }
} }
@@ -31,8 +31,7 @@ object WebSocketManager {
private val _messages = MutableSharedFlow<WebSocketMessage>(replay = 0, extraBufferCapacity = 64) private val _messages = MutableSharedFlow<WebSocketMessage>(replay = 0, extraBufferCapacity = 64)
val messages = _messages.asSharedFlow() val messages = _messages.asSharedFlow()
@Volatile private val globalHandlers = mutableListOf<((WebSocketMessage) -> Unit)>()
private var globalHandlers = mutableListOf<((WebSocketMessage) -> Unit)>()
fun addGlobalMessageHandler(handler: ((WebSocketMessage) -> Unit)) { fun addGlobalMessageHandler(handler: ((WebSocketMessage) -> Unit)) {
globalHandlers += handler globalHandlers += handler
@@ -1,6 +1,9 @@
package ru.fromchat.ui package ru.fromchat.ui
import android.util.Log import android.util.Log
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.fadeOut
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
@@ -10,6 +13,7 @@ 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.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.safeDrawing
@@ -29,8 +33,10 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.Send import androidx.compose.material.icons.automirrored.filled.Send
import androidx.compose.material.icons.filled.DoneAll import androidx.compose.material.icons.filled.DoneAll
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LoadingIndicator
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
@@ -51,11 +57,15 @@ import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.format import kotlinx.datetime.format
import kotlinx.datetime.toLocalDateTime import kotlinx.datetime.toLocalDateTime
@@ -76,7 +86,7 @@ import ru.fromchat.utils.exclude
import kotlin.time.ExperimentalTime import kotlin.time.ExperimentalTime
import kotlin.time.Instant import kotlin.time.Instant
@OptIn(ExperimentalMaterial3Api::class, ExperimentalTime::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalTime::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
fun PublicChatScreen() { fun PublicChatScreen() {
val navController = LocalNavController.current val navController = LocalNavController.current
@@ -84,8 +94,12 @@ fun PublicChatScreen() {
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
val messages = remember { mutableStateListOf<Message>() } val messages = remember { mutableStateListOf<Message>() }
var loading by remember { mutableStateOf(true) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
val coroutineContext = coroutineContext
CoroutineScope(Dispatchers.IO).launch {
apiRequest { apiRequest {
ApiClient.getMessages() ApiClient.getMessages()
}.onSuccess { }.onSuccess {
@@ -109,7 +123,11 @@ fun PublicChatScreen() {
} }
delay(500) delay(500)
scrollState.animateScrollTo(scrollState.maxValue) withContext(coroutineContext) {
scrollState.scrollTo(scrollState.maxValue)
}
loading = false
}.join()
} }
Scaffold( Scaffold(
@@ -205,6 +223,7 @@ fun PublicChatScreen() {
} }
} }
) { innerPadding -> ) { innerPadding ->
Box(Modifier.fillMaxSize()) {
Column( Column(
Modifier Modifier
.padding(innerPadding) .padding(innerPadding)
@@ -215,7 +234,8 @@ fun PublicChatScreen() {
text: String, text: String,
isAuthor: Boolean, isAuthor: Boolean,
isRead: Boolean, isRead: Boolean,
timestamp: String timestamp: String,
username: String
) { ) {
var rowWidth by remember { mutableStateOf(0.dp) } var rowWidth by remember { mutableStateOf(0.dp) }
val density = LocalDensity.current val density = LocalDensity.current
@@ -225,7 +245,7 @@ fun PublicChatScreen() {
.padding(horizontal = 16.dp, vertical = 4.dp) .padding(horizontal = 16.dp, vertical = 4.dp)
.fillMaxWidth() .fillMaxWidth()
.onSizeChanged { .onSizeChanged {
rowWidth = with (density) { rowWidth = with(density) {
it.width.toDp() it.width.toDp()
} }
}, },
@@ -251,6 +271,14 @@ fun PublicChatScreen() {
.width(IntrinsicSize.Max) .width(IntrinsicSize.Max)
.widthIn(max = rowWidth * 0.80f) .widthIn(max = rowWidth * 0.80f)
) { ) {
if (!isAuthor) {
Text(
text = username,
fontWeight = FontWeight.W600,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
Text(text) Text(text)
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@@ -282,9 +310,26 @@ fun PublicChatScreen() {
timestamp = Instant timestamp = Instant
.parse(it.utcTimestamp) .parse(it.utcTimestamp)
.toLocalDateTime(TimeZone.currentSystemDefault()) .toLocalDateTime(TimeZone.currentSystemDefault())
.format(DATETIME_FORMAT) .format(DATETIME_FORMAT),
username = it.username
) )
} }
} }
AnimatedVisibility(
visible = loading,
enter = EnterTransition.None,
exit = fadeOut()
) {
Box(
Modifier
.background(MaterialTheme.colorScheme.surface)
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
LoadingIndicator(Modifier.size(70.dp))
}
}
}
} }
} }
+11 -9
View File
@@ -1,6 +1,6 @@
[versions] [versions]
agp = "8.12.3" agp = "8.12.3"
kotlin = "2.2.10" kotlin = "2.2.20"
coreKtx = "1.17.0" coreKtx = "1.17.0"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.3.0" junitVersion = "1.3.0"
@@ -11,8 +11,10 @@ activityCompose = "1.11.0"
composeBom = "2025.09.00" composeBom = "2025.09.00"
materialIconsExtended = "1.7.8" materialIconsExtended = "1.7.8"
navigationCompose = "2.9.4" navigationCompose = "2.9.4"
ktor = "3.2.3" ktor = "3.3.0"
kotlinxSerializationJson = "1.9.0" kotlinxSerializationJson = "1.9.0"
compose = "1.9.1"
material3 = "1.5.0-alpha04"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -23,13 +25,13 @@ androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-co
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" } androidx-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "compose" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics", version.ref = "compose" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "compose" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "compose" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "compose" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "compose" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" } kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" }
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" } ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }