From 429025537f750b85e70ea4a914e7ca8ff6d107fc Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Fri, 6 Mar 2026 20:03:50 +0500 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BF=D0=B5=D1=80=D0=B5=D1=85=D0=BE=D0=B4=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D1=81=D0=B2=D0=BE=D0=B5=D0=BC=D1=83=20=D1=82=D1=8D?= =?UTF-8?q?=D0=B3=D1=83=20=D0=B2=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D0=B0?= =?UTF-8?q?=D1=85=20=D0=B8=20=D1=83=D0=B1=D1=80=D0=B0=D0=BB=20=D0=BB=D0=B8?= =?UTF-8?q?=D1=88=D0=BD=D1=8E=D1=8E=20=D0=BF=D0=BE=D0=B4=D1=81=D0=B2=D0=B5?= =?UTF-8?q?=D1=82=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Клик по своему упоминанию теперь сразу открывает My Profile без экрана OtherProfile и kebab-меню\n- Нормализовал сравнение аккаунта по publicKey/username (trim + ignoreCase)\n- Убрал жёлтую подсветку сообщений с упоминанием в группах\n- Подровнял положение бейджа верификации рядом с именем --- .../com/rosetta/messenger/MainActivity.kt | 33 ++++++++++++++++--- .../messenger/ui/chats/ChatDetailScreen.kt | 32 +++++++++++++----- .../chats/components/ChatDetailComponents.kt | 29 +--------------- .../messenger/ui/components/VerifiedBadge.kt | 7 ++++ 4 files changed, 61 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/MainActivity.kt b/app/src/main/java/com/rosetta/messenger/MainActivity.kt index e88ee14..1ce0afc 100644 --- a/app/src/main/java/com/rosetta/messenger/MainActivity.kt +++ b/app/src/main/java/com/rosetta/messenger/MainActivity.kt @@ -640,9 +640,34 @@ fun MainScreen( if (screen is Screen.Requests && navStack.any { it is Screen.Requests }) return navStack = navStack + screen } + fun isCurrentAccountUser(user: SearchUser): Boolean { + val candidatePublicKey = user.publicKey.trim() + val normalizedAccountPublicKey = accountPublicKey.trim() + if ( + candidatePublicKey.isNotBlank() && + normalizedAccountPublicKey.isNotBlank() && + candidatePublicKey.equals(normalizedAccountPublicKey, ignoreCase = true) + ) { + return true + } + + val candidateUsername = user.username.trim().trimStart('@') + val normalizedAccountUsername = accountUsername.trim().trimStart('@') + return candidatePublicKey.isBlank() && + candidateUsername.isNotBlank() && + normalizedAccountUsername.isNotBlank() && + candidateUsername.equals(normalizedAccountUsername, ignoreCase = true) + } fun popScreen() { navStack = navStack.dropLast(1) } + fun openOwnProfile() { + navStack = + navStack.filterNot { + it is Screen.ChatDetail || it is Screen.OtherProfile || it is Screen.GroupInfo + } + pushScreen(Screen.Profile) + } fun popProfileAndChildren() { navStack = navStack.filterNot { @@ -977,9 +1002,9 @@ fun MainScreen( totalUnreadFromOthers = totalUnreadFromOthers, onBack = { popChatAndChildren() }, onUserProfileClick = { user -> - if (user.publicKey == accountPublicKey) { + if (isCurrentAccountUser(user)) { // Свой профиль — открываем My Profile - pushScreen(Screen.Profile) + openOwnProfile() } else { // Открываем профиль другого пользователя pushScreen(Screen.OtherProfile(user)) @@ -1025,8 +1050,8 @@ fun MainScreen( avatarRepository = avatarRepository, onBack = { navStack = navStack.filterNot { it is Screen.GroupInfo } }, onMemberClick = { member -> - if (member.publicKey == accountPublicKey) { - pushScreen(Screen.Profile) + if (isCurrentAccountUser(member)) { + openOwnProfile() } else { pushScreen(Screen.OtherProfile(member)) } 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 9f8a99f..82c4223 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 @@ -2322,6 +2322,10 @@ fun ChatDetailScreen( username.trim().trimStart('@').lowercase(Locale.ROOT) if (normalizedUsername.isBlank()) return@MessageBubble scope.launch { + val normalizedCurrentUsername = + currentUserUsername.trim().trimStart('@').lowercase(Locale.ROOT) + val normalizedOpponentUsername = + user.username.trim().trimStart('@').lowercase(Locale.ROOT) val targetPublicKey = mentionCandidates .firstOrNull { @@ -2331,22 +2335,34 @@ fun ChatDetailScreen( ) } ?.publicKey + ?.trim() ?.takeIf { it.isNotBlank() } - ?: run { - val normalizedCurrent = - currentUserUsername.trim().trimStart('@').lowercase(Locale.ROOT) - if (normalizedCurrent == normalizedUsername && currentUserPublicKey.isNotBlank()) { - currentUserPublicKey + ?: run { + if (normalizedCurrentUsername == normalizedUsername && currentUserPublicKey.isNotBlank()) { + currentUserPublicKey.trim() } else { - val normalizedOpponent = - user.username.trim().trimStart('@').lowercase(Locale.ROOT) - if (normalizedOpponent == normalizedUsername && user.publicKey.isNotBlank()) user.publicKey + if (normalizedOpponentUsername == normalizedUsername && user.publicKey.isNotBlank()) user.publicKey.trim() else "" } } if (targetPublicKey.isBlank()) return@launch + if (targetPublicKey.equals(currentUserPublicKey.trim(), ignoreCase = true)) { + showContextMenu = false + contextMenuMessage = null + onUserProfileClick( + SearchUser( + title = currentUserName.ifBlank { "You" }, + username = currentUserUsername.trim().trimStart('@'), + publicKey = currentUserPublicKey.trim(), + verified = 0, + online = 0 + ) + ) + return@launch + } + val resolvedUser = viewModel.resolveUserForProfile(targetPublicKey) if (resolvedUser != null) { showContextMenu = false diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt index f4ffb85..7dca103 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt @@ -90,22 +90,6 @@ import kotlinx.coroutines.withContext * organization */ -private fun containsUserMention(text: String, username: String): Boolean { - val normalizedUsername = username.trim().trimStart('@') - if (normalizedUsername.isBlank()) return false - val mentionRegex = - Regex( - pattern = """(^|\s)@${Regex.escape(normalizedUsername)}(?=\b)""", - options = setOf(RegexOption.IGNORE_CASE) - ) - return mentionRegex.containsMatchIn(text) -} - -private fun containsAllMention(text: String): Boolean { - val mentionRegex = Regex("""(^|\s)@all(?=\b)""", setOf(RegexOption.IGNORE_CASE)) - return mentionRegex.containsMatchIn(text) -} - /** * Telegram-style layout для текста сообщения с временем. Если текст + время помещаются в одну * строку - располагает их рядом. Если текст длинный и переносится - время встаёт в правый нижний @@ -361,21 +345,10 @@ fun MessageBubble( ) // Colors - val isMentionedIncoming = - remember(message.text, message.isOutgoing, isGroupChat, currentUserUsername) { - !message.isOutgoing && - isGroupChat && - message.text.isNotBlank() && - (containsAllMention(message.text) || - containsUserMention(message.text, currentUserUsername)) - } - val bubbleColor = - remember(message.isOutgoing, isDarkTheme, isMentionedIncoming) { + remember(message.isOutgoing, isDarkTheme) { if (message.isOutgoing) { PrimaryBlue - } else if (isMentionedIncoming) { - if (isDarkTheme) Color(0xFF3A3422) else Color(0xFFFFF3CD) } else { if (isDarkTheme) Color(0xFF212121) else Color(0xFFF5F5F5) } diff --git a/app/src/main/java/com/rosetta/messenger/ui/components/VerifiedBadge.kt b/app/src/main/java/com/rosetta/messenger/ui/components/VerifiedBadge.kt index db955f8..497a8ef 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/components/VerifiedBadge.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/components/VerifiedBadge.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -35,6 +36,11 @@ fun VerifiedBadge( if (verified <= 0) return var showDialog by remember { mutableStateOf(false) } + val inlineOffsetY: Dp = when { + size <= 16 -> (-0.5).dp + size <= 20 -> (-1).dp + else -> 0.dp + } // Цвет верификации: в тёмной теме — как индикаторы прочтения (PrimaryBlue), в светлой — #ACD2F9 val badgeColor = @@ -58,6 +64,7 @@ fun VerifiedBadge( contentDescription = "Verified", tint = badgeColor, modifier = modifier + .offset(y = inlineOffsetY) .size(size.dp) .clickable { showDialog = true } )