android: polish fullscreen chats search interactions
Some checks failed
Android CI / android (push) Has been cancelled
Android Release / release (push) Has been cancelled
CI / test (push) Has been cancelled

This commit is contained in:
Codex
2026-03-09 22:12:51 +03:00
parent 4a31612df0
commit 4f53e3ef99
2 changed files with 41 additions and 5 deletions

View File

@@ -592,3 +592,10 @@
### Step 92 - Search filter leak fix on exit ### Step 92 - Search filter leak fix on exit
- Fixed chats search state leak: leaving fullscreen search now resets local/global query. - Fixed chats search state leak: leaving fullscreen search now resets local/global query.
- Main chats list no longer stays filtered by previous search input after returning from search mode. - Main chats list no longer stays filtered by previous search input after returning from search mode.
### Step 93 - Fullscreen search UX polish
- Added system back-handler for search mode with safe query reset.
- Improved fullscreen search result sections:
- `Показать больше / Свернуть` toggle for global users,
- `Показать больше / Свернуть` toggle for message results.
- Added explicit empty-state text when local/global/message search sections all have no results.

View File

@@ -1,6 +1,7 @@
package ru.daemonlord.messenger.ui.chats package ru.daemonlord.messenger.ui.chats
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -45,8 +46,8 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@@ -176,6 +177,12 @@ fun ChatListScreen(
var manageRoleText by remember { mutableStateOf("member") } var manageRoleText by remember { mutableStateOf("member") }
val isTabletLayout = LocalConfiguration.current.screenWidthDp >= 840 val isTabletLayout = LocalConfiguration.current.screenWidthDp >= 840
val listState = rememberLazyListState() val listState = rememberLazyListState()
BackHandler(enabled = isSearchMode) {
isSearchMode = false
localSearchQuery = ""
onSearchChanged("")
onGlobalSearchChanged("")
}
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
onMainBarVisibilityChanged(true) onMainBarVisibilityChanged(true)
@@ -712,6 +719,12 @@ private fun ChatSearchFullscreen(
onOpenChat: (Long) -> Unit, onOpenChat: (Long) -> Unit,
) { ) {
val trimmedQuery = searchQuery.trim() val trimmedQuery = searchQuery.trim()
var showMoreGlobalUsers by remember { mutableStateOf(false) }
var showMoreMessages by remember { mutableStateOf(false) }
LaunchedEffect(trimmedQuery) {
showMoreGlobalUsers = false
showMoreMessages = false
}
val sectionChats = remember(state.chats, searchSection) { val sectionChats = remember(state.chats, searchSection) {
when (searchSection) { when (searchSection) {
SearchSection.Chats -> state.chats SearchSection.Chats -> state.chats
@@ -864,11 +877,13 @@ private fun ChatSearchFullscreen(
item(key = "global_header") { item(key = "global_header") {
SectionHeader( SectionHeader(
title = "Глобальный поиск", title = "Глобальный поиск",
action = "Показать больше", action = if (showMoreGlobalUsers) "Свернуть" else "Показать больше",
onActionClick = { showMoreGlobalUsers = !showMoreGlobalUsers },
) )
} }
if (state.globalUsers.isNotEmpty()) { if (state.globalUsers.isNotEmpty()) {
items(state.globalUsers.take(8), key = { "user_${it.id}" }) { user -> val users = if (showMoreGlobalUsers) state.globalUsers else state.globalUsers.take(5)
items(users, key = { "user_${it.id}" }) { user ->
SearchUserRow( SearchUserRow(
title = user.name, title = user.name,
subtitle = buildString { subtitle = buildString {
@@ -890,10 +905,12 @@ private fun ChatSearchFullscreen(
item(key = "messages_header") { item(key = "messages_header") {
SectionHeader( SectionHeader(
title = "Сообщения", title = "Сообщения",
action = "Из всех чатов", action = if (showMoreMessages) "Свернуть" else "Показать больше",
onActionClick = { showMoreMessages = !showMoreMessages },
) )
} }
items(state.globalMessages.take(12), key = { "msg_${it.id}" }) { message -> val messages = if (showMoreMessages) state.globalMessages else state.globalMessages.take(8)
items(messages, key = { "msg_${it.id}" }) { message ->
SearchMessageRow( SearchMessageRow(
state = state, state = state,
messageText = message.text?.take(70).orEmpty().ifBlank { "[${message.type}]" }, messageText = message.text?.take(70).orEmpty().ifBlank { "[${message.type}]" },
@@ -905,6 +922,16 @@ private fun ChatSearchFullscreen(
chatId = message.chatId, chatId = message.chatId,
) )
} }
if (localQueryResults.isEmpty() && state.globalUsers.isEmpty() && state.globalMessages.isEmpty()) {
item(key = "all_empty") {
Text(
text = "Ничего не найдено",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 8.dp, vertical = 12.dp),
)
}
}
} }
} }
} }
@@ -914,6 +941,7 @@ private fun ChatSearchFullscreen(
private fun SectionHeader( private fun SectionHeader(
title: String, title: String,
action: String, action: String,
onActionClick: (() -> Unit)? = null,
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@@ -931,6 +959,7 @@ private fun SectionHeader(
text = action, text = action,
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = if (onActionClick != null) Modifier.clickable(onClick = onActionClick) else Modifier,
) )
} }
} }