From c5cbcf7418c47b72446c2fb6ff4a99d54c30936d Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:40:51 +0800 Subject: [PATCH] Reintroduce confirm dialog for swipe-to-dismiss (#914) Observe the state changes instead of using `confirmValueChange`, which will be called twice. --- .../ehviewer/download/DownloadManager.kt | 13 +++---- .../ehviewer/ui/screen/DownloadsScreen.kt | 27 +++++++++----- .../ehviewer/ui/screen/GalleryListScreen.kt | 36 ++++++++++++------- app/src/main/res/values-zh-rCN/strings.xml | 2 ++ app/src/main/res/values-zh-rTW/strings.xml | 2 ++ app/src/main/res/values/strings.xml | 2 ++ 6 files changed, 54 insertions(+), 28 deletions(-) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt b/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt index bd3080a04..6f7f62202 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt @@ -94,12 +94,7 @@ object DownloadManager : OnSpiderListener, CoroutineScope { if (label == null) { return false } - for ((label1) in labelList) { - if (label == label1) { - return true - } - } - return false + return labelList.any { it.label == label } } fun containDownloadInfo(gid: Long) = mAllInfoMap.containsKey(gid) @@ -518,12 +513,14 @@ object DownloadManager : OnSpiderListener, CoroutineScope { } suspend fun deleteLabel(label: String) { - labelList.run { + with(labelList) { val index = indexOfFirst { it.label == label } + val item = get(index) + EhDB.removeDownloadLabel(item) subList(index + 1, size).forEach { it.position-- } - EhDB.removeDownloadLabel(removeAt(index)) + removeAt(index) } allInfoList.forEach { if (it.label == label) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt index 528268d49..c94de2e6c 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt @@ -46,11 +46,13 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SwipeToDismissBoxDefaults +import androidx.compose.material3.SwipeToDismissBoxState import androidx.compose.material3.SwipeToDismissBoxValue import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar -import androidx.compose.material3.rememberSwipeToDismissBoxState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf @@ -61,6 +63,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset @@ -142,6 +145,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) { val activity = remember { findActivity() } val density = LocalDensity.current val view = LocalView.current + val positionalThreshold = SwipeToDismissBoxDefaults.positionalThreshold val allName = stringResource(R.string.download_all) val defaultName = stringResource(R.string.default_download_label_name) val title = stringResource( @@ -261,21 +265,28 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) { }, ) } - itemsIndexed(labelList, key = { _, item -> item.label }) { index, (item) -> - val dismissState = rememberSwipeToDismissBoxState( - confirmValueChange = { + itemsIndexed(labelList, key = { _, item -> item.id!! }) { index, (item) -> + // Not using rememberSwipeToDismissBoxState to prevent LazyColumn from reusing it + val dismissState = remember { SwipeToDismissBoxState(SwipeToDismissBoxValue.Settled, density, positionalThreshold = positionalThreshold) } + LaunchedEffect(dismissState) { + snapshotFlow { dismissState.currentValue }.collect { if (it == SwipeToDismissBoxValue.EndToStart) { - launch { + runCatching { + awaitPermissionOrCancel(confirmText = R.string.delete) { + Text(text = stringResource(R.string.delete_label, item)) + } + }.onSuccess { DownloadManager.deleteLabel(item) when (filterState.label) { item -> switchLabel("") null -> invalidateKey = !invalidateKey } + }.onFailure { + dismissState.reset() } } - true - }, - ) + } + } ReorderableItem(reorderableLabelState, key = item) { isDragging -> SwipeToDismissBox2( state = dismissState, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt index bd45264c2..579cdcdf1 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt @@ -46,11 +46,12 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SwipeToDismissBoxDefaults +import androidx.compose.material3.SwipeToDismissBoxState import androidx.compose.material3.SwipeToDismissBoxValue import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState -import androidx.compose.material3.rememberSwipeToDismissBoxState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.Stable @@ -62,6 +63,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset @@ -201,6 +203,7 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) = c onBack = { showSearchLayout = false }, ) val density = LocalDensity.current + val positionalThreshold = SwipeToDismissBoxDefaults.positionalThreshold val listState = rememberLazyGridState() val gridState = rememberLazyStaggeredGridState() val isTopList = remember(urlBuilder) { urlBuilder.mode == MODE_TOPLIST } @@ -385,21 +388,30 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) = c } itemsIndexed(quickSearchList, key = { _, item -> item.id!! }) { index, item -> ReorderableItem(reorderableLazyListState, key = item.id!!) { isDragging -> - val dismissState = rememberSwipeToDismissBoxState( - confirmValueChange = { value -> + // Not using rememberSwipeToDismissBoxState to prevent LazyColumn from reusing it + val dismissState = remember { SwipeToDismissBoxState(SwipeToDismissBoxValue.Settled, density, positionalThreshold = positionalThreshold) } + LaunchedEffect(dismissState) { + snapshotFlow { dismissState.currentValue }.collect { value -> if (value == SwipeToDismissBoxValue.EndToStart) { - with(quickSearchList) { - val removeIndex = indexOf(item) - subList(removeIndex + 1, size).forEach { - it.position-- + runCatching { + awaitPermissionOrCancel(confirmText = R.string.delete) { + Text(text = stringResource(R.string.delete_quick_search, item.name)) } - removeAt(removeIndex) + }.onSuccess { + EhDB.deleteQuickSearch(item) + with(quickSearchList) { + val removeIndex = indexOf(item) + subList(removeIndex + 1, size).forEach { + it.position-- + } + removeAt(removeIndex) + } + }.onFailure { + dismissState.reset() } - launchIO { EhDB.deleteQuickSearch(item) } } - true - }, - ) + } + } SwipeToDismissBox2( state = dismissState, backgroundContent = {}, diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 474f30aac..305a80b08 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -85,6 +85,7 @@ 已存在相同的快速搜索,名称为“%s”。 已存在相同名称 保存进度 + 删除快速搜索“%s”? 第 %1$d 页,共 %2$d 页 不限 @@ -245,6 +246,7 @@ “默认”是无效标签 标签已存在 重命名标签 + 删除标签“%s”? 这里是阅读历史 全部清除 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index c550c8ece..ceb0c298b 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -85,6 +85,7 @@ 已存在相同的快速搜尋,名稱為“%s”。 名稱重複 儲存進度 + 刪除快速搜尋“%s”? 第 %1$d 頁,共 %2$d 頁 不限 @@ -244,6 +245,7 @@ “預設”是無效的標籤 標籤已存在 重新命名標籤 + 刪除標籤“%s”? 歷史紀錄將會顯示在這裡 全部清除 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index aaf34745a..0a8a33f13 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -125,6 +125,7 @@ A duplicate quick search exists. The name is \"%s\". This name is already in use. Save progress + Delete quick search \"%s\"? Page %1$d, total %2$d pages Any @@ -287,6 +288,7 @@ \"Default\" is an invalid label Label exists Rename label + Delete label \"%s\"? Viewed galleries will be shown here Clear all