feat: Add block/unblock functionality and profile menu in OtherProfileScreen
This commit is contained in:
@@ -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
|
@Composable
|
||||||
private fun ProfilePhotoMenuItem(
|
private fun ProfilePhotoMenuItem(
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import androidx.compose.ui.platform.LocalConfiguration
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
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.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -140,19 +141,6 @@ fun OtherProfileScreen(
|
|||||||
isDarkTheme = isDarkTheme
|
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))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,7 +157,9 @@ fun OtherProfileScreen(
|
|||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
showAvatarMenu = showAvatarMenu,
|
showAvatarMenu = showAvatarMenu,
|
||||||
onAvatarMenuChange = { showAvatarMenu = it }
|
onAvatarMenuChange = { showAvatarMenu = it },
|
||||||
|
isBlocked = isBlocked,
|
||||||
|
onBlockToggle = { isBlocked = !isBlocked }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,7 +176,9 @@ private fun CollapsingOtherProfileHeader(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
isDarkTheme: Boolean,
|
isDarkTheme: Boolean,
|
||||||
showAvatarMenu: Boolean,
|
showAvatarMenu: Boolean,
|
||||||
onAvatarMenuChange: (Boolean) -> Unit
|
onAvatarMenuChange: (Boolean) -> Unit,
|
||||||
|
isBlocked: Boolean,
|
||||||
|
onBlockToggle: () -> Unit
|
||||||
) {
|
) {
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
val configuration = LocalConfiguration.current
|
val configuration = LocalConfiguration.current
|
||||||
@@ -208,108 +200,47 @@ private fun CollapsingOtherProfileHeader(
|
|||||||
val avatarFontSize = androidx.compose.ui.unit.lerp(40.sp, 0.sp, collapseProgress)
|
val avatarFontSize = androidx.compose.ui.unit.lerp(40.sp, 0.sp, collapseProgress)
|
||||||
|
|
||||||
// Text animation - always centered
|
// Text animation - always centered
|
||||||
val textX = screenWidthDp / 2 // Always center
|
|
||||||
|
|
||||||
val textExpandedY = statusBarHeight + 32.dp + AVATAR_SIZE_EXPANDED_OTHER + 48.dp
|
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 textY = androidx.compose.ui.unit.lerp(textExpandedY, textCollapsedY, collapseProgress)
|
||||||
|
|
||||||
val nameFontSize = androidx.compose.ui.unit.lerp(24.sp, 17.sp, collapseProgress)
|
val nameFontSize = androidx.compose.ui.unit.lerp(24.sp, 18.sp, collapseProgress)
|
||||||
val usernameFontSize = androidx.compose.ui.unit.lerp(15.sp, 13.sp, collapseProgress)
|
val onlineFontSize = androidx.compose.ui.unit.lerp(14.sp, 13.sp, collapseProgress)
|
||||||
|
|
||||||
val headerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(headerHeight)
|
.height(headerHeight)
|
||||||
.background(headerColor)
|
.drawBehind {
|
||||||
|
drawRect(avatarColors.backgroundColor)
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
// Background color
|
// ═══════════════════════════════════════════════════════════
|
||||||
|
// 🔙 BACK BUTTON
|
||||||
|
// ═══════════════════════════════════════════════════════════
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.padding(top = statusBarHeight)
|
||||||
.background(headerColor)
|
.padding(start = 4.dp, top = 4.dp)
|
||||||
)
|
.size(48.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
// 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(
|
IconButton(
|
||||||
text = name,
|
onClick = onBack,
|
||||||
fontSize = nameFontSize,
|
modifier = Modifier.size(48.dp)
|
||||||
fontWeight = FontWeight.Bold,
|
) {
|
||||||
color = if (isDarkTheme) Color.White else Color.Black,
|
Icon(
|
||||||
maxLines = 1,
|
imageVector = Icons.Filled.ArrowBack,
|
||||||
overflow = TextOverflow.Ellipsis
|
contentDescription = "Back",
|
||||||
)
|
tint = Color.White,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
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(
|
// ⋮ MENU BUTTON (top right corner)
|
||||||
onClick = onBack,
|
// ═══════════════════════════════════════════════════════════
|
||||||
modifier = Modifier
|
|
||||||
.padding(top = statusBarHeight)
|
|
||||||
.padding(start = 4.dp, top = 4.dp)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Filled.ArrowBack,
|
|
||||||
contentDescription = "Back",
|
|
||||||
tint = if (isDarkTheme) Color.White else Color.Black
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Menu button (top right corner)
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.TopEnd)
|
.align(Alignment.TopEnd)
|
||||||
@@ -325,22 +256,88 @@ private fun CollapsingOtherProfileHeader(
|
|||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.MoreVert,
|
imageVector = Icons.Default.MoreVert,
|
||||||
contentDescription = "Profile menu",
|
contentDescription = "Profile menu",
|
||||||
tint = if (isDarkTheme) Color.White else Color.Black,
|
tint = Color.White,
|
||||||
modifier = Modifier.size(24.dp)
|
modifier = Modifier.size(24.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Меню для установки фото профиля
|
// Меню с блокировкой пользователя
|
||||||
com.rosetta.messenger.ui.chats.components.ProfilePhotoMenu(
|
com.rosetta.messenger.ui.chats.components.OtherProfileMenu(
|
||||||
expanded = showAvatarMenu,
|
expanded = showAvatarMenu,
|
||||||
onDismiss = { onAvatarMenuChange(false) },
|
onDismiss = { onAvatarMenuChange(false) },
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
onSetPhotoClick = {
|
isBlocked = isBlocked,
|
||||||
|
onBlockClick = {
|
||||||
onAvatarMenuChange(false)
|
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)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user