android: update chats list header and archive flow to match reference
This commit is contained in:
@@ -509,3 +509,8 @@
|
||||
### Step 81 - Chats bottom gap fix when tabs bar hidden
|
||||
- Fixed blank gap at the bottom of chats list when global tabs bar auto-hides on scroll.
|
||||
- Chats screen bottom padding is now dynamic and applied only while tabs bar is visible.
|
||||
|
||||
### Step 82 - Chats list header closer to Telegram reference
|
||||
- Removed `Archived` top tab from chats list UI.
|
||||
- Added search action in top app bar and unified single search field with leading search icon.
|
||||
- Kept archive as dedicated row inside chats list; opening archive now happens from that row and back navigation appears in app bar while archive is active.
|
||||
|
||||
@@ -30,8 +30,6 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
@@ -53,8 +51,10 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import coil.compose.AsyncImage
|
||||
import ru.daemonlord.messenger.domain.chat.model.ChatItem
|
||||
@@ -134,6 +134,7 @@ fun ChatListScreen(
|
||||
onUnbanMember: (Long, Long) -> Unit,
|
||||
) {
|
||||
var managementExpanded by remember { mutableStateOf(false) }
|
||||
var searchExpanded by remember { mutableStateOf(true) }
|
||||
var createTitle by remember { mutableStateOf("") }
|
||||
var createMemberIds by remember { mutableStateOf("") }
|
||||
var createHandle by remember { mutableStateOf("") }
|
||||
@@ -178,49 +179,54 @@ fun ChatListScreen(
|
||||
.padding(bottom = if (isMainBarVisible) 92.dp else 0.dp),
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text("Chats") },
|
||||
navigationIcon = {
|
||||
if (state.selectedTab == ChatTab.ARCHIVED) {
|
||||
IconButton(onClick = { onTabSelected(ChatTab.ALL) }) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back to chats",
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(if (state.selectedTab == ChatTab.ARCHIVED) "Archived" else "Chats")
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = { searchExpanded = !searchExpanded }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Search,
|
||||
contentDescription = if (searchExpanded) "Hide search" else "Show search",
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { managementExpanded = !managementExpanded }) {
|
||||
Icon(
|
||||
imageVector = if (managementExpanded) Icons.Filled.Close else Icons.Filled.Add,
|
||||
contentDescription = if (managementExpanded) "Close management" else "Open management",
|
||||
imageVector = if (managementExpanded) Icons.Filled.Close else Icons.Filled.MoreVert,
|
||||
contentDescription = if (managementExpanded) "Close menu" else "Open menu",
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
TabRow(
|
||||
selectedTabIndex = if (state.selectedTab == ChatTab.ALL) 0 else 1,
|
||||
) {
|
||||
Tab(
|
||||
selected = state.selectedTab == ChatTab.ALL,
|
||||
onClick = { onTabSelected(ChatTab.ALL) },
|
||||
text = { Text(text = "All") },
|
||||
)
|
||||
Tab(
|
||||
selected = state.selectedTab == ChatTab.ARCHIVED,
|
||||
onClick = { onTabSelected(ChatTab.ARCHIVED) },
|
||||
text = { Text(text = "Archived") },
|
||||
if (searchExpanded) {
|
||||
OutlinedTextField(
|
||||
value = state.searchQuery,
|
||||
onValueChange = {
|
||||
onSearchChanged(it)
|
||||
onGlobalSearchChanged(it)
|
||||
},
|
||||
label = { Text(text = "Search chats") },
|
||||
singleLine = true,
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Search,
|
||||
contentDescription = "Search",
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedTextField(
|
||||
value = state.searchQuery,
|
||||
onValueChange = onSearchChanged,
|
||||
label = { Text(text = "Search title / username / handle") },
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = state.globalSearchQuery,
|
||||
onValueChange = onGlobalSearchChanged,
|
||||
label = { Text(text = "Global search users/chats/messages") },
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
)
|
||||
if (state.globalSearchQuery.trim().length >= 2) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
- [ ] Chat row: avatar + title + preview + time + unread badge.
|
||||
- [ ] Состояния pinned/muted и mention/read indicator в preview-строке.
|
||||
- [ ] FAB справа снизу (compose/add contact).
|
||||
- [x] Архив вынесен из верхних tab и отображается отдельной строкой списка.
|
||||
|
||||
## P1 — Privacy/Data/Notifications Screens
|
||||
- [ ] Privacy screen: value справа фиолетовым текстом ("Все", "Контакты").
|
||||
|
||||
Reference in New Issue
Block a user