android: update chats list header and archive flow to match reference
Some checks failed
Android CI / android (push) Has started running
Android Release / release (push) Has been cancelled
CI / test (push) Has been cancelled

This commit is contained in:
Codex
2026-03-09 21:23:01 +03:00
parent b75df4967f
commit 3b3c740ae0
3 changed files with 49 additions and 37 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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 справа фиолетовым текстом ("Все", "Контакты").