feat: Enhance logging and state management in ChatsList and MessageRepository for improved user experience
This commit is contained in:
@@ -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)}...")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 как основное имя (как в десктопной версии)
|
||||
// Обрезаем до 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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user