package chats.presentation.components import androidx.compose.animation.* import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.rounded.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext 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 coil.compose.AsyncImage import chats.data.remote.api.KlipyGifDto import core.utils.CoilUtils import kotlinx.coroutines.launch enum class MediaTab { EMOJI, GIF } @Composable fun MediaPicker( trendingGifs: List, searchedGifs: List, recentGifs: List = emptyList(), gifCategories: List = emptyList(), isGifsLoading: Boolean, error: String?, onEmojiSelected: (String) -> Unit, onGifSelected: (String) -> Unit, onGifSearch: (String) -> Unit, modifier: Modifier = Modifier ) { var activeTab by remember { mutableStateOf(MediaTab.EMOJI) } // Вычисляем индексы для прыжков (учитываем заголовки) val emojiCategories = remember { listOf( EmojiCategory("Недавние", Icons.Rounded.Schedule, listOf("👍", "❤️", "😂", "😍", "🙏", "🔥", "😭", "😊")), EmojiCategory("Смайлы", Icons.Rounded.EmojiEmotions, listOf("😀", "😃", "😄", "😁", "😆", "😅", "🤣", "😂", "🙂", "🙃", "😉", "😊", "😇")), EmojiCategory("Животные", Icons.Rounded.Pets, listOf("🐶", "🐱", "🐭", "🐹", "🐰", "🦊", "🐻", "🐼", "🐨", "🐯", "🦁", "🐮", "🐷")), EmojiCategory("Еда", Icons.Rounded.Fastfood, listOf("🍎", "🍐", "🍊", "🍋", "🍌", "🍉", "🍇", "🍓", "🫐", "🍈", "🍒", "🍑", "🥭")), EmojiCategory("Спорт", Icons.Rounded.SportsFootball, listOf("⚽", "🏀", "🏈", "⚾", "🎾", "🏐", "🏉", "🎱", "🏓", "🏸", "🥅", "🏒")), EmojiCategory("Транспорт", Icons.Rounded.DirectionsCar, listOf("🚗", "🚕", "🚙", "🚌", "🚎", "🏎️", "🚓", "🚑", "🚒", "🚐", "🚚", "🚛")), EmojiCategory("Объекты", Icons.Rounded.Lightbulb, listOf("💡", "🔦", "🕯️", "🪔", "📔", "📕", "📖", "💚", "💙", "💜", "🤎", "🖤")), EmojiCategory("Музыка", Icons.Rounded.MusicNote, listOf("🎵", "🎶", "🎼", "🎹", "🎸", "🎻", "🎷", "🎺", "🥁", "🎤", "🎧", "📻")), EmojiCategory("Флаги", Icons.Rounded.Flag, listOf("🏁", "🚩", "🎌", "🏴", "🏳️", "🏳️‍🌈", "🏳️‍⚧️", "🏴‍☠️", "🇦🇱", "🇩🇿", "🇦🇸", "🇦🇩")) ) } val categoryIndices = remember(emojiCategories) { var currentIdx = 0 emojiCategories.map { cat -> val startIdx = currentIdx currentIdx += 1 + cat.emojis.size cat.icon to startIdx } } Column( modifier = modifier .fillMaxWidth() .height(450.dp) .background(Color(0xFF1C1C1C), RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)) ) { // Табы (Эмодзи / GIF) Row( modifier = Modifier .fillMaxWidth() .padding(8.dp) .background(Color(0xFF2C2C2C), RoundedCornerShape(12.dp)) .padding(2.dp) ) { MediaTabItem( title = "ЭМОДЗИ", isSelected = activeTab == MediaTab.EMOJI, onClick = { activeTab = MediaTab.EMOJI }, modifier = Modifier.weight(1f) ) MediaTabItem( title = "GIF", isSelected = activeTab == MediaTab.GIF, onClick = { activeTab = MediaTab.GIF }, modifier = Modifier.weight(1f) ) } Crossfade(targetState = activeTab, label = "media_tab") { tab -> when (tab) { MediaTab.EMOJI -> EmojiContent( emojiCategories = emojiCategories, categoryIndices = categoryIndices, onEmojiSelected = onEmojiSelected ) MediaTab.GIF -> GifContent( trendingGifs = trendingGifs, searchedGifs = searchedGifs, recentGifs = recentGifs, gifCategories = gifCategories, isGifsLoading = isGifsLoading, error = error, onGifSelected = onGifSelected, onGifSearch = onGifSearch ) } } } } @Composable private fun MediaTabItem( title: String, isSelected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier ) { Box( modifier = modifier .clip(RoundedCornerShape(10.dp)) .background(if (isSelected) Color(0xFF3C3C3C) else Color.Transparent) .clickable { onClick() } .padding(vertical = 8.dp), contentAlignment = Alignment.Center ) { Text( text = title, color = if (isSelected) Color.White else Color.Gray, style = MaterialTheme.typography.labelLarge, fontWeight = FontWeight.Bold ) } } @Composable private fun EmojiContent( emojiCategories: List, categoryIndices: List>, onEmojiSelected: (String) -> Unit ) { val scrollState = androidx.compose.foundation.lazy.grid.rememberLazyGridState() val scope = rememberCoroutineScope() Column(modifier = Modifier.fillMaxSize()) { // Категории LazyRow( modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), horizontalArrangement = Arrangement.spacedBy(12.dp) ) { items(categoryIndices) { (icon, index) -> Icon( imageVector = icon, contentDescription = null, tint = Color.Gray, modifier = Modifier .size(24.dp) .clickable { scope.launch { scrollState.animateScrollToItem(index) } } ) } } // Поиск TextField( value = "", onValueChange = {}, modifier = Modifier .fillMaxWidth() .padding(8.dp) .clip(RoundedCornerShape(8.dp)), placeholder = { Text("Поиск", color = Color.Gray) }, leadingIcon = { Icon(Icons.Default.Search, contentDescription = null, tint = Color.Gray) }, colors = TextFieldDefaults.colors( focusedContainerColor = Color(0xFF2C2C2C), unfocusedContainerColor = Color(0xFF2C2C2C), focusedIndicatorColor = Color.Transparent, unfocusedIndicatorColor = Color.Transparent ) ) LazyVerticalGrid( columns = GridCells.Fixed(8), state = scrollState, modifier = Modifier.weight(1f).padding(horizontal = 4.dp) ) { emojiCategories.forEach { category -> // Заголовок категории item(span = { androidx.compose.foundation.lazy.grid.GridItemSpan(8) }) { Text( text = category.name, color = Color.Gray, fontSize = 12.sp, fontWeight = FontWeight.Medium, modifier = Modifier.padding(8.dp) ) } items(category.emojis) { emoji -> Box( modifier = Modifier .aspectRatio(1f) .clickable { onEmojiSelected(emoji) }, contentAlignment = Alignment.Center ) { Text(text = emoji, fontSize = 28.sp) } } } } } } data class EmojiCategory(val name: String, val icon: ImageVector, val emojis: List) @Composable private fun GifContent( trendingGifs: List, searchedGifs: List, recentGifs: List, gifCategories: List, isGifsLoading: Boolean, error: String?, onGifSelected: (String) -> Unit, onGifSearch: (String) -> Unit ) { var query by remember { mutableStateOf("") } var subTab by remember { mutableStateOf("trending") } val context = LocalContext.current val imageLoader = CoilUtils.getGifImageLoader(context) Column(modifier = Modifier.fillMaxSize()) { TextField( value = query, onValueChange = { query = it if (it.isNotEmpty()) subTab = "search" else if (subTab == "search") subTab = "trending" onGifSearch(it) }, modifier = Modifier .fillMaxWidth() .padding(horizontal = 8.dp, vertical = 4.dp) .clip(RoundedCornerShape(8.dp)), placeholder = { Text("Поиск GIF...", color = Color.Gray) }, leadingIcon = { Icon(Icons.Default.Search, contentDescription = null, tint = Color.Gray) }, colors = TextFieldDefaults.colors( focusedContainerColor = Color(0xFF2C2C2C), unfocusedContainerColor = Color(0xFF2C2C2C), focusedIndicatorColor = Color.Transparent, unfocusedIndicatorColor = Color.Transparent ) ) // Под-вкладки GIF (Recent, Trending, Categories) Row( modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically ) { Icon( Icons.Rounded.Schedule, contentDescription = null, tint = if (subTab == "recent") Color(0xFF3B82F6) else Color.Gray, modifier = Modifier.size(20.dp).clickable { subTab = "recent"; query = "" } ) Icon( Icons.Rounded.TrendingUp, contentDescription = null, tint = if (subTab == "trending") Color(0xFF3B82F6) else Color.Gray, modifier = Modifier.size(20.dp).clickable { subTab = "trending"; query = "" } ) Icon( Icons.Rounded.GridView, contentDescription = null, tint = if (subTab == "categories") Color(0xFF3B82F6) else Color.Gray, modifier = Modifier.size(20.dp).clickable { subTab = "categories"; query = "" } ) } if (subTab == "categories") { LazyVerticalGrid( columns = GridCells.Fixed(2), modifier = Modifier.weight(1f).padding(4.dp), contentPadding = PaddingValues(4.dp) ) { items(gifCategories) { category -> Box( modifier = Modifier .padding(4.dp) .aspectRatio(1.77f) .clip(RoundedCornerShape(12.dp)) .background(Color(0xFF2C2C2C)) .clickable { query = category.query subTab = "search" onGifSearch(category.query) } ) { AsyncImage( model = category.preview_url, contentDescription = category.category, modifier = Modifier.fillMaxSize().alpha(0.6f), contentScale = ContentScale.Crop ) Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Text( text = category.category.uppercase(), color = Color.White, fontSize = 10.sp, fontWeight = FontWeight.Black, letterSpacing = 2.sp ) } } } } } else { val displayGifs = when (subTab) { "recent" -> recentGifs "search" -> searchedGifs else -> trendingGifs } if (isGifsLoading && displayGifs.isEmpty()) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator(color = Color(0xFF3B82F6)) } } else if (!isGifsLoading && displayGifs.isEmpty()) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Column(horizontalAlignment = Alignment.CenterHorizontally) { Text(error ?: "Ничего не найдено", color = Color.Gray) if (error != null) { Button(onClick = { onGifSearch(query) }) { Text("Повторить") } } } } } else { LazyVerticalGrid( columns = GridCells.Fixed(3), modifier = Modifier.weight(1f).padding(4.dp), contentPadding = PaddingValues(4.dp) ) { items(displayGifs) { gif -> val url = gif.files?.get("hd")?.get("gif")?.url ?: gif.files?.get("sd")?.get("gif")?.url ?: gif.file?.get("hd")?.get("gif")?.url ?: gif.file?.get("sd")?.get("gif")?.url ?: gif.media_formats?.get("gif")?.url ?: gif.images?.original?.url val preview = gif.files?.get("sd")?.get("webp")?.url ?: gif.files?.get("sd")?.get("gif")?.url ?: gif.file?.get("sd")?.get("webp")?.url ?: gif.media_formats?.get("tinygif")?.url ?: gif.images?.fixed_height_small?.url ?: url if (url != null) { AsyncImage( model = preview ?: url, imageLoader = imageLoader, contentDescription = gif.title, modifier = Modifier .padding(2.dp) .aspectRatio(1f) .clip(RoundedCornerShape(8.dp)) .background(Color(0xFF2C2C2C)) .clickable { onGifSelected(url) }, contentScale = ContentScale.Crop ) } else { Box( modifier = Modifier .padding(2.dp) .aspectRatio(1f) .clip(RoundedCornerShape(8.dp)) .background(Color(0xFF3C3C3C)), contentAlignment = Alignment.Center ) { Text(gif.title ?: "GIF", color = Color.Gray, fontSize = 10.sp) } } } } } } } }