feat: Implement collapsing header for ProfileScreen with enhanced navigation and logging features

This commit is contained in:
k1ngsterr1
2026-01-21 00:08:33 +05:00
parent db61ebb79f
commit f856459494

View File

@@ -4,8 +4,10 @@ import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import androidx.compose.animation.* import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.* import androidx.compose.foundation.*
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.BasicTextField
@@ -16,22 +18,28 @@ import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip 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.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
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.TextStyle import androidx.compose.ui.text.TextStyle
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.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.roundToInt
// 🎨 Avatar colors - используем те же цвета что и в ChatsListScreen // 🎨 Avatar colors - используем те же цвета что и в ChatsListScreen
private val avatarColorsLight = private val avatarColorsLight =
@@ -91,6 +99,15 @@ private fun getInitials(name: String): String {
} }
} }
// ═════════════════════════════════════════════════════════════
// 🎯 COLLAPSING HEADER CONSTANTS
// ═════════════════════════════════════════════════════════════
private val EXPANDED_HEADER_HEIGHT = 280.dp
private val COLLAPSED_HEADER_HEIGHT = 56.dp
private val AVATAR_SIZE_EXPANDED = 120.dp
private val AVATAR_SIZE_COLLAPSED = 36.dp
private val STATUS_BAR_HEIGHT = 24.dp
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ProfileScreen( fun ProfileScreen(
@@ -107,13 +124,12 @@ fun ProfileScreen(
onNavigateToLogs: () -> Unit = {}, onNavigateToLogs: () -> Unit = {},
viewModel: ProfileViewModel = androidx.lifecycle.viewmodel.compose.viewModel() viewModel: ProfileViewModel = androidx.lifecycle.viewmodel.compose.viewModel()
) { ) {
// Цвета в зависимости от темы - такие же как в ChatsListScreen // Цвета в зависимости от темы
val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF) val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF)
val surfaceColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color(0xFFF2F2F7) val surfaceColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color(0xFFF2F2F7)
val textColor = if (isDarkTheme) Color.White else Color.Black val textColor = if (isDarkTheme) Color.White else Color.Black
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666) val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8) val avatarColors = getAvatarColor(accountPublicKey, isDarkTheme)
val iconTintColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
// State for editing // State for editing
var editedName by remember { mutableStateOf(accountName) } var editedName by remember { mutableStateOf(accountName) }
@@ -123,8 +139,50 @@ fun ProfileScreen(
// ViewModel state // ViewModel state
val profileState by viewModel.state.collectAsState() val profileState by viewModel.state.collectAsState()
// Show success toast // Scroll state for collapsing header animation
val density = LocalDensity.current
val expandedHeightPx = with(density) { EXPANDED_HEADER_HEIGHT.toPx() }
val collapsedHeightPx = with(density) { (COLLAPSED_HEADER_HEIGHT + STATUS_BAR_HEIGHT).toPx() }
// Track scroll offset with animated state for smooth transitions
var scrollOffset by remember { mutableFloatStateOf(0f) }
val maxScrollOffset = expandedHeightPx - collapsedHeightPx
// Calculate collapse progress (0 = expanded, 1 = collapsed)
val collapseProgress by remember {
derivedStateOf {
(scrollOffset / maxScrollOffset).coerceIn(0f, 1f)
}
}
// Nested scroll connection for tracking scroll
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
}
return Offset(0f, consumed)
}
}
}
// Context
val context = LocalContext.current val context = LocalContext.current
// Show success toast
LaunchedEffect(profileState.saveSuccess) { LaunchedEffect(profileState.saveSuccess) {
if (profileState.saveSuccess) { if (profileState.saveSuccess) {
android.widget.Toast.makeText( android.widget.Toast.makeText(
@@ -159,198 +217,365 @@ fun ProfileScreen(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(backgroundColor) .background(backgroundColor)
.nestedScroll(nestedScrollConnection)
) { ) {
Column( // Scrollable content
LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .padding(top = with(density) { (expandedHeightPx - scrollOffset).toDp() })
) { ) {
// ═════════════════════════════════════════════════════════════ item {
// 👤 PROFILE CARD with colored header Spacer(modifier = Modifier.height(16.dp))
// ═════════════════════════════════════════════════════════════
ProfileCard( // ═════════════════════════════════════════════════════════════
name = editedName.ifBlank { accountPublicKey.take(10) }, // ✏️ EDITABLE FIELDS
username = editedUsername, // ═════════════════════════════════════════════════════════════
publicKey = accountPublicKey, ProfileSectionTitle(title = "Profile Information", isDarkTheme = isDarkTheme)
isDarkTheme = isDarkTheme,
onBack = null, // Кнопка назад будет снаружи Surface(
hasChanges = hasChanges, modifier = Modifier
onSave = { .fillMaxWidth()
// Save via ViewModel .padding(horizontal = 16.dp),
viewModel.saveProfile( color = surfaceColor,
publicKey = accountPublicKey, shape = RoundedCornerShape(16.dp)
privateKeyHash = accountPrivateKeyHash, ) {
name = editedName, Column {
username = editedUsername ProfileEditableField(
) label = "Your name",
// Also update local account name value = editedName,
viewModel.updateLocalAccountName(accountPublicKey, editedName) onValueChange = { editedName = it },
placeholder = "ex. Freddie Gibson",
isDarkTheme = isDarkTheme,
showDivider = true
)
ProfileEditableField(
label = "Username",
value = editedUsername,
onValueChange = { editedUsername = it },
placeholder = "ex. freddie871",
isDarkTheme = isDarkTheme
)
}
} }
)
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(8.dp))
// ═════════════════════════════════════════════════════════════ // Public Key Copy Field
// ✏️ EDITABLE FIELDS ProfileCopyField(
// ═════════════════════════════════════════════════════════════ label = "Public Key",
ProfileSectionTitle(title = "Profile Information", isDarkTheme = isDarkTheme) value = accountPublicKey,
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
color = surfaceColor,
shape = RoundedCornerShape(16.dp)
) {
Column {
ProfileEditableField(
label = "Your name",
value = editedName,
onValueChange = { editedName = it },
placeholder = "ex. Freddie Gibson",
isDarkTheme = isDarkTheme,
showDivider = true
)
ProfileEditableField(
label = "Username",
value = editedUsername,
onValueChange = { editedUsername = it },
placeholder = "ex. freddie871",
isDarkTheme = isDarkTheme
)
}
}
Spacer(modifier = Modifier.height(8.dp))
// Public Key Copy Field
ProfileCopyField(
label = "Public Key",
value = accountPublicKey,
isDarkTheme = isDarkTheme
)
Text(
text = "This is your public key. If you haven't set a @username yet, you can ask a friend to message you using your public key.",
fontSize = 12.sp,
color = secondaryTextColor,
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
lineHeight = 16.sp
)
Spacer(modifier = Modifier.height(24.dp))
// ═════════════════════════════════════════════════════════════
// 🔧 SETTINGS SECTION
// ═════════════════════════════════════════════════════════════
ProfileSectionTitle(title = "Settings", isDarkTheme = isDarkTheme)
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
color = surfaceColor,
shape = RoundedCornerShape(16.dp)
) {
Column {
ProfileNavigationItem(
icon = Icons.Outlined.Brush,
iconBackground = Color(0xFF6366F1), // indigo
title = "Theme",
subtitle = "Customize appearance",
onClick = onNavigateToTheme,
isDarkTheme = isDarkTheme,
showDivider = true
)
ProfileNavigationItem(
icon = Icons.Outlined.AdminPanelSettings,
iconBackground = Color(0xFF9333EA), // grape/purple
title = "Safety",
subtitle = "Backup and security settings",
onClick = onNavigateToSafety,
isDarkTheme = isDarkTheme
)
}
}
Text(
text = "You can learn more about your safety on the safety page, please make sure you are viewing the screen alone before proceeding to the safety page.",
fontSize = 12.sp,
color = secondaryTextColor,
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
lineHeight = 16.sp
)
Spacer(modifier = Modifier.height(24.dp))
// ═════════════════════════════════════════════════════════════
// <20> DEBUG / LOGS SECTION
// ═════════════════════════════════════════════════════════════
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
color = surfaceColor,
shape = RoundedCornerShape(16.dp)
) {
ProfileNavigationItem(
icon = Icons.Outlined.BugReport,
iconBackground = Color(0xFFFB8C00), // orange
title = "View Logs",
subtitle = "Debug profile save operations",
onClick = onNavigateToLogs,
isDarkTheme = isDarkTheme isDarkTheme = isDarkTheme
) )
}
Text( Text(
text = "View detailed logs of profile save operations for debugging.", text = "This is your public key. If you haven't set a @username yet, you can ask a friend to message you using your public key.",
fontSize = 12.sp, fontSize = 12.sp,
color = secondaryTextColor, color = secondaryTextColor,
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp), modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
lineHeight = 16.sp lineHeight = 16.sp
)
Spacer(modifier = Modifier.height(24.dp))
// ═════════════════════════════════════════════════════════════
// <20>🚪 LOGOUT SECTION
// ═════════════════════════════════════════════════════════════
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
color = surfaceColor,
shape = RoundedCornerShape(16.dp)
) {
ProfileNavigationItem(
icon = Icons.Outlined.Logout,
iconBackground = if (isDarkTheme) Color(0xFFFF8787) else Color(0xFFEF4444),
title = "Logout",
subtitle = "Sign out of your account",
onClick = onLogout,
isDarkTheme = isDarkTheme,
hideChevron = true,
textColor = if (isDarkTheme) Color(0xFFFF8787) else Color(0xFFEF4444)
) )
Spacer(modifier = Modifier.height(24.dp))
// ═════════════════════════════════════════════════════════════
// 🔧 SETTINGS SECTION
// ═════════════════════════════════════════════════════════════
ProfileSectionTitle(title = "Settings", isDarkTheme = isDarkTheme)
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
color = surfaceColor,
shape = RoundedCornerShape(16.dp)
) {
Column {
ProfileNavigationItem(
icon = Icons.Outlined.Brush,
iconBackground = Color(0xFF6366F1),
title = "Theme",
subtitle = "Customize appearance",
onClick = onNavigateToTheme,
isDarkTheme = isDarkTheme,
showDivider = true
)
ProfileNavigationItem(
icon = Icons.Outlined.AdminPanelSettings,
iconBackground = Color(0xFF9333EA),
title = "Safety",
subtitle = "Backup and security settings",
onClick = onNavigateToSafety,
isDarkTheme = isDarkTheme
)
}
}
Text(
text = "You can learn more about your safety on the safety page, please make sure you are viewing the screen alone before proceeding to the safety page.",
fontSize = 12.sp,
color = secondaryTextColor,
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
lineHeight = 16.sp
)
Spacer(modifier = Modifier.height(24.dp))
// ═════════════════════════════════════════════════════════════
// 🐛 DEBUG / LOGS SECTION
// ═════════════════════════════════════════════════════════════
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
color = surfaceColor,
shape = RoundedCornerShape(16.dp)
) {
ProfileNavigationItem(
icon = Icons.Outlined.BugReport,
iconBackground = Color(0xFFFB8C00),
title = "View Logs",
subtitle = "Debug profile save operations",
onClick = onNavigateToLogs,
isDarkTheme = isDarkTheme
)
}
Text(
text = "View detailed logs of profile save operations for debugging.",
fontSize = 12.sp,
color = secondaryTextColor,
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
lineHeight = 16.sp
)
Spacer(modifier = Modifier.height(24.dp))
// ═════════════════════════════════════════════════════════════
// 🚪 LOGOUT SECTION
// ═════════════════════════════════════════════════════════════
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
color = surfaceColor,
shape = RoundedCornerShape(16.dp)
) {
ProfileNavigationItem(
icon = Icons.Outlined.Logout,
iconBackground = if (isDarkTheme) Color(0xFFFF8787) else Color(0xFFEF4444),
title = "Logout",
subtitle = "Sign out of your account",
onClick = onLogout,
isDarkTheme = isDarkTheme,
hideChevron = true,
textColor = if (isDarkTheme) Color(0xFFFF8787) else Color(0xFFEF4444)
)
}
Text(
text = "Logging out of your account. After logging out, you will be redirected to the password entry page.",
fontSize = 12.sp,
color = secondaryTextColor,
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
lineHeight = 16.sp
)
Spacer(modifier = Modifier.height(32.dp))
} }
Text(
text = "Logging out of your account. After logging out, you will be redirected to the password entry page.",
fontSize = 12.sp,
color = secondaryTextColor,
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
lineHeight = 16.sp
)
Spacer(modifier = Modifier.height(32.dp))
} }
// Fixed back button at top // ═════════════════════════════════════════════════════════════
// 🎨 COLLAPSING HEADER - Telegram style
// ═════════════════════════════════════════════════════════════
CollapsingProfileHeader(
name = editedName.ifBlank { accountPublicKey.take(10) },
username = editedUsername,
publicKey = accountPublicKey,
avatarColors = avatarColors,
collapseProgress = collapseProgress,
onBack = onBack,
hasChanges = hasChanges,
onSave = {
viewModel.saveProfile(
publicKey = accountPublicKey,
privateKeyHash = accountPrivateKeyHash,
name = editedName,
username = editedUsername
)
viewModel.updateLocalAccountName(accountPublicKey, editedName)
},
isDarkTheme = isDarkTheme
)
}
}
// ═════════════════════════════════════════════════════════════
// 🎯 COLLAPSING PROFILE HEADER - Telegram Style Animation
// ═════════════════════════════════════════════════════════════
@Composable
private fun CollapsingProfileHeader(
name: String,
username: String,
publicKey: String,
avatarColors: AvatarColors,
collapseProgress: Float,
onBack: () -> Unit,
hasChanges: Boolean,
onSave: () -> Unit,
isDarkTheme: Boolean
) {
val density = LocalDensity.current
// Animated values using lerp
val headerHeight = androidx.compose.ui.unit.lerp(
EXPANDED_HEADER_HEIGHT,
COLLAPSED_HEADER_HEIGHT + STATUS_BAR_HEIGHT,
collapseProgress
)
val avatarSize = androidx.compose.ui.unit.lerp(
AVATAR_SIZE_EXPANDED,
AVATAR_SIZE_COLLAPSED,
collapseProgress
)
// Avatar position - smooth transition from center to left
val screenWidthDp = with(density) { 360.dp } // approximate
val avatarStartX = (screenWidthDp - AVATAR_SIZE_EXPANDED) / 2
val avatarEndX = 56.dp // After back button
val avatarX = androidx.compose.ui.unit.lerp(avatarStartX, avatarEndX, collapseProgress)
val avatarY = androidx.compose.ui.unit.lerp(70.dp, STATUS_BAR_HEIGHT + 10.dp, collapseProgress)
// Text alpha animations
val expandedContentAlpha = (1f - collapseProgress * 2f).coerceIn(0f, 1f) // Fades out faster
val collapsedContentAlpha = ((collapseProgress - 0.5f) * 2f).coerceIn(0f, 1f) // Fades in at 50%
// Avatar scale for "drop" effect - shrinks faster at the end
val avatarScaleMultiplier = if (collapseProgress > 0.7f) {
1f - ((collapseProgress - 0.7f) / 0.3f) * 0.15f
} else 1f
Box(
modifier = Modifier
.fillMaxWidth()
.height(headerHeight)
.drawBehind {
drawRect(avatarColors.backgroundColor)
}
) {
// ═══════════════════════════════════════════════════════════
// 👤 ANIMATED AVATAR - Telegram "drop" effect
// ═══════════════════════════════════════════════════════════
Box(
modifier = Modifier
.offset(x = avatarX, y = avatarY)
.size(avatarSize)
.graphicsLayer {
scaleX = avatarScaleMultiplier
scaleY = avatarScaleMultiplier
// Subtle alpha decrease at the very end for smooth collapse
alpha = if (collapseProgress > 0.9f) {
1f - ((collapseProgress - 0.9f) / 0.1f) * 0.2f
} else 1f
}
.clip(CircleShape)
.background(Color.White.copy(alpha = 0.2f))
.padding(2.dp)
.clip(CircleShape)
.background(avatarColors.backgroundColor),
contentAlignment = Alignment.Center
) {
Text(
text = getInitials(name),
fontSize = androidx.compose.ui.unit.lerp(40.sp, 14.sp, collapseProgress),
fontWeight = FontWeight.Bold,
color = avatarColors.textColor
)
}
// ═══════════════════════════════════════════════════════════
// 📝 EXPANDED STATE - Name and info below avatar
// ═══════════════════════════════════════════════════════════
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = 200.dp)
.graphicsLayer { alpha = expandedContentAlpha },
horizontalAlignment = Alignment.CenterHorizontally
) {
// Name
Text(
text = name,
fontSize = 24.sp,
fontWeight = FontWeight.SemiBold,
color = Color.White,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(4.dp))
// Username and public key
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
if (username.isNotBlank()) {
Text(
text = "@$username",
fontSize = 14.sp,
color = Color.White.copy(alpha = 0.8f)
)
Text(
text = "",
fontSize = 14.sp,
color = Color.White.copy(alpha = 0.8f)
)
}
Text(
text = "${publicKey.take(3)}...${publicKey.takeLast(3)}",
fontSize = 14.sp,
color = Color.White.copy(alpha = 0.8f)
)
}
}
// ═══════════════════════════════════════════════════════════
// 📝 COLLAPSED STATE - Toolbar with name next to avatar
// ═══════════════════════════════════════════════════════════
Row(
modifier = Modifier
.fillMaxWidth()
.statusBarsPadding()
.height(COLLAPSED_HEADER_HEIGHT)
.padding(start = 100.dp) // Space for back button + avatar
.graphicsLayer { alpha = collapsedContentAlpha },
verticalAlignment = Alignment.CenterVertically
) {
Column {
Text(
text = name,
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold,
color = Color.White,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = "online",
fontSize = 13.sp,
color = Color.White.copy(alpha = 0.7f)
)
}
}
// ═══════════════════════════════════════════════════════════
// 🔙 BACK BUTTON - Always visible
// ═══════════════════════════════════════════════════════════
IconButton( IconButton(
onClick = onBack, onClick = onBack,
modifier = Modifier modifier = Modifier
.align(Alignment.TopStart)
.statusBarsPadding() .statusBarsPadding()
.padding(4.dp) .padding(4.dp)
) { ) {
@@ -361,7 +586,9 @@ fun ProfileScreen(
) )
} }
// Fixed save button (if changes) // ═══════════════════════════════════════════════════════════
// 💾 SAVE BUTTON (if changes)
// ═══════════════════════════════════════════════════════════
AnimatedVisibility( AnimatedVisibility(
visible = hasChanges, visible = hasChanges,
enter = fadeIn() + expandHorizontally(), enter = fadeIn() + expandHorizontally(),
@@ -371,10 +598,7 @@ fun ProfileScreen(
.statusBarsPadding() .statusBarsPadding()
.padding(4.dp) .padding(4.dp)
) { ) {
TextButton(onClick = { TextButton(onClick = onSave) {
onSaveProfile(editedName, editedUsername)
hasChanges = false
}) {
Text( Text(
text = "Save", text = "Save",
color = Color.White, color = Color.White,
@@ -386,7 +610,7 @@ fun ProfileScreen(
} }
// ═════════════════════════════════════════════════════════════ // ═════════════════════════════════════════════════════════════
// 📦 PROFILE CARD COMPONENT - Large Avatar Telegram Style // 📦 PROFILE CARD COMPONENT - Legacy (kept for OtherProfileScreen)
// ═════════════════════════════════════════════════════════════ // ═════════════════════════════════════════════════════════════
@Composable @Composable
fun ProfileCard( fun ProfileCard(
@@ -649,7 +873,7 @@ private fun ProfileCopyField(
Text( Text(
text = "Copied!", text = "Copied!",
fontSize = 14.sp, fontSize = 14.sp,
color = Color(0xFF22C55E), // green color = Color(0xFF22C55E),
fontWeight = FontWeight.Medium fontWeight = FontWeight.Medium
) )
} else { } else {