From 31c4ff270584bf679e9256ff02df0874c85d62a0 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:36:01 +0000 Subject: [PATCH] android, desktop: group info remembers scroll position and search (#4817) --- .../views/chat/group/GroupChatInfoView.kt | 11 +++---- .../common/views/helpers/SearchTextField.kt | 6 ---- .../common/views/newchat/NewChatSheet.kt | 32 ++++++++++++++----- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt index 81a0de7bb9..a14d227074 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.launch const val SMALL_GROUPS_RCPS_MEM_LIMIT: Int = 20 @Composable -fun GroupChatInfoView(chatModel: ChatModel, rhId: Long?, chatId: String, groupLink: String?, groupLinkMemberRole: GroupMemberRole?, onGroupLinkUpdated: (Pair?) -> Unit, close: () -> Unit, onSearchClicked: () -> Unit) { +fun ModalData.GroupChatInfoView(chatModel: ChatModel, rhId: Long?, chatId: String, groupLink: String?, groupLinkMemberRole: GroupMemberRole?, onGroupLinkUpdated: (Pair?) -> Unit, close: () -> Unit, onSearchClicked: () -> Unit) { BackHandler(onBack = close) // TODO derivedStateOf? val chat = chatModel.chats.value.firstOrNull { ch -> ch.id == chatId && ch.remoteHostId == rhId } @@ -249,9 +249,8 @@ fun AddGroupMembersButton( ) } - @Composable -fun GroupChatInfoLayout( +fun ModalData.GroupChatInfoLayout( chat: Chat, groupInfo: GroupInfo, currentUser: User, @@ -272,12 +271,12 @@ fun GroupChatInfoLayout( close: () -> Unit = { ModalManager.closeAllModalsEverywhere()}, onSearchClicked: () -> Unit ) { - val listState = rememberLazyListState() + val listState = remember { appBarHandler.listState } val scope = rememberCoroutineScope() KeyChangeEffect(chat.id) { scope.launch { listState.scrollToItem(0) } } - val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) } + val searchText = remember { stateGetOrPut("searchText") { TextFieldValue() } } val filteredMembers = remember(members) { derivedStateOf { val s = searchText.value.text.trim().lowercase() @@ -674,7 +673,7 @@ private fun SearchRowView( @Composable fun PreviewGroupChatInfoLayout() { SimpleXTheme { - GroupChatInfoLayout( + ModalData().GroupChatInfoLayout( chat = Chat( remoteHostId = null, chatInfo = ChatInfo.Direct.sampleData, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/SearchTextField.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/SearchTextField.kt index c6d5d6ad16..60dceab4ad 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/SearchTextField.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/SearchTextField.kt @@ -71,12 +71,6 @@ fun SearchTextField( } } - DisposableEffect(Unit) { - onDispose { - if (searchText.value.text.isNotEmpty()) onValueChange("") - } - } - val colors = TextFieldDefaults.textFieldColors( backgroundColor = Color.Unspecified, textColor = MaterialTheme.colors.onBackground, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt index 99ba7be8d2..7a9fec100d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt @@ -36,11 +36,13 @@ import chat.simplex.common.views.chatlist.ScrollDirection import chat.simplex.common.views.contacts.* import chat.simplex.common.views.helpers.* import chat.simplex.res.MR +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter import java.net.URI @Composable -fun NewChatSheet(rh: RemoteHostInfo?, close: () -> Unit) { +fun ModalData.NewChatSheet(rh: RemoteHostInfo?, close: () -> Unit) { val oneHandUI = remember { appPrefs.oneHandUI.state } val keyboardState by getKeyboardState() val showToolbarInOneHandUI = remember { derivedStateOf { keyboardState == KeyboardState.Closed && oneHandUI.value } } @@ -111,10 +113,8 @@ private fun filterContactTypes(c: List, contactTypes: List): return c.filter { chat -> contactTypes.contains(chatContactType(chat)) } } -private var lazyListState = 0 to 0 - @Composable -private fun NewChatSheetLayout( +private fun ModalData.NewChatSheetLayout( rh: RemoteHostInfo?, addContact: () -> Unit, scanPaste: () -> Unit, @@ -122,7 +122,23 @@ private fun NewChatSheetLayout( close: () -> Unit, ) { val oneHandUI = remember { appPrefs.oneHandUI.state } - val listState = rememberLazyListState(lazyListState.first, lazyListState.second) + val listState = remember { appBarHandler.listState } + // This is workaround of an issue when position of a list is not restored (when going back to that screen) when a header exists. + // Upon returning back, this code returns correct index and position if number of items is the same + LaunchedEffect(Unit) { + val prevIndex = listState.firstVisibleItemIndex + val prevOffset = listState.firstVisibleItemScrollOffset + val total = listState.layoutInfo.totalItemsCount + if (prevIndex == 0 && prevOffset == 0) return@LaunchedEffect + snapshotFlow { listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset } + .filter { it == 0 to 0 } + .collect { + if (total <= listState.layoutInfo.totalItemsCount) { + listState.scrollToItem(prevIndex, prevOffset) + } + cancel() + } + } val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) } val searchShowingSimplexLink = remember { mutableStateOf(false) } val searchChatFilteredBySimplexLink = remember { mutableStateOf(null) } @@ -536,7 +552,7 @@ private fun contactTypesSearchTargets(baseContactTypes: List, searc } @Composable -private fun DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats: () -> Unit, close: () -> Unit) { +private fun ModalData.DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats: () -> Unit, close: () -> Unit) { val oneHandUI = remember { appPrefs.oneHandUI.state } val keyboardState by getKeyboardState() val showToolbarInOneHandUI = remember { derivedStateOf { keyboardState == KeyboardState.Closed && oneHandUI.value } } @@ -558,7 +574,7 @@ private fun DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats: () -> Un } } ) { contentPadding -> - val listState = rememberLazyListState(lazyListState.first, lazyListState.second) + val listState = remember { appBarHandler.listState } val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) } val searchShowingSimplexLink = remember { mutableStateOf(false) } val searchChatFilteredBySimplexLink = remember { mutableStateOf(null) } @@ -723,6 +739,6 @@ fun ActionButton( @Composable private fun PreviewNewChatSheet() { SimpleXTheme { - NewChatSheet(rh = null, close = {}) + ModalData().NewChatSheet(rh = null, close = {}) } }