Исправил переход по своему тэгу в группах и убрал лишнюю подсветку
- Клик по своему упоминанию теперь сразу открывает My Profile без экрана OtherProfile и kebab-меню\n- Нормализовал сравнение аккаунта по publicKey/username (trim + ignoreCase)\n- Убрал жёлтую подсветку сообщений с упоминанием в группах\n- Подровнял положение бейджа верификации рядом с именем
This commit is contained in:
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user