From 9d593bcaff33ec73d4608cafb458b56ebde3b5c8 Mon Sep 17 00:00:00 2001 From: Jeffrey Starke Date: Thu, 24 Apr 2025 11:21:09 -0400 Subject: [PATCH] Fix chat folders flickering during drag and drop. Fixes the UI flickering that occurs when reordering chat folders. The issue was caused by the ViewModel updating the database each time a list item position changes when we were already updating list order in the UI state manually at the same time. --- .../app/chats/folders/ChatFoldersFragment.kt | 16 +++++++----- .../app/chats/folders/ChatFoldersViewModel.kt | 26 ++++++++++++------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt index d430e8f0df..209e945b9d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersFragment.kt @@ -101,7 +101,13 @@ class ChatFoldersFragment : ComposeFragment() { onDeleteDismissed = { viewModel.showDeleteDialog(false) }, - onPositionUpdated = { fromIndex, toIndex -> viewModel.updatePosition(fromIndex, toIndex) } + onDragAndDropEvent = { event -> + when (event) { + is DragAndDropEvent.OnItemMove -> viewModel.updateItemPosition(event.fromIndex, event.toIndex) + is DragAndDropEvent.OnItemDrop -> viewModel.saveItemPositions() + is DragAndDropEvent.OnDragCancel -> {} + } + } ) } } @@ -117,16 +123,12 @@ fun FoldersScreen( onDeleteClicked: (ChatFolderRecord) -> Unit = {}, onDeleteConfirmed: () -> Unit = {}, onDeleteDismissed: () -> Unit = {}, - onPositionUpdated: (Int, Int) -> Unit = { _, _ -> } + onDragAndDropEvent: (DragAndDropEvent) -> Unit = {} ) { val screenWidth = LocalConfiguration.current.screenWidthDp.dp val isRtl = ViewUtil.isRtl(LocalContext.current) val listState = rememberLazyListState() - val dragDropState = rememberDragDropState(listState, includeHeader = true, includeFooter = true) { event -> - if (event is DragAndDropEvent.OnItemMove) { - onPositionUpdated(event.fromIndex, event.toIndex) - } - } + val dragDropState = rememberDragDropState(listState, includeHeader = true, includeFooter = true, onEvent = onDragAndDropEvent) LaunchedEffect(Unit) { if (!SignalStore.uiHints.hasSeenChatFoldersEducationSheet) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt index 5912a604f5..897650b91d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/folders/ChatFoldersViewModel.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import org.signal.core.util.swap import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.contacts.paged.ChatType import org.thoughtcrime.securesms.database.SignalDatabase @@ -177,17 +178,22 @@ class ChatFoldersViewModel : ViewModel() { } } - fun updatePosition(fromIndex: Int, toIndex: Int) { - viewModelScope.launch(Dispatchers.IO) { - val folders = state.value.folders.toMutableList().apply { add(toIndex, removeAt(fromIndex)) } - val updatedFolders = folders.mapIndexed { index, chatFolderRecord -> - chatFolderRecord.copy(position = index) - } - ChatFoldersRepository.updatePositions(updatedFolders) + fun updateItemPosition(fromIndex: Int, toIndex: Int) { + val folders = state.value.folders.swap(fromIndex, toIndex) + val updatedFolders = folders.mapIndexed { index, chatFolderRecord -> + chatFolderRecord.copy(position = index) + } + internalState.update { + it.copy(folders = updatedFolders) + } + } - internalState.update { - it.copy(folders = updatedFolders) - } + fun saveItemPositions() { + val updatedFolders = state.value.folders.mapIndexed { index, chatFolderRecord -> + chatFolderRecord.copy(position = index) + } + viewModelScope.launch(Dispatchers.IO) { + ChatFoldersRepository.updatePositions(updatedFolders) } }