feat: Add block/unblock functionality and profile menu in OtherProfileScreen

This commit is contained in:
k1ngsterr1
2026-01-22 18:51:14 +05:00
parent d0f6de1772
commit 538181cf39
2 changed files with 132 additions and 106 deletions

View File

@@ -770,6 +770,35 @@ fun ProfilePhotoMenu(
}
}
/** Other user profile menu with block option */
@Composable
fun OtherProfileMenu(
expanded: Boolean,
onDismiss: () -> Unit,
isDarkTheme: Boolean,
isBlocked: Boolean,
onBlockClick: () -> Unit
) {
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismiss,
modifier = Modifier.width(220.dp),
properties = PopupProperties(
focusable = true,
dismissOnBackPress = true,
dismissOnClickOutside = true
)
) {
ProfilePhotoMenuItem(
icon = if (isBlocked) Icons.Default.CheckCircle else Icons.Default.Block,
text = if (isBlocked) "Unblock User" else "Block User",
onClick = onBlockClick,
tintColor = if (isBlocked) Color(0xFF4CAF50) else Color(0xFFFF3B30),
textColor = if (isBlocked) Color(0xFF4CAF50) else Color(0xFFFF3B30)
)
}
}
@Composable
private fun ProfilePhotoMenuItem(
icon: ImageVector,

View File

@@ -29,6 +29,7 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
@@ -140,19 +141,6 @@ fun OtherProfileScreen(
isDarkTheme = isDarkTheme
)
Spacer(modifier = Modifier.height(24.dp))
// ═══════════════════════════════════════════════════════════
// 🚫 BLOCK SECTION
// ═══════════════════════════════════════════════════════════
TelegramSectionTitle(title = "Privacy", isDarkTheme = isDarkTheme)
TelegramBlockItem(
isBlocked = isBlocked,
onToggle = { isBlocked = !isBlocked },
isDarkTheme = isDarkTheme
)
Spacer(modifier = Modifier.height(32.dp))
}
}
@@ -169,7 +157,9 @@ fun OtherProfileScreen(
onBack = onBack,
isDarkTheme = isDarkTheme,
showAvatarMenu = showAvatarMenu,
onAvatarMenuChange = { showAvatarMenu = it }
onAvatarMenuChange = { showAvatarMenu = it },
isBlocked = isBlocked,
onBlockToggle = { isBlocked = !isBlocked }
)
}
}
@@ -186,7 +176,9 @@ private fun CollapsingOtherProfileHeader(
onBack: () -> Unit,
isDarkTheme: Boolean,
showAvatarMenu: Boolean,
onAvatarMenuChange: (Boolean) -> Unit
onAvatarMenuChange: (Boolean) -> Unit,
isBlocked: Boolean,
onBlockToggle: () -> Unit
) {
val density = LocalDensity.current
val configuration = LocalConfiguration.current
@@ -208,108 +200,47 @@ private fun CollapsingOtherProfileHeader(
val avatarFontSize = androidx.compose.ui.unit.lerp(40.sp, 0.sp, collapseProgress)
// Text animation - always centered
val textX = screenWidthDp / 2 // Always center
val textExpandedY = statusBarHeight + 32.dp + AVATAR_SIZE_EXPANDED_OTHER + 48.dp
val textCollapsedY = statusBarHeight + (COLLAPSED_HEADER_HEIGHT_OTHER - 40.dp) / 2
val textCollapsedY = statusBarHeight + COLLAPSED_HEADER_HEIGHT_OTHER / 2
val textY = androidx.compose.ui.unit.lerp(textExpandedY, textCollapsedY, collapseProgress)
val nameFontSize = androidx.compose.ui.unit.lerp(24.sp, 17.sp, collapseProgress)
val usernameFontSize = androidx.compose.ui.unit.lerp(15.sp, 13.sp, collapseProgress)
val headerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White
val nameFontSize = androidx.compose.ui.unit.lerp(24.sp, 18.sp, collapseProgress)
val onlineFontSize = androidx.compose.ui.unit.lerp(14.sp, 13.sp, collapseProgress)
Box(
modifier = Modifier
.fillMaxWidth()
.height(headerHeight)
.background(headerColor)
.drawBehind {
drawRect(avatarColors.backgroundColor)
}
) {
// Background color
// ═══════════════════════════════════════════════════════════
// 🔙 BACK BUTTON
// ═══════════════════════════════════════════════════════════
Box(
modifier = Modifier
.fillMaxSize()
.background(headerColor)
)
// Avatar
if (avatarSize > 0.dp) {
Box(
modifier = Modifier
.offset {
IntOffset(
with(density) { avatarCenterX.toPx().roundToInt() },
with(density) { avatarY.toPx().roundToInt() }
)
}
.size(avatarSize)
.clip(CircleShape)
.background(avatarColors.backgroundColor),
contentAlignment = Alignment.Center
) {
if (avatarFontSize.value > 0) {
Text(
text = getInitials(name),
fontSize = avatarFontSize,
fontWeight = FontWeight.Bold,
color = avatarColors.textColor
)
}
}
}
// Name and username - always centered
Column(
modifier = Modifier
.align(Alignment.TopCenter)
.offset(y = textY)
.graphicsLayer {
val centerOffsetY = with(density) {
androidx.compose.ui.unit.lerp(24.dp, 18.dp, collapseProgress).toPx()
}
translationY = -centerOffsetY
},
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = name,
fontSize = nameFontSize,
fontWeight = FontWeight.Bold,
color = if (isDarkTheme) Color.White else Color.Black,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
if (username.isNotEmpty() && collapseProgress < 0.9f) {
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "@$username",
fontSize = usernameFontSize,
color = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.graphicsLayer {
alpha = 1f - (collapseProgress * 2f).coerceIn(0f, 1f)
}
)
}
}
// Back button
IconButton(
onClick = onBack,
modifier = Modifier
.padding(top = statusBarHeight)
.padding(start = 4.dp, top = 4.dp)
.size(48.dp),
contentAlignment = Alignment.Center
) {
IconButton(
onClick = onBack,
modifier = Modifier.size(48.dp)
) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Back",
tint = if (isDarkTheme) Color.White else Color.Black
tint = Color.White,
modifier = Modifier.size(24.dp)
)
}
}
// Menu button (top right corner)
// ═══════════════════════════════════════════════════════════
// ⋮ MENU BUTTON (top right corner)
// ═══════════════════════════════════════════════════════════
Box(
modifier = Modifier
.align(Alignment.TopEnd)
@@ -325,22 +256,88 @@ private fun CollapsingOtherProfileHeader(
Icon(
imageVector = Icons.Default.MoreVert,
contentDescription = "Profile menu",
tint = if (isDarkTheme) Color.White else Color.Black,
tint = Color.White,
modifier = Modifier.size(24.dp)
)
}
// Меню для установки фото профиля
com.rosetta.messenger.ui.chats.components.ProfilePhotoMenu(
// Меню с блокировкой пользователя
com.rosetta.messenger.ui.chats.components.OtherProfileMenu(
expanded = showAvatarMenu,
onDismiss = { onAvatarMenuChange(false) },
isDarkTheme = isDarkTheme,
onSetPhotoClick = {
isBlocked = isBlocked,
onBlockClick = {
onAvatarMenuChange(false)
// TODO: Реализовать выбор фото профиля
onBlockToggle()
}
)
}
// ═══════════════════════════════════════════════════════════
// 👤 AVATAR - shrinks and moves up
// ═══════════════════════════════════════════════════════════
if (avatarSize > 1.dp) {
Box(
modifier = Modifier
.offset(
x = avatarCenterX + (AVATAR_SIZE_EXPANDED_OTHER - avatarSize) / 2,
y = avatarY
)
.size(avatarSize)
.clip(CircleShape)
.background(Color.White.copy(alpha = 0.15f))
.padding(2.dp)
.clip(CircleShape)
.background(avatarColors.backgroundColor),
contentAlignment = Alignment.Center
) {
if (avatarFontSize > 1.sp) {
Text(
text = getInitials(name),
fontSize = avatarFontSize,
fontWeight = FontWeight.Bold,
color = avatarColors.textColor
)
}
}
}
// ═══════════════════════════════════════════════════════════
// 📝 TEXT BLOCK - Name + Online, always centered
// ═══════════════════════════════════════════════════════════
Column(
modifier = Modifier
.align(Alignment.TopCenter)
.offset(y = textY)
.graphicsLayer {
val centerOffsetY = with(density) {
androidx.compose.ui.unit.lerp(24.dp, 18.dp, collapseProgress).toPx()
}
translationY = -centerOffsetY
},
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = name,
fontSize = nameFontSize,
fontWeight = FontWeight.SemiBold,
color = Color.White,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.widthIn(max = 220.dp),
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(2.dp))
// Online text - always centered
Text(
text = "online",
fontSize = onlineFontSize,
color = Color(0xFF4CAF50)
)
}
}
}