android: search members (#2621)
* android: search members * changed placement of search fields * less diff * remove unused function --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
f23c0b55f8
commit
a5f8641d50
@@ -11,12 +11,16 @@ import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -34,15 +38,17 @@ fun AddGroupMembersView(groupInfo: GroupInfo, creatingGroup: Boolean = false, ch
|
||||
val selectedContacts = remember { mutableStateListOf<Long>() }
|
||||
val selectedRole = remember { mutableStateOf(GroupMemberRole.Member) }
|
||||
var allowModifyMembers by remember { mutableStateOf(true) }
|
||||
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
|
||||
BackHandler(onBack = close)
|
||||
AddGroupMembersLayout(
|
||||
chatModel.incognito.value,
|
||||
groupInfo = groupInfo,
|
||||
creatingGroup = creatingGroup,
|
||||
contactsToAdd = getContactsToAdd(chatModel),
|
||||
contactsToAdd = getContactsToAdd(chatModel, searchText.value.text),
|
||||
selectedContacts = selectedContacts,
|
||||
selectedRole = selectedRole,
|
||||
allowModifyMembers = allowModifyMembers,
|
||||
searchText,
|
||||
openPreferences = {
|
||||
ModalManager.shared.showCustomModal { close ->
|
||||
GroupPreferencesView(chatModel, groupInfo.id, close)
|
||||
@@ -69,7 +75,8 @@ fun AddGroupMembersView(groupInfo: GroupInfo, creatingGroup: Boolean = false, ch
|
||||
)
|
||||
}
|
||||
|
||||
fun getContactsToAdd(chatModel: ChatModel): List<Contact> {
|
||||
fun getContactsToAdd(chatModel: ChatModel, search: String): List<Contact> {
|
||||
val s = search.trim().lowercase()
|
||||
val memberContactIds = chatModel.groupMembers
|
||||
.filter { it.memberCurrent }
|
||||
.mapNotNull { it.memberContactId }
|
||||
@@ -78,7 +85,7 @@ fun getContactsToAdd(chatModel: ChatModel): List<Contact> {
|
||||
.map { it.chatInfo }
|
||||
.filterIsInstance<ChatInfo.Direct>()
|
||||
.map { it.contact }
|
||||
.filter { it.contactId !in memberContactIds }
|
||||
.filter { it.contactId !in memberContactIds && it.chatViewName.lowercase().contains(s) }
|
||||
.sortedBy { it.displayName.lowercase() }
|
||||
.toList()
|
||||
}
|
||||
@@ -92,6 +99,7 @@ fun AddGroupMembersLayout(
|
||||
selectedContacts: List<Long>,
|
||||
selectedRole: MutableState<GroupMemberRole>,
|
||||
allowModifyMembers: Boolean,
|
||||
searchText: MutableState<TextFieldValue>,
|
||||
openPreferences: () -> Unit,
|
||||
inviteMembers: () -> Unit,
|
||||
clearSelection: () -> Unit,
|
||||
@@ -125,7 +133,7 @@ fun AddGroupMembersLayout(
|
||||
}
|
||||
SectionSpacer()
|
||||
|
||||
if (contactsToAdd.isEmpty()) {
|
||||
if (contactsToAdd.isEmpty() && searchText.value.text.isEmpty()) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
@@ -154,8 +162,10 @@ fun AddGroupMembersLayout(
|
||||
InviteSectionFooter(selectedContactsCount = selectedContacts.size, allowModifyMembers, clearSelection)
|
||||
}
|
||||
SectionDividerSpaced(maxTopPadding = true)
|
||||
|
||||
SectionView(stringResource(R.string.select_contacts)) {
|
||||
SectionItemView(padding = PaddingValues(start = DEFAULT_PADDING, end = DEFAULT_PADDING_HALF)) {
|
||||
SearchRowView(searchText, selectedContacts.size)
|
||||
}
|
||||
ContactList(contacts = contactsToAdd, selectedContacts, groupInfo, allowModifyMembers, addContact, removeContact)
|
||||
}
|
||||
}
|
||||
@@ -163,6 +173,25 @@ fun AddGroupMembersLayout(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchRowView(
|
||||
searchText: MutableState<TextFieldValue> = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) },
|
||||
selectedContactsSize: Int
|
||||
) {
|
||||
Box(Modifier.width(36.dp), contentAlignment = Alignment.Center) {
|
||||
Icon(painterResource(R.drawable.ic_search), stringResource(android.R.string.search_go), tint = MaterialTheme.colors.secondary)
|
||||
}
|
||||
Spacer(Modifier.width(DEFAULT_SPACE_AFTER_ICON))
|
||||
SearchTextField(Modifier.fillMaxWidth(), searchText = searchText, alwaysVisible = true) {
|
||||
searchText.value = searchText.value.copy(it)
|
||||
}
|
||||
val view = LocalView.current
|
||||
LaunchedEffect(selectedContactsSize) {
|
||||
searchText.value = searchText.value.copy("")
|
||||
hideKeyboard(view)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState<GroupMemberRole>, enabled: Boolean) {
|
||||
Row(
|
||||
@@ -325,6 +354,7 @@ fun PreviewAddGroupMembersLayout() {
|
||||
selectedContacts = remember { mutableStateListOf() },
|
||||
selectedRole = remember { mutableStateOf(GroupMemberRole.Admin) },
|
||||
allowModifyMembers = true,
|
||||
searchText = remember { mutableStateOf(TextFieldValue("")) },
|
||||
openPreferences = {},
|
||||
inviteMembers = {},
|
||||
clearSelection = {},
|
||||
|
||||
@@ -13,12 +13,14 @@ import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -196,10 +198,17 @@ fun GroupChatInfoLayout(
|
||||
val tint = if (chat.chatInfo.incognito) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
|
||||
AddMembersButton(tint, onAddMembersClick)
|
||||
}
|
||||
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) }
|
||||
val filteredMembers = derivedStateOf { members.filter { it.chatViewName.lowercase().contains(searchText.value.text.trim()) } }
|
||||
if (members.size > 8) {
|
||||
SectionItemView(padding = PaddingValues(start = 14.dp, end = DEFAULT_PADDING_HALF)) {
|
||||
SearchRowView(searchText)
|
||||
}
|
||||
}
|
||||
SectionItemView(minHeight = 54.dp) {
|
||||
MemberRow(groupInfo.membership, user = true)
|
||||
}
|
||||
MembersList(members, showMemberInfo)
|
||||
MembersList(filteredMembers.value, showMemberInfo)
|
||||
}
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
SectionView {
|
||||
@@ -393,6 +402,19 @@ private fun DeleteGroupButton(onClick: () -> Unit) {
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchRowView(
|
||||
searchText: MutableState<TextFieldValue> = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) }
|
||||
) {
|
||||
Box(Modifier.width(36.dp), contentAlignment = Alignment.Center) {
|
||||
Icon(painterResource(R.drawable.ic_search), stringResource(android.R.string.search_go), tint = MaterialTheme.colors.secondary)
|
||||
}
|
||||
Spacer(Modifier.width(14.dp))
|
||||
SearchTextField(Modifier.fillMaxWidth(), searchText = searchText, alwaysVisible = true) {
|
||||
searchText.value = searchText.value.copy(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewGroupChatInfoLayout() {
|
||||
|
||||
@@ -33,7 +33,7 @@ fun DefaultTopAppBar(
|
||||
if (!showSearch) {
|
||||
title?.invoke()
|
||||
} else {
|
||||
SearchTextField(Modifier.fillMaxWidth(), stringResource(android.R.string.search_go), alwaysVisible = false, onSearchValueChanged)
|
||||
SearchTextField(Modifier.fillMaxWidth(), alwaysVisible = false, onValueChange = onSearchValueChanged)
|
||||
}
|
||||
},
|
||||
backgroundColor = if (isInDarkTheme()) ToolbarDark else ToolbarLight,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package chat.simplex.app.views.helpers
|
||||
|
||||
import android.R
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.*
|
||||
@@ -8,8 +9,13 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.capitalize
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import chat.simplex.app.TAG
|
||||
import chat.simplex.app.ui.theme.isInDarkTheme
|
||||
import chat.simplex.app.ui.theme.themedBackground
|
||||
|
||||
@@ -29,8 +29,13 @@ import kotlinx.coroutines.delay
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Boolean, onValueChange: (String) -> Unit) {
|
||||
var searchText by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
|
||||
fun SearchTextField(
|
||||
modifier: Modifier,
|
||||
alwaysVisible: Boolean,
|
||||
searchText: MutableState<TextFieldValue> = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) },
|
||||
placeholder: String = stringResource(android.R.string.search_go),
|
||||
onValueChange: (String) -> Unit
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val focusManager = LocalFocusManager.current
|
||||
val keyboard = LocalSoftwareKeyboardController.current
|
||||
@@ -45,7 +50,7 @@ fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Bool
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
if (searchText.text.isNotEmpty()) onValueChange("")
|
||||
if (searchText.value.text.isNotEmpty()) onValueChange("")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +64,7 @@ fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Bool
|
||||
val shape = MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize)
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
BasicTextField(
|
||||
value = searchText,
|
||||
value = searchText.value,
|
||||
modifier = modifier
|
||||
.background(colors.backgroundColor(enabled).value, shape)
|
||||
.indicatorLine(enabled, false, interactionSource, colors)
|
||||
@@ -69,7 +74,7 @@ fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Bool
|
||||
minHeight = TextFieldDefaults.MinHeight
|
||||
),
|
||||
onValueChange = {
|
||||
searchText = it
|
||||
searchText.value = it
|
||||
onValueChange(it.text)
|
||||
},
|
||||
cursorBrush = SolidColor(colors.cursorColor(false).value),
|
||||
@@ -84,18 +89,18 @@ fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Bool
|
||||
interactionSource = interactionSource,
|
||||
decorationBox = @Composable { innerTextField ->
|
||||
TextFieldDefaults.TextFieldDecorationBox(
|
||||
value = searchText.text,
|
||||
value = searchText.value.text,
|
||||
innerTextField = innerTextField,
|
||||
placeholder = {
|
||||
Text(placeholder)
|
||||
},
|
||||
trailingIcon = if (searchText.text.isNotEmpty()) {{
|
||||
trailingIcon = if (searchText.value.text.isNotEmpty()) {{
|
||||
IconButton({
|
||||
if (alwaysVisible) {
|
||||
keyboard?.hide()
|
||||
focusManager.clearFocus()
|
||||
}
|
||||
searchText = TextFieldValue("");
|
||||
searchText.value = TextFieldValue("");
|
||||
onValueChange("")
|
||||
}) {
|
||||
Icon(painterResource(R.drawable.ic_close), stringResource(R.string.icon_descr_close_button), tint = MaterialTheme.colors.primary,)
|
||||
|
||||
@@ -62,7 +62,7 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity)
|
||||
ModalView(
|
||||
{ close() },
|
||||
endButtons = {
|
||||
SearchTextField(Modifier.fillMaxWidth(), stringResource(android.R.string.search_go), alwaysVisible = true) { search.value = it }
|
||||
SearchTextField(Modifier.fillMaxWidth(), alwaysVisible = true) { search.value = it }
|
||||
},
|
||||
content = { modalView(chatModel, search) })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user