mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
+1
@@ -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" />
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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" }
|
||||||
|
|||||||
Reference in New Issue
Block a user