From 1473f6326d2131768782e88fe9b4d6942a670aee Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Mon, 3 Jul 2023 02:50:58 +0800 Subject: [PATCH] Refactor addToFavorites() --- .../com/hippo/ehviewer/client/EhEngine.kt | 6 +- .../client/parser/GalleryDetailParser.kt | 4 +- .../client/parser/GalleryListParser.kt | 3 - .../com/hippo/ehviewer/ui/CommonOperations.kt | 119 ++++++++++-------- .../ehviewer/ui/scene/GalleryDetailScene.kt | 93 ++++---------- .../com/hippo/ehviewer/ui/tools/Dialog.kt | 39 ++++-- 6 files changed, 128 insertions(+), 136 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt index c8b112218..82d119c43 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt @@ -238,8 +238,8 @@ object EhEngine { suspend fun addFavorites( gid: Long, token: String?, - dstCat: Int, - note: String?, + dstCat: Int = -1, + note: String? = null, ) { val catStr: String = when (dstCat) { -1 -> "favdel" @@ -450,6 +450,6 @@ object EhEngine { } suspend fun addFavoritesRange(gidTokenArray: Array>, dstCat: Int) { - gidTokenArray.forEach { (gid, token) -> addFavorites(gid, token, dstCat, null) } + gidTokenArray.forEach { (gid, token) -> addFavorites(gid, token, dstCat) } } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.kt b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.kt index b30eae9a2..6e4ea79ac 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.kt @@ -201,9 +201,7 @@ object GalleryDetailParser { gd.isFavorited = false if (gdf != null) { val favoriteName = gdf.text().trim() - if (favoriteName == "Add to Favorites") { - gd.favoriteName = null - } else { + if (favoriteName != "Add to Favorites") { gd.isFavorited = true gd.favoriteName = gdf.text().trim() PATTERN_FAVORITE_SLOT.find(body)?.run { diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.kt b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.kt index 98c136724..4c6228528 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.kt @@ -17,14 +17,12 @@ package com.hippo.ehviewer.client.parser import android.util.Log import com.hippo.ehviewer.EhDB -import com.hippo.ehviewer.R import com.hippo.ehviewer.client.data.GalleryInfo import com.hippo.ehviewer.client.exception.ParseException import com.hippo.ehviewer.util.ExceptionUtils import com.hippo.ehviewer.yorozuya.toIntOrDefault import org.jsoup.Jsoup import org.jsoup.nodes.Document -import splitties.init.appCtx import java.util.regex.Pattern object GalleryListParser { @@ -96,7 +94,6 @@ object GalleryListParser { runCatching { parseGalleryInfo(it.toString()) }.onFailure { it.printStackTrace() }.getOrNull()?.apply { if (favoriteSlot == -2 && EhDB.containLocalFavorites(gid)) { favoriteSlot = -1 - favoriteName = appCtx.getString(R.string.local_favorites) } generateSLang() } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.kt b/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.kt index 6f97eb771..7f6701143 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.kt @@ -25,6 +25,7 @@ import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.DriveFileMove import androidx.compose.material.icons.filled.Favorite +import androidx.compose.material.icons.filled.FavoriteBorder import androidx.compose.material.icons.filled.HeartBroken import androidx.compose.material.icons.filled.MenuBook import androidx.compose.material3.Text @@ -51,13 +52,11 @@ import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.util.lang.launchNow import eu.kanade.tachiyomi.util.lang.withUIContext -import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import moe.tarsin.coroutines.runSuspendCatching import rikka.core.util.ContextUtils.requireActivity import splitties.init.appCtx -import kotlin.coroutines.resume object CommonOperations { fun startDownload(activity: MainActivity?, galleryInfo: GalleryInfo, forceDefault: Boolean) { @@ -191,64 +190,82 @@ suspend fun keepNoMediaFileStatus() { } } -suspend fun Context.addToFavorites(galleryInfo: GalleryInfo, select: Boolean = false) { - val slot = Settings.defaultFavSlot - val localFav = getString(R.string.local_favorites) - val items = Settings.favCat.toMutableList().apply { add(0, localFav) } - suspend fun doAddToFavorites(slot: Int, newFavoriteName: String?) { - when (slot) { - -1 -> { +suspend fun DialogState.addToFavorites(galleryInfo: GalleryInfo): Boolean { + val localFaved = EhDB.containLocalFavorites(galleryInfo.gid) + val localFav = if (localFaved) { + Icons.Default.Favorite + } else { + Icons.Default.FavoriteBorder + } to appCtx.getString(R.string.local_favorites) + val items = Settings.favCat.mapIndexed { index, name -> + if (galleryInfo.favoriteSlot == index) { + Icons.Default.Favorite + } else { + Icons.Default.FavoriteBorder + } to name + }.toTypedArray() + val slot = showSelectItemWithIcon(localFav, *items, title = R.string.add_favorites_dialog_title) - 1 + val newFavoriteName = Settings.favCat.getOrNull(slot) + return doAddToFavorites(galleryInfo, slot, newFavoriteName) +} + +private suspend fun doAddToFavorites( + galleryInfo: GalleryInfo, + slot: Int = -2, + newFavoriteName: String? = null, + note: String? = null, + localFaved: Boolean = false, +): Boolean { + val add = when (slot) { + -2 -> { + EhDB.removeLocalFavorites(galleryInfo.gid) + if (galleryInfo.favoriteSlot >= 0) { + EhEngine.addFavorites(galleryInfo.gid, galleryInfo.token) + } + false + } + + -1 -> { + if (localFaved) { + EhDB.removeLocalFavorites(galleryInfo.gid) + } else { EhDB.putLocalFavorites(galleryInfo) } + !localFaved + } - in 0..9 -> { - EhEngine.addFavorites(galleryInfo.gid, galleryInfo.token, slot, "") - } - - else -> { - throw EhException("Invalid favslot!") + in 0..9 -> { + if (galleryInfo.favoriteSlot == slot) { + EhEngine.addFavorites(galleryInfo.gid, galleryInfo.token) + false + } else { + EhEngine.addFavorites(galleryInfo.gid, galleryInfo.token, slot, note) + true } } - galleryInfo.favoriteName = newFavoriteName - galleryInfo.favoriteSlot = slot + + else -> throw EhException("Invalid favorite slot!") + } + if (add) { + if (slot != -1 || galleryInfo.favoriteSlot == -2) { // Cloud favorites have priority + galleryInfo.favoriteSlot = slot + galleryInfo.favoriteName = newFavoriteName + withUIContext { + FavouriteStatusRouter.modifyFavourites(galleryInfo.gid, slot) + } + } + } else if (slot != -1 || galleryInfo.favoriteSlot == -1) { + val newSlot = if (slot >= 0 && localFaved) -1 else -2 + galleryInfo.favoriteSlot = newSlot + galleryInfo.favoriteName = null withUIContext { - FavouriteStatusRouter.modifyFavourites(galleryInfo.gid, slot) - } - } - if (!select && slot >= -1 && slot <= 9) { - val newFavoriteName = if (slot >= 0) items[slot + 1] else null - doAddToFavorites(slot, newFavoriteName) - } else { - val (slot1, checked) = withUIContext { - suspendCancellableCoroutine { cont -> - ListCheckBoxDialogBuilder( - this@addToFavorites, - items, - { b, _, position -> cont.resume(position - 1 to b.isChecked) }, - getString(R.string.remember_favorite_collection), - false, - ).setTitle(R.string.add_favorites_dialog_title).setOnCancelListener { cont.cancel() }.show() - } - } - val newFavoriteName = if (slot1 in 0..9) items[slot1 + 1] else null - doAddToFavorites(slot1, newFavoriteName) - if (checked) { - Settings.defaultFavSlot = slot1 - } else { - Settings.defaultFavSlot = -2 + FavouriteStatusRouter.modifyFavourites(galleryInfo.gid, newSlot) } } + return add } -suspend fun removeFromFavorites(galleryInfo: GalleryInfo) { - EhDB.removeLocalFavorites(galleryInfo.gid) - EhEngine.addFavorites(galleryInfo.gid, galleryInfo.token, -1, "") - galleryInfo.favoriteName = null - galleryInfo.favoriteSlot = -2 - withUIContext { - FavouriteStatusRouter.modifyFavourites(galleryInfo.gid, -2) - } -} +suspend fun removeFromFavorites(galleryInfo: GalleryInfo) = doAddToFavorites(galleryInfo) fun Context.navToReader(info: GalleryInfo, page: Int = -1) { val intent = Intent(this, ReaderActivity::class.java) @@ -287,6 +304,7 @@ suspend fun DialogState.doGalleryInfoAction(info: GalleryInfo, context: Context) CommonOperations.startDownload(this@with, info, false) } } + 2 -> if (favourite) { runSuspendCatching { removeFromFavorites(info) @@ -302,6 +320,7 @@ suspend fun DialogState.doGalleryInfoAction(info: GalleryInfo, context: Context) showTip(R.string.add_to_favorite_failure, BaseScene.LENGTH_LONG) } } + 3 -> showMoveDownloadLabel(info) } true diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt index 75141f1aa..f0cea9644 100755 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt @@ -150,10 +150,10 @@ import com.hippo.ehviewer.ui.main.GalleryDetailHeaderCard import com.hippo.ehviewer.ui.main.GalleryTags import com.hippo.ehviewer.ui.navToReader import com.hippo.ehviewer.ui.openBrowser -import com.hippo.ehviewer.ui.removeFromFavorites import com.hippo.ehviewer.ui.scene.GalleryListScene.Companion.toStartArgs import com.hippo.ehviewer.ui.setMD3Content import com.hippo.ehviewer.ui.tools.CrystalCard +import com.hippo.ehviewer.ui.tools.DialogState import com.hippo.ehviewer.ui.tools.FilledTertiaryIconButton import com.hippo.ehviewer.ui.tools.FilledTertiaryIconToggleButton import com.hippo.ehviewer.ui.tools.GalleryDetailRating @@ -168,6 +168,8 @@ import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.withUIContext import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Job +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import moe.tarsin.coroutines.runSuspendCatching import okhttp3.HttpUrl.Companion.toHttpUrl import splitties.systemservices.downloadManager @@ -209,7 +211,7 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { private var mArchiveFormParamOr: String? = null private var mArchiveList: List? = null private var mCurrentFunds: HomeParser.Funds? = null - private var mModifyingFavorites = false + private var favoritesLock = Mutex() @StringRes private fun getRatingText(rating: Float): Int { @@ -277,6 +279,8 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { private val category: Int get() = composeBindingGD?.category ?: composeBindingGI?.category ?: -1 + private val dialogState = DialogState() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (savedInstanceState == null) { @@ -396,6 +400,7 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { (requireActivity() as MainActivity).mShareUrl = galleryDetailUrl return ComposeView(requireContext()).apply { setMD3Content { + dialogState.Handler() val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() Scaffold( topBar = { @@ -872,37 +877,26 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { private fun modifyFavourite() { val galleryDetail = composeBindingGD ?: return lifecycleScope.launchIO { - if (!mModifyingFavorites) { + favoritesLock.withLock { var remove = false - val containLocalFavorites = EhDB.containLocalFavorites(galleryDetail.gid) - if (containLocalFavorites || galleryDetail.isFavorited) { - mModifyingFavorites = true - runCatching { - removeFromFavorites(galleryDetail) + runCatching { + remove = !dialogState.addToFavorites(galleryDetail) + if (remove) { showTip(R.string.remove_from_favorite_success, LENGTH_SHORT) - onModifyFavoritesSuccess(true) - }.onFailure { - if (it !is CancellationException) { - showTip(R.string.remove_from_favorite_failure, LENGTH_LONG) - } - } - mModifyingFavorites = false - remove = true - } - if (!remove) { - mModifyingFavorites = true - runCatching { - requireContext().addToFavorites(galleryDetail) + } else { showTip(R.string.add_to_favorite_success, LENGTH_SHORT) - withUIContext { - onModifyFavoritesSuccess(false) - } - }.onFailure { - if (it !is CancellationException) { + } + withUIContext { + onModifyFavoritesSuccess() + } + }.onFailure { + if (it !is CancellationException) { + if (remove) { + showTip(R.string.remove_from_favorite_failure, LENGTH_LONG) + } else { showTip(R.string.add_to_favorite_failure, LENGTH_LONG) } } - mModifyingFavorites = false } withUIContext { // Update UI @@ -1245,47 +1239,6 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { .show() } - private fun modifyFavouriteLongClick() { - lifecycleScope.launchIO { - if (composeBindingGD != null && !mModifyingFavorites) { - var remove = false - if (EhDB.containLocalFavorites(composeBindingGD!!.gid) || composeBindingGD!!.isFavorited) { - mModifyingFavorites = true - runCatching { - removeFromFavorites(composeBindingGD!!) - showTip(R.string.remove_from_favorite_success, LENGTH_SHORT) - onModifyFavoritesSuccess(true) - }.onFailure { - if (it !is CancellationException) { - showTip(R.string.remove_from_favorite_failure, LENGTH_LONG) - } - } - mModifyingFavorites = false - remove = true - } - if (!remove) { - mModifyingFavorites = true - runCatching { - requireContext().addToFavorites(composeBindingGD!!, true) - showTip(R.string.add_to_favorite_success, LENGTH_SHORT) - withUIContext { - onModifyFavoritesSuccess(false) - } - }.onFailure { - if (it !is CancellationException) { - showTip(R.string.add_to_favorite_failure, LENGTH_LONG) - } - } - mModifyingFavorites = false - } - withUIContext { - // Update UI - updateFavoriteDrawable() - } - } - } - } - private fun updateDownloadText() { downloadButtonText = when (mDownloadState) { DownloadInfo.STATE_INVALID -> getString(R.string.download) @@ -1339,9 +1292,9 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener { override fun onRenameLabel(from: String, to: String) {} override fun onUpdateLabels() {} - private fun onModifyFavoritesSuccess(addOrRemove: Boolean) { + private fun onModifyFavoritesSuccess() { if (composeBindingGD != null) { - composeBindingGD!!.isFavorited = !addOrRemove && composeBindingGD!!.favoriteName != null + composeBindingGD!!.isFavorited = composeBindingGD!!.favoriteName != null updateFavoriteDrawable() } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/tools/Dialog.kt b/app/src/main/java/com/hippo/ehviewer/ui/tools/Dialog.kt index 0f678cdb5..2c0a578ee 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/tools/Dialog.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/tools/Dialog.kt @@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.Icon @@ -164,13 +166,36 @@ class DialogState { ): Int = showNoButton { Column { Text(text = title, modifier = Modifier.padding(horizontal = 16.dp).padding(top = 16.dp), style = MaterialTheme.typography.titleMedium) - items.forEachIndexed { index, (icon, text) -> - Row( - modifier = Modifier.clickable { dismissWith(index) }.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Icon(imageVector = icon, contentDescription = null, modifier = Modifier.padding(16.dp), tint = AlertDialogDefaults.iconContentColor) - Text(text = stringResource(id = text), style = MaterialTheme.typography.titleMedium) + LazyColumn { + itemsIndexed(items) { index, (icon, text) -> + Row( + modifier = Modifier.clickable { dismissWith(index) }.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon(imageVector = icon, contentDescription = null, modifier = Modifier.padding(16.dp), tint = AlertDialogDefaults.iconContentColor) + Text(text = stringResource(id = text), style = MaterialTheme.typography.titleMedium) + } + } + } + Spacer(modifier = Modifier.size(8.dp)) + } + } + + suspend fun showSelectItemWithIcon( + vararg items: Pair, + @StringRes title: Int, + ): Int = showNoButton { + Column { + Text(text = stringResource(id = title), modifier = Modifier.padding(horizontal = 16.dp).padding(top = 16.dp), style = MaterialTheme.typography.titleMedium) + LazyColumn { + itemsIndexed(items) { index, (icon, text) -> + Row( + modifier = Modifier.clickable { dismissWith(index) }.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon(imageVector = icon, contentDescription = null, modifier = Modifier.padding(16.dp), tint = AlertDialogDefaults.iconContentColor) + Text(text = text, style = MaterialTheme.typography.titleMedium) + } } } Spacer(modifier = Modifier.size(8.dp))