feat: Add appearance customization screen with background blur options

- Introduced `BackgroundBlurOption` data class and `BackgroundBlurPresets` object for managing background blur options.
- Created `AppearanceScreen` composable for selecting background colors and gradients, including a live preview of the selected option.
- Updated `OtherProfileScreen` and `ProfileScreen` to accept and utilize `backgroundBlurColorId` for consistent background blur across profiles.
- Enhanced `CollapsingOtherProfileHeader` and `CollapsingProfileHeader` to apply selected background blur options.
This commit is contained in:
2026-02-07 08:10:26 +05:00
parent eef254a9cf
commit 71181f49d9
9 changed files with 1864 additions and 661 deletions

View File

@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
@@ -28,6 +29,8 @@ import kotlinx.coroutines.withContext
* @param fallbackColor Цвет фона если нет аватарки
* @param blurRadius Радиус размытия (в пикселях) - применяется при обработке
* @param alpha Прозрачность (0.0 - 1.0)
* @param overlayColors Опциональные цвета overlay поверх blur. Если null — стандартное поведение.
* Если 1 цвет — сплошной overlay, если 2+ — градиент.
*/
@Composable
fun BoxScope.BlurredAvatarBackground(
@@ -35,7 +38,8 @@ fun BoxScope.BlurredAvatarBackground(
avatarRepository: AvatarRepository?,
fallbackColor: Color,
blurRadius: Float = 25f,
alpha: Float = 0.3f
alpha: Float = 0.3f,
overlayColors: List<Color>? = null
) {
// Получаем аватары из репозитория
val avatars by avatarRepository?.getAvatars(publicKey, allDecode = false)?.collectAsState()
@@ -91,19 +95,55 @@ fun BoxScope.BlurredAvatarBackground(
contentScale = ContentScale.Crop
)
// Дополнительный overlay для затемнения
Box(
modifier = Modifier
.fillMaxSize()
.background(fallbackColor.copy(alpha = 0.3f))
)
// Дополнительный overlay — кастомный или стандартный
if (overlayColors != null && overlayColors.isNotEmpty()) {
// Кастомный цветной overlay
val overlayModifier = if (overlayColors.size == 1) {
Modifier
.fillMaxSize()
.background(overlayColors[0].copy(alpha = 0.55f))
} else {
Modifier
.fillMaxSize()
.background(
Brush.linearGradient(
colors = overlayColors.map { it.copy(alpha = 0.55f) }
)
)
}
Box(modifier = overlayModifier)
} else {
// Стандартный overlay для затемнения
Box(
modifier = Modifier
.fillMaxSize()
.background(fallbackColor.copy(alpha = 0.3f))
)
}
} else {
// Fallback: цветной фон
Box(
modifier = Modifier
.fillMaxSize()
.background(fallbackColor)
)
// Fallback: когда нет аватарки
if (overlayColors != null && overlayColors.isNotEmpty()) {
// Кастомный фон без blur
val bgModifier = if (overlayColors.size == 1) {
Modifier
.fillMaxSize()
.background(overlayColors[0])
} else {
Modifier
.fillMaxSize()
.background(
Brush.linearGradient(colors = overlayColors)
)
}
Box(modifier = bgModifier)
} else {
// Стандартный fallback: цветной фон
Box(
modifier = Modifier
.fillMaxSize()
.background(fallbackColor)
)
}
}
}
}