Исправил переход по своему тэгу в группах и убрал лишнюю подсветку
- Клик по своему упоминанию теперь сразу открывает 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
|
if (screen is Screen.Requests && navStack.any { it is Screen.Requests }) return
|
||||||
navStack = navStack + screen
|
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() {
|
fun popScreen() {
|
||||||
navStack = navStack.dropLast(1)
|
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() {
|
fun popProfileAndChildren() {
|
||||||
navStack =
|
navStack =
|
||||||
navStack.filterNot {
|
navStack.filterNot {
|
||||||
@@ -977,9 +1002,9 @@ fun MainScreen(
|
|||||||
totalUnreadFromOthers = totalUnreadFromOthers,
|
totalUnreadFromOthers = totalUnreadFromOthers,
|
||||||
onBack = { popChatAndChildren() },
|
onBack = { popChatAndChildren() },
|
||||||
onUserProfileClick = { user ->
|
onUserProfileClick = { user ->
|
||||||
if (user.publicKey == accountPublicKey) {
|
if (isCurrentAccountUser(user)) {
|
||||||
// Свой профиль — открываем My Profile
|
// Свой профиль — открываем My Profile
|
||||||
pushScreen(Screen.Profile)
|
openOwnProfile()
|
||||||
} else {
|
} else {
|
||||||
// Открываем профиль другого пользователя
|
// Открываем профиль другого пользователя
|
||||||
pushScreen(Screen.OtherProfile(user))
|
pushScreen(Screen.OtherProfile(user))
|
||||||
@@ -1025,8 +1050,8 @@ fun MainScreen(
|
|||||||
avatarRepository = avatarRepository,
|
avatarRepository = avatarRepository,
|
||||||
onBack = { navStack = navStack.filterNot { it is Screen.GroupInfo } },
|
onBack = { navStack = navStack.filterNot { it is Screen.GroupInfo } },
|
||||||
onMemberClick = { member ->
|
onMemberClick = { member ->
|
||||||
if (member.publicKey == accountPublicKey) {
|
if (isCurrentAccountUser(member)) {
|
||||||
pushScreen(Screen.Profile)
|
openOwnProfile()
|
||||||
} else {
|
} else {
|
||||||
pushScreen(Screen.OtherProfile(member))
|
pushScreen(Screen.OtherProfile(member))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2322,6 +2322,10 @@ fun ChatDetailScreen(
|
|||||||
username.trim().trimStart('@').lowercase(Locale.ROOT)
|
username.trim().trimStart('@').lowercase(Locale.ROOT)
|
||||||
if (normalizedUsername.isBlank()) return@MessageBubble
|
if (normalizedUsername.isBlank()) return@MessageBubble
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
val normalizedCurrentUsername =
|
||||||
|
currentUserUsername.trim().trimStart('@').lowercase(Locale.ROOT)
|
||||||
|
val normalizedOpponentUsername =
|
||||||
|
user.username.trim().trimStart('@').lowercase(Locale.ROOT)
|
||||||
val targetPublicKey =
|
val targetPublicKey =
|
||||||
mentionCandidates
|
mentionCandidates
|
||||||
.firstOrNull {
|
.firstOrNull {
|
||||||
@@ -2331,22 +2335,34 @@ fun ChatDetailScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
?.publicKey
|
?.publicKey
|
||||||
|
?.trim()
|
||||||
?.takeIf { it.isNotBlank() }
|
?.takeIf { it.isNotBlank() }
|
||||||
?: run {
|
?: run {
|
||||||
val normalizedCurrent =
|
if (normalizedCurrentUsername == normalizedUsername && currentUserPublicKey.isNotBlank()) {
|
||||||
currentUserUsername.trim().trimStart('@').lowercase(Locale.ROOT)
|
currentUserPublicKey.trim()
|
||||||
if (normalizedCurrent == normalizedUsername && currentUserPublicKey.isNotBlank()) {
|
|
||||||
currentUserPublicKey
|
|
||||||
} else {
|
} else {
|
||||||
val normalizedOpponent =
|
if (normalizedOpponentUsername == normalizedUsername && user.publicKey.isNotBlank()) user.publicKey.trim()
|
||||||
user.username.trim().trimStart('@').lowercase(Locale.ROOT)
|
|
||||||
if (normalizedOpponent == normalizedUsername && user.publicKey.isNotBlank()) user.publicKey
|
|
||||||
else ""
|
else ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetPublicKey.isBlank()) return@launch
|
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)
|
val resolvedUser = viewModel.resolveUserForProfile(targetPublicKey)
|
||||||
if (resolvedUser != null) {
|
if (resolvedUser != null) {
|
||||||
showContextMenu = false
|
showContextMenu = false
|
||||||
|
|||||||
@@ -90,22 +90,6 @@ import kotlinx.coroutines.withContext
|
|||||||
* organization
|
* 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 для текста сообщения с временем. Если текст + время помещаются в одну
|
* Telegram-style layout для текста сообщения с временем. Если текст + время помещаются в одну
|
||||||
* строку - располагает их рядом. Если текст длинный и переносится - время встаёт в правый нижний
|
* строку - располагает их рядом. Если текст длинный и переносится - время встаёт в правый нижний
|
||||||
@@ -361,21 +345,10 @@ fun MessageBubble(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Colors
|
// 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 =
|
val bubbleColor =
|
||||||
remember(message.isOutgoing, isDarkTheme, isMentionedIncoming) {
|
remember(message.isOutgoing, isDarkTheme) {
|
||||||
if (message.isOutgoing) {
|
if (message.isOutgoing) {
|
||||||
PrimaryBlue
|
PrimaryBlue
|
||||||
} else if (isMentionedIncoming) {
|
|
||||||
if (isDarkTheme) Color(0xFF3A3422) else Color(0xFFFFF3CD)
|
|
||||||
} else {
|
} else {
|
||||||
if (isDarkTheme) Color(0xFF212121) else Color(0xFFF5F5F5)
|
if (isDarkTheme) Color(0xFF212121) else Color(0xFFF5F5F5)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import androidx.compose.runtime.*
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
@@ -35,6 +36,11 @@ fun VerifiedBadge(
|
|||||||
if (verified <= 0) return
|
if (verified <= 0) return
|
||||||
|
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
val inlineOffsetY: Dp = when {
|
||||||
|
size <= 16 -> (-0.5).dp
|
||||||
|
size <= 20 -> (-1).dp
|
||||||
|
else -> 0.dp
|
||||||
|
}
|
||||||
|
|
||||||
// Цвет верификации: в тёмной теме — как индикаторы прочтения (PrimaryBlue), в светлой — #ACD2F9
|
// Цвет верификации: в тёмной теме — как индикаторы прочтения (PrimaryBlue), в светлой — #ACD2F9
|
||||||
val badgeColor =
|
val badgeColor =
|
||||||
@@ -58,6 +64,7 @@ fun VerifiedBadge(
|
|||||||
contentDescription = "Verified",
|
contentDescription = "Verified",
|
||||||
tint = badgeColor,
|
tint = badgeColor,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
.offset(y = inlineOffsetY)
|
||||||
.size(size.dp)
|
.size(size.dp)
|
||||||
.clickable { showDialog = true }
|
.clickable { showDialog = true }
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user