From 3b3c740ae0ca7475afdb16656740fab17f647de0 Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 9 Mar 2026 21:23:01 +0300 Subject: [PATCH] android: update chats list header and archive flow to match reference --- android/CHANGELOG.md | 5 ++ .../messenger/ui/chats/ChatListScreen.kt | 80 ++++++++++--------- docs/android-ui-batch-1-checklist.md | 1 + 3 files changed, 49 insertions(+), 37 deletions(-) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 0709c03..579d2be 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -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. diff --git a/android/app/src/main/java/ru/daemonlord/messenger/ui/chats/ChatListScreen.kt b/android/app/src/main/java/ru/daemonlord/messenger/ui/chats/ChatListScreen.kt index ce364ec..d76c753 100644 --- a/android/app/src/main/java/ru/daemonlord/messenger/ui/chats/ChatListScreen.kt +++ b/android/app/src/main/java/ru/daemonlord/messenger/ui/chats/ChatListScreen.kt @@ -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 diff --git a/docs/android-ui-batch-1-checklist.md b/docs/android-ui-batch-1-checklist.md index a5c7ad4..5da27c2 100644 --- a/docs/android-ui-batch-1-checklist.md +++ b/docs/android-ui-batch-1-checklist.md @@ -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 справа фиолетовым текстом ("Все", "Контакты").