feat: Implement modern popup menu with iOS/Telegram style; enhance user interaction with smooth animations and improved design

This commit is contained in:
k1ngsterr1
2026-01-16 23:13:10 +05:00
parent 431e3755c6
commit da065ef7f7

View File

@@ -43,6 +43,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.StrokeJoin
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.path
@@ -822,113 +823,41 @@ fun ChatDetailScreen(
)
}
// Выпадающее меню - чистый дизайн без артефактов
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(
surface = inputBackgroundColor
)
// 🔥 Современное выпадающее меню в стиле iOS/Telegram
ModernPopupMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false },
isDarkTheme = isDarkTheme
) {
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false },
modifier = Modifier
.width(220.dp)
.background(
color = inputBackgroundColor,
shape = RoundedCornerShape(16.dp)
)
) {
// Delete Chat - красный
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = 4.dp)
) {
Icon(
Icons.Default.Delete,
contentDescription = null,
tint = Color(0xFFE53935),
modifier = Modifier.size(22.dp)
)
Spacer(modifier = Modifier.width(14.dp))
Text(
"Delete Chat",
color = Color(0xFFE53935),
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
}
},
// Block/Unblock User (не показываем для Saved Messages)
if (!isSavedMessages) {
ModernMenuItem(
icon = if (isBlocked) Icons.Default.CheckCircle else Icons.Default.Block,
text = if (isBlocked) "Unblock User" else "Block User",
onClick = {
showMenu = false
showDeleteConfirm = true
if (isBlocked) {
showUnblockConfirm = true
} else {
showBlockConfirm = true
}
},
modifier = Modifier.padding(horizontal = 8.dp)
.background(inputBackgroundColor),
colors = MenuDefaults.itemColors(
textColor = Color(0xFFE53935)
)
isDarkTheme = isDarkTheme,
tintColor = PrimaryBlue
)
}
// Delete Chat - деструктивное действие
ModernMenuItem(
icon = Icons.Default.Delete,
text = "Delete Chat",
onClick = {
showMenu = false
showDeleteConfirm = true
},
isDarkTheme = isDarkTheme,
isDestructive = true
)
// Разделитель
if (!isSavedMessages) {
Divider(
modifier = Modifier.padding(horizontal = 16.dp),
thickness = 0.5.dp,
color = if (isDarkTheme) Color.White.copy(alpha = 0.1f)
else Color.Black.copy(alpha = 0.08f)
)
}
// Block/Unblock User - синий (не показываем для Saved Messages)
if (!isSavedMessages) {
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = 4.dp)
) {
Icon(
if (isBlocked) Icons.Default.Check else Icons.Default.Block,
contentDescription = null,
tint = PrimaryBlue,
modifier = Modifier.size(22.dp)
)
Spacer(modifier = Modifier.width(14.dp))
Text(
if (isBlocked) "Unblock User" else "Block User",
color = PrimaryBlue,
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
}
},
onClick = {
showMenu = false
if (isBlocked) {
showUnblockConfirm = true
} else {
showBlockConfirm = true
}
},
modifier = Modifier.padding(horizontal = 8.dp)
.background(inputBackgroundColor),
colors = MenuDefaults.itemColors(
textColor = PrimaryBlue
)
)
// Разделитель
Divider(
modifier = Modifier.padding(horizontal = 16.dp),
thickness = 0.5.dp,
color = if (isDarkTheme) Color.White.copy(alpha = 0.1f)
else Color.Black.copy(alpha = 0.08f)
)
}
}
}
}
}
@@ -2697,3 +2626,118 @@ private fun SkeletonBubble(
)
}
}
/**
* 🔥 Современное выпадающее меню в стиле iOS/Telegram
* С blur эффектом, красивыми тенями и плавными анимациями
*/
@Composable
private fun ModernPopupMenu(
expanded: Boolean,
onDismissRequest: () -> Unit,
isDarkTheme: Boolean,
content: @Composable ColumnScope.() -> Unit
) {
// Анимация появления
val transition = updateTransition(targetState = expanded, label = "menu")
val scale by transition.animateFloat(
transitionSpec = {
if (targetState) {
spring(dampingRatio = 0.8f, stiffness = 400f)
} else {
tween(150, easing = FastOutSlowInEasing)
}
},
label = "scale"
) { if (it) 1f else 0.92f }
val alpha by transition.animateFloat(
transitionSpec = {
if (targetState) tween(200) else tween(100)
},
label = "alpha"
) { if (it) 1f else 0f }
// Цвета меню
val menuBackgroundColor = if (isDarkTheme) {
Color(0xFF2C2C2E) // iOS dark mode menu color
} else {
Color(0xFFFFFFFF)
}
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
modifier = Modifier
.graphicsLayer {
scaleX = scale
scaleY = scale
this.alpha = alpha
transformOrigin = TransformOrigin(0.85f, 0f)
}
.width(200.dp)
.shadow(
elevation = 16.dp,
shape = RoundedCornerShape(14.dp),
ambientColor = Color.Black.copy(alpha = 0.12f),
spotColor = Color.Black.copy(alpha = 0.08f)
)
.clip(RoundedCornerShape(14.dp))
.background(menuBackgroundColor)
) {
content()
}
}
/**
* 🔥 Современный элемент меню с иконкой
* Плавные hover эффекты и красивая типографика
*/
@Composable
private fun ModernMenuItem(
icon: ImageVector,
text: String,
onClick: () -> Unit,
isDarkTheme: Boolean,
tintColor: Color = if (isDarkTheme) Color.White else Color.Black,
isDestructive: Boolean = false
) {
val actualTintColor = if (isDestructive) Color(0xFFFF3B30) else tintColor // iOS red
val textColor = if (isDestructive) {
Color(0xFFFF3B30)
} else if (isDarkTheme) {
Color.White
} else {
Color.Black
}
// Hover/pressed состояние
val interactionSource = remember { MutableInteractionSource() }
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(
interactionSource = interactionSource,
indication = null
) { onClick() }
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = icon,
contentDescription = null,
tint = actualTintColor,
modifier = Modifier.size(22.dp)
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = text,
color = textColor,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
letterSpacing = (-0.2).sp // iOS стиль
)
}
}