Add option to tap on certain stats for a filtered library

For series type, status, source, tracker, and tag; open a subclass of the library to only show the filtered results
This commit is contained in:
Jays2Kings 2022-12-15 17:26:50 -05:00
parent 46e0eb4233
commit 173fc8b32c
8 changed files with 256 additions and 57 deletions

View file

@ -0,0 +1,78 @@
package eu.kanade.tachiyomi.ui.library
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.View
import androidx.core.view.isVisible
import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet
import eu.kanade.tachiyomi.util.view.collapse
import uy.kohesive.injekt.api.get
class FilteredLibraryController(bundle: Bundle? = null) : LibraryController(bundle) {
private var queryText: String? = null
var filterDownloaded: Int = 0
private set
var filterUnread: Int = 0
private set
var filterStatus: Int? = null
private set
var filterTracked: Int = 0
private set
var filterMangaType: Int = 0
private set
private var customTitle: String? = null
override fun getTitle(): String? {
return customTitle ?: super.getTitle()
}
constructor(
title: String,
queryText: String? = null,
filterDownloaded: Int = 0,
filterUnread: Int = 0,
filterStatus: Int? = null,
filterTracked: Int = 0,
filterTrackerName: String? = null,
filterMangaType: Int = 0,
) : this() {
customTitle = title
this.filterDownloaded = filterDownloaded
this.filterUnread = filterUnread
this.filterStatus = filterStatus
this.filterTracked = filterTracked
if (filterTracked != 0 && filterTrackerName != null) {
FilterBottomSheet.FILTER_TRACKER = filterTrackerName
}
this.filterMangaType = filterMangaType
this.queryText = queryText
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.filterBottomSheet.root.sheetBehavior?.isHideable = false
binding.filterBottomSheet.root.sheetBehavior?.collapse()
binding.filterBottomSheet.filterScroll.isVisible = false
binding.filterBottomSheet.secondLayout.isVisible = false
binding.filterBottomSheet.viewOptions.isVisible = false
binding.filterBottomSheet.pill.isVisible = false
queryText?.let { search(it) }
}
override fun showFloatingBar() = false
override fun handleBack(): Boolean {
if (binding.recyclerCover.isClickable) {
showCategories(false)
return true
}
return false
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { }
override fun toggleCategoryVisibility(position: Int) { }
override fun hasActiveFiltersFromPref(): Boolean = false
}

View file

@ -137,7 +137,7 @@ import kotlin.math.roundToInt
import kotlin.random.Random import kotlin.random.Random
import kotlin.random.nextInt import kotlin.random.nextInt
class LibraryController( open class LibraryController(
bundle: Bundle? = null, bundle: Bundle? = null,
val preferences: PreferencesHelper = Injekt.get(), val preferences: PreferencesHelper = Injekt.get(),
) : BaseCoroutineController<LibraryControllerBinding, LibraryPresenter>(bundle), ) : BaseCoroutineController<LibraryControllerBinding, LibraryPresenter>(bundle),
@ -181,6 +181,9 @@ class LibraryController(
*/ */
private var query = "" private var query = ""
val isSubClass: Boolean
get() = this is FilteredLibraryController
/** /**
* Currently selected mangas. * Currently selected mangas.
*/ */
@ -235,7 +238,7 @@ class LibraryController(
private lateinit var elevateAppBar: ((Boolean) -> Unit) private lateinit var elevateAppBar: ((Boolean) -> Unit)
private var hopperOffset = 0f private var hopperOffset = 0f
private val maxHopperOffset: Float private val maxHopperOffset: Float
get() = if (activityBinding?.bottomNav != null) { get() = if (activityBinding?.bottomNav != null && !isSubClass) {
55f.dpToPx 55f.dpToPx
} else { } else {
( (
@ -301,7 +304,9 @@ class LibraryController(
hopperOffset += dy hopperOffset += dy
hopperOffset = hopperOffset.coerceIn(0f, maxHopperOffset) hopperOffset = hopperOffset.coerceIn(0f, maxHopperOffset)
} }
if (!preferences.hideBottomNavOnScroll().get() || activityBinding?.bottomNav == null) { if (!preferences.hideBottomNavOnScroll().get() || activityBinding?.bottomNav == null ||
isSubClass
) {
updateFilterSheetY() updateFilterSheetY()
} }
if (!binding.fastScroller.isFastScrolling) { if (!binding.fastScroller.isFastScrolling) {
@ -328,9 +333,11 @@ class LibraryController(
val savedCurrentCategory = getHeader(true)?.category ?: return val savedCurrentCategory = getHeader(true)?.category ?: return
if (savedCurrentCategory.order != lastUsedCategory) { if (savedCurrentCategory.order != lastUsedCategory) {
lastUsedCategory = savedCurrentCategory.order lastUsedCategory = savedCurrentCategory.order
if (!isSubClass) {
preferences.lastUsedCategory().set(savedCurrentCategory.order) preferences.lastUsedCategory().set(savedCurrentCategory.order)
} }
} }
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState) super.onScrollStateChanged(recyclerView, newState)
@ -363,7 +370,7 @@ class LibraryController(
} }
fun updateFilterSheetY() { fun updateFilterSheetY() {
val bottomBar = activityBinding?.bottomNav val bottomBar = if (!isSubClass) activityBinding?.bottomNav else null
val systemInsets = view?.rootWindowInsetsCompat?.getInsets(systemBars()) val systemInsets = view?.rootWindowInsetsCompat?.getInsets(systemBars())
val bottomSheet = binding.filterBottomSheet.filterBottomSheet val bottomSheet = binding.filterBottomSheet.filterBottomSheet
if (bottomBar != null) { if (bottomBar != null) {
@ -393,13 +400,14 @@ class LibraryController(
android.R.integer.config_shortAnimTime, android.R.integer.config_shortAnimTime,
) ?: 0 ) ?: 0
if (preferences.autohideHopper().get()) { if (preferences.autohideHopper().get()) {
val bottomBar = if (isSubClass) null else activityBinding?.bottomNav
// Flow same snap rules as bottom nav // Flow same snap rules as bottom nav
val closerToHopperBottom = hopperOffset > maxHopperOffset / 2 val closerToHopperBottom = hopperOffset > maxHopperOffset / 2
val halfWayBottom = activityBinding?.bottomNav?.height?.toFloat()?.div(2) ?: 0f val halfWayBottom = bottomBar?.height?.toFloat()?.div(2) ?: 0f
val closerToBottom = (activityBinding?.bottomNav?.translationY ?: 0f) > halfWayBottom val closerToBottom = (bottomBar?.translationY ?: 0f) > halfWayBottom
val atTop = !binding.libraryGridRecycler.recycler.canScrollVertically(-1) val atTop = !binding.libraryGridRecycler.recycler.canScrollVertically(-1)
val closerToEdge = val closerToEdge =
if (preferences.hideBottomNavOnScroll().get() && activityBinding?.bottomNav != null) { if (preferences.hideBottomNavOnScroll().get() && bottomBar != null) {
closerToBottom && !atTop closerToBottom && !atTop
} else { } else {
closerToHopperBottom closerToHopperBottom
@ -529,7 +537,9 @@ class LibraryController(
activity!!.getString(R.string.group_library_by), activity!!.getString(R.string.group_library_by),
presenter.groupType, presenter.groupType,
) { _, item -> ) { _, item ->
if (!isSubClass) {
preferences.groupLibraryBy().set(item) preferences.groupLibraryBy().set(item)
}
presenter.groupType = item presenter.groupType = item
shouldScrollToTop = true shouldScrollToTop = true
presenter.getLibrary() presenter.getLibrary()
@ -538,7 +548,7 @@ class LibraryController(
} }
private fun showDisplayOptions() { private fun showDisplayOptions() {
if (displaySheet == null) { if (displaySheet == null && !isSubClass) {
displaySheet = TabbedLibraryDisplaySheet(this) displaySheet = TabbedLibraryDisplaySheet(this)
displaySheet?.show() displaySheet?.show()
} }
@ -548,9 +558,11 @@ class LibraryController(
if (filterTooltip != null) { if (filterTooltip != null) {
filterTooltip?.close() filterTooltip?.close()
filterTooltip = null filterTooltip = null
if (!isSubClass) {
preferences.shownFilterTutorial().set(true) preferences.shownFilterTutorial().set(true)
} }
} }
}
override fun createBinding(inflater: LayoutInflater) = LibraryControllerBinding.inflate(inflater) override fun createBinding(inflater: LayoutInflater) = LibraryControllerBinding.inflate(inflater)
@ -623,7 +635,7 @@ class LibraryController(
createActionModeIfNeeded() createActionModeIfNeeded()
} }
if (presenter.libraryItems.isNotEmpty()) { if (presenter.libraryItems.isNotEmpty() && !isSubClass) {
presenter.restoreLibrary() presenter.restoreLibrary()
if (justStarted) { if (justStarted) {
val activityBinding = activityBinding ?: return val activityBinding = activityBinding ?: return
@ -800,15 +812,16 @@ class LibraryController(
} }
} }
fun updateHopperY(windowInsets: WindowInsetsCompat? = null) { open fun updateHopperY(windowInsets: WindowInsetsCompat? = null) {
val view = view ?: return val view = view ?: return
val insets = windowInsets ?: view.rootWindowInsetsCompat val insets = windowInsets ?: view.rootWindowInsetsCompat
val bottomNav = if (isSubClass) null else activityBinding?.bottomNav
val listOfYs = mutableListOf( val listOfYs = mutableListOf(
binding.filterBottomSheet.filterBottomSheet.y, binding.filterBottomSheet.filterBottomSheet.y,
activityBinding?.bottomNav?.y ?: binding.filterBottomSheet.filterBottomSheet.y, bottomNav?.y ?: binding.filterBottomSheet.filterBottomSheet.y,
) )
val insetBottom = insets?.getInsets(systemBars())?.bottom ?: 0 val insetBottom = insets?.getInsets(systemBars())?.bottom ?: 0
if (!preferences.autohideHopper().get() || activityBinding?.bottomNav == null) { if (!preferences.autohideHopper().get() || bottomNav == null) {
listOfYs.add(view.height - (insetBottom).toFloat()) listOfYs.add(view.height - (insetBottom).toFloat())
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && insets?.isImeVisible() == true) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && insets?.isImeVisible() == true) {
@ -819,9 +832,14 @@ class LibraryController(
(listOfYs.minOrNull() ?: binding.filterBottomSheet.filterBottomSheet.y) + (listOfYs.minOrNull() ?: binding.filterBottomSheet.filterBottomSheet.y) +
hopperOffset + hopperOffset +
binding.libraryGridRecycler.recycler.translationY binding.libraryGridRecycler.recycler.translationY
if (view.height - insetBottom < binding.categoryHopperFrame.y) { val bottom = if (isSubClass) {
binding.filterBottomSheet.root.height + binding.jumperCategoryText.height + 12.dpToPx
} else {
insetBottom
}
if (view.height - bottom < binding.categoryHopperFrame.y) {
binding.jumperCategoryText.translationY = binding.jumperCategoryText.translationY =
-(binding.categoryHopperFrame.y - (view.height - insetBottom)) + -(binding.categoryHopperFrame.y - (view.height - bottom)) +
binding.libraryGridRecycler.recycler.translationY binding.libraryGridRecycler.recycler.translationY
} else { } else {
binding.jumperCategoryText.translationY = binding.libraryGridRecycler.recycler.translationY binding.jumperCategoryText.translationY = binding.libraryGridRecycler.recycler.translationY
@ -945,9 +963,10 @@ class LibraryController(
private fun setRecyclerLayout() { private fun setRecyclerLayout() {
with(binding.libraryGridRecycler.recycler) { with(binding.libraryGridRecycler.recycler) {
val bottomNav = if (isSubClass) null else activityBinding?.bottomNav
viewScope.launchUI { viewScope.launchUI {
updatePaddingRelative( updatePaddingRelative(
bottom = 50.dpToPx + (activityBinding?.bottomNav?.height ?: 0), bottom = 50.dpToPx + (bottomNav?.height ?: 0),
) )
} }
useStaggered(preferences) useStaggered(preferences)
@ -1110,7 +1129,7 @@ class LibraryController(
if (binding.recyclerLayout.alpha == 0f) { if (binding.recyclerLayout.alpha == 0f) {
binding.recyclerLayout.animate().alpha(1f).setDuration(500).start() binding.recyclerLayout.animate().alpha(1f).setDuration(500).start()
} }
if (justStarted && freshStart) { if (justStarted && freshStart && !isSubClass) {
val activeC = activeCategory val activeC = activeCategory
scrollToHeader(activeCategory) scrollToHeader(activeCategory)
binding.libraryGridRecycler.recycler.post { binding.libraryGridRecycler.recycler.post {
@ -1238,7 +1257,7 @@ class LibraryController(
.setDuration(duration) .setDuration(duration)
} }
private fun showCategories(show: Boolean, closeSearch: Boolean = false, category: Int = -1) { internal fun showCategories(show: Boolean, closeSearch: Boolean = false, category: Int = -1) {
binding.recyclerCover.isClickable = show binding.recyclerCover.isClickable = show
binding.recyclerCover.isFocusable = show binding.recyclerCover.isFocusable = show
(activity as? MainActivity)?.apply { (activity as? MainActivity)?.apply {
@ -1326,7 +1345,9 @@ class LibraryController(
saveActiveCategory(it) saveActiveCategory(it)
} }
activeCategory = pos activeCategory = pos
if (!isSubClass) {
preferences.lastUsedCategory().set(pos) preferences.lastUsedCategory().set(pos)
}
binding.libraryGridRecycler.recycler.post { binding.libraryGridRecycler.recycler.post {
if (isControllerVisible) { if (isControllerVisible) {
activityBinding.appBar.y = 0f activityBinding.appBar.y = 0f
@ -1364,7 +1385,10 @@ class LibraryController(
presenter.forceShowAllCategories = preferences.showAllCategoriesWhenSearchingSingleCategory().get() presenter.forceShowAllCategories = preferences.showAllCategoriesWhenSearchingSingleCategory().get()
presenter.getLibrary() presenter.getLibrary()
} else if (query.isNullOrBlank() && this.query.isNotBlank() && !isShowAllCategoriesSet) { } else if (query.isNullOrBlank() && this.query.isNotBlank() && !isShowAllCategoriesSet) {
preferences.showAllCategoriesWhenSearchingSingleCategory().set(presenter.forceShowAllCategories) if (!isSubClass) {
preferences.showAllCategoriesWhenSearchingSingleCategory()
.set(presenter.forceShowAllCategories)
}
presenter.forceShowAllCategories = false presenter.forceShowAllCategories = false
presenter.getLibrary() presenter.getLibrary()
} }
@ -2121,4 +2145,13 @@ class LibraryController(
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
} }
} }
open fun hasActiveFiltersFromPref(): Boolean {
return preferences.filterDownloaded().get() > 0 ||
preferences.filterUnread().get() > 0 ||
preferences.filterCompleted().get() > 0 ||
preferences.filterTracked().get() > 0 ||
preferences.filterMangaType().get() > 0 ||
FilterBottomSheet.FILTER_TRACKER.isNotEmpty()
}
} }

View file

@ -196,7 +196,7 @@ class LibraryHeaderHolder(val view: View, val adapter: LibraryCategoryAdapter) :
binding.updateButton.isVisible = false binding.updateButton.isVisible = false
setSelection() setSelection()
} }
(category.id ?: -1) < 0 -> { (category.id ?: -1) < 0 || adapter.libraryListener is FilteredLibraryController -> {
binding.collapseArrow.isVisible = false binding.collapseArrow.isVisible = false
binding.checkbox.isVisible = false binding.checkbox.isVisible = false
setRefreshing(false) setRefreshing(false)

View file

@ -103,6 +103,9 @@ class LibraryPresenter(
private val libraryIsGrouped private val libraryIsGrouped
get() = groupType != UNGROUPED get() = groupType != UNGROUPED
private val controllerIsSubClass
get() = controller?.isSubClass == true
var hasActiveFilters: Boolean = run { var hasActiveFilters: Boolean = run {
val filterDownloaded = preferences.filterDownloaded().get() val filterDownloaded = preferences.filterDownloaded().get()
@ -119,17 +122,22 @@ class LibraryPresenter(
/** Save the current list to speed up loading later */ /** Save the current list to speed up loading later */
override fun onDestroy() { override fun onDestroy() {
val isSubController = controllerIsSubClass
super.onDestroy() super.onDestroy()
if (!isSubController) {
lastLibraryItems = libraryItems lastLibraryItems = libraryItems
lastCategories = categories lastCategories = categories
} }
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
if (!controllerIsSubClass) {
lastLibraryItems?.let { libraryItems = it } lastLibraryItems?.let { libraryItems = it }
lastCategories?.let { categories = it } lastCategories?.let { categories = it }
lastCategories = null lastCategories = null
lastLibraryItems = null lastLibraryItems = null
}
getLibrary() getLibrary()
if (preferences.showLibrarySearchSuggestions().isNotSet()) { if (preferences.showLibrarySearchSuggestions().isNotSet()) {
DelayedLibrarySuggestionsJob.setupTask(context, true) DelayedLibrarySuggestionsJob.setupTask(context, true)
@ -261,15 +269,16 @@ class LibraryPresenter(
* @param items the items to filter. * @param items the items to filter.
*/ */
private fun applyFilters(items: List<LibraryItem>): List<LibraryItem> { private fun applyFilters(items: List<LibraryItem>): List<LibraryItem> {
val filterDownloaded = preferences.filterDownloaded().get() val customFilters = controller as? FilteredLibraryController
val filterDownloaded = customFilters?.filterDownloaded ?: preferences.filterDownloaded().get()
val filterUnread = preferences.filterUnread().get() val filterUnread = customFilters?.filterUnread ?: preferences.filterUnread().get()
val filterCompleted = preferences.filterCompleted().get() val filterCompleted = preferences.filterCompleted().get()
val filterTracked = preferences.filterTracked().get() val filterTracked = customFilters?.filterTracked ?: preferences.filterTracked().get()
val filterMangaType = preferences.filterMangaType().get() val filterMangaType = customFilters?.filterMangaType ?: preferences.filterMangaType().get()
val showEmptyCategoriesWhileFiltering = preferences.showEmptyCategoriesWhileFiltering().get() val showEmptyCategoriesWhileFiltering = preferences.showEmptyCategoriesWhileFiltering().get()
@ -295,6 +304,7 @@ class LibraryPresenter(
filterTracked, filterTracked,
filterMangaType, filterMangaType,
filterTrackers, filterTrackers,
customFilters?.filterStatus,
) )
} }
} }
@ -314,6 +324,7 @@ class LibraryPresenter(
filterTracked, filterTracked,
filterMangaType, filterMangaType,
filterTrackers, filterTrackers,
customFilters?.filterStatus,
) )
if (matches) { if (matches) {
missingCategorySet.remove(item.manga.category) missingCategorySet.remove(item.manga.category)
@ -336,6 +347,7 @@ class LibraryPresenter(
filterTracked: Int, filterTracked: Int,
filterMangaType: Int, filterMangaType: Int,
filterTrackers: String, filterTrackers: String,
filterStatus: Int? = null,
): Boolean { ): Boolean {
if (filterUnread == STATE_INCLUDE && item.manga.unread == 0) return false if (filterUnread == STATE_INCLUDE && item.manga.unread == 0) return false
if (filterUnread == STATE_EXCLUDE && item.manga.unread > 0) return false if (filterUnread == STATE_EXCLUDE && item.manga.unread > 0) return false
@ -355,9 +367,13 @@ class LibraryPresenter(
} }
} }
if (filterStatus != null) {
if (filterStatus != item.manga.status) return false
} else {
// Filter for completed status of manga // Filter for completed status of manga
if (filterCompleted == STATE_INCLUDE && item.manga.status != SManga.COMPLETED) return false if (filterCompleted == STATE_INCLUDE && item.manga.status != SManga.COMPLETED) return false
if (filterCompleted == STATE_EXCLUDE && item.manga.status == SManga.COMPLETED) return false if (filterCompleted == STATE_EXCLUDE && item.manga.status == SManga.COMPLETED) return false
}
// Filter for tracked (or per tracked service) // Filter for tracked (or per tracked service)
if (filterTracked != STATE_IGNORE) { if (filterTracked != STATE_IGNORE) {
@ -615,7 +631,7 @@ class LibraryPresenter(
LibraryItem(it, headerItem, viewContext) LibraryItem(it, headerItem, viewContext)
}.toMutableList() }.toMutableList()
val categoriesHidden = if (forceShowAllCategories) { val categoriesHidden = if (forceShowAllCategories || controllerIsSubClass) {
emptySet() emptySet()
} else { } else {
preferences.collapsedCategories().get().mapNotNull { it.toIntOrNull() }.toSet() preferences.collapsedCategories().get().mapNotNull { it.toIntOrNull() }.toSet()
@ -786,7 +802,11 @@ class LibraryPresenter(
} }
}.flatten().toMutableList() }.flatten().toMutableList()
val hiddenDynamics = preferences.collapsedDynamicCategories().get() val hiddenDynamics = if (controllerIsSubClass) {
emptySet()
} else {
preferences.collapsedDynamicCategories().get()
}
var headers = tagItems.map { item -> var headers = tagItems.map { item ->
Category.createCustom( Category.createCustom(
item.key, item.key,

View file

@ -147,7 +147,7 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
sheetBehavior?.isGestureInsetBottomIgnored = true sheetBehavior?.isGestureInsetBottomIgnored = true
val activeFilters = hasActiveFiltersFromPref() val activeFilters = hasActiveFiltersFromPref() && !controller.isSubClass
if (activeFilters && sheetBehavior.isHidden() && sheetBehavior?.skipCollapsed == false) { if (activeFilters && sheetBehavior.isHidden() && sheetBehavior?.skipCollapsed == false) {
sheetBehavior?.collapse() sheetBehavior?.collapse()
controller.viewScope.launchUI { controller.viewScope.launchUI {
@ -415,7 +415,7 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
} }
override fun onFilterClicked(view: FilterTagGroup, index: Int, updatePreference: Boolean) { override fun onFilterClicked(view: FilterTagGroup, index: Int, updatePreference: Boolean) {
if (updatePreference) { if (updatePreference && controller?.isSubClass != true) {
when (view) { when (view) {
trackers -> { trackers -> {
FILTER_TRACKER = view.nameOf(index) ?: "" FILTER_TRACKER = view.nameOf(index) ?: ""
@ -523,7 +523,6 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
const val STATE_EXCLUDE = 2 const val STATE_EXCLUDE = 2
var FILTER_TRACKER = "" var FILTER_TRACKER = ""
private set
} }
enum class Filters(val value: Char, @StringRes val stringRes: Int) { enum class Filters(val value: Char, @StringRes val stringRes: Int) {

View file

@ -52,7 +52,7 @@ class StatsDetailsAdapter(
override fun getItemId(position: Int): Long { override fun getItemId(position: Int): Long {
return when (stat) { return when (stat) {
Stats.READ_DURATION -> list[position].mangaId Stats.READ_DURATION -> list[position].id
else -> list[position].label?.hashCode()?.toLong() else -> list[position].label?.hashCode()?.toLong()
} ?: 0L } ?: 0L
} }
@ -64,12 +64,18 @@ class StatsDetailsAdapter(
Stats.READ_DURATION -> handleDurationLayout(holder, position) Stats.READ_DURATION -> handleDurationLayout(holder, position)
else -> handleLayout(holder, position) else -> handleLayout(holder, position)
} }
list[position].mangaId?.let { mangaId ->
holder.itemView.setOnClickListener { holder.itemView.setOnClickListener {
listener?.onItemClicked(mangaId) val item = list[position]
listener?.onItemClicked(item.id, item.casedLabel ?: item.label)
} }
} holder.itemView.isClickable = stat in arrayOf(
holder.itemView.isClickable = list[position].mangaId != null Stats.SERIES_TYPE,
Stats.STATUS,
Stats.READ_DURATION,
Stats.SOURCE,
Stats.TRACKER,
Stats.TAG,
)
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
@ -231,7 +237,7 @@ class StatsDetailsAdapter(
} }
fun interface OnItemClickedListener { fun interface OnItemClickedListener {
fun onItemClicked(mangaId: Long) fun onItemClicked(id: Long?, label: String?)
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {

View file

@ -32,6 +32,8 @@ import eu.kanade.tachiyomi.databinding.StatsDetailsChartBinding
import eu.kanade.tachiyomi.databinding.StatsDetailsControllerBinding import eu.kanade.tachiyomi.databinding.StatsDetailsControllerBinding
import eu.kanade.tachiyomi.ui.base.SmallToolbarInterface import eu.kanade.tachiyomi.ui.base.SmallToolbarInterface
import eu.kanade.tachiyomi.ui.base.controller.BaseCoroutineController import eu.kanade.tachiyomi.ui.base.controller.BaseCoroutineController
import eu.kanade.tachiyomi.ui.library.FilteredLibraryController
import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
import eu.kanade.tachiyomi.ui.more.stats.StatsHelper.getReadDuration import eu.kanade.tachiyomi.ui.more.stats.StatsHelper.getReadDuration
import eu.kanade.tachiyomi.ui.more.stats.details.StatsDetailsPresenter.Stats import eu.kanade.tachiyomi.ui.more.stats.details.StatsDetailsPresenter.Stats
@ -580,9 +582,7 @@ class StatsDetailsController :
concatAdapter.addAdapter(0, headAdapter) concatAdapter.addAdapter(0, headAdapter)
} }
binding.statsRecyclerView.adapter = concatAdapter binding.statsRecyclerView.adapter = concatAdapter
statsAdapter.listener = StatsDetailsAdapter.OnItemClickedListener { mangaId -> statsAdapter.listener = StatsDetailsAdapter.OnItemClickedListener(::onItemClicked)
router.pushController(MangaDetailsController(mangaId).withFadeTransaction())
}
} else { } else {
updateStatsAdapter(onlyUpdateDetails) updateStatsAdapter(onlyUpdateDetails)
concatAdapter?.notifyDataSetChanged() concatAdapter?.notifyDataSetChanged()
@ -590,6 +590,58 @@ class StatsDetailsController :
if (query.isNotBlank()) statsAdapter?.filter(query) if (query.isNotBlank()) statsAdapter?.filter(query)
} }
fun onItemClicked(id: Long?, name: String?) {
name ?: return
when (presenter.selectedStat) {
Stats.SERIES_TYPE -> {
val seriesType = presenter.seriesTypeStats.indexOf(name) + 1
if (seriesType > 0) {
router.pushController(
FilteredLibraryController(name, filterMangaType = seriesType)
.withFadeTransaction(),
)
}
}
Stats.STATUS -> {
val status = presenter.statusStats.indexOf(name) + 1
router.pushController(
FilteredLibraryController(name, filterStatus = status)
.withFadeTransaction(),
)
}
Stats.SOURCE, Stats.TAG -> {
router.pushController(
FilteredLibraryController(name, queryText = name).withFadeTransaction(),
)
}
Stats.TRACKER -> {
if (id != null) {
val loggedServices = presenter.trackManager.services.filter { it.isLogged }
val service = loggedServices.find { it.id == id.toInt() } ?: return
val serviceName = binding.root.context.getString(service.nameRes())
router.pushController(
FilteredLibraryController(
serviceName,
filterTracked = FilterBottomSheet.STATE_INCLUDE,
filterTrackerName = serviceName,
).withFadeTransaction(),
)
} else {
router.pushController(
FilteredLibraryController(
name,
filterTracked = FilterBottomSheet.STATE_EXCLUDE,
).withFadeTransaction(),
)
}
}
Stats.READ_DURATION -> id?.let {
router.pushController(MangaDetailsController(id).withFadeTransaction())
}
else -> return
}
}
private fun updateStatsAdapter(onlyUpdateDetails: Boolean) { private fun updateStatsAdapter(onlyUpdateDetails: Boolean) {
val oldCount = statsAdapter?.list?.size ?: 0 val oldCount = statsAdapter?.list?.size ?: 0
statsAdapter?.stat = presenter.selectedStat!! statsAdapter?.stat = presenter.selectedStat!!

View file

@ -36,7 +36,7 @@ import kotlin.math.roundToInt
class StatsDetailsPresenter( class StatsDetailsPresenter(
private val db: DatabaseHelper = Injekt.get(), private val db: DatabaseHelper = Injekt.get(),
prefs: PreferencesHelper = Injekt.get(), prefs: PreferencesHelper = Injekt.get(),
private val trackManager: TrackManager = Injekt.get(), val trackManager: TrackManager = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
) : BaseCoroutinePresenter<StatsDetailsController>() { ) : BaseCoroutinePresenter<StatsDetailsController>() {
@ -141,6 +141,7 @@ class StatsDetailsPresenter(
totalChapters = mangaList.sumOf { it.totalChapters }, totalChapters = mangaList.sumOf { it.totalChapters },
label = context.mapSeriesType(seriesType).uppercase(), label = context.mapSeriesType(seriesType).uppercase(),
readDuration = mangaList.getReadDuration(), readDuration = mangaList.getReadDuration(),
casedLabel = context.mapSeriesType(seriesType),
), ),
) )
} }
@ -161,6 +162,7 @@ class StatsDetailsPresenter(
totalChapters = mangaList.sumOf { it.totalChapters }, totalChapters = mangaList.sumOf { it.totalChapters },
label = context.mapStatus(status).uppercase(), label = context.mapStatus(status).uppercase(),
readDuration = mangaList.getReadDuration(), readDuration = mangaList.getReadDuration(),
casedLabel = context.mapStatus(status),
), ),
) )
} }
@ -256,6 +258,8 @@ class StatsDetailsPresenter(
iconRes = service?.getLogo(), iconRes = service?.getLogo(),
iconBGColor = service?.getLogoColor(), iconBGColor = service?.getLogoColor(),
readDuration = mangaAndTrack.map { it.first }.getReadDuration(), readDuration = mangaAndTrack.map { it.first }.getReadDuration(),
id = service?.id?.toLong(),
casedLabel = label,
), ),
) )
} }
@ -279,6 +283,7 @@ class StatsDetailsPresenter(
label = sourceName.uppercase(), label = sourceName.uppercase(),
icon = source?.icon(), icon = source?.icon(),
readDuration = mangaList.getReadDuration(), readDuration = mangaList.getReadDuration(),
casedLabel = source?.name,
), ),
) )
} }
@ -310,8 +315,12 @@ class StatsDetailsPresenter(
private fun setupTags() { private fun setupTags() {
currentStats = ArrayList() currentStats = ArrayList()
val mangaFiltered = mangasDistinct.filterByChip() val mangaFiltered = mangasDistinct.filterByChip()
val tags = mangaFiltered.flatMap { it.getTags() }.distinct() val tags = mangaFiltered.flatMap { it.getTags() }.distinctBy { it.uppercase() }
val libraryFormat = tags.map { tag -> tag to mangaFiltered.filter { tag in it.getTags() } } val libraryFormat = tags.map { tag ->
tag to mangaFiltered.filter {
it.getTags().any { mangaTag -> mangaTag.equals(tag, true) }
}
}
libraryFormat.forEach { (tag, mangaList) -> libraryFormat.forEach { (tag, mangaList) ->
currentStats?.add( currentStats?.add(
@ -323,6 +332,7 @@ class StatsDetailsPresenter(
totalChapters = mangaList.sumOf { it.totalChapters }, totalChapters = mangaList.sumOf { it.totalChapters },
label = tag.uppercase(), label = tag.uppercase(),
readDuration = mangaList.getReadDuration(), readDuration = mangaList.getReadDuration(),
casedLabel = tag,
), ),
) )
} }
@ -370,7 +380,7 @@ class StatsDetailsPresenter(
label = manga.title, label = manga.title,
subLabel = sources.find { it.id == manga.source }?.toString(), subLabel = sources.find { it.id == manga.source }?.toString(),
readDuration = history.sumOf { it.time_read }, readDuration = history.sumOf { it.time_read },
mangaId = manga.id, id = manga.id,
), ),
) )
} }
@ -457,7 +467,7 @@ class StatsDetailsPresenter(
} }
private fun Manga.getTags(): List<String> { private fun Manga.getTags(): List<String> {
return getGenres()?.map { it.uppercase() } ?: emptyList() return getGenres() ?: emptyList()
} }
/** /**
@ -669,6 +679,7 @@ class StatsDetailsPresenter(
var icon: Drawable? = null, var icon: Drawable? = null,
var subLabel: String? = null, var subLabel: String? = null,
var readDuration: Long = 0, var readDuration: Long = 0,
var mangaId: Long? = null, var id: Long? = null,
var casedLabel: String? = null,
) )
} }