package profiles.presentation import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.CalendarToday import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Clear 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.layout.ContentScale import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.platform.LocalContext import coil.compose.AsyncImage import core.presentation.components.AppAvatar import core.presentation.theme.SoftSquareShape import ru.knot.messager.R import android.app.DatePickerDialog import java.text.SimpleDateFormat import java.util.* import android.app.Activity import android.content.Intent import com.yalantis.ucrop.UCrop import core.utils.ImageCropper import java.io.File import java.util.UUID @OptIn(ExperimentalMaterial3Api::class) @Composable fun EditProfileScreen( currentDisplayName: String?, currentUsername: String, currentBio: String?, currentBirthday: String?, currentAvatarUrl: String?, isSaving: Boolean = false, error: String? = null, onSave: (displayName: String, bio: String, birthday: String?, avatarUri: Uri?) -> Unit, onBack: () -> Unit ) { val context = LocalContext.current var displayName by remember(currentDisplayName) { mutableStateOf(currentDisplayName ?: "") } var bio by remember(currentBio) { mutableStateOf(currentBio ?: "") } var birthday by remember(currentBirthday) { mutableStateOf(currentBirthday ?: "") } var selectedImageUri by remember { mutableStateOf(null) } var showDatePicker by remember { mutableStateOf(false) } // Парсим текущую дату для инициализации DatePicker и отображения val calendar = Calendar.getInstance() var formattedBirthday by remember { mutableStateOf("") } if (birthday.isNotEmpty()) { try { // Пробуем распарсить ISO8601 формат (yyyy-MM-dd или yyyy-MM-ddTHH:mm:ss) val format = when { birthday.contains("T") -> SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()) birthday.length == 10 -> SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) else -> SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()) } format.parse(birthday)?.let { calendar.time = it // Форматируем для отображения: ДД.ММ.ГГГГ formattedBirthday = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()).format(it) } } catch (e: Exception) { formattedBirthday = birthday } } // DatePickerDialog if (showDatePicker) { DatePickerDialog( context, { _, year, month, dayOfMonth -> val selectedCalendar = Calendar.getInstance() selectedCalendar.set(year, month, dayOfMonth) // Используем ISO8601 формат для отправки на сервер val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) birthday = format.format(selectedCalendar.time) }, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH) ).apply { datePicker.maxDate = System.currentTimeMillis() show() } showDatePicker = false } // Лаунчер для результата кропа val cropLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult(), onResult = { result -> if (result.resultCode == Activity.RESULT_OK && result.data != null) { selectedImageUri = UCrop.getOutput(result.data!!) } } ) // Лаунчер для выбора фото из галереи val photoPickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.PickVisualMedia(), onResult = { uri -> if (uri != null) { // После выбора фото запускаем UCrop val destinationUri = Uri.fromFile(File(context.cacheDir, "${UUID.randomUUID()}.jpg")) val cropIntent = ImageCropper.getCropIntent(context, uri, destinationUri).getIntent(context) cropLauncher.launch(cropIntent) } } ) Scaffold( topBar = { TopAppBar( title = { Text(stringResource(R.string.edit_profile), fontWeight = FontWeight.Bold) }, navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.Default.ArrowBack, contentDescription = stringResource(R.string.back)) } }, actions = { TextButton( onClick = { onSave(displayName, bio, birthday, selectedImageUri) }, enabled = !isSaving ) { Text( if (isSaving) stringResource(R.string.saving) else stringResource(R.string.save), fontWeight = FontWeight.Bold, color = if (isSaving) Color.Gray else MaterialTheme.colorScheme.primary ) } }, colors = TopAppBarDefaults.topAppBarColors( containerColor = Color.Transparent, titleContentColor = Color.White ) ) } ) { paddingValues -> Column( modifier = Modifier .fillMaxSize() .padding(paddingValues) .verticalScroll(rememberScrollState()) .padding(24.dp), horizontalAlignment = Alignment.CenterHorizontally ) { // Смена фото Box( modifier = Modifier .size(120.dp) .clip(SoftSquareShape) .background(MaterialTheme.colorScheme.surfaceVariant) .clickable { photoPickerLauncher.launch( PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly) ) }, contentAlignment = Alignment.Center ) { if (selectedImageUri != null) { AsyncImage( model = selectedImageUri, contentDescription = null, modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Crop ) } else { AppAvatar( url = currentAvatarUrl, name = currentUsername, size = 120.dp ) } // Overlay Box( modifier = Modifier .fillMaxSize() .background(Color.Black.copy(alpha = 0.4f)), contentAlignment = Alignment.Center ) { Icon( Icons.Default.CameraAlt, contentDescription = stringResource(R.string.change_photo), tint = Color.White.copy(alpha = 0.8f), modifier = Modifier.size(28.dp) ) } } TextButton( onClick = { photoPickerLauncher.launch( PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly) ) }, modifier = Modifier.padding(top = 8.dp) ) { Text(stringResource(R.string.change_photo)) } Spacer(modifier = Modifier.height(32.dp)) // Поля ввода OutlinedTextField( value = displayName, onValueChange = { displayName = it }, label = { Text(stringResource(R.string.display_name)) }, modifier = Modifier.fillMaxWidth(), singleLine = true, shape = SoftSquareShape ) Spacer(modifier = Modifier.height(16.dp)) OutlinedTextField( value = "@$currentUsername", onValueChange = {}, label = { Text(stringResource(R.string.username_label)) }, modifier = Modifier.fillMaxWidth(), enabled = false, // Обычно username меняется отдельно или вообще не меняется shape = SoftSquareShape, colors = OutlinedTextFieldDefaults.colors( disabledBorderColor = MaterialTheme.colorScheme.outlineVariant, disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant ) ) Spacer(modifier = Modifier.height(16.dp)) OutlinedTextField( value = bio, onValueChange = { bio = it }, label = { Text(stringResource(R.string.bio)) }, modifier = Modifier .fillMaxWidth() .heightIn(min = 100.dp), shape = SoftSquareShape, maxLines = 5 ) Spacer(modifier = Modifier.height(16.dp)) OutlinedTextField( value = formattedBirthday, onValueChange = { }, label = { Text("Дата рождения") }, modifier = Modifier.fillMaxWidth(), singleLine = true, shape = SoftSquareShape, placeholder = { Text("ДД.ММ.ГГГГ") }, trailingIcon = { Row { if (birthday.isNotEmpty()) { IconButton(onClick = { birthday = "" }) { Icon( Icons.Default.Clear, contentDescription = "Очистить", tint = Color.Gray ) } } IconButton(onClick = { showDatePicker = true }) { Icon( Icons.Default.CalendarToday, contentDescription = "Выбрать дату", tint = MaterialTheme.colorScheme.primary ) } } } ) if (error != null) { Text( text = error, color = MaterialTheme.colorScheme.error, modifier = Modifier.padding(top = 8.dp) ) } Spacer(modifier = Modifier.height(32.dp)) Button( onClick = { onSave(displayName, bio, birthday, selectedImageUri) }, modifier = Modifier.fillMaxWidth(), shape = SoftSquareShape, enabled = !isSaving ) { if (isSaving) { CircularProgressIndicator( modifier = Modifier.size(24.dp), color = Color.White, strokeWidth = 2.dp ) Spacer(modifier = Modifier.width(8.dp)) Text(stringResource(R.string.saving)) } else { Text(stringResource(R.string.save)) } } } } }