From 0353f845a592955178d8cbb38e8006c52eb49b65 Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Fri, 20 Mar 2026 12:26:33 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D1=81=D0=BA=D0=B5?= =?UTF-8?q?=D0=BB=D0=B5=D1=82=D0=BE=D0=BD=D0=B0=20=D0=B8=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=BF=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B2=D0=BA=D0=BB?= =?UTF-8?q?=D0=B0=D0=B4=D0=BE=D0=BA=20=D0=B2=20=D0=BF=D1=80=D0=BE=D1=84?= =?UTF-8?q?=D0=B8=D0=BB=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../messenger/ui/chats/ChatDetailScreen.kt | 15 +++++++++++++- .../messenger/ui/chats/ChatsListScreen.kt | 18 ++++++++++++++++- .../ui/settings/OtherProfileScreen.kt | 20 ++++++++++--------- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt index 19a4125..a14a170 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt @@ -120,6 +120,7 @@ import kotlinx.coroutines.withContext private val groupMembersCountCache = java.util.concurrent.ConcurrentHashMap() private const val GROUP_MESSAGE_STACK_TIME_DIFF_MS = 5 * 60_000L private const val DIRECT_MESSAGE_STACK_TIME_DIFF_MS = 60_000L +private const val MESSAGE_SKELETON_VISIBILITY_DELAY_MS = 180L private fun isSameLocalDay(firstTimestampMs: Long, secondTimestampMs: Long): Boolean { val firstCalendar = @@ -631,6 +632,15 @@ fun ChatDetailScreen( // If typing, the user is obviously online — never show "offline" while typing val isOnline = rawIsOnline || isTyping val isLoading by viewModel.isLoading.collectAsState() // 🔥 Для скелетона + val showMessageSkeleton by + produceState(initialValue = false, key1 = isLoading) { + if (!isLoading) { + value = false + return@produceState + } + delay(MESSAGE_SKELETON_VISIBILITY_DELAY_MS) + value = isLoading + } // �🔥 Reply/Forward state val replyMessages by viewModel.replyMessages.collectAsState() @@ -2390,7 +2400,7 @@ fun ChatDetailScreen( when { // 🔥 СКЕЛЕТОН - показываем пока загружаются // сообщения - isLoading -> { + showMessageSkeleton -> { MessageSkeletonList( isDarkTheme = isDarkTheme, isGroupChat = isGroupChat, @@ -2398,6 +2408,9 @@ fun ChatDetailScreen( Modifier.fillMaxSize() ) } + isLoading && messages.isEmpty() -> { + Box(modifier = Modifier.fillMaxSize()) + } // Пустое состояние (нет сообщений) messages.isEmpty() -> { Column( diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt index 11f9312..ed902c0 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt @@ -85,6 +85,7 @@ import compose.icons.tablericons.* import com.rosetta.messenger.ui.icons.TelegramIcons import java.text.SimpleDateFormat import java.util.* +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeoutOrNull import kotlin.math.hypot @@ -108,6 +109,8 @@ private enum class DeviceResolveAction { DECLINE } +private const val SKELETON_VISIBILITY_DELAY_MS = 180L + // Avatar colors matching React Native app (Mantine inspired) // Light theme colors (background lighter, text darker) private val avatarColorsLight = @@ -1791,7 +1794,18 @@ fun ChatsListScreen( val requests = chatsState.requests val requestsCount = chatsState.requestsCount - val showSkeleton = isLoading + val showSkeleton by + produceState( + initialValue = false, + key1 = isLoading + ) { + if (!isLoading) { + value = false + return@produceState + } + delay(SKELETON_VISIBILITY_DELAY_MS) + value = isLoading + } val chatListState = rememberLazyListState() var isRequestsVisible by remember { mutableStateOf(true) } var requestsPullProgress by remember { mutableStateOf(0f) } @@ -2010,6 +2024,8 @@ fun ChatsListScreen( } // Close Box wrapper } else if (showSkeleton) { ChatsListSkeleton(isDarkTheme = isDarkTheme) + } else if (isLoading && chatsState.isEmpty) { + Box(modifier = Modifier.fillMaxSize()) } else if (chatsState.isEmpty) { EmptyChatsState( isDarkTheme = isDarkTheme, diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt index feeef34..f18cdb1 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt @@ -108,6 +108,7 @@ import compose.icons.TablerIcons import compose.icons.tablericons.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.flowOf import androidx.compose.runtime.snapshotFlow import kotlinx.coroutines.launch @@ -199,19 +200,12 @@ fun OtherProfileScreen( var avatarViewerTimestamp by remember { mutableStateOf(0L) } var imageViewerInitialIndex by remember { mutableIntStateOf(0) } var selectedTab by remember { mutableStateOf(OtherProfileTab.MEDIA) } + var tabSwitchJob by remember { mutableStateOf(null) } val pagerState = rememberPagerState( initialPage = 0, pageCount = { OtherProfileTab.entries.size } ) - // Tab click → animate pager - LaunchedEffect(selectedTab) { - val page = OtherProfileTab.entries.indexOf(selectedTab) - if (pagerState.currentPage != page) { - pagerState.animateScrollToPage(page) - } - } - // Pager swipe → update tab + control swipe-back LaunchedEffect(pagerState) { snapshotFlow { pagerState.currentPage }.collect { page -> @@ -826,7 +820,15 @@ fun OtherProfileScreen( ) { OtherProfileSharedTabs( selectedTab = selectedTab, - onTabSelected = { tab -> selectedTab = tab }, + onTabSelected = { tab -> + val targetPage = OtherProfileTab.entries.indexOf(tab) + if (targetPage == -1) return@OtherProfileSharedTabs + selectedTab = tab + tabSwitchJob?.cancel() + tabSwitchJob = coroutineScope.launch { + runCatching { pagerState.animateScrollToPage(targetPage) } + } + }, isDarkTheme = isDarkTheme ) }