From c6f1998dc964b4a7705b17d52fae6c18dae9a5bc Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Sat, 31 Jan 2026 04:09:28 +0500 Subject: [PATCH] refactor: Clean up imports and improve code formatting in OtherProfileScreen --- .../ui/settings/OtherProfileScreen.kt | 509 +++++++++--------- 1 file changed, 255 insertions(+), 254 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt index b04e177..7a0a0b9 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt @@ -1,32 +1,21 @@ package com.rosetta.messenger.ui.settings import androidx.activity.compose.BackHandler -import androidx.compose.animation.core.Spring -import androidx.compose.animation.core.spring import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.outlined.Block import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.lifecycle.viewmodel.compose.viewModel -import com.rosetta.messenger.data.MessageRepository -import com.rosetta.messenger.database.RosettaDatabase -import com.rosetta.messenger.ui.chats.ChatViewModel -import com.rosetta.messenger.ui.chats.ChatsListViewModel -import com.rosetta.messenger.ui.onboarding.PrimaryBlue -import com.rosetta.messenger.ui.components.VerifiedBadge import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer @@ -41,17 +30,22 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController 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 import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.rosetta.messenger.data.MessageRepository +import com.rosetta.messenger.database.RosettaDatabase import com.rosetta.messenger.network.SearchUser import com.rosetta.messenger.repository.AvatarRepository +import com.rosetta.messenger.ui.chats.ChatViewModel +import com.rosetta.messenger.ui.chats.ChatsListViewModel 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 kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlin.math.roundToInt // Collapsing header constants private val EXPANDED_HEADER_HEIGHT_OTHER = 280.dp @@ -62,10 +56,10 @@ private val AVATAR_SIZE_COLLAPSED_OTHER = 36.dp @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @Composable fun OtherProfileScreen( - user: SearchUser, - isDarkTheme: Boolean, - onBack: () -> Unit, - avatarRepository: AvatarRepository? = null + user: SearchUser, + isDarkTheme: Boolean, + onBack: () -> Unit, + avatarRepository: AvatarRepository? = null ) { var isBlocked by remember { mutableStateOf(false) } var showAvatarMenu by remember { mutableStateOf(false) } @@ -75,91 +69,98 @@ fun OtherProfileScreen( val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666) val avatarColors = getAvatarColor(user.publicKey, isDarkTheme) val context = LocalContext.current - + // ๐Ÿ”ฅ ะŸะพะปัƒั‡ะฐะตะผ ั‚ะพั‚ ะถะต ChatViewModel ั‡ั‚ะพ ะธ ะฒ ChatDetailScreen ะดะปั ะพั‡ะธัั‚ะบะธ ะธัั‚ะพั€ะธะธ val viewModel: ChatViewModel = viewModel(key = "chat_${user.publicKey}") - + // ๐Ÿ”ฅ ChatsListViewModel ะดะปั ะฑะปะพะบะธั€ะพะฒะบะธ/ั€ะฐะทะฑะปะพะบะธั€ะพะฒะบะธ val chatsListViewModel: ChatsListViewModel = viewModel() val coroutineScope = rememberCoroutineScope() - + // ๐Ÿ”ฅ ะ—ะฐะณั€ัƒะถะฐะตะผ ัั‚ะฐั‚ัƒั ะฑะปะพะบะธั€ะพะฒะบะธ ะฟั€ะธ ะพั‚ะบั€ั‹ั‚ะธะธ ัะบั€ะฐะฝะฐ - LaunchedEffect(user.publicKey) { - isBlocked = chatsListViewModel.isUserBlocked(user.publicKey) - } - + LaunchedEffect(user.publicKey) { isBlocked = chatsListViewModel.isUserBlocked(user.publicKey) } + // ๐ŸŽน ะ”ะปั ะทะฐะบั€ั‹ั‚ะธั ะบะปะฐะฒะธะฐั‚ัƒั€ั‹ val keyboardController = LocalSoftwareKeyboardController.current val focusManager = LocalFocusManager.current - + // ๐Ÿ—‘๏ธ ะ”ะปั ัƒะดะฐะปะตะฝะธั ะดะธะฐะปะพะณะฐ val database = remember { RosettaDatabase.getDatabase(context) } - + // ๐Ÿ”ฅ ะ—ะฐะบั€ั‹ะฒะฐะตะผ ะบะปะฐะฒะธะฐั‚ัƒั€ัƒ ะฟั€ะธ ะพั‚ะบั€ั‹ั‚ะธะธ ัะบั€ะฐะฝะฐ LaunchedEffect(Unit) { keyboardController?.hide() focusManager.clearFocus() } - + // ๏ฟฝ๐ŸŸข ะะฐะฑะปัŽะดะฐะตะผ ะทะฐ ะพะฝะปะฐะนะฝ ัั‚ะฐั‚ัƒัะพะผ ะฟะพะปัŒะทะพะฒะฐั‚ะตะปั ะฒ ั€ะตะฐะปัŒะฝะพะผ ะฒั€ะตะผะตะฝะธ val messageRepository = remember { MessageRepository.getInstance(context) } - val onlineStatus by messageRepository.observeUserOnlineStatus(user.publicKey) - .collectAsState(initial = false to 0L) + val onlineStatus by + messageRepository + .observeUserOnlineStatus(user.publicKey) + .collectAsState(initial = false to 0L) val isOnline = onlineStatus.first val lastSeen = onlineStatus.second - + // Scroll state for collapsing header animation val density = LocalDensity.current val statusBarHeight = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() val expandedHeightPx = with(density) { (EXPANDED_HEADER_HEIGHT_OTHER + statusBarHeight).toPx() } - val collapsedHeightPx = with(density) { (COLLAPSED_HEADER_HEIGHT_OTHER + statusBarHeight).toPx() } - + val collapsedHeightPx = + with(density) { (COLLAPSED_HEADER_HEIGHT_OTHER + statusBarHeight).toPx() } + var scrollOffset by remember { mutableFloatStateOf(0f) } val maxScrollOffset = expandedHeightPx - collapsedHeightPx - + val collapseProgress by remember { - derivedStateOf { - (scrollOffset / maxScrollOffset).coerceIn(0f, 1f) - } + derivedStateOf { (scrollOffset / maxScrollOffset).coerceIn(0f, 1f) } } - + val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val delta = available.y val newOffset = scrollOffset - delta - val consumed = when { - delta < 0 && scrollOffset < maxScrollOffset -> { - val consumed = (newOffset.coerceIn(0f, maxScrollOffset) - scrollOffset) - scrollOffset = newOffset.coerceIn(0f, maxScrollOffset) - -consumed - } - delta > 0 && scrollOffset > 0 -> { - val consumed = scrollOffset - newOffset.coerceIn(0f, maxScrollOffset) - scrollOffset = newOffset.coerceIn(0f, maxScrollOffset) - consumed - } - else -> 0f - } + val consumed = + when { + delta < 0 && scrollOffset < maxScrollOffset -> { + val consumed = + (newOffset.coerceIn(0f, maxScrollOffset) - scrollOffset) + scrollOffset = newOffset.coerceIn(0f, maxScrollOffset) + -consumed + } + delta > 0 && scrollOffset > 0 -> { + val consumed = + scrollOffset - newOffset.coerceIn(0f, maxScrollOffset) + scrollOffset = newOffset.coerceIn(0f, maxScrollOffset) + consumed + } + else -> 0f + } return Offset(0f, consumed) } } } - + // Handle back gesture BackHandler { onBack() } Box( - modifier = Modifier - .fillMaxSize() - .background(backgroundColor) - .nestedScroll(nestedScrollConnection) + modifier = + Modifier.fillMaxSize() + .background(backgroundColor) + .nestedScroll(nestedScrollConnection) ) { // Scrollable content LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(top = with(density) { (expandedHeightPx - scrollOffset).toDp() }) + modifier = + Modifier.fillMaxSize() + .padding( + top = + with(density) { + (expandedHeightPx - scrollOffset).toDp() + } + ) ) { item { Spacer(modifier = Modifier.height(16.dp)) @@ -169,25 +170,25 @@ fun OtherProfileScreen( // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• if (user.username.isNotBlank()) { TelegramSectionTitle(title = "Info", isDarkTheme = isDarkTheme) - + TelegramCopyField( - value = "@${user.username}", - fullValue = user.username, - label = "Username", - isDarkTheme = isDarkTheme + value = "@${user.username}", + fullValue = user.username, + label = "Username", + isDarkTheme = isDarkTheme ) - + Spacer(modifier = Modifier.height(24.dp)) } // Public Key Section TelegramSectionTitle(title = "Public Key", isDarkTheme = isDarkTheme) - + TelegramCopyField( - value = user.publicKey.take(16) + "..." + user.publicKey.takeLast(6), - fullValue = user.publicKey, - label = "Public Key", - isDarkTheme = isDarkTheme + value = user.publicKey.take(16) + "..." + user.publicKey.takeLast(6), + fullValue = user.publicKey, + label = "Public Key", + isDarkTheme = isDarkTheme ) Spacer(modifier = Modifier.height(32.dp)) @@ -198,43 +199,43 @@ fun OtherProfileScreen( // ๐ŸŽจ COLLAPSING HEADER // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• CollapsingOtherProfileHeader( - name = user.title.ifEmpty { "Unknown User" }, - username = user.username, - publicKey = user.publicKey, - verified = user.verified, - isOnline = isOnline, - lastSeen = lastSeen, - avatarColors = avatarColors, - collapseProgress = collapseProgress, - onBack = onBack, - isDarkTheme = isDarkTheme, - showAvatarMenu = showAvatarMenu, - onAvatarMenuChange = { showAvatarMenu = it }, - isBlocked = isBlocked, - onBlockToggle = { - coroutineScope.launch { - if (isBlocked) { - chatsListViewModel.unblockUser(user.publicKey) - } else { - chatsListViewModel.blockUser(user.publicKey) + name = user.title.ifEmpty { "Unknown User" }, + username = user.username, + publicKey = user.publicKey, + verified = user.verified, + isOnline = isOnline, + lastSeen = lastSeen, + avatarColors = avatarColors, + collapseProgress = collapseProgress, + onBack = onBack, + isDarkTheme = isDarkTheme, + showAvatarMenu = showAvatarMenu, + onAvatarMenuChange = { showAvatarMenu = it }, + isBlocked = isBlocked, + onBlockToggle = { + coroutineScope.launch { + if (isBlocked) { + chatsListViewModel.unblockUser(user.publicKey) + } else { + chatsListViewModel.blockUser(user.publicKey) + } + // ะžะฑะฝะพะฒะปัะตะผ ะปะพะบะฐะปัŒะฝะพะต ัะพัั‚ะพัะฝะธะต + isBlocked = !isBlocked } - // ะžะฑะฝะพะฒะปัะตะผ ะปะพะบะฐะปัŒะฝะพะต ัะพัั‚ะพัะฝะธะต - isBlocked = !isBlocked - } - }, - avatarRepository = avatarRepository, - onClearChat = { - viewModel.clearChatHistory() - // ๐Ÿ—‘๏ธ ะฃะดะฐะปัะตะผ ะดะธะฐะปะพะณ ะธะท ัะฟะธัะบะฐ ะฟะพัะปะต ะพั‡ะธัั‚ะบะธ ะธัั‚ะพั€ะธะธ - CoroutineScope(Dispatchers.IO).launch { - try { - val account = viewModel.myPublicKey ?: return@launch - database.dialogDao().deleteDialog(account, user.publicKey) - } catch (e: Exception) { - android.util.Log.e("OtherProfileScreen", "Failed to delete dialog", e) + }, + avatarRepository = avatarRepository, + onClearChat = { + viewModel.clearChatHistory() + // ๐Ÿ—‘๏ธ ะฃะดะฐะปัะตะผ ะดะธะฐะปะพะณ ะธะท ัะฟะธัะบะฐ ะฟะพัะปะต ะพั‡ะธัั‚ะบะธ ะธัั‚ะพั€ะธะธ + CoroutineScope(Dispatchers.IO).launch { + try { + val account = viewModel.myPublicKey ?: return@launch + database.dialogDao().deleteDialog(account, user.publicKey) + } catch (e: Exception) { + android.util.Log.e("OtherProfileScreen", "Failed to delete dialog", e) + } } } - } ) } } @@ -243,205 +244,207 @@ fun OtherProfileScreen( // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• @Composable private fun CollapsingOtherProfileHeader( - name: String, - username: String, - publicKey: String, - verified: Int, - isOnline: Boolean, - lastSeen: Long, - avatarColors: AvatarColors, - collapseProgress: Float, - onBack: () -> Unit, - isDarkTheme: Boolean, - showAvatarMenu: Boolean, - onAvatarMenuChange: (Boolean) -> Unit, - isBlocked: Boolean, - onBlockToggle: () -> Unit, - avatarRepository: AvatarRepository? = null, - onClearChat: () -> Unit + name: String, + username: String, + publicKey: String, + verified: Int, + isOnline: Boolean, + lastSeen: Long, + avatarColors: AvatarColors, + collapseProgress: Float, + onBack: () -> Unit, + isDarkTheme: Boolean, + showAvatarMenu: Boolean, + onAvatarMenuChange: (Boolean) -> Unit, + isBlocked: Boolean, + onBlockToggle: () -> Unit, + avatarRepository: AvatarRepository? = null, + onClearChat: () -> Unit ) { val density = LocalDensity.current val configuration = LocalConfiguration.current val screenWidthDp = configuration.screenWidthDp.dp - + val statusBarHeight = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() - + val expandedHeight = EXPANDED_HEADER_HEIGHT_OTHER + statusBarHeight val collapsedHeight = COLLAPSED_HEADER_HEIGHT_OTHER + statusBarHeight - - val headerHeight = androidx.compose.ui.unit.lerp(expandedHeight, collapsedHeight, collapseProgress) - + + val headerHeight = + androidx.compose.ui.unit.lerp(expandedHeight, collapsedHeight, collapseProgress) + // Avatar animation val avatarCenterX = (screenWidthDp - AVATAR_SIZE_EXPANDED_OTHER) / 2 val avatarStartY = statusBarHeight + 32.dp val avatarEndY = statusBarHeight - 60.dp val avatarY = androidx.compose.ui.unit.lerp(avatarStartY, avatarEndY, collapseProgress) - val avatarSize = androidx.compose.ui.unit.lerp(AVATAR_SIZE_EXPANDED_OTHER, 0.dp, collapseProgress) + val avatarSize = + androidx.compose.ui.unit.lerp(AVATAR_SIZE_EXPANDED_OTHER, 0.dp, collapseProgress) val avatarFontSize = androidx.compose.ui.unit.lerp(40.sp, 0.sp, collapseProgress) - + // Text animation - always centered val textExpandedY = statusBarHeight + 32.dp + AVATAR_SIZE_EXPANDED_OTHER + 48.dp 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, 18.sp, collapseProgress) val onlineFontSize = androidx.compose.ui.unit.lerp(14.sp, 13.sp, collapseProgress) - - Box( - modifier = Modifier - .fillMaxWidth() - .height(headerHeight) - ) { + + Box(modifier = Modifier.fillMaxWidth().height(headerHeight)) { // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ๐ŸŽจ BLURRED AVATAR BACKGROUND // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• BlurredAvatarBackground( - publicKey = publicKey, - avatarRepository = avatarRepository, - fallbackColor = avatarColors.backgroundColor, - blurRadius = 25f, - alpha = 0.3f + publicKey = publicKey, + avatarRepository = avatarRepository, + fallbackColor = avatarColors.backgroundColor, + blurRadius = 25f, + alpha = 0.3f ) - + // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ๐Ÿ”™ BACK BUTTON // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Box( - modifier = Modifier - .padding(top = statusBarHeight) - .padding(start = 4.dp, top = 4.dp) - .size(48.dp), - contentAlignment = Alignment.Center + 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) - ) { + IconButton(onClick = onBack, modifier = Modifier.size(48.dp)) { Icon( - imageVector = Icons.Filled.ArrowBack, - contentDescription = "Back", - tint = if (isColorLight(avatarColors.backgroundColor)) Color.Black else Color.White, - modifier = Modifier.size(24.dp) + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Back", + tint = if (isDarkTheme) Color.White else Color(0xFF007AFF), + modifier = Modifier.size(24.dp) ) } } - + // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // โ‹ฎ MENU BUTTON (top right corner) // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Box( - modifier = Modifier - .align(Alignment.TopEnd) - .padding(top = statusBarHeight) - .padding(end = 4.dp, top = 4.dp) - .size(48.dp), - contentAlignment = Alignment.Center + modifier = + Modifier.align(Alignment.TopEnd) + .padding(top = statusBarHeight) + .padding(end = 4.dp, top = 4.dp) + .size(48.dp), + contentAlignment = Alignment.Center ) { - IconButton( - onClick = { onAvatarMenuChange(true) }, - modifier = Modifier.size(48.dp) - ) { + IconButton(onClick = { onAvatarMenuChange(true) }, modifier = Modifier.size(48.dp)) { Icon( - imageVector = Icons.Default.MoreVert, - contentDescription = "Profile menu", - tint = if (isColorLight(avatarColors.backgroundColor)) Color.Black else Color.White, - modifier = Modifier.size(24.dp) + imageVector = Icons.Default.MoreVert, + contentDescription = "Profile menu", + tint = + if (isColorLight(avatarColors.backgroundColor)) Color.Black + else Color.White, + modifier = Modifier.size(24.dp) ) } - + // ะœะตะฝัŽ ั ะฑะปะพะบะธั€ะพะฒะบะพะน ะฟะพะปัŒะทะพะฒะฐั‚ะตะปั com.rosetta.messenger.ui.chats.components.OtherProfileMenu( - expanded = showAvatarMenu, - onDismiss = { onAvatarMenuChange(false) }, - isDarkTheme = isDarkTheme, - isBlocked = isBlocked, - onBlockClick = { - onAvatarMenuChange(false) - onBlockToggle() - }, - onClearChatClick = { - onAvatarMenuChange(false) - onClearChat() - } + expanded = showAvatarMenu, + onDismiss = { onAvatarMenuChange(false) }, + isDarkTheme = isDarkTheme, + isBlocked = isBlocked, + onBlockClick = { + onAvatarMenuChange(false) + onBlockToggle() + }, + onClearChatClick = { + onAvatarMenuChange(false) + onClearChat() + } ) } - + // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ๐Ÿ‘ค 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), - contentAlignment = Alignment.Center + 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), + contentAlignment = Alignment.Center ) { AvatarImage( - publicKey = publicKey, - avatarRepository = avatarRepository, - size = avatarSize - 4.dp, - isDarkTheme = isDarkTheme + publicKey = publicKey, + avatarRepository = avatarRepository, + size = avatarSize - 4.dp, + isDarkTheme = isDarkTheme ) } } - + // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ๐Ÿ“ TEXT BLOCK - Name + Verified + 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 + 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 ) { // Name + Verified Badge Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center ) { Text( - text = name, - fontSize = nameFontSize, - fontWeight = FontWeight.SemiBold, - color = if (isColorLight(avatarColors.backgroundColor)) Color.Black else Color.White, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - textAlign = TextAlign.Center + text = name, + fontSize = nameFontSize, + fontWeight = FontWeight.SemiBold, + color = + if (isColorLight(avatarColors.backgroundColor)) Color.Black + else Color.White, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + textAlign = TextAlign.Center ) - + if (verified > 0) { Spacer(modifier = Modifier.width(4.dp)) - VerifiedBadge( - verified = verified, - size = (nameFontSize.value * 0.8f).toInt() - ) + VerifiedBadge(verified = verified, size = (nameFontSize.value * 0.8f).toInt()) } } - + Spacer(modifier = Modifier.height(2.dp)) - + // Online/Offline status Text( - text = if (isOnline) "online" else "offline", - fontSize = onlineFontSize, - color = if (isOnline) { - Color(0xFF4CAF50) - } else { - if (isColorLight(avatarColors.backgroundColor)) Color.Black.copy(alpha = 0.6f) else Color.White.copy(alpha = 0.6f) - } + text = if (isOnline) "online" else "offline", + fontSize = onlineFontSize, + color = + if (isOnline) { + Color(0xFF4CAF50) + } else { + if (isColorLight(avatarColors.backgroundColor)) + Color.Black.copy(alpha = 0.6f) + else Color.White.copy(alpha = 0.6f) + } ) } } @@ -451,44 +454,42 @@ private fun CollapsingOtherProfileHeader( // ๐Ÿšซ BLOCK/UNBLOCK ITEM // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• @Composable -private fun TelegramBlockItem( - isBlocked: Boolean, - onToggle: () -> Unit, - isDarkTheme: Boolean -) { +private fun TelegramBlockItem(isBlocked: Boolean, onToggle: () -> Unit, isDarkTheme: Boolean) { val textColor = if (isDarkTheme) Color.White else Color.Black val iconColor = PrimaryBlue Row( - modifier = Modifier - .fillMaxWidth() - .clickable(onClick = onToggle) - .padding(horizontal = 16.dp, vertical = 14.dp), - verticalAlignment = Alignment.CenterVertically + modifier = + Modifier.fillMaxWidth() + .clickable(onClick = onToggle) + .padding(horizontal = 16.dp, vertical = 14.dp), + verticalAlignment = Alignment.CenterVertically ) { Icon( - imageVector = Icons.Outlined.Block, - contentDescription = null, - tint = iconColor, - modifier = Modifier.size(24.dp) + imageVector = Icons.Outlined.Block, + contentDescription = null, + tint = iconColor, + modifier = Modifier.size(24.dp) ) Spacer(modifier = Modifier.width(20.dp)) Column(modifier = Modifier.weight(1f)) { Text( - text = if (isBlocked) "Unblock User" else "Block User", - fontSize = 16.sp, - color = iconColor, - fontWeight = FontWeight.Medium + text = if (isBlocked) "Unblock User" else "Block User", + fontSize = 16.sp, + color = iconColor, + fontWeight = FontWeight.Medium ) Spacer(modifier = Modifier.height(2.dp)) Text( - text = if (isBlocked) "Allow this user to message you" else "Prevent this user from messaging you", - fontSize = 13.sp, - color = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666), - lineHeight = 16.sp + text = + if (isBlocked) "Allow this user to message you" + else "Prevent this user from messaging you", + fontSize = 13.sp, + color = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666), + lineHeight = 16.sp ) } } -} \ No newline at end of file +}