fix: add detailed logging for unlock process to improve debugging and performance tracking
This commit is contained in:
@@ -0,0 +1,430 @@
|
||||
package com.rosetta.messenger.ui.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.airbnb.lottie.compose.*
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.*
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
// Auth colors
|
||||
private val AuthBackground = Color(0xFF1A1A1A)
|
||||
private val AuthBackgroundLight = Color(0xFFFFFFFF)
|
||||
private val AuthSurface = Color(0xFF2C2C2E)
|
||||
private val AuthSurfaceLight = Color(0xFFF2F2F7)
|
||||
|
||||
/**
|
||||
* Biometric Enable Screen - структура как в auth flow
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun BiometricEnableScreen(
|
||||
isDarkTheme: Boolean,
|
||||
onBack: () -> Unit,
|
||||
onEnable: (password: String, onSuccess: () -> Unit, onError: (String) -> Unit) -> Unit
|
||||
) {
|
||||
// Theme animation
|
||||
val themeAnimSpec = tween<Color>(durationMillis = 500, easing = CubicBezierEasing(0.4f, 0f, 0.2f, 1f))
|
||||
val backgroundColor by animateColorAsState(
|
||||
if (isDarkTheme) AuthBackground else AuthBackgroundLight,
|
||||
animationSpec = themeAnimSpec,
|
||||
label = "bg"
|
||||
)
|
||||
val textColor by animateColorAsState(
|
||||
if (isDarkTheme) Color.White else Color.Black,
|
||||
animationSpec = themeAnimSpec,
|
||||
label = "text"
|
||||
)
|
||||
val secondaryTextColor by animateColorAsState(
|
||||
if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666),
|
||||
animationSpec = themeAnimSpec,
|
||||
label = "secondary"
|
||||
)
|
||||
val cardColor by animateColorAsState(
|
||||
if (isDarkTheme) AuthSurface else AuthSurfaceLight,
|
||||
animationSpec = themeAnimSpec,
|
||||
label = "card"
|
||||
)
|
||||
val errorRed = Color(0xFFFF3B30)
|
||||
|
||||
// State
|
||||
var password by remember { mutableStateOf("") }
|
||||
var passwordVisible by remember { mutableStateOf(false) }
|
||||
var passwordError by remember { mutableStateOf<String?>(null) }
|
||||
var isLoading by remember { mutableStateOf(false) }
|
||||
var showSuccess by remember { mutableStateOf(false) }
|
||||
var visible by remember { mutableStateOf(false) }
|
||||
val focusManager = LocalFocusManager.current
|
||||
val context = LocalContext.current
|
||||
val view = LocalView.current
|
||||
|
||||
// Function to hide keyboard
|
||||
fun hideKeyboard() {
|
||||
focusManager.clearFocus()
|
||||
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(view.windowToken, 0)
|
||||
}
|
||||
|
||||
// Track keyboard visibility
|
||||
var isKeyboardVisible by remember { mutableStateOf(false) }
|
||||
|
||||
DisposableEffect(view) {
|
||||
val listener = android.view.ViewTreeObserver.OnGlobalLayoutListener {
|
||||
val rect = android.graphics.Rect()
|
||||
view.getWindowVisibleDisplayFrame(rect)
|
||||
val screenHeight = view.rootView.height
|
||||
val keypadHeight = screenHeight - rect.bottom
|
||||
isKeyboardVisible = keypadHeight > screenHeight * 0.15
|
||||
}
|
||||
view.viewTreeObserver.addOnGlobalLayoutListener(listener)
|
||||
onDispose { view.viewTreeObserver.removeOnGlobalLayoutListener(listener) }
|
||||
}
|
||||
|
||||
// Lottie animation - одноразовая
|
||||
val lockComposition by rememberLottieComposition(
|
||||
LottieCompositionSpec.Asset("lottie/lock.json")
|
||||
)
|
||||
val lockProgress by animateLottieCompositionAsState(
|
||||
composition = lockComposition,
|
||||
iterations = 1,
|
||||
speed = 0.8f
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) { visible = true }
|
||||
|
||||
// Handle success - close screen after delay
|
||||
LaunchedEffect(showSuccess) {
|
||||
if (showSuccess) {
|
||||
delay(1500)
|
||||
onBack()
|
||||
}
|
||||
}
|
||||
|
||||
// Animated sizes
|
||||
val lottieSize by animateDpAsState(
|
||||
targetValue = if (isKeyboardVisible) 80.dp else 160.dp,
|
||||
animationSpec = tween(300, easing = FastOutSlowInEasing),
|
||||
label = "lottie"
|
||||
)
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize().background(backgroundColor).navigationBarsPadding()) {
|
||||
Column(modifier = Modifier.fillMaxSize().statusBarsPadding()) {
|
||||
// Top Bar - как в auth flow
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
IconButton(onClick = onBack, enabled = !isLoading) {
|
||||
Icon(
|
||||
imageVector = TablerIcons.ArrowLeft,
|
||||
contentDescription = "Back",
|
||||
tint = textColor.copy(alpha = 0.6f)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
text = "Biometric",
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = textColor
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Spacer(modifier = Modifier.width(48.dp))
|
||||
}
|
||||
|
||||
// Content
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.imePadding()
|
||||
.padding(horizontal = 24.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(if (isKeyboardVisible) 8.dp else 16.dp))
|
||||
|
||||
// Lottie Animation
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
enter = fadeIn(tween(250)) + scaleIn(
|
||||
initialScale = 0.5f,
|
||||
animationSpec = tween(250, easing = FastOutSlowInEasing)
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.size(lottieSize),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (lockComposition != null) {
|
||||
LottieAnimation(
|
||||
composition = lockComposition,
|
||||
progress = { lockProgress },
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.graphicsLayer {
|
||||
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen
|
||||
},
|
||||
maintainOriginalImageBounds = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(if (isKeyboardVisible) 12.dp else 24.dp))
|
||||
|
||||
// Title
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
enter = fadeIn(tween(400, delayMillis = 100))
|
||||
) {
|
||||
Text(
|
||||
text = if (showSuccess) "Biometric Enabled!" else "Enable Biometric",
|
||||
fontSize = if (isKeyboardVisible) 20.sp else 24.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = if (showSuccess) PrimaryBlue else textColor
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(if (isKeyboardVisible) 6.dp else 8.dp))
|
||||
|
||||
// Subtitle
|
||||
AnimatedVisibility(
|
||||
visible = visible && !showSuccess,
|
||||
enter = fadeIn(tween(500, delayMillis = 200)),
|
||||
exit = fadeOut(tween(200))
|
||||
) {
|
||||
Text(
|
||||
text = "Use Face ID or Touch ID to quickly\nunlock your account.",
|
||||
fontSize = if (isKeyboardVisible) 12.sp else 14.sp,
|
||||
color = secondaryTextColor,
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = if (isKeyboardVisible) 16.sp else 20.sp
|
||||
)
|
||||
}
|
||||
|
||||
// Success message
|
||||
AnimatedVisibility(
|
||||
visible = showSuccess,
|
||||
enter = fadeIn(tween(300)) + expandVertically()
|
||||
) {
|
||||
Text(
|
||||
text = "You can now unlock with biometrics",
|
||||
fontSize = 14.sp,
|
||||
color = secondaryTextColor,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(if (isKeyboardVisible) 16.dp else 32.dp))
|
||||
|
||||
// Password Field
|
||||
AnimatedVisibility(
|
||||
visible = visible && !showSuccess,
|
||||
enter = fadeIn(tween(400, delayMillis = 300)),
|
||||
exit = fadeOut(tween(200))
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
onValueChange = {
|
||||
password = it
|
||||
passwordError = null
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = {
|
||||
Text(
|
||||
"Enter your password",
|
||||
color = secondaryTextColor.copy(alpha = 0.6f)
|
||||
)
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = TablerIcons.Lock,
|
||||
contentDescription = null,
|
||||
tint = if (passwordError != null) errorRed else secondaryTextColor,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { passwordVisible = !passwordVisible }) {
|
||||
Icon(
|
||||
imageVector = if (passwordVisible) TablerIcons.EyeOff else TablerIcons.Eye,
|
||||
contentDescription = if (passwordVisible) "Hide" else "Show",
|
||||
tint = secondaryTextColor,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
}
|
||||
},
|
||||
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
|
||||
isError = passwordError != null,
|
||||
singleLine = true,
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
focusedBorderColor = PrimaryBlue,
|
||||
unfocusedBorderColor = if (isDarkTheme) Color(0xFF48484A) else Color(0xFFD1D1D6),
|
||||
focusedContainerColor = cardColor,
|
||||
unfocusedContainerColor = cardColor,
|
||||
focusedTextColor = textColor,
|
||||
unfocusedTextColor = textColor,
|
||||
cursorColor = PrimaryBlue,
|
||||
errorBorderColor = errorRed,
|
||||
errorContainerColor = cardColor
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Password,
|
||||
imeAction = ImeAction.Done
|
||||
)
|
||||
)
|
||||
|
||||
// Error message
|
||||
AnimatedVisibility(
|
||||
visible = passwordError != null,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically()
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(top = 8.dp, start = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = TablerIcons.AlertCircle,
|
||||
contentDescription = null,
|
||||
tint = errorRed,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
Text(
|
||||
text = passwordError ?: "",
|
||||
color = errorRed,
|
||||
fontSize = 13.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// Enable button
|
||||
AnimatedVisibility(
|
||||
visible = visible && !showSuccess,
|
||||
enter = fadeIn(tween(400, delayMillis = 400)),
|
||||
exit = fadeOut(tween(200))
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
if (password.isEmpty()) {
|
||||
passwordError = "Please enter your password"
|
||||
return@Button
|
||||
}
|
||||
|
||||
hideKeyboard()
|
||||
isLoading = true
|
||||
onEnable(
|
||||
password,
|
||||
{
|
||||
isLoading = false
|
||||
showSuccess = true
|
||||
},
|
||||
{ error ->
|
||||
isLoading = false
|
||||
passwordError = error
|
||||
}
|
||||
)
|
||||
},
|
||||
enabled = password.isNotEmpty() && !isLoading,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(54.dp),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = PrimaryBlue,
|
||||
contentColor = Color.White,
|
||||
disabledContainerColor = PrimaryBlue.copy(alpha = 0.4f),
|
||||
disabledContentColor = Color.White.copy(alpha = 0.6f)
|
||||
)
|
||||
) {
|
||||
if (isLoading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(22.dp),
|
||||
color = Color.White,
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = TablerIcons.Fingerprint,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Text(
|
||||
text = "Enable Biometric",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
// Info - hide when keyboard visible
|
||||
AnimatedVisibility(
|
||||
visible = visible && !isKeyboardVisible && !showSuccess,
|
||||
enter = fadeIn(tween(300)),
|
||||
exit = fadeOut(tween(200))
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(cardColor)
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.Top
|
||||
) {
|
||||
Icon(
|
||||
imageVector = TablerIcons.ShieldLock,
|
||||
contentDescription = null,
|
||||
tint = PrimaryBlue,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Text(
|
||||
text = "Your password is encrypted and stored securely on this device only.",
|
||||
fontSize = 13.sp,
|
||||
color = secondaryTextColor,
|
||||
lineHeight = 18.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,6 +191,7 @@ fun ProfileScreen(
|
||||
onNavigateToSafety: () -> Unit = {},
|
||||
onNavigateToLogs: () -> Unit = {},
|
||||
onNavigateToCrashLogs: () -> Unit = {},
|
||||
onNavigateToBiometric: () -> Unit = {},
|
||||
viewModel: ProfileViewModel = androidx.lifecycle.viewmodel.compose.viewModel(),
|
||||
avatarRepository: AvatarRepository? = null,
|
||||
dialogDao: com.rosetta.messenger.database.DialogDao? = null
|
||||
@@ -198,22 +199,20 @@ fun ProfileScreen(
|
||||
val context = LocalContext.current
|
||||
val activity = context as? FragmentActivity
|
||||
val biometricManager = remember { BiometricAuthManager(context) }
|
||||
val biometricPrefs = remember { BiometricPreferences(context) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
// Biometric state
|
||||
// Biometric availability
|
||||
var biometricAvailable by remember {
|
||||
mutableStateOf<BiometricAvailability>(BiometricAvailability.NotAvailable("Checking..."))
|
||||
}
|
||||
var isBiometricEnabled by remember { mutableStateOf(false) }
|
||||
var showPasswordDialog by remember { mutableStateOf(false) }
|
||||
var passwordInput by remember { mutableStateOf("") }
|
||||
var passwordError by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
// Biometric enabled state - read directly to always show current state
|
||||
val biometricPrefs = remember { BiometricPreferences(context) }
|
||||
val isBiometricEnabled by biometricPrefs.isBiometricEnabled.collectAsState(initial = false)
|
||||
|
||||
// Check biometric availability
|
||||
LaunchedEffect(Unit) {
|
||||
biometricAvailable = biometricManager.isBiometricAvailable()
|
||||
isBiometricEnabled = biometricPrefs.isBiometricEnabled.first()
|
||||
}
|
||||
|
||||
// Состояние меню аватара для установки фото профиля
|
||||
@@ -627,32 +626,17 @@ fun ProfileScreen(
|
||||
showDivider = biometricAvailable is BiometricAvailability.Available
|
||||
)
|
||||
|
||||
// Biometric toggle (only show if available)
|
||||
// Biometric settings (only show if available)
|
||||
if (biometricAvailable is BiometricAvailability.Available && activity != null) {
|
||||
TelegramBiometricItem(
|
||||
isEnabled = isBiometricEnabled,
|
||||
onToggle = {
|
||||
if (isBiometricEnabled) {
|
||||
// Disable biometric
|
||||
scope.launch {
|
||||
biometricPrefs.disableBiometric()
|
||||
biometricPrefs.removeEncryptedPassword(accountPublicKey)
|
||||
isBiometricEnabled = false
|
||||
android.widget.Toast.makeText(
|
||||
context,
|
||||
"Biometric authentication disabled",
|
||||
android.widget.Toast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
// Enable biometric - show password dialog first
|
||||
passwordInput = ""
|
||||
passwordError = null
|
||||
showPasswordDialog = true
|
||||
}
|
||||
TelegramSettingsItem(
|
||||
icon = TablerIcons.Fingerprint,
|
||||
title = "Biometric Authentication",
|
||||
onClick = {
|
||||
onNavigateToBiometric()
|
||||
},
|
||||
isDarkTheme = isDarkTheme
|
||||
isDarkTheme = isDarkTheme,
|
||||
showDivider = false,
|
||||
subtitle = if (isBiometricEnabled) "Enabled" else "Disabled"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -713,96 +697,6 @@ fun ProfileScreen(
|
||||
)
|
||||
}
|
||||
|
||||
// Password dialog for biometric setup
|
||||
if (showPasswordDialog && activity != null) {
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
showPasswordDialog = false
|
||||
passwordInput = ""
|
||||
passwordError = null
|
||||
},
|
||||
title = { Text("Enable Biometric Authentication") },
|
||||
text = {
|
||||
Column {
|
||||
Text("Enter your password to securely save it for biometric unlock:")
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
OutlinedTextField(
|
||||
value = passwordInput,
|
||||
onValueChange = {
|
||||
passwordInput = it
|
||||
passwordError = null
|
||||
},
|
||||
label = { Text("Password") },
|
||||
singleLine = true,
|
||||
visualTransformation =
|
||||
androidx.compose.ui.text.input
|
||||
.PasswordVisualTransformation(),
|
||||
isError = passwordError != null,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
if (passwordError != null) {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(text = passwordError!!, color = Color.Red, fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
if (passwordInput.isEmpty()) {
|
||||
passwordError = "Password cannot be empty"
|
||||
return@TextButton
|
||||
}
|
||||
|
||||
// Try to encrypt the password with biometric
|
||||
biometricManager.encryptPassword(
|
||||
activity = activity,
|
||||
password = passwordInput,
|
||||
onSuccess = { encryptedPassword ->
|
||||
scope.launch {
|
||||
// Save encrypted password
|
||||
biometricPrefs.saveEncryptedPassword(
|
||||
accountPublicKey,
|
||||
encryptedPassword
|
||||
)
|
||||
// Enable biometric
|
||||
biometricPrefs.enableBiometric()
|
||||
isBiometricEnabled = true
|
||||
|
||||
showPasswordDialog = false
|
||||
passwordInput = ""
|
||||
passwordError = null
|
||||
|
||||
android.widget.Toast.makeText(
|
||||
context,
|
||||
"Biometric authentication enabled successfully",
|
||||
android.widget.Toast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
}
|
||||
},
|
||||
onError = { error -> passwordError = error },
|
||||
onCancel = {
|
||||
showPasswordDialog = false
|
||||
passwordInput = ""
|
||||
passwordError = null
|
||||
}
|
||||
)
|
||||
}
|
||||
) { Text("Enable") }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
showPasswordDialog = false
|
||||
passwordInput = ""
|
||||
passwordError = null
|
||||
}
|
||||
) { Text("Cancel") }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 🖼️ Кастомный быстрый Photo Picker
|
||||
ProfilePhotoPicker(
|
||||
isVisible = showPhotoPicker,
|
||||
@@ -1342,9 +1236,11 @@ private fun TelegramSettingsItem(
|
||||
title: String,
|
||||
onClick: () -> Unit,
|
||||
isDarkTheme: Boolean,
|
||||
showDivider: Boolean = false
|
||||
showDivider: Boolean = false,
|
||||
subtitle: String? = null
|
||||
) {
|
||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||
val iconColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||
val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE0E0E0)
|
||||
|
||||
@@ -1365,7 +1261,13 @@ private fun TelegramSettingsItem(
|
||||
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
|
||||
Text(text = title, fontSize = 16.sp, color = textColor, modifier = Modifier.weight(1f))
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(text = title, fontSize = 16.sp, color = textColor)
|
||||
if (subtitle != null) {
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Text(text = subtitle, fontSize = 13.sp, color = secondaryTextColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showDivider) {
|
||||
|
||||
Reference in New Issue
Block a user