diff --git a/app/build.gradle b/app/build.gradle index 47685583c2..72b3eca7c0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -226,7 +226,6 @@ dependencies { implementation libs.composeAccompanistNavigation implementation libs.preference implementation libs.activityCompose - implementation libs.composeReorderableLegacy implementation libs.composeReorderable implementation libs.room diff --git a/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreen.kt b/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreen.kt index 0626ed7094..dcc8bfdda5 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreen.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/home/HomeScreen.kt @@ -1,6 +1,5 @@ package com.anytypeio.anytype.ui.home -import android.view.View import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize @@ -25,7 +24,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape @@ -51,6 +49,7 @@ import com.anytypeio.anytype.core_ui.extensions.throttledClick import com.anytypeio.anytype.core_ui.foundation.components.BottomNavigationMenu import com.anytypeio.anytype.core_ui.foundation.noRippleClickable import com.anytypeio.anytype.core_ui.views.UXBody +import com.anytypeio.anytype.core_ui.widgets.dv.DefaultDragAndDropModifier import com.anytypeio.anytype.presentation.home.InteractionMode import com.anytypeio.anytype.presentation.navigation.NavPanelState import com.anytypeio.anytype.presentation.widgets.DropDownMenuAction @@ -72,11 +71,8 @@ import com.anytypeio.anytype.ui.widgets.types.ListWidgetCard import com.anytypeio.anytype.ui.widgets.types.SpaceChatWidgetCard import com.anytypeio.anytype.ui.widgets.types.SpaceWidgetCard import com.anytypeio.anytype.ui.widgets.types.TreeWidgetCard -import sh.calvin.reorderable.ReorderableCollectionItemScope import sh.calvin.reorderable.ReorderableItem -import sh.calvin.reorderable.ReorderableLazyListState import sh.calvin.reorderable.rememberReorderableLazyListState -import timber.log.Timber @Composable fun HomeScreen( @@ -559,28 +555,6 @@ private fun WidgetList( } } -@Composable -private fun ReorderableCollectionItemScope.DefaultDragAndDropModifier( - view: View, - onDragStopped: () -> Unit -): Modifier { - return Modifier.draggableHandle( - onDragStarted = { - ViewCompat.performHapticFeedback( - view, - HapticFeedbackConstantsCompat.GESTURE_START - ) - }, - onDragStopped = { - ViewCompat.performHapticFeedback( - view, - HapticFeedbackConstantsCompat.GESTURE_END - ) - onDragStopped() - } - ) -} - @Composable private fun ListOfObjectsItem( modifier: Modifier = Modifier, diff --git a/app/src/main/java/com/anytypeio/anytype/ui/widgets/collection/CollectionScreen.kt b/app/src/main/java/com/anytypeio/anytype/ui/widgets/collection/CollectionScreen.kt index aabb055c82..4c936c9fe5 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/widgets/collection/CollectionScreen.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/widgets/collection/CollectionScreen.kt @@ -28,7 +28,6 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.absolutePadding -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -40,6 +39,7 @@ import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.BottomSheetScaffold import androidx.compose.material.BottomSheetValue import androidx.compose.material.Divider @@ -76,6 +76,8 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.view.HapticFeedbackConstantsCompat +import androidx.core.view.ViewCompat import androidx.core.widget.doAfterTextChanged import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.anytypeio.anytype.BuildConfig @@ -90,6 +92,7 @@ import com.anytypeio.anytype.core_ui.views.Title1 import com.anytypeio.anytype.core_ui.views.UXBody import com.anytypeio.anytype.core_ui.widgets.CollectionActionWidget import com.anytypeio.anytype.core_ui.widgets.ListWidgetObjectIcon +import com.anytypeio.anytype.core_ui.widgets.dv.DefaultDragAndDropModifier import com.anytypeio.anytype.core_utils.ext.invisible import com.anytypeio.anytype.core_utils.ext.setVisible import com.anytypeio.anytype.core_utils.ext.visible @@ -108,10 +111,8 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.drop import kotlinx.coroutines.launch -import org.burnoutcrew.reorderable.ReorderableItem -import org.burnoutcrew.reorderable.detectReorderAfterLongPress -import org.burnoutcrew.reorderable.rememberReorderableLazyListState -import org.burnoutcrew.reorderable.reorderable +import sh.calvin.reorderable.ReorderableItem +import sh.calvin.reorderable.rememberReorderableLazyListState @SuppressLint("UnusedMaterialScaffoldPaddingParameter") @Composable @@ -126,7 +127,7 @@ fun ScreenContent( ) { Box( - modifier = if (BuildConfig.USE_EDGE_TO_EDGE && Build.VERSION.SDK_INT >= EDGE_TO_EDGE_MIN_SDK) + modifier = if (Build.VERSION.SDK_INT >= EDGE_TO_EDGE_MIN_SDK) Modifier.windowInsetsPadding(WindowInsets.systemBars) else Modifier @@ -210,34 +211,47 @@ fun ListView( itemBackground: Color = colorResource(id = R.color.background_primary) ) { + val view = LocalView.current + val views = remember { mutableStateOf>(listOf()) } - val lazyListState = rememberReorderableLazyListState( - onMove = { from, to -> + + val lazyListState = rememberLazyListState() + + val lastFromIndex = remember { mutableStateOf(null) } + val lastToIndex = remember { mutableStateOf(null) } + + val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to -> + lastFromIndex.value = from.index + lastToIndex.value = to.index + views.value = views.value.toMutableList().apply { add(to.index, removeAt(from.index)) } - }, - onDragEnd = { from, to -> + ViewCompat.performHapticFeedback( + view, + HapticFeedbackConstantsCompat.SEGMENT_FREQUENT_TICK + ) + } + + val onDragStoppedHandler = { + val from = lastFromIndex.value + val to = lastToIndex.value + if (from != null && to != null && from != to) { vm.onMove(views.value, from, to) - }, - ) + } + // Reset after firing + lastFromIndex.value = null + lastToIndex.value = null + } uiState.views.fold( onSuccess = { list -> views.value = list LazyColumn( - state = lazyListState.listState, - modifier = Modifier - .then( - if (uiState.inDragMode) - Modifier.reorderable(lazyListState) - else - Modifier - ) - .fillMaxHeight() - .fillMaxWidth(), + state = lazyListState, + modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(bottom = 180.dp) ) { items( @@ -256,18 +270,13 @@ fun ListView( when (item) { is CollectionObjectView -> { if (uiState.inDragMode) { - ReorderableItem( - lazyListState, - key = item.obj.id, - ) { isDragging -> + ReorderableItem(reorderableLazyListState, key = item.obj.id,) { isDragging -> val alpha = animateFloatAsState(if (isDragging) 0.8f else 1.0f) Column( modifier = Modifier .then( if (uiState.inDragMode) { - Modifier.detectReorderAfterLongPress( - lazyListState - ) + DefaultDragAndDropModifier(view, onDragStoppedHandler) } else { Modifier } @@ -303,7 +312,7 @@ fun ListView( is SectionView -> { if (uiState.inDragMode) { ReorderableItem( - lazyListState, + reorderableLazyListState, key = item.name, ) { SectionItem(item) diff --git a/app/src/main/java/com/anytypeio/anytype/ui/widgets/types/AllContentWidget.kt b/app/src/main/java/com/anytypeio/anytype/ui/widgets/types/AllContentWidget.kt index 9599b0a78c..d047590d03 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/widgets/types/AllContentWidget.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/widgets/types/AllContentWidget.kt @@ -1,7 +1,6 @@ package com.anytypeio.anytype.ui.widgets.types import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.animateContentSize import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.slideInHorizontally @@ -15,7 +14,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Text @@ -40,9 +38,6 @@ import com.anytypeio.anytype.core_ui.views.HeadlineSubheading import com.anytypeio.anytype.presentation.home.InteractionMode import com.anytypeio.anytype.presentation.widgets.DropDownMenuAction import com.anytypeio.anytype.ui.widgets.menu.WidgetMenu -import org.burnoutcrew.reorderable.ReorderableLazyListState -import org.burnoutcrew.reorderable.detectReorderAfterLongPress -import org.burnoutcrew.reorderable.rememberReorderableLazyListState @OptIn(ExperimentalFoundationApi::class) @Composable @@ -86,7 +81,6 @@ fun AllContentWidgetCard( } ) } else { -// Modifier.detectReorderAfterLongPress(lazyListState) Modifier } ) @@ -142,7 +136,6 @@ fun AllContentWidgetCard( @DefaultPreviews @Composable fun AllContentWidgetPreview() { - val lazyListState = rememberLazyListState() AllContentWidgetCard( index = 0, onWidgetClicked = {}, diff --git a/core-ui/build.gradle b/core-ui/build.gradle index 4b85e07cd3..2ddccb1fa7 100644 --- a/core-ui/build.gradle +++ b/core-ui/build.gradle @@ -58,7 +58,7 @@ dependencies { implementation libs.coilCompose implementation libs.coilNetwork implementation libs.composeConstraintLayout - implementation libs.composeReorderableLegacy + implementation libs.composeReorderable testImplementation libs.fragmentTesting testImplementation project(':test:android-utils') diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/dv/ViewersWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/dv/ViewersWidget.kt index 3aac7b107d..e3a52fc0f0 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/dv/ViewersWidget.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/dv/ViewersWidget.kt @@ -1,6 +1,7 @@ package com.anytypeio.anytype.core_ui.widgets.dv import android.view.HapticFeedbackConstants +import android.view.View import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.Spring import androidx.compose.animation.core.animateFloatAsState @@ -18,6 +19,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Text @@ -26,6 +28,7 @@ import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -40,6 +43,8 @@ import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import androidx.constraintlayout.compose.Visibility +import androidx.core.view.HapticFeedbackConstantsCompat +import androidx.core.view.ViewCompat import com.anytypeio.anytype.core_ui.R import com.anytypeio.anytype.core_ui.extensions.swapList import com.anytypeio.anytype.core_ui.foundation.Divider @@ -56,11 +61,9 @@ import com.anytypeio.anytype.presentation.sets.ViewersWidgetUi.Action.DoneMode import com.anytypeio.anytype.presentation.sets.ViewersWidgetUi.Action.Edit import com.anytypeio.anytype.presentation.sets.ViewersWidgetUi.Action.EditMode import com.anytypeio.anytype.presentation.sets.viewer.ViewerView -import org.burnoutcrew.reorderable.ReorderableItem -import org.burnoutcrew.reorderable.ReorderableLazyListState -import org.burnoutcrew.reorderable.detectReorder -import org.burnoutcrew.reorderable.rememberReorderableLazyListState -import org.burnoutcrew.reorderable.reorderable +import sh.calvin.reorderable.ReorderableCollectionItemScope +import sh.calvin.reorderable.ReorderableItem +import sh.calvin.reorderable.rememberReorderableLazyListState @OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @@ -114,17 +117,19 @@ private fun ViewersWidgetContent( action: (ViewersWidgetUi.Action) -> Unit ) { + val view = LocalView.current + val lastFromIndex = remember { mutableStateOf(null) } + val lastToIndex = remember { mutableStateOf(null) } + + val lazyListState = rememberLazyListState() + val views = remember { mutableStateListOf() } views.swapList(state.items) - val lazyListState = rememberReorderableLazyListState( - onMove = { from, to -> - val newList = views.toMutableList().apply { - add(to.index, removeAt(from.index)) - } - views.swapList(newList) - }, - onDragEnd = { from, to -> + val onDragStoppedHandler = { + val from = lastFromIndex.value + val to = lastToIndex.value + if (from != null && to != null && from != to) { action( ViewersWidgetUi.Action.OnMove( currentViews = views, @@ -133,7 +138,25 @@ private fun ViewersWidgetContent( ) ) } - ) + // Reset after firing + lastFromIndex.value = null + lastToIndex.value = null + } + + val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to -> + lastFromIndex.value = from.index + lastToIndex.value = to.index + + val newList = views.toMutableList().apply { + add(to.index, removeAt(from.index)) + } + views.swapList(newList) + + ViewCompat.performHapticFeedback( + view, + HapticFeedbackConstantsCompat.SEGMENT_FREQUENT_TICK + ) + } Column( modifier = modifier @@ -150,9 +173,8 @@ private fun ViewersWidgetContent( ) LazyColumn( - state = lazyListState.listState, + state = lazyListState, modifier = Modifier - .reorderable(lazyListState) .fillMaxWidth() .wrapContentHeight() ) { @@ -160,26 +182,20 @@ private fun ViewersWidgetContent( count = views.size, key = { index -> views[index].id }, ) { index -> - - ReorderableItem( - modifier = Modifier.animateItem(), - reorderableState = lazyListState, - key = views[index].id - ) { isDragging -> + ReorderableItem(reorderableLazyListState, key = views[index].id) { isDragging -> val currentItem = LocalView.current if (isDragging) { currentItem.isHapticFeedbackEnabled = true currentItem.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) } Item( + dndModifier = DefaultDragAndDropModifier(view, onDragStoppedHandler), modifier = Modifier, - lazyListState = lazyListState, isDragging = isDragging, isEditing = state.isEditing, action = action, view = views[index] ) - } if (index != views.size - 1) { Divider() @@ -191,8 +207,8 @@ private fun ViewersWidgetContent( @Composable private fun Item( + dndModifier: Modifier = Modifier, modifier: Modifier, - lazyListState: ReorderableLazyListState, isDragging: Boolean, isEditing: Boolean, action: (ViewersWidgetUi.Action) -> Unit, @@ -228,8 +244,7 @@ private fun Item( contentDescription = "Delete view" ) Image( - modifier = Modifier - .detectReorder(lazyListState) + modifier = dndModifier .constrainAs(dnd) { end.linkTo(parent.end) top.linkTo(parent.top) @@ -386,4 +401,26 @@ private fun ActionText(modifier: Modifier, text: String, click: () -> Unit) { color = colorResource(id = R.color.glyph_active), textAlign = TextAlign.Center ) -} \ No newline at end of file +} + +@Composable +fun ReorderableCollectionItemScope.DefaultDragAndDropModifier( + view: View, + onDragStopped: () -> Unit +): Modifier { + return Modifier.draggableHandle( + onDragStarted = { + ViewCompat.performHapticFeedback( + view, + HapticFeedbackConstantsCompat.GESTURE_START + ) + }, + onDragStopped = { + ViewCompat.performHapticFeedback( + view, + HapticFeedbackConstantsCompat.GESTURE_END + ) + onDragStopped() + } + ) +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4917680d03..66d4385aca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -83,7 +83,6 @@ composeAccompanistPermissions = { module = "com.google.accompanist:accompanist-p composeAccompanistPagerIndicators = { module = "com.google.accompanist:accompanist-pager-indicators", version.ref = "accompanistVersion" } composeAccompanistThemeAdapter = { module = "com.google.accompanist:accompanist-themeadapter-material", version.ref = "accompanistVersion" } composeAccompanistNavigation = { module = "com.google.accompanist:accompanist-navigation-material", version.ref = "accompanistVersion" } -composeReorderableLegacy = { module = "com.github.fat-fellow.ComposeReorderable:reorderable", version.ref = "composeReorderableLegacyVersion" } composeReorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "composeReorderableVersion" } composeConstraintLayout = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "composeConstraintLayoutVersion" } kotlinxSerializationJson = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.3" } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt index 8c59597cd8..f084a4750c 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt @@ -2608,11 +2608,6 @@ class ObjectSetViewModel( } is ViewersWidgetUi.Action.OnMove -> { if (action.from == action.to) return - if (action.to == 0 && session.currentViewerId.value.isNullOrEmpty()) { - state.dataViewContent.viewers.firstOrNull()?.let { - session.currentViewerId.value = it.id - } - } viewModelScope.launch { val startTime = System.currentTimeMillis() val type = action.currentViews[action.to].type @@ -2634,6 +2629,7 @@ class ObjectSetViewModel( } ) ) + session.currentViewerId.value = action.currentViews.firstOrNull()?.id } } is ViewersWidgetUi.Action.SetActive -> { diff --git a/sample/build.gradle b/sample/build.gradle index befca471b1..f72f873688 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -65,7 +65,7 @@ dependencies { implementation libs.composeAccompanistPagerIndicators implementation libs.preference implementation libs.activityCompose - implementation libs.composeReorderableLegacy + implementation libs.composeReorderable debugImplementation libs.composeTooling testImplementation libs.junit