package profiles.presentation import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.* import androidx.compose.foundation.lazy.grid.* import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* 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.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import coil.compose.AsyncImage import core.presentation.components.AppAvatar import core.presentation.theme.SoftSquareShape import kotlinx.coroutines.launch import profiles.presentation.viewmodel.ProfileViewModel import ru.knot.messager.R @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable fun ProfileScreen( profileId: String? = null, // null for own profile viewModel: ProfileViewModel = hiltViewModel(), onEditProfile: () -> Unit = {}, onSendMessage: (String) -> Unit = {}, onCall: (String) -> Unit = {}, onBack: () -> Unit = {} ) { val state by viewModel.state.collectAsState() // Загружаем профиль при первом запуске LaunchedEffect(Unit) { android.util.Log.d("ProfileScreen", "Loading profile with profileId=$profileId") viewModel.loadProfile(profileId) } val profile = state.profile val isOwnProfile = profileId == null android.util.Log.d("ProfileScreen", "Profile loaded: displayName=${profile?.displayName}, bio=${profile?.bio}, birthday=${profile?.birthday}") val mediaTabs = listOf( stringResource(R.string.media), "GIF", "Файлы", "Ссылки" ) val pagerState = rememberPagerState(pageCount = { mediaTabs.size }) val coroutineScope = rememberCoroutineScope() if (state.isLoading) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } return } Scaffold( topBar = { TopAppBar( title = { Text(if (isOwnProfile) stringResource(R.string.profile) else stringResource(R.string.profile), fontWeight = FontWeight.Bold) }, navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.Default.ArrowBack, contentDescription = stringResource(R.string.back)) } }, actions = { if (isOwnProfile) { IconButton(onClick = onEditProfile) { Icon(Icons.Default.Edit, contentDescription = stringResource(R.string.edit_profile)) } } }, colors = TopAppBarDefaults.topAppBarColors( containerColor = Color.Transparent, titleContentColor = Color.White, actionIconContentColor = Color.White, navigationIconContentColor = Color.White ) ) } ) { paddingValues -> LazyColumn( modifier = Modifier .fillMaxSize() .padding(paddingValues) ) { item { ProfileHeader( username = profile?.username ?: "", displayName = profile?.displayName, avatarUrl = profile?.avatarUrl, bio = profile?.bio, birthday = profile?.birthday, isOwnProfile = isOwnProfile, isCallsEnabled = true, onSendMessage = { profile?.id?.let { id -> onSendMessage(id) } }, onCall = { profile?.id?.let { id -> onCall(id) } } ) } stickyHeader { Surface(modifier = Modifier.fillMaxWidth()) { ScrollableTabRow( selectedTabIndex = pagerState.currentPage, edgePadding = 16.dp, containerColor = MaterialTheme.colorScheme.surface, divider = {} ) { mediaTabs.forEachIndexed { index, title -> Tab( selected = pagerState.currentPage == index, onClick = { coroutineScope.launch { pagerState.animateScrollToPage(index) } }, text = { Text(title) } ) } } } } // Using item with fillParentMaxSize for Pager inside LazyColumn // is tricky, but here we render content directly in LazyColumn // to maintain scrolling, or use a fixed height. // For gestures, we implement HorizontalPager for the content: } // Alternative: Use HorizontalPager for the whole content area below header // But to keep header scrolling, we need nested scroll or a different approach. // Let's implement HorizontalPager for media content items specifically. } } @Composable fun ProfileHeader( username: String, displayName: String?, avatarUrl: String?, bio: String?, birthday: String?, isOwnProfile: Boolean, isCallsEnabled: Boolean, onSendMessage: () -> Unit, onCall: () -> Unit ) { Column( modifier = Modifier .fillMaxWidth() .padding(24.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Box(contentAlignment = Alignment.BottomEnd) { AppAvatar( url = avatarUrl, name = username, size = 120.dp ) if (isOwnProfile) { FilledIconButton( onClick = { /* Выбор фото */ }, modifier = Modifier.size(32.dp).offset(x = 4.dp, y = 4.dp), shape = CircleShape, colors = IconButtonDefaults.filledIconButtonColors(containerColor = MaterialTheme.colorScheme.primary) ) { Icon(Icons.Default.CameraAlt, contentDescription = null, modifier = Modifier.size(16.dp)) } } } Spacer(modifier = Modifier.height(16.dp)) Text(text = displayName ?: username, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold) Text(text = "@$username", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant) if (!isOwnProfile) { Spacer(modifier = Modifier.height(24.dp)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly ) { ActionCircleButton(Icons.Default.Message, stringResource(R.string.message), onSendMessage) if (isCallsEnabled) { ActionCircleButton(Icons.Default.Call, stringResource(R.string.call), onCall) } ActionCircleButton(Icons.Default.Notifications, stringResource(R.string.notifications), {}) } } if (bio != null || birthday != null) { Spacer(modifier = Modifier.height(24.dp)) Column(modifier = Modifier.fillMaxWidth()) { if (birthday != null) { Text(text = "Дата рождения", style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary) // Форматируем дату из ISO8601 в dd.MM.yyyy val formattedBirthday = try { val format = java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.getDefault()) val date = format.parse(birthday) val outputFormat = java.text.SimpleDateFormat("dd.MM.yyyy", java.util.Locale.getDefault()) date?.let { outputFormat.format(it) } ?: birthday } catch (e: Exception) { birthday } Text(text = formattedBirthday, style = MaterialTheme.typography.bodyMedium) Spacer(modifier = Modifier.height(8.dp)) } if (bio != null) { Text(text = stringResource(R.string.bio), style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary) Text(text = bio, style = MaterialTheme.typography.bodyMedium) } } } } } fun LazyListScope.renderMediaContent(tabIndex: Int) { // В реальном приложении данные приходят из ViewModel // Группировка по месяцам val months = listOf("Октябрь 2023", "Сентябрь 2023") months.forEach { month -> item { Text( text = month, modifier = Modifier.padding(16.dp, 8.dp), style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) } when (tabIndex) { 0 -> renderMediaGrid() // Медиа (сетка 3x3) 1 -> renderGifs() // GIF 2 -> renderFiles() // Файлы 3 -> renderLinks() // Ссылки } } } // Вспомогательные функции для рендеринга контента... fun LazyListScope.renderMediaGrid() { item { // Упрощенная сетка внутри LazyColumn Row(modifier = Modifier.padding(horizontal = 16.dp)) { repeat(3) { Box( modifier = Modifier .weight(1f) .aspectRatio(1f) .padding(2.dp) .clip(RoundedCornerShape(8.dp)) .background(Color.Gray.copy(alpha = 0.2f)) ) } } } } fun LazyListScope.renderFiles() { items(3) { ListItem( headlineContent = { Text("Document.pdf") }, supportingContent = { Text("2.4 MB • 12.10.23") }, leadingContent = { Icon(Icons.Default.Description, contentDescription = null) } ) } } fun LazyListScope.renderLinks() { items(2) { ListItem( headlineContent = { Text("https://github.com/forkmessager") }, supportingContent = { Text("GitHub - ForkMessager Project") }, leadingContent = { Icon(Icons.Default.Link, contentDescription = null) } ) } } fun LazyListScope.renderGifs() { item { Row(modifier = Modifier.padding(horizontal = 16.dp)) { repeat(2) { Box( modifier = Modifier .weight(1f) .height(120.dp) .padding(2.dp) .clip(RoundedCornerShape(8.dp)) .background(Color.Gray.copy(alpha = 0.1f)), contentAlignment = Alignment.Center ) { Text("GIF", style = MaterialTheme.typography.labelSmall) } } } } } @Composable fun ActionCircleButton( icon: androidx.compose.ui.graphics.vector.ImageVector, label: String, onClick: () -> Unit, color: Color = MaterialTheme.colorScheme.primary ) { Column(horizontalAlignment = Alignment.CenterHorizontally) { FilledIconButton( onClick = onClick, modifier = Modifier.size(56.dp), colors = IconButtonDefaults.filledIconButtonColors(containerColor = color.copy(alpha = 0.1f)) ) { Icon(icon, contentDescription = label, tint = color) } Spacer(modifier = Modifier.height(4.dp)) Text(text = label, style = MaterialTheme.typography.labelSmall) } }