feat: Enhance search functionality and user experience

- Added local account metadata handling in SearchScreen for improved "Saved Messages" search fallback.
- Updated search logic to include username and account name checks when searching for the user.
- Introduced search logging in SearchUsersViewModel for better debugging and tracking of search queries.
- Refactored image download process in AttachmentComponents to include detailed logging for debugging.
- Created AttachmentDownloadDebugLogger to manage and display download logs.
- Improved DeviceVerificationBanner UI for better user engagement during device verification.
- Adjusted OtherProfileScreen layout to enhance information visibility and user interaction.
- Updated network security configuration to include new Let's Encrypt certificate for CDN.
This commit is contained in:
2026-02-19 17:34:16 +05:00
parent cacd6dc029
commit 53d0e44ef8
26 changed files with 972 additions and 613 deletions

View File

@@ -89,7 +89,6 @@ import com.rosetta.messenger.ui.chats.components.ViewableImage
import com.rosetta.messenger.ui.components.AvatarImage
import com.rosetta.messenger.ui.components.BlurredAvatarBackground
import com.rosetta.messenger.ui.components.VerifiedBadge
import com.rosetta.messenger.ui.components.metaball.ProfileMetaballEffect
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import com.rosetta.messenger.utils.AttachmentFileManager
import com.rosetta.messenger.utils.AvatarFileManager
@@ -545,48 +544,7 @@ fun OtherProfileScreen(
) {
item {
// ═══════════════════════════════════════════════════════════
// 📋 INFORMATION SECTION — первый элемент
// ═══════════════════════════════════════════════════════════
TelegramSectionTitle(
title = "Information",
isDarkTheme = isDarkTheme,
color = PrimaryBlue
)
if (user.username.isNotBlank()) {
TelegramCopyField(
value = "@${user.username}",
fullValue = user.username,
label = "Username",
isDarkTheme = isDarkTheme
)
Divider(
color = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE0E0E0),
thickness = 0.5.dp,
modifier = Modifier.padding(start = 16.dp)
)
}
TelegramCopyField(
value = user.publicKey.take(16) + "..." + user.publicKey.takeLast(6),
fullValue = user.publicKey,
label = "Public Key",
isDarkTheme = isDarkTheme
)
// ═══════════════════════════════════════════════════════════
// Разделитель секций
// ═══════════════════════════════════════════════════════════
Box(
modifier = Modifier
.fillMaxWidth()
.height(8.dp)
.background(if (isDarkTheme) Color(0xFF0F0F0F) else Color(0xFFF0F0F0))
)
// ═══════════════════════════════════════════════════════════
// ✉️ WRITE MESSAGE + 📞 CALL BUTTONS
// ✉️ MESSAGE + 📞 CALL — первые элементы
// ═══════════════════════════════════════════════════════════
Spacer(modifier = Modifier.height(12.dp))
@@ -661,6 +619,47 @@ fun OtherProfileScreen(
Spacer(modifier = Modifier.height(12.dp))
// ═══════════════════════════════════════════════════════════
// Разделитель секций
// ═══════════════════════════════════════════════════════════
Box(
modifier = Modifier
.fillMaxWidth()
.height(8.dp)
.background(if (isDarkTheme) Color(0xFF0F0F0F) else Color(0xFFF0F0F0))
)
// ═══════════════════════════════════════════════════════════
// 📋 INFORMATION SECTION — после кнопок
// ═══════════════════════════════════════════════════════════
TelegramSectionTitle(
title = "Information",
isDarkTheme = isDarkTheme,
color = PrimaryBlue
)
if (user.username.isNotBlank()) {
TelegramCopyField(
value = "@${user.username}",
fullValue = user.username,
label = "Username",
isDarkTheme = isDarkTheme
)
Divider(
color = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE0E0E0),
thickness = 0.5.dp,
modifier = Modifier.padding(start = 16.dp)
)
}
TelegramCopyField(
value = user.publicKey.take(16) + "..." + user.publicKey.takeLast(6),
fullValue = user.publicKey,
label = "Public Key",
isDarkTheme = isDarkTheme
)
// ═══════════════════════════════════════════════════════════
// 🔔 NOTIFICATIONS SECTION
// ═══════════════════════════════════════════════════════════
@@ -1825,7 +1824,6 @@ private fun CollapsingOtherProfileHeader(
// ═══════════════════════════════════════════════════════════
// 👤 AVATAR — Telegram-style expansion on pull-down
// При скролле вверх: metaball merge с Dynamic Island
// При свайпе вниз: аватарка раскрывается на весь блок (circle → rect)
// ═══════════════════════════════════════════════════════════
val avatarSize = androidx.compose.ui.unit.lerp(
@@ -1851,80 +1849,37 @@ private fun CollapsingOtherProfileHeader(
val expandedAvatarXPx = with(density) { expandedAvatarX.toPx() }
val expandedAvatarYPx = with(density) { expandedAvatarY.toPx() }
// Metaball alpha: visible only when NOT expanding (normal collapse animation)
val metaballAlpha = (1f - expandFraction * 10f).coerceIn(0f, 1f)
// Expansion avatar alpha: visible when expanding
val expansionAvatarAlpha = (expandFraction * 10f).coerceIn(0f, 1f)
// Layer 1: Metaball effect for normal collapse (fades out when expanding)
if (metaballAlpha > 0.01f) {
Box(modifier = Modifier.fillMaxSize().graphicsLayer { alpha = metaballAlpha }) {
ProfileMetaballEffect(
collapseProgress = collapseProgress,
expansionProgress = 0f,
statusBarHeight = statusBarHeight,
headerHeight = headerHeight,
hasAvatar = hasAvatar,
avatarColor = avatarColors.backgroundColor,
modifier = Modifier.fillMaxSize()
) {
if (hasAvatar && avatarRepository != null) {
OtherProfileFullSizeAvatar(
publicKey = publicKey,
avatarRepository = avatarRepository,
isDarkTheme = isDarkTheme
)
} else {
Box(
modifier = Modifier.fillMaxSize().background(avatarColors.backgroundColor),
contentAlignment = Alignment.Center
) {
Text(
text = getInitials(name),
fontSize = avatarFontSize,
fontWeight = FontWeight.Bold,
color = avatarColors.textColor
)
Box(
modifier = Modifier
.size(width = expandedAvatarWidth, height = expandedAvatarHeight)
.graphicsLayer {
translationX = expandedAvatarXPx
translationY = expandedAvatarYPx
alpha = avatarAlpha
shape = RoundedCornerShape(cornerRadius)
clip = true
}
}
}
}
}
// Layer 2: Expanding avatar (fades in when pulling down)
if (expansionAvatarAlpha > 0.01f) {
Box(
modifier = Modifier
.size(width = expandedAvatarWidth, height = expandedAvatarHeight)
.graphicsLayer {
translationX = expandedAvatarXPx
translationY = expandedAvatarYPx
alpha = avatarAlpha * expansionAvatarAlpha
shape = RoundedCornerShape(cornerRadius)
clip = true
}
.background(avatarColors.backgroundColor),
contentAlignment = Alignment.Center
) {
if (hasAvatar && avatarRepository != null) {
OtherProfileFullSizeAvatar(
publicKey = publicKey,
avatarRepository = avatarRepository,
isDarkTheme = isDarkTheme
)
} else {
Text(
text = getInitials(name),
fontSize = avatarFontSize,
fontWeight = FontWeight.Bold,
color = avatarColors.textColor
)
}
.background(avatarColors.backgroundColor),
contentAlignment = Alignment.Center
) {
if (hasAvatar && avatarRepository != null) {
OtherProfileFullSizeAvatar(
publicKey = publicKey,
avatarRepository = avatarRepository,
isDarkTheme = isDarkTheme
)
} else {
Text(
text = getInitials(name),
fontSize = avatarFontSize,
fontWeight = FontWeight.Bold,
color = avatarColors.textColor
)
}
}
// Gradient overlays when avatar is expanded
if (expansionAvatarAlpha > 0.01f) {
if (expandFraction > 0.01f) {
// Top gradient
Box(
modifier = Modifier

View File

@@ -1795,7 +1795,7 @@ fun TelegramToggleItem(
val textColor = if (isDarkTheme) Color.White else Color.Black
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
val iconColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
val accentColor = if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFF0D8CF4)
val accentColor = if (isDarkTheme) PrimaryBlueDark else Color(0xFF0D8CF4)
val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE0E0E0)
Column {