feat: implement swipe back navigation and integrate VerifiedBadge in chat dialogs
This commit is contained in:
@@ -2115,6 +2115,7 @@ fun ChatDetailScreen(
|
||||
dialogs = dialogsList,
|
||||
isDarkTheme = isDarkTheme,
|
||||
currentUserPublicKey = currentUserPublicKey,
|
||||
avatarRepository = avatarRepository,
|
||||
onDismiss = {
|
||||
showForwardPicker = false
|
||||
ForwardManager.clear()
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.rosetta.messenger.network.ProtocolState
|
||||
import com.rosetta.messenger.ui.components.AppleEmojiText
|
||||
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.onboarding.PrimaryBlue
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
@@ -1807,15 +1808,23 @@ fun DialogItemContent(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = displayName,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 16.sp,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = displayName,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 16.sp,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
if (dialog.verified > 0) {
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
VerifiedBadge(verified = dialog.verified, size = 16)
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
@@ -1932,6 +1941,7 @@ fun DialogItemContent(
|
||||
dialog.lastMessageAttachmentType == "Photo" -> "Photo"
|
||||
dialog.lastMessageAttachmentType == "File" -> "File"
|
||||
dialog.lastMessageAttachmentType == "Avatar" -> "Avatar"
|
||||
dialog.lastMessageAttachmentType == "Forwarded" -> "Forwarded message"
|
||||
dialog.lastMessage.isEmpty() -> "No messages"
|
||||
else -> dialog.lastMessage
|
||||
}
|
||||
|
||||
@@ -174,6 +174,7 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
|
||||
val type = firstAttachment.optInt("type", -1)
|
||||
when (type) {
|
||||
0 -> "Photo" // AttachmentType.IMAGE = 0
|
||||
1 -> "Forwarded" // AttachmentType.MESSAGES = 1
|
||||
2 -> "File" // AttachmentType.FILE = 2
|
||||
3 -> "Avatar" // AttachmentType.AVATAR = 3
|
||||
else -> null
|
||||
@@ -261,6 +262,7 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
|
||||
val type = firstAttachment.optInt("type", -1)
|
||||
when (type) {
|
||||
0 -> "Photo" // AttachmentType.IMAGE = 0
|
||||
1 -> "Forwarded" // AttachmentType.MESSAGES = 1
|
||||
2 -> "File" // AttachmentType.FILE = 2
|
||||
3 -> "Avatar" // AttachmentType.AVATAR = 3
|
||||
else -> null
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.rosetta.messenger.ui.chats
|
||||
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -17,11 +18,15 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.rosetta.messenger.data.ForwardManager
|
||||
import com.rosetta.messenger.repository.AvatarRepository
|
||||
import com.rosetta.messenger.ui.components.AppleEmojiText
|
||||
import com.rosetta.messenger.ui.components.AvatarImage
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -39,11 +44,13 @@ fun ForwardChatPickerBottomSheet(
|
||||
dialogs: List<DialogUiModel>,
|
||||
isDarkTheme: Boolean,
|
||||
currentUserPublicKey: String,
|
||||
avatarRepository: AvatarRepository? = null,
|
||||
onDismiss: () -> Unit,
|
||||
onChatSelected: (DialogUiModel) -> Unit
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = false)
|
||||
val scope = rememberCoroutineScope()
|
||||
val view = LocalView.current
|
||||
|
||||
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color.White
|
||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||
@@ -53,6 +60,11 @@ fun ForwardChatPickerBottomSheet(
|
||||
val forwardMessages by ForwardManager.forwardMessages.collectAsState()
|
||||
val messagesCount = forwardMessages.size
|
||||
|
||||
// 🔥 Haptic feedback при открытии
|
||||
LaunchedEffect(Unit) {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
}
|
||||
|
||||
// 🔥 Функция для красивого закрытия с анимацией
|
||||
fun dismissWithAnimation() {
|
||||
scope.launch {
|
||||
@@ -65,6 +77,7 @@ fun ForwardChatPickerBottomSheet(
|
||||
onDismissRequest = { dismissWithAnimation() },
|
||||
sheetState = sheetState,
|
||||
containerColor = backgroundColor,
|
||||
scrimColor = Color.Black.copy(alpha = 0.6f), // 🔥 Более тёмный overlay - перекрывает status bar
|
||||
dragHandle = {
|
||||
// Кастомный handle
|
||||
Column(
|
||||
@@ -85,7 +98,8 @@ fun ForwardChatPickerBottomSheet(
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
},
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||
modifier = Modifier.statusBarsPadding() // 🔥 Учитываем status bar
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth().navigationBarsPadding()) {
|
||||
// Header
|
||||
@@ -170,6 +184,7 @@ fun ForwardChatPickerBottomSheet(
|
||||
dialog = dialog,
|
||||
isDarkTheme = isDarkTheme,
|
||||
isSavedMessages = dialog.opponentKey == currentUserPublicKey,
|
||||
avatarRepository = avatarRepository,
|
||||
onClick = { onChatSelected(dialog) }
|
||||
)
|
||||
|
||||
@@ -197,16 +212,12 @@ private fun ForwardDialogItem(
|
||||
dialog: DialogUiModel,
|
||||
isDarkTheme: Boolean,
|
||||
isSavedMessages: Boolean = false,
|
||||
avatarRepository: AvatarRepository? = null,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||
|
||||
val avatarColors =
|
||||
remember(dialog.opponentKey, isDarkTheme) {
|
||||
getAvatarColor(dialog.opponentKey, isDarkTheme)
|
||||
}
|
||||
|
||||
val displayName =
|
||||
remember(dialog.opponentTitle, dialog.opponentUsername, dialog.opponentKey, isSavedMessages) {
|
||||
when {
|
||||
@@ -217,21 +228,14 @@ private fun ForwardDialogItem(
|
||||
}
|
||||
}
|
||||
|
||||
val initials =
|
||||
remember(dialog.opponentTitle, dialog.opponentUsername, dialog.opponentKey, isSavedMessages) {
|
||||
when {
|
||||
isSavedMessages -> "📁"
|
||||
dialog.opponentTitle.isNotEmpty() -> {
|
||||
dialog.opponentTitle
|
||||
.split(" ")
|
||||
.take(2)
|
||||
.mapNotNull { it.firstOrNull()?.uppercase() }
|
||||
.joinToString("")
|
||||
}
|
||||
dialog.opponentUsername.isNotEmpty() -> dialog.opponentUsername.take(2).uppercase()
|
||||
else -> dialog.opponentKey.take(2).uppercase()
|
||||
}
|
||||
}
|
||||
// Display name for avatar initials
|
||||
val avatarDisplayName = remember(dialog.opponentTitle, dialog.opponentUsername) {
|
||||
when {
|
||||
dialog.opponentTitle.isNotEmpty() -> dialog.opponentTitle
|
||||
dialog.opponentUsername.isNotEmpty() -> dialog.opponentUsername
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier =
|
||||
@@ -240,22 +244,34 @@ private fun ForwardDialogItem(
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Avatar
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.background(
|
||||
if (isSavedMessages) PrimaryBlue.copy(alpha = 0.15f)
|
||||
else avatarColors.backgroundColor
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = initials,
|
||||
color = if (isSavedMessages) PrimaryBlue else avatarColors.textColor,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 16.sp
|
||||
// Avatar with real image support
|
||||
if (isSavedMessages) {
|
||||
// Saved Messages - special icon
|
||||
val avatarColors = remember(dialog.opponentKey, isDarkTheme) {
|
||||
getAvatarColor(dialog.opponentKey, isDarkTheme)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.background(PrimaryBlue.copy(alpha = 0.15f)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "📁",
|
||||
color = PrimaryBlue,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Regular user - use AvatarImage with real avatar support
|
||||
AvatarImage(
|
||||
publicKey = dialog.opponentKey,
|
||||
avatarRepository = avatarRepository,
|
||||
size = 48.dp,
|
||||
isDarkTheme = isDarkTheme,
|
||||
displayName = avatarDisplayName
|
||||
)
|
||||
}
|
||||
|
||||
@@ -274,14 +290,23 @@ private fun ForwardDialogItem(
|
||||
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
|
||||
Text(
|
||||
text =
|
||||
if (isSavedMessages) "Your personal notes"
|
||||
else dialog.lastMessage.ifEmpty { "No messages" },
|
||||
// 📎 Определяем текст превью с учетом attachments
|
||||
val previewText = when {
|
||||
isSavedMessages -> "Your personal notes"
|
||||
dialog.lastMessageAttachmentType == "Photo" -> "Photo"
|
||||
dialog.lastMessageAttachmentType == "File" -> "File"
|
||||
dialog.lastMessageAttachmentType == "Avatar" -> "Avatar"
|
||||
dialog.lastMessageAttachmentType == "Forwarded" -> "Forwarded message"
|
||||
dialog.lastMessage.isNotEmpty() -> dialog.lastMessage
|
||||
else -> "No messages"
|
||||
}
|
||||
|
||||
AppleEmojiText(
|
||||
text = previewText,
|
||||
fontSize = 14.sp,
|
||||
color = secondaryTextColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
enableLinks = false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user