From 29f992fe33f9510225a3f3ff77c6d329b3403ee9 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Sun, 19 Jun 2022 23:29:49 +0700 Subject: [PATCH] ChaptersSettingsSheet: Single source of truth and use new manga class (#7342) Currently breaks initial settings state until the source of truth is properly updated. (cherry picked from commit 005b9b595cfe41484eea94998d2f3c9918759a94) # Conflicts: # app/src/main/java/eu/kanade/domain/manga/model/Manga.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt --- .../eu/kanade/domain/manga/model/Manga.kt | 89 ++++++++++- .../tachiyomi/ui/manga/MangaController.kt | 7 +- .../ui/manga/chapter/ChaptersSettingsSheet.kt | 138 ++++++++++++------ 3 files changed, 183 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt index 49db1c8de..126ecee65 100644 --- a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt @@ -2,8 +2,10 @@ package eu.kanade.domain.manga.model import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.library.CustomMangaManager +import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.widget.ExtendedNavigationView import tachiyomi.source.model.MangaInfo import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -76,15 +78,85 @@ data class Manga( } } - companion object { + val displayMode: Long + get() = chapterFlags and CHAPTER_DISPLAY_MASK + val unreadFilterRaw: Long + get() = chapterFlags and CHAPTER_UNREAD_MASK + + val downloadedFilterRaw: Long + get() = chapterFlags and CHAPTER_DOWNLOADED_MASK + + val bookmarkedFilterRaw: Long + get() = chapterFlags and CHAPTER_BOOKMARKED_MASK + + val unreadFilter: TriStateFilter + get() = when (unreadFilterRaw) { + CHAPTER_SHOW_UNREAD -> TriStateFilter.ENABLED_IS + CHAPTER_SHOW_READ -> TriStateFilter.ENABLED_NOT + else -> TriStateFilter.DISABLED + } + + val downloadedFilter: TriStateFilter + get() { + if (forceDownloaded()) return TriStateFilter.ENABLED_IS + return when (downloadedFilterRaw) { + CHAPTER_SHOW_DOWNLOADED -> TriStateFilter.ENABLED_IS + CHAPTER_SHOW_NOT_DOWNLOADED -> TriStateFilter.ENABLED_NOT + else -> TriStateFilter.DISABLED + } + } + + val bookmarkedFilter: TriStateFilter + get() = when (bookmarkedFilterRaw) { + CHAPTER_SHOW_BOOKMARKED -> TriStateFilter.ENABLED_IS + CHAPTER_SHOW_NOT_BOOKMARKED -> TriStateFilter.ENABLED_NOT + else -> TriStateFilter.DISABLED + } + + fun chaptersFiltered(): Boolean { + return unreadFilter != TriStateFilter.DISABLED || + downloadedFilter != TriStateFilter.DISABLED || + bookmarkedFilter != TriStateFilter.DISABLED + } + + fun forceDownloaded(): Boolean { + return favorite && Injekt.get().downloadedOnly().get() + } + + fun sortDescending(): Boolean { + return chapterFlags and CHAPTER_SORT_DIR_MASK == CHAPTER_SORTING_DESC + } + + companion object { // Generic filter that does not filter anything const val SHOW_ALL = 0x00000000L + const val CHAPTER_SORT_DESC = 0x00000000L + const val CHAPTER_SORT_ASC = 0x00000001L + const val CHAPTER_SORT_DIR_MASK = 0x00000001L + + const val CHAPTER_SHOW_UNREAD = 0x00000002L + const val CHAPTER_SHOW_READ = 0x00000004L + const val CHAPTER_UNREAD_MASK = 0x00000006L + + const val CHAPTER_SHOW_DOWNLOADED = 0x00000008L + const val CHAPTER_SHOW_NOT_DOWNLOADED = 0x00000010L + const val CHAPTER_DOWNLOADED_MASK = 0x00000018L + + const val CHAPTER_SHOW_BOOKMARKED = 0x00000020L + const val CHAPTER_SHOW_NOT_BOOKMARKED = 0x00000040L + const val CHAPTER_BOOKMARKED_MASK = 0x00000060L + const val CHAPTER_SORTING_SOURCE = 0x00000000L const val CHAPTER_SORTING_NUMBER = 0x00000100L const val CHAPTER_SORTING_UPLOAD_DATE = 0x00000200L const val CHAPTER_SORTING_MASK = 0x00000300L + const val CHAPTER_SORTING_DESC = 0x00000000L + + const val CHAPTER_DISPLAY_NAME = 0x00000000L + const val CHAPTER_DISPLAY_NUMBER = 0x00100000L + const val CHAPTER_DISPLAY_MASK = 0x00100000L // SY --> private val customMangaManager: CustomMangaManager by injectLazy() @@ -92,6 +164,20 @@ data class Manga( } } +enum class TriStateFilter { + DISABLED, // Disable filter + ENABLED_IS, // Enabled with "is" filter + ENABLED_NOT, // Enabled with "not" filter +} + +fun TriStateFilter.toTriStateGroupState(): ExtendedNavigationView.Item.TriStateGroup.State { + return when (this) { + TriStateFilter.DISABLED -> ExtendedNavigationView.Item.TriStateGroup.State.IGNORE + TriStateFilter.ENABLED_IS -> ExtendedNavigationView.Item.TriStateGroup.State.INCLUDE + TriStateFilter.ENABLED_NOT -> ExtendedNavigationView.Item.TriStateGroup.State.EXCLUDE + } +} + // TODO: Remove when all deps are migrated fun Manga.toDbManga(): DbManga = DbManga.create(url, ogTitle, source).also { it.id = id @@ -101,6 +187,7 @@ fun Manga.toDbManga(): DbManga = DbManga.create(url, ogTitle, source).also { it.viewer_flags = viewerFlags.toInt() it.chapter_flags = chapterFlags.toInt() it.cover_last_modified = coverLastModified + it.thumbnail_url = thumbnailUrl } fun Manga.toMangaInfo(): MangaInfo = MangaInfo( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index 3481222a4..e11fe4999 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -378,11 +378,7 @@ class MangaController : } .launchIn(viewScope) - settingsSheet = ChaptersSettingsSheet(router, presenter) { group -> - if (group is ChaptersSettingsSheet.Filter.FilterGroup) { - updateFilterIconState() - } - } + settingsSheet = ChaptersSettingsSheet(router, presenter) trackSheet = TrackSheet(this, manga!!, (activity as MainActivity).supportFragmentManager) @@ -1092,6 +1088,7 @@ class MangaController : } updateFabVisibility() + updateFilterIconState() settingsSheet?.filters?.updateScanlatorFilter() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt index 4cb667407..2c6b9dae6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt @@ -7,8 +7,10 @@ import android.view.View import androidx.core.view.isVisible import com.bluelinelabs.conductor.Router import com.google.android.material.dialog.MaterialAlertDialogBuilder +import eu.kanade.domain.manga.model.Manga +import eu.kanade.domain.manga.model.toTriStateGroupState import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.database.models.toDomainManga import eu.kanade.tachiyomi.ui.manga.MangaPresenter import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.withUIContext @@ -17,29 +19,43 @@ import eu.kanade.tachiyomi.widget.ExtendedNavigationView import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.State import eu.kanade.tachiyomi.widget.sheet.TabbedBottomSheetDialog import exh.md.utils.MdUtil +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel import kotlinx.coroutines.supervisorScope class ChaptersSettingsSheet( private val router: Router, private val presenter: MangaPresenter, - private val onGroupClickListener: (ExtendedNavigationView.Group) -> Unit, ) : TabbedBottomSheetDialog(router.activity!!) { - val filters = Filter(router.activity!!) - private val sort = Sort(router.activity!!) - private val display = Display(router.activity!!) + private lateinit var scope: CoroutineScope + + private var manga: Manga? = null + + val filters = Filter(context) + private val sort = Sort(context) + private val display = Display(context) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - filters.onGroupClicked = onGroupClickListener - sort.onGroupClicked = onGroupClickListener - display.onGroupClicked = onGroupClickListener - binding.menu.isVisible = true binding.menu.setOnClickListener { it.post { showPopupMenu(it) } } } + override fun onAttachedToWindow() { + super.onAttachedToWindow() + scope = MainScope() + // TODO: Listen to changes + updateManga() + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + scope.cancel() + } + override fun getTabViews(): List = listOf( filters, sort, @@ -52,6 +68,10 @@ class ChaptersSettingsSheet( R.string.action_display, ) + private fun updateManga() { + manga = presenter.manga.toDomainManga() + } + private fun showPopupMenu(view: View) { view.popupMenu( menuRes = R.menu.default_chapter_filter, @@ -88,6 +108,10 @@ class ChaptersSettingsSheet( filterGroup.updateScanlatorFilter() } + override fun updateView() { + filterGroup.updateModels() + } + inner class FilterGroup : Group { private val downloaded = Item.TriStateGroup(R.string.action_filter_downloaded, this) @@ -101,20 +125,30 @@ class ChaptersSettingsSheet( override val footer: Item? = null override fun initModels() { - if (presenter.forceDownloaded()) { + val manga = manga ?: return + if (manga.forceDownloaded()) { downloaded.state = State.INCLUDE.value downloaded.enabled = false } else { - downloaded.state = presenter.onlyDownloaded().value + downloaded.state = manga.downloadedFilter.toTriStateGroupState().value } - unread.state = presenter.onlyUnread().value - bookmarked.state = presenter.onlyBookmarked().value + unread.state = manga.unreadFilter.toTriStateGroupState().value + bookmarked.state = manga.bookmarkedFilter.toTriStateGroupState().value + // SY --> updateScanlatorFilter() + // SY <-- } + fun updateModels() { + initModels() + adapter.notifyItemRangeChanged(0, 3) + } + + // SY --> fun updateScanlatorFilter() { scanlatorFilters.isVisible = presenter.allChapterScanlators.size > 1 } + // SY <-- override fun onItemClicked(item: Item) { if (item is Item.DrawableSelection) { @@ -156,7 +190,6 @@ class ChaptersSettingsSheet( State.EXCLUDE.value -> State.IGNORE else -> throw Exception("Unknown State") } - item.state = newState.value when (item) { downloaded -> presenter.setDownloadedFilter(newState) unread -> presenter.setUnreadFilter(newState) @@ -164,8 +197,9 @@ class ChaptersSettingsSheet( else -> {} } - initModels() - adapter.notifyItemChanged(items.indexOf(item), item) + // TODO: Remove + updateManga() + updateView() } } } @@ -176,8 +210,14 @@ class ChaptersSettingsSheet( inner class Sort @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : Settings(context, attrs) { + private val group = SortGroup() + init { - setGroups(listOf(SortGroup())) + setGroups(listOf(group)) + } + + override fun updateView() { + group.updateModels() } inner class SortGroup : Group { @@ -191,8 +231,9 @@ class ChaptersSettingsSheet( override val footer: Item? = null override fun initModels() { - val sorting = presenter.manga.sorting - val order = if (presenter.manga.sortDescending()) { + val manga = manga ?: return + val sorting = manga.sorting + val order = if (manga.sortDescending()) { Item.MultiSort.SORT_DESC } else { Item.MultiSort.SORT_ASC @@ -206,29 +247,23 @@ class ChaptersSettingsSheet( if (sorting == Manga.CHAPTER_SORTING_UPLOAD_DATE) order else Item.MultiSort.SORT_NONE } - override fun onItemClicked(item: Item) { - items.forEachIndexed { i, multiSort -> - multiSort.state = if (multiSort == item) { - when (item.state) { - Item.MultiSort.SORT_NONE -> Item.MultiSort.SORT_ASC - Item.MultiSort.SORT_ASC -> Item.MultiSort.SORT_DESC - Item.MultiSort.SORT_DESC -> Item.MultiSort.SORT_ASC - else -> throw Exception("Unknown state") - } - } else { - Item.MultiSort.SORT_NONE - } - adapter.notifyItemChanged(i, multiSort) - } + fun updateModels() { + initModels() + adapter.notifyItemRangeChanged(0, 3) + } + override fun onItemClicked(item: Item) { when (item) { - source -> presenter.setSorting(Manga.CHAPTER_SORTING_SOURCE) - chapterNum -> presenter.setSorting(Manga.CHAPTER_SORTING_NUMBER) - uploadDate -> presenter.setSorting(Manga.CHAPTER_SORTING_UPLOAD_DATE) + source -> presenter.setSorting(Manga.CHAPTER_SORTING_SOURCE.toInt()) + chapterNum -> presenter.setSorting(Manga.CHAPTER_SORTING_NUMBER.toInt()) + uploadDate -> presenter.setSorting(Manga.CHAPTER_SORTING_UPLOAD_DATE.toInt()) else -> throw Exception("Unknown sorting") } + // TODO: Remove presenter.reverseSortOrder() + updateManga() + updateView() } } } @@ -239,8 +274,14 @@ class ChaptersSettingsSheet( inner class Display @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : Settings(context, attrs) { + private val group = DisplayGroup() + init { - setGroups(listOf(DisplayGroup())) + setGroups(listOf(group)) + } + + override fun updateView() { + group.updateModels() } inner class DisplayGroup : Group { @@ -253,25 +294,29 @@ class ChaptersSettingsSheet( override val footer: Item? = null override fun initModels() { - val mode = presenter.manga.displayMode + val mode = manga?.displayMode ?: return displayTitle.checked = mode == Manga.CHAPTER_DISPLAY_NAME displayChapterNum.checked = mode == Manga.CHAPTER_DISPLAY_NUMBER } + fun updateModels() { + initModels() + adapter.notifyItemRangeChanged(0, 2) + } + override fun onItemClicked(item: Item) { item as Item.Radio if (item.checked) return - items.forEachIndexed { index, radio -> - radio.checked = item == radio - adapter.notifyItemChanged(index, radio) - } - when (item) { - displayTitle -> presenter.setDisplayMode(Manga.CHAPTER_DISPLAY_NAME) - displayChapterNum -> presenter.setDisplayMode(Manga.CHAPTER_DISPLAY_NUMBER) + displayTitle -> presenter.setDisplayMode(Manga.CHAPTER_DISPLAY_NAME.toInt()) + displayChapterNum -> presenter.setDisplayMode(Manga.CHAPTER_DISPLAY_NUMBER.toInt()) else -> throw NotImplementedError("Unknown display mode") } + + // TODO: Remove + updateManga() + updateView() } } } @@ -294,6 +339,9 @@ class ChaptersSettingsSheet( addView(recycler) } + open fun updateView() { + } + /** * Adapter of the recycler view. */