feat: Add avatar deletion functionality and update ProfileScreen to handle avatar presence

This commit is contained in:
2026-01-30 03:36:01 +05:00
parent 7691926ef6
commit 5091eb557a
4 changed files with 149 additions and 29 deletions

View File

@@ -267,6 +267,11 @@ fun ProfileScreen(
val textColor = if (isDarkTheme) Color.White else Color.Black
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
val avatarColors = getAvatarColor(accountPublicKey, isDarkTheme)
// Проверяем наличие аватара
val avatars by avatarRepository?.getAvatars(accountPublicKey, allDecode = false)?.collectAsState()
?: remember { mutableStateOf(emptyList()) }
val hasAvatar = avatars.isNotEmpty()
// State for editing - Update when account data changes
var editedName by remember(accountName) { mutableStateOf(accountName) }
@@ -520,6 +525,18 @@ fun ProfileScreen(
onSetPhotoClick = {
imagePickerLauncher.launch("image/*")
},
onDeletePhotoClick = {
// Удаляем аватар
scope.launch {
avatarRepository?.deleteMyAvatar()
android.widget.Toast.makeText(
context,
"Avatar deleted",
android.widget.Toast.LENGTH_SHORT
).show()
}
},
hasAvatar = hasAvatar,
avatarRepository = avatarRepository
)
}
@@ -636,6 +653,8 @@ private fun CollapsingProfileHeader(
showAvatarMenu: Boolean,
onAvatarMenuChange: (Boolean) -> Unit,
onSetPhotoClick: () -> Unit,
onDeletePhotoClick: () -> Unit,
hasAvatar: Boolean,
avatarRepository: AvatarRepository?
) {
val density = LocalDensity.current
@@ -654,35 +673,57 @@ private fun CollapsingProfileHeader(
val headerHeight = androidx.compose.ui.unit.lerp(expandedHeight, collapsedHeight, collapseProgress)
// ═══════════════════════════════════════════════════════════
// 👤 AVATAR - По умолчанию квадратный во весь экран, при скролле становится круглым
// 👤 AVATAR - По умолчанию прямоугольный во весь header, при скролле становится круглым
// ═══════════════════════════════════════════════════════════
// Размер: ширина = screenWidthDp (полная ширина), высота = expandedHeight
// При скролле уменьшается до 0
val avatarWidth = androidx.compose.ui.unit.lerp(screenWidthDp, 0.dp, collapseProgress)
val avatarHeight = androidx.compose.ui.unit.lerp(expandedHeight, 0.dp, collapseProgress)
// Размеры аватара - всегда помещается в header
// При collapseProgress=0: ширина=screenWidth, высота=expandedHeight
// При collapseProgress=1: размер=0
// Для cornerRadius и других расчётов используем меньшую сторону
// Промежуточный размер для круга (когда становится круглым)
val circleSize = AVATAR_SIZE_EXPANDED
// Плавный переход: сначала уменьшается до круга, потом до 0
val avatarWidth: Dp
val avatarHeight: Dp
if (collapseProgress < 0.4f) {
// Фаза 1: от полного размера до круга
val phase1Progress = collapseProgress / 0.4f
avatarWidth = androidx.compose.ui.unit.lerp(screenWidthDp, circleSize, phase1Progress)
avatarHeight = androidx.compose.ui.unit.lerp(expandedHeight, circleSize, phase1Progress)
} else {
// Фаза 2: от круга до 0
val phase2Progress = (collapseProgress - 0.4f) / 0.6f
avatarWidth = androidx.compose.ui.unit.lerp(circleSize, 0.dp, phase2Progress)
avatarHeight = androidx.compose.ui.unit.lerp(circleSize, 0.dp, phase2Progress)
}
// Для cornerRadius используем меньшую сторону
val avatarSize = minOf(avatarWidth, avatarHeight)
// Позиция X: от 0 (весь экран) до центра
val avatarCenterX = (screenWidthDp - AVATAR_SIZE_EXPANDED) / 2
val avatarX = androidx.compose.ui.unit.lerp(0.dp, avatarCenterX + (AVATAR_SIZE_EXPANDED - avatarSize) / 2, collapseProgress.coerceIn(0f, 0.5f) * 2f)
// Позиция X: центрируем аватар
val avatarX = (screenWidthDp - avatarWidth) / 2
// Позиция Y: от 0 (верх экрана) до обычной позиции, потом уходит вверх
val avatarStartY = 0.dp // Начинаем с самого верха (закрываем status bar)
val avatarMidY = statusBarHeight + 32.dp // Средняя позиция (круглый аватар)
val avatarEndY = statusBarHeight - 60.dp // Уходит вверх за экран
val avatarY = if (collapseProgress < 0.5f) {
// Первая половина: от 0 до обычной позиции
androidx.compose.ui.unit.lerp(avatarStartY, avatarMidY, collapseProgress * 2f)
// Позиция Y: от 0 до центра header, потом уходит вверх
val avatarY = if (collapseProgress < 0.4f) {
// Фаза 1: остаётся наверху
0.dp
} else if (collapseProgress < 0.7f) {
// Фаза 2: опускается в позицию круга
val phase2Progress = (collapseProgress - 0.4f) / 0.3f
androidx.compose.ui.unit.lerp(0.dp, statusBarHeight + 32.dp, phase2Progress)
} else {
// Вторая половина: уходит вверх
androidx.compose.ui.unit.lerp(avatarMidY, avatarEndY, (collapseProgress - 0.5f) * 2f)
// Фаза 3: уходит вверх за экран
val phase3Progress = (collapseProgress - 0.7f) / 0.3f
androidx.compose.ui.unit.lerp(statusBarHeight + 32.dp, statusBarHeight - 60.dp, phase3Progress)
}
// Закругление: от 0 (квадрат) до половины размера (круг)
val cornerRadius = androidx.compose.ui.unit.lerp(0.dp, avatarSize / 2, collapseProgress.coerceIn(0f, 0.3f) / 0.3f)
val cornerRadius = if (collapseProgress < 0.3f) {
androidx.compose.ui.unit.lerp(0.dp, avatarSize / 2, collapseProgress / 0.3f)
} else {
avatarSize / 2 // Полный круг
}
val avatarFontSize = androidx.compose.ui.unit.lerp(40.sp, 0.sp, collapseProgress)
@@ -736,18 +777,19 @@ private fun CollapsingProfileHeader(
) {
// Используем AvatarImage если репозиторий доступен
if (avatarRepository != null) {
// При collapseProgress < 0.2 - fullscreen аватар
if (collapseProgress < 0.2f) {
// При collapseProgress < 0.35 - fullscreen аватар (ещё не полностью круглый)
if (collapseProgress < 0.35f) {
FullSizeAvatar(
publicKey = publicKey,
avatarRepository = avatarRepository
avatarRepository = avatarRepository,
isDarkTheme = isDarkTheme
)
} else {
AvatarImage(
publicKey = publicKey,
avatarRepository = avatarRepository,
size = avatarSize - 4.dp,
isDarkTheme = false,
size = avatarSize,
isDarkTheme = isDarkTheme,
onClick = null,
showOnlineIndicator = false
)
@@ -827,7 +869,12 @@ private fun CollapsingProfileHeader(
onSetPhotoClick = {
onAvatarMenuChange(false)
onSetPhotoClick()
}
},
onDeletePhotoClick = {
onAvatarMenuChange(false)
onDeletePhotoClick()
},
hasAvatar = hasAvatar
)
}
@@ -896,7 +943,8 @@ private fun CollapsingProfileHeader(
@Composable
private fun FullSizeAvatar(
publicKey: String,
avatarRepository: AvatarRepository?
avatarRepository: AvatarRepository?,
isDarkTheme: Boolean = false
) {
val avatars by avatarRepository?.getAvatars(publicKey, allDecode = false)?.collectAsState()
?: remember { mutableStateOf(emptyList()) }
@@ -921,7 +969,7 @@ private fun FullSizeAvatar(
contentScale = ContentScale.Crop
)
} else {
val avatarColors = getAvatarColor(publicKey, false)
val avatarColors = getAvatarColor(publicKey, isDarkTheme)
Box(
modifier = Modifier
.fillMaxSize()