feat: Optimize chat screen transitions by removing redundant animations for a smoother user experience

This commit is contained in:
k1ngsterr1
2026-01-13 18:36:17 +05:00
parent 435c07bf01
commit 4881024a9c
5 changed files with 128 additions and 111 deletions

View File

@@ -229,16 +229,7 @@ fun ChatDetailScreen(
// Цвет иконок в хедере - синий как в React Native
val headerIconColor = if (isDarkTheme) Color.White else PrimaryBlue
// <EFBFBD> Fade-in анимация для всего экрана
var isVisible by remember { mutableStateOf(false) }
val screenAlpha by
animateFloatAsState(
targetValue = if (isVisible) 1f else 0f,
animationSpec = tween(durationMillis = 250, easing = TelegramEasing),
label = "screenFade"
)
LaunchedEffect(Unit) { isVisible = true }
// 🚀 Убираем дополнительную анимацию - используем только анимацию навигации из MainActivity
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
@@ -297,18 +288,13 @@ fun ChatDetailScreen(
var selectedMessages by remember { mutableStateOf<Set<String>>(emptySet()) }
val isSelectionMode = selectedMessages.isNotEmpty()
// 🔥 Быстрое закрытие с fade-out анимацией
// 🔥 Быстрое закрытие - мгновенный выход без дополнительной анимации
val hideKeyboardAndBack: () -> Unit = {
// Мгновенно убираем фокус и клавиатуру
focusManager.clearFocus(force = true)
keyboardController?.hide()
// Запускаем fade-out
isVisible = false
// Выходим после короткой анимации
scope.launch {
delay(150)
onBack()
}
// Сразу выходим - анимация в MainActivity
onBack()
Unit
}
@@ -328,9 +314,11 @@ fun ChatDetailScreen(
var showBlockConfirm by remember { mutableStateOf(false) }
var showUnblockConfirm by remember { mutableStateOf(false) }
// Проверяем, заблокирован ли пользователь
// Проверяем, заблокирован ли пользователь (отложенно, не блокирует UI)
var isBlocked by remember { mutableStateOf(false) }
LaunchedEffect(user.publicKey, currentUserPublicKey) {
// Отложенная проверка - не блокирует анимацию
kotlinx.coroutines.delay(50) // Даём анимации завершиться
isBlocked = database.blacklistDao().isUserBlocked(user.publicKey, currentUserPublicKey)
}
@@ -432,11 +420,8 @@ fun ChatDetailScreen(
isDarkTheme
)
// 🚀 Весь контент с fade-in анимацией
Box(modifier = Modifier
.fillMaxSize()
.graphicsLayer { alpha = screenAlpha }
) {
// 🚀 Весь контент без дополнительной анимации (анимация в MainActivity)
Box(modifier = Modifier.fillMaxSize()) {
// Telegram-style solid header background (без blur)
val headerBackground = if (isDarkTheme) Color(0xFF212121) else Color(0xFFFFFFFF)

View File

@@ -396,8 +396,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
// Подписываемся на онлайн статус
subscribeToOnlineStatus()
// Загружаем сообщения из БД
loadMessagesFromDatabase()
// 🔥 ОПТИМИЗАЦИЯ: Загружаем сообщения ПОСЛЕ задержки для плавной анимации
// 250ms - это время анимации перехода в чат
loadMessagesFromDatabase(delayMs = 250L)
}
/**
@@ -411,8 +412,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
/**
* 🚀 СУПЕР-оптимизированная загрузка сообщений
* 🔥 ОПТИМИЗАЦИЯ: Задержка для завершения анимации + чанковая расшифровка
*/
private fun loadMessagesFromDatabase() {
private fun loadMessagesFromDatabase(delayMs: Long = 0L) {
val account = myPublicKey ?: return
val opponent = opponentKey ?: return
@@ -421,6 +423,12 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
loadingJob = viewModelScope.launch(Dispatchers.IO) {
try {
// 🔥 Задержка перед загрузкой чтобы анимация перехода успела завершиться!
// Это критично для плавности - иначе расшифровка блокирует UI thread
if (delayMs > 0) {
delay(delayMs)
}
// 🔥 Сначала показываем loading НА ГЛАВНОМ потоке - мгновенно
withContext(Dispatchers.Main.immediate) {
_isLoading.value = true
@@ -441,11 +449,18 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
hasMoreMessages = entities.size >= PAGE_SIZE
currentOffset = entities.size
// 🔥 Расшифровка сообщений при загрузке (как в архиве)
// 🔥 ЧАНКОВАЯ расшифровка - по DECRYPT_CHUNK_SIZE сообщений с yield между ними
// Это предотвращает блокировку UI thread
val messages = ArrayList<ChatMessage>(entities.size)
for (entity in entities.asReversed()) {
val reversedEntities = entities.asReversed()
for ((index, entity) in reversedEntities.withIndex()) {
val chatMsg = entityToChatMessage(entity)
messages.add(chatMsg)
// Каждые DECRYPT_CHUNK_SIZE сообщений даём UI thread "подышать"
if ((index + 1) % DECRYPT_CHUNK_SIZE == 0) {
yield() // Позволяем другим корутинам выполниться
}
}
ProtocolManager.addLog("📋 Decrypted and loaded ${messages.size} messages from DB")