package contacts.presentation import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.PersonAdd import androidx.compose.material.icons.filled.Chat import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.Cancel import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.res.stringResource import contacts.data.remote.api.ContactDto import core.presentation.components.AppAvatar import core.presentation.theme.SoftSquareShape import ru.knot.messager.R @OptIn(ExperimentalMaterial3Api::class) @Composable fun ContactListScreen( contacts: List, requests: List = emptyList(), isLoading: Boolean = false, onContactClick: (String) -> Unit, onSearchChange: (String) -> Unit, onAddContact: (String) -> Unit, onStartChat: (String) -> Unit, onAcceptRequest: (String) -> Unit, onDeclineRequest: (String) -> Unit ) { var searchQuery by remember { mutableStateOf("") } var selectedTab by remember { mutableIntStateOf(0) } val tabs = listOf( stringResource(R.string.all), stringResource(R.string.online_tab) ) Scaffold( topBar = { Column { TopAppBar( title = { Text(stringResource(R.string.contacts_title), fontWeight = androidx.compose.ui.text.font.FontWeight.Bold) }, colors = TopAppBarDefaults.topAppBarColors( containerColor = Color.Transparent, titleContentColor = Color.White ) ) TabRow(selectedTabIndex = selectedTab) { tabs.forEachIndexed { index, title -> Tab( selected = selectedTab == index, onClick = { selectedTab = index }, text = { Text(title) } ) } } } } ) { paddingValues -> Column( modifier = Modifier .fillMaxSize() .padding(paddingValues) ) { // Поисковая строка OutlinedTextField( value = searchQuery, onValueChange = { searchQuery = it onSearchChange(it) }, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp), placeholder = { Text(stringResource(R.string.search_hint)) }, leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) }, shape = SoftSquareShape ) if (isLoading) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } else { val filteredContacts = if (searchQuery.isNotEmpty()) { contacts } else { contacts.filter { if (selectedTab == 1) it.isOnline else true } } LazyColumn(modifier = Modifier.fillMaxSize()) { // Contact Requests Section if (searchQuery.isEmpty() && requests.isNotEmpty()) { item { Text( text = "Заявки в контакты", style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.primary, modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp) ) } items(requests) { request -> ContactRequestItem( request = request, onAccept = { onAcceptRequest(request.id) }, onDecline = { onDeclineRequest(request.id) } ) } item { Divider(modifier = Modifier.padding(vertical = 8.dp), color = MaterialTheme.colorScheme.outlineVariant) } } if (filteredContacts.isEmpty() && requests.isEmpty()) { item { Box(modifier = Modifier.fillParentMaxSize(), contentAlignment = Alignment.Center) { Text( text = if (searchQuery.isNotEmpty()) "Пользователи не найдены" else "Список контактов пуст", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } } else { items(filteredContacts) { contact -> ContactItem( contact = contact, onClick = { onContactClick(contact.id) }, onAddClick = { onAddContact(contact.id) }, onChatClick = { onStartChat(contact.id) }, isSearchMode = searchQuery.isNotEmpty() ) } } } } } } } @Composable fun ContactRequestItem( request: contacts.data.remote.api.FriendRequestDto, onAccept: () -> Unit, onDecline: () -> Unit ) { Row( modifier = Modifier .fillMaxWidth() .padding(16.dp, 8.dp), verticalAlignment = Alignment.CenterVertically ) { AppAvatar( url = request.user.effectiveAvatarUrl, name = request.user.effectiveUsername, size = 48.dp ) Spacer(modifier = Modifier.width(12.dp)) Column(modifier = Modifier.weight(1f)) { Text( text = request.user.displayName ?: request.user.effectiveUsername, style = MaterialTheme.typography.titleSmall, color = MaterialTheme.colorScheme.onSurface ) Text( text = if (request.isOutgoing) "Исходящий запрос" else "Входящий запрос", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant ) } Row { if (!request.isOutgoing) { IconButton(onClick = onAccept) { Icon(Icons.Default.CheckCircle, contentDescription = "Accept", tint = Color(0xFF4CAF50)) } } IconButton(onClick = onDecline) { Icon(Icons.Default.Cancel, contentDescription = "Decline", tint = MaterialTheme.colorScheme.error) } } } } @Composable fun ContactItem( contact: contacts.data.remote.api.ContactDto, onClick: () -> Unit, onAddClick: () -> Unit, onChatClick: () -> Unit, isSearchMode: Boolean ) { Row( modifier = Modifier .fillMaxWidth() .clickable(onClick = onClick) .padding(16.dp, 8.dp), verticalAlignment = Alignment.CenterVertically ) { AppAvatar( url = contact.effectiveAvatarUrl, name = contact.effectiveUsername, size = 56.dp ) Spacer(modifier = Modifier.width(16.dp)) Column(modifier = Modifier.weight(1f)) { Text( text = contact.displayName ?: contact.effectiveUsername, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurface ) val statusText = if (contact.isOnline) { stringResource(R.string.online) } else { stringResource(R.string.last_seen, contact.lastSeen ?: stringResource(R.string.last_seen_recently)) } Text( text = statusText, style = MaterialTheme.typography.bodySmall, color = if (contact.isOnline) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant ) } Row { if (isSearchMode) { IconButton(onClick = onAddClick) { Icon( imageVector = Icons.Default.PersonAdd, contentDescription = "Add Contact", tint = MaterialTheme.colorScheme.primary ) } } IconButton(onClick = onChatClick) { Icon( imageVector = Icons.Default.Chat, contentDescription = "Start Chat", tint = MaterialTheme.colorScheme.primary ) } } } }