ChaptersSettingsSheet: Single source of truth and use new manga class (#7342)

Currently breaks initial settings state until the source of truth is
properly updated.
This commit is contained in:
Ivan Iskandar 2022-06-19 23:29:49 +07:00 committed by GitHub
parent e6a9d0b090
commit 005b9b595c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 179 additions and 51 deletions

View file

@ -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<PreferencesHelper>().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(

View file

@ -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) {

View file

@ -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<View> = 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.
*/