From 005b9b595cfe41484eea94998d2f3c9918759a94 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. --- .../eu/kanade/domain/manga/model/Manga.kt | 89 +++++++++++- .../tachiyomi/ui/manga/MangaController.kt | 7 +- .../ui/manga/chapter/ChaptersSettingsSheet.kt | 134 ++++++++++++------ 3 files changed, 179 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 30c3b4a244..65b4b4414b 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 @@ -1,8 +1,10 @@ package eu.kanade.domain.manga.model import eu.kanade.tachiyomi.data.cache.CoverCache +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 @@ -45,15 +47,99 @@ 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 + } +} + +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 } } @@ -66,6 +152,7 @@ fun Manga.toDbManga(): DbManga = DbManga.create(url, title, 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 d2198f8b7f..b8bd6b189b 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 @@ -305,11 +305,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) @@ -873,6 +869,7 @@ class MangaController : } updateFabVisibility() + updateFilterIconState() } private fun fetchChaptersFromSource(manualFetch: Boolean = false) { 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 a8d04394d1..d85ceabd4c 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 @@ -6,35 +6,51 @@ import android.util.AttributeSet import android.view.View import androidx.core.view.isVisible import com.bluelinelabs.conductor.Router +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.view.popupMenu import eu.kanade.tachiyomi.widget.ExtendedNavigationView import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.State import eu.kanade.tachiyomi.widget.sheet.TabbedBottomSheetDialog +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel 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, @@ -47,6 +63,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, @@ -79,6 +99,10 @@ class ChaptersSettingsSheet( return filterGroup.items.any { it.state != State.IGNORE.value } } + override fun updateView() { + filterGroup.updateModels() + } + inner class FilterGroup : Group { private val downloaded = Item.TriStateGroup(R.string.action_filter_downloaded, this) @@ -90,14 +114,20 @@ 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 + } + + fun updateModels() { + initModels() + adapter.notifyItemRangeChanged(0, 3) } override fun onItemClicked(item: Item) { @@ -108,7 +138,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) @@ -116,8 +145,9 @@ class ChaptersSettingsSheet( else -> {} } - initModels() - adapter.notifyItemChanged(items.indexOf(item), item) + // TODO: Remove + updateManga() + updateView() } } } @@ -128,8 +158,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 { @@ -143,8 +179,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 @@ -158,29 +195,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() } } } @@ -191,8 +222,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 { @@ -205,25 +242,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() } } } @@ -246,6 +287,9 @@ class ChaptersSettingsSheet( addView(recycler) } + open fun updateView() { + } + /** * Adapter of the recycler view. */