feat: Enhance logging and state management in ChatsList and MessageRepository for improved user experience

This commit is contained in:
k1ngsterr1
2026-01-18 21:48:02 +05:00
parent f141145245
commit 89d639a474
4 changed files with 51 additions and 10 deletions

View File

@@ -572,10 +572,19 @@ class MessageRepository private constructor(private val context: Context) {
suspend fun updateDialogUserInfo(publicKey: String, title: String, username: String, verified: Int) {
val account = currentAccount ?: return
android.util.Log.d("MessageRepo", "🔄 updateDialogUserInfo: ${publicKey.take(16)}... title='$title' username='$username'")
// Проверяем существует ли диалог с этим пользователем
val existing = dialogDao.getDialog(account, publicKey)
if (existing != null) {
android.util.Log.d("MessageRepo", "✅ Updating dialog opponent info in DB for ${publicKey.take(16)}...")
dialogDao.updateOpponentInfo(account, publicKey, title, username, verified)
// 🔥 Проверим что данные сохранились
val updated = dialogDao.getDialog(account, publicKey)
android.util.Log.d("MessageRepo", "📝 After update: title='${updated?.opponentTitle}' username='${updated?.opponentUsername}'")
} else {
android.util.Log.w("MessageRepo", "⚠️ Dialog not found for ${publicKey.take(16)}...")
}
}

View File

@@ -179,12 +179,14 @@ object ProtocolManager {
waitPacket(0x03) { packet ->
val searchPacket = packet as PacketSearch
addLog("🔍 Search/UserInfo response: ${searchPacket.users.size} users")
android.util.Log.d("Protocol", "🔍 Search/UserInfo response: ${searchPacket.users.size} users")
// Обновляем информацию о пользователях в диалогах
if (searchPacket.users.isNotEmpty()) {
scope.launch {
scope.launch(Dispatchers.IO) { // 🔥 Запускаем на IO потоке для работы с БД
searchPacket.users.forEach { user ->
addLog(" 📝 Updating user info: ${user.publicKey.take(16)}... title='${user.title}' username='${user.username}'")
android.util.Log.d("Protocol", "📝 Updating user info: ${user.publicKey.take(16)}... title='${user.title}' username='${user.username}'")
messageRepository?.updateDialogUserInfo(
user.publicKey,
user.title,

View File

@@ -918,6 +918,16 @@ fun ChatsListScreen(
val requests = chatsState.requests
val requestsCount = chatsState.requestsCount
// 🔥 ИСПРАВЛЕНИЕ МЕРЦАНИЯ: Запоминаем, что контент УЖЕ был показан
// Это предотвращает показ EmptyState при временных пустых обновлениях
var hasShownContent by rememberSaveable { mutableStateOf(false) }
if (chatsState.hasContent) {
hasShownContent = true
}
// 🎯 Показываем Empty State только если контент НИКОГДА не показывался
val shouldShowEmptyState = chatsState.isEmpty && !hasShownContent
// 🎬 Animated content transition between main list and requests
AnimatedContent(
targetState = showRequestsScreen,
@@ -939,8 +949,8 @@ fun ChatsListScreen(
onUserSelect(user)
}
)
} else if (chatsState.isEmpty) {
// 🔥 Empty state - используем chatsState.isEmpty для атомарной проверки
} else if (shouldShowEmptyState) {
// 🔥 Empty state - показываем только если контент НЕ был показан ранее
EmptyChatsState(
isDarkTheme = isDarkTheme,
modifier = Modifier.fillMaxSize()
@@ -1553,33 +1563,48 @@ fun DialogItemContent(
}
// 📁 Для Saved Messages показываем специальное имя
// 🔥 Как в Архиве: title > username > "DELETED"
val displayName =
remember(dialog.opponentTitle, dialog.opponentUsername, dialog.opponentKey, dialog.isSavedMessages) {
if (dialog.isSavedMessages) {
"Saved Messages"
} else if (dialog.opponentTitle.isNotEmpty()) {
} else if (dialog.opponentTitle.isNotEmpty() &&
dialog.opponentTitle != dialog.opponentKey &&
dialog.opponentTitle != dialog.opponentKey.take(7) &&
dialog.opponentTitle != dialog.opponentKey.take(8)) {
// 🔥 Показываем title как основное имя (как в десктопной версии)
dialog.opponentTitle
// Обрезаем до 15 символов как в Архиве
if (dialog.opponentTitle.length > 15) {
dialog.opponentTitle.take(15) + "..."
} else {
dialog.opponentTitle
}
} else if (dialog.opponentUsername.isNotEmpty()) {
// Username только если нет title
"@${dialog.opponentUsername}"
} else {
dialog.opponentKey.take(8)
// 🔥 Как в Архиве - если нет информации, показываем часть ключа
dialog.opponentKey.take(7)
}
}
// 📁 Для Saved Messages показываем иконку закладки
// 🔥 Как в Архиве: инициалы из title или username или DELETED
val initials =
remember(dialog.opponentTitle, dialog.opponentUsername, dialog.opponentKey, dialog.isSavedMessages) {
if (dialog.isSavedMessages) {
"" // Для Saved Messages - пустая строка, будет использоваться иконка
} else if (dialog.opponentTitle.isNotEmpty()) {
} else if (dialog.opponentTitle.isNotEmpty() &&
dialog.opponentTitle != dialog.opponentKey &&
dialog.opponentTitle != dialog.opponentKey.take(7) &&
dialog.opponentTitle != dialog.opponentKey.take(8)) {
// Используем title для инициалов
dialog.opponentTitle
.split(" ")
.take(2)
.mapNotNull { it.firstOrNull()?.uppercase() }
.joinToString("")
.ifEmpty { dialog.opponentTitle.take(2).uppercase() }
} else if (dialog.opponentUsername.isNotEmpty()) {
// Если только username - берем первые 2 символа
dialog.opponentUsername.take(2).uppercase()

View File

@@ -80,15 +80,18 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
val requestsCount: StateFlow<Int> = _requestsCount.asStateFlow()
// 🔥 НОВОЕ: Комбинированное состояние - обновляется атомарно!
// 🎯 ИСПРАВЛЕНИЕ: debounce предотвращает мерцание при быстрых обновлениях
val chatsState: StateFlow<ChatsUiState> = combine(
_dialogs,
_requests,
_requestsCount
) { dialogs, requests, count ->
ChatsUiState(dialogs, requests, count)
}.stateIn(
}
.distinctUntilChanged() // 🔥 Игнорируем дублирующиеся состояния
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5000),
SharingStarted.Eagerly, // 🔥 КРИТИЧНО: Eagerly вместо WhileSubscribed - сразу начинаем следить
ChatsUiState()
)
@@ -156,7 +159,7 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
}
}
.flowOn(Dispatchers.Default) // 🚀 map выполняется на Default (CPU)
.flowOn(Dispatchers.Main) // 🎯 КРИТИЧНО: Обновляем UI на главном потоке!
.distinctUntilChanged() // 🔥 ИСПРАВЛЕНИЕ: Игнорируем дублирующиеся списки
.collect { decryptedDialogs ->
_dialogs.value = decryptedDialogs
@@ -215,6 +218,7 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
}
}
.flowOn(Dispatchers.Default)
.distinctUntilChanged() // 🔥 ИСПРАВЛЕНИЕ: Игнорируем дублирующиеся списки
.collect { decryptedRequests ->
_requests.value = decryptedRequests
}
@@ -224,6 +228,7 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
viewModelScope.launch {
dialogDao.getRequestsCountFlow(publicKey)
.flowOn(Dispatchers.IO)
.distinctUntilChanged() // 🔥 ИСПРАВЛЕНИЕ: Игнорируем дублирующиеся значения
.collect { count ->
_requestsCount.value = count
}