feat: add avatar presence handling in OtherProfileScreen for improved scroll behavior

This commit is contained in:
2026-02-14 03:13:19 +05:00
parent 2df37611e2
commit 9b0bde95d2
7 changed files with 70 additions and 12 deletions

View File

@@ -285,6 +285,31 @@ class MessageRepository private constructor(private val context: Context) {
// 🔥 КРИТИЧНО: Обновляем диалог через updateDialogFromMessages
dialogDao.updateDialogFromMessages(account, toPublicKey)
// 📁 Для saved messages - гарантируем создание/обновление dialog
if (isSavedMessages) {
val existing = dialogDao.getDialog(account, account)
dialogDao.insertDialog(
com.rosetta.messenger.database.DialogEntity(
id = existing?.id ?: 0,
account = account,
opponentKey = account,
opponentTitle = existing?.opponentTitle ?: "Saved Messages",
opponentUsername = existing?.opponentUsername ?: "",
lastMessage = encryptedPlainMessage,
lastMessageTimestamp = timestamp,
unreadCount = 0,
isOnline = 0,
lastSeen = existing?.lastSeen ?: 0,
verified = existing?.verified ?: 0,
iHaveSent = 1,
lastMessageFromMe = 1,
lastMessageDelivered = 1,
lastMessageRead = 1,
lastMessageAttachments = serializeAttachments(attachments)
)
)
}
// 🔥 Логируем что записалось в диалог
val dialog = dialogDao.getDialog(account, toPublicKey)
MessageLogger.logDialogUpdate(

View File

@@ -579,8 +579,11 @@ interface DialogDao {
*/
@Transaction
suspend fun updateDialogFromMessages(account: String, opponentKey: String) {
// 📁 Для saved messages dialogKey = account (не "$account:$account")
val dialogKey =
if (account < opponentKey) "$account:$opponentKey" else "$opponentKey:$account"
if (account == opponentKey) account
else if (account < opponentKey) "$account:$opponentKey"
else "$opponentKey:$account"
// 1. Получаем последнее сообщение — O(1) по индексу (account, dialog_key, timestamp)
val lastMsg = getLastMessageByDialogKey(account, dialogKey) ?: return
@@ -624,7 +627,8 @@ interface DialogDao {
*/
@Transaction
suspend fun updateSavedMessagesDialogFromMessages(account: String) {
val dialogKey = "$account:$account"
// 📁 dialogKey для saved messages = account (не "$account:$account")
val dialogKey = account
val lastMsg = getLastMessageByDialogKey(account, dialogKey) ?: return
val existing = getDialog(account, account)

View File

@@ -536,9 +536,11 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
// 🔥 Обновляем счетчик requests
_requestsCount.value = _requests.value.size
// Вычисляем правильный dialog_key (отсортированная комбинация ключей)
// Вычисляем правильный dialog_key
val dialogKey =
if (currentAccount < opponentKey) {
if (currentAccount == opponentKey) {
currentAccount // 📁 Saved Messages
} else if (currentAccount < opponentKey) {
"$currentAccount:$opponentKey"
} else {
"$opponentKey:$currentAccount"

View File

@@ -62,11 +62,15 @@ fun SearchScreen(
val view = LocalView.current
val focusManager = LocalFocusManager.current
if (!view.isInEditMode) {
SideEffect {
DisposableEffect(isDarkTheme) {
val window = (view.context as android.app.Activity).window
val insetsController = androidx.core.view.WindowCompat.getInsetsController(window, view)
insetsController.isAppearanceLightStatusBars = !isDarkTheme
window.statusBarColor = android.graphics.Color.TRANSPARENT
onDispose {
// Restore white status bar icons for chat list header
insetsController.isAppearanceLightStatusBars = false
}
}
}

View File

@@ -227,12 +227,28 @@ fun MessageInputBar(
},
hideEmoji = { onToggleEmojiPicker(false) }
)
} else {
// KEYBOARD → EMOJI
} else if (coordinator.isKeyboardVisible || coordinator.keyboardHeight > 0.dp) {
// KEYBOARD → EMOJI (клавиатура открыта)
coordinator.requestShowEmoji(
hideKeyboard = { imm.hideSoftInputFromWindow(view.windowToken, 0) },
showEmoji = { onToggleEmojiPicker(true) }
)
} else {
// Клавиатура НЕ открыта → открываем emoji напрямую
// Устанавливаем высоту из сохранённой или дефолт
if (coordinator.emojiHeight == 0.dp) {
val savedPx = com.rosetta.messenger.ui.components.KeyboardHeightProvider
.getSavedKeyboardHeight(context)
if (savedPx > 0) {
coordinator.emojiHeight = with(density) { savedPx.toDp() }
} else {
coordinator.emojiHeight = 300.dp // разумный дефолт
}
}
coordinator.isEmojiBoxVisible = true
coordinator.openEmojiOnly(
showEmoji = { onToggleEmojiPicker(true) }
)
}
}

View File

@@ -420,11 +420,18 @@ fun OtherProfileScreen(
}
}
// DEBUG LOGS
// Коллапс при отсутствии аватара
LaunchedEffect(hasAvatar) {
if (!hasAvatar) {
isPulledDown = false
overscrollOffset = 0f
}
}
// ═══════════════════════════════════════════════════════════════
// NESTED SCROLL - Telegram style with overscroll support
// ═══════════════════════════════════════════════════════════════
val nestedScrollConnection = remember {
val nestedScrollConnection = remember(hasAvatar) {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.y
@@ -474,8 +481,8 @@ fun OtherProfileScreen(
available: Offset,
source: NestedScrollSource
): Offset {
// Overscroll при свайпе вниз от верха
if (available.y > 0 && scrollOffset == 0f) {
// Overscroll при свайпе вниз от верха (только если есть аватар)
if (available.y > 0 && scrollOffset == 0f && hasAvatar) {
// Telegram: сопротивление если ещё не isPulledDown
val resistance = if (isPulledDown) 1f else 0.5f
val delta = available.y * resistance

View File

@@ -71,7 +71,7 @@ fun RosettaAndroidTheme(
val insetsController = WindowCompat.getInsetsController(window, view)
// Make status bar transparent for wave animation overlay
window.statusBarColor = AndroidColor.TRANSPARENT
insetsController.isAppearanceLightStatusBars = !darkTheme
// Note: isAppearanceLightStatusBars is managed per-screen, not globally
// Navigation bar: показываем только если есть нативные кнопки
NavigationModeUtils.applyNavigationBarVisibility(insetsController, context, darkTheme)