refactor(library): Utilise flow even more (#272)

* revert: "revert: "refactor(library): Some adjustments""

This reverts commit 2b639d0630.

* fix: Don't use emptyFlow

* fix: Fix build

* fix: Don't overwrite allCategories inside `combine {}`

* fix: Fix build
This commit is contained in:
Ahmad Ansori Palembani 2024-12-23 07:07:11 +07:00 committed by GitHub
parent 09bd1a9f78
commit 365d259e94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 87 additions and 77 deletions

View file

@ -557,7 +557,7 @@ open class LibraryController(
}
presenter.groupType = item
shouldScrollToTop = true
presenter.getLibrary()
presenter.updateLibrary()
true
}.show()
}
@ -1056,7 +1056,7 @@ open class LibraryController(
if (type.isEnter) {
binding.filterBottomSheet.filterBottomSheet.isVisible = true
if (type == ControllerChangeType.POP_ENTER) {
presenter.getLibrary()
presenter.updateLibrary()
isPoppingIn = true
}
binding.recyclerCover.isClickable = false
@ -1095,7 +1095,7 @@ open class LibraryController(
if (!isBindingInitialized) return
updateFilterSheetY()
if (observeLater) {
presenter.getLibrary()
presenter.updateLibrary()
}
}
@ -1408,7 +1408,7 @@ open class LibraryController(
private fun onRefresh() {
showCategories(false)
presenter.getLibrary()
presenter.updateLibrary()
destroyActionModeIfNeeded()
}
@ -1432,14 +1432,14 @@ open class LibraryController(
val isShowAllCategoriesSet = preferences.showAllCategories().get()
if (!query.isNullOrBlank() && this.query.isBlank() && !isShowAllCategoriesSet) {
presenter.forceShowAllCategories = preferences.showAllCategoriesWhenSearchingSingleCategory().get()
presenter.getLibrary()
presenter.updateLibrary()
} else if (query.isNullOrBlank() && this.query.isNotBlank() && !isShowAllCategoriesSet) {
if (!isSubClass) {
preferences.showAllCategoriesWhenSearchingSingleCategory()
.set(presenter.forceShowAllCategories)
}
presenter.forceShowAllCategories = false
presenter.getLibrary()
presenter.updateLibrary()
}
if (query != this.query && !query.isNullOrBlank()) {
@ -1641,7 +1641,7 @@ open class LibraryController(
if (mangaId == null) {
adapter.getHeaderPositions().forEach { adapter.notifyItemChanged(it) }
} else {
presenter.updateManga()
presenter.updateLibrary()
}
}
@ -1820,7 +1820,7 @@ open class LibraryController(
val category = (adapter.getItem(position) as? LibraryHeaderItem)?.category ?: return
if (!category.isDynamic) {
ManageCategoryDialog(category) {
presenter.getLibrary()
presenter.updateLibrary()
}.showDialog(router)
}
}
@ -1913,7 +1913,7 @@ open class LibraryController(
isGone = true
setOnClickListener {
presenter.forceShowAllCategories = !presenter.forceShowAllCategories
presenter.getLibrary()
presenter.updateLibrary()
isSelected = presenter.forceShowAllCategories
}
val pad = 12.dpToPx
@ -2193,7 +2193,7 @@ open class LibraryController(
val activity = activity ?: return
viewScope.launchIO {
selectedMangas.toList().moveCategories(activity) {
presenter.getLibrary()
presenter.updateLibrary()
destroyActionModeIfNeeded()
}
}

View file

@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.removeCover
import eu.kanade.tachiyomi.data.database.models.seriesType
import eu.kanade.tachiyomi.data.download.DownloadCache
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.preference.DelayedLibrarySuggestionsJob
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
@ -51,11 +52,9 @@ import kotlin.random.Random
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.retry
import kotlinx.coroutines.flow.shareIn
@ -89,6 +88,7 @@ class LibraryPresenter(
private val preferences: PreferencesHelper = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(),
val sourceManager: SourceManager = Injekt.get(),
private val downloadCache: DownloadCache = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(),
private val chapterFilter: ChapterFilter = Injekt.get(),
private val trackManager: TrackManager = Injekt.get(),
@ -103,10 +103,7 @@ class LibraryPresenter(
private val getTrack: GetTrack by injectLazy()
private val getHistory: GetHistory by injectLazy()
private val _fetchLibrary: Channel<Unit> = Channel(Channel.UNLIMITED)
val fetchLibrary = _fetchLibrary.receiveAsFlow()
.onStart { emit(Unit) }
.shareIn(presenterScope, SharingStarted.Lazily, 1)
private val forceUpdateEvent: Channel<Unit> = Channel(Channel.UNLIMITED)
private val context = preferences.context
private val viewContext
@ -129,6 +126,8 @@ class LibraryPresenter(
private var allCategories: List<Category> = emptyList()
/** List of all manga to update the */
// TODO: Store sectioned before flattening it out for "show all categories"
private var currentLibrary: Map<Category, List<LibraryItem>> = mapOf()
var libraryItems: List<LibraryItem> = emptyList()
private var sectionedLibraryItems: MutableMap<Int, List<LibraryItem>> = mutableMapOf()
var currentCategory = -1
@ -197,6 +196,7 @@ class LibraryPresenter(
}
subscribeLibrary()
updateLibrary()
if (!preferences.showLibrarySearchSuggestions().isSet()) {
DelayedLibrarySuggestionsJob.setupTask(context, true)
@ -233,38 +233,29 @@ class LibraryPresenter(
combine(
getLibraryFlow(),
fetchLibrary,
) { data, _ ->
downloadCache.changes,
) { data, _ -> data }.collectLatest { data ->
categories = data.categories
allCategories = data.allCategories
data.items
val library = data.items
val hiddenItems = library.filter { it.manga.isHidden() }.mapNotNull { it.manga.items }.flatten()
setDownloadCount(library)
setUnreadBadge(library)
setSourceLanguage(library)
setDownloadCount(hiddenItems)
setUnreadBadge(hiddenItems)
setSourceLanguage(hiddenItems)
allLibraryItems = library
hiddenLibraryItems = hiddenItems
val mangaMap = library
.applyFilters()
.applySort()
val freshStart = libraryItems.isEmpty()
sectionLibrary(mangaMap, freshStart)
}
.collectLatest { library ->
val hiddenItems = library.filter { it.manga.isHidden() }.mapNotNull { it.manga.items }.flatten()
setDownloadCount(library)
setUnreadBadge(library)
setSourceLanguage(library)
setDownloadCount(hiddenItems)
setUnreadBadge(hiddenItems)
setSourceLanguage(hiddenItems)
allLibraryItems = library
hiddenLibraryItems = hiddenItems
val mangaMap = library
.applyFilters()
.applySort()
val freshStart = libraryItems.isEmpty()
sectionLibrary(mangaMap, freshStart)
}
}
}
/** Get favorited manga for library and sort and filter it */
fun getLibrary() {
presenterScope.launch {
_fetchLibrary.send(Unit)
}
}
@ -321,8 +312,7 @@ class LibraryPresenter(
private suspend fun sectionLibrary(items: List<LibraryItem>, freshStart: Boolean = false) {
libraryItems = items
val showAll = showAllCategories || !libraryIsGrouped ||
categories.size <= 1
val showAll = showAllCategories || !libraryIsGrouped || categories.size <= 1
sectionedLibraryItems = items.groupBy { it.header.category.id ?: 0 }.toMutableMap()
if (!showAll && currentCategory == -1) {
currentCategory = categories.find {
@ -758,6 +748,9 @@ class LibraryPresenter(
preferences.librarySortingMode().changes(),
preferences.librarySortingAscending().changes(),
preferences.collapsedCategories().changes(),
preferences.collapsedDynamicCategories().changes(),
) {
ItemPreferences(
filterDownloaded = it[0] as Int,
@ -771,6 +764,8 @@ class LibraryPresenter(
showAllCategories = it[8] as Boolean,
sortingMode = it[9] as Int,
sortAscending = it[10] as Boolean,
collapsedCategories = it[11] as Set<String>,
collapsedDynamicCategories = it[12] as Set<String>,
)
}
@ -808,26 +803,27 @@ class LibraryPresenter(
* If category id '-1' is not empty, it means the library not grouped by categories
*/
private fun getLibraryFlow(): Flow<LibraryData> {
return combine(
val libraryFlow = combine(
getCategories.subscribe(),
// FIXME: Remove retry once a real solution is found
getLibraryManga.subscribe().retry(1) { e -> e is NullPointerException },
getPreferencesFlow(),
preferences.removeArticles().changes(),
fetchLibrary
) { dbCategories, libraryMangaList, prefs, removeArticles, _ ->
forceUpdateEvent.receiveAsFlow(),
) { dbCategories, libraryMangaList, prefs, _ ->
groupType = prefs.groupType
val defaultCategory = createDefaultCategory()
val allCategories = listOf(defaultCategory) + dbCategories
val (items, categories, hiddenItems) = if (groupType <= BY_DEFAULT || !libraryIsGrouped) {
// FIXME: Should return Map<Int, LibraryItem> where Int is category id
if (groupType <= BY_DEFAULT || !libraryIsGrouped) {
getLibraryItems(
dbCategories,
allCategories, // FIXME: Don't depends on allCategories
libraryMangaList,
prefs.sortingMode,
prefs.sortAscending,
prefs.showAllCategories,
prefs.collapsedCategories,
defaultCategory,
)
} else {
@ -836,8 +832,17 @@ class LibraryPresenter(
prefs.sortingMode,
prefs.sortAscending,
groupType,
prefs.collapsedDynamicCategories,
)
}
} to allCategories
}
return combine(
libraryFlow,
preferences.removeArticles().changes(),
) { library, removeArticles ->
val (libraryItems, allCategories) = library
val (items, categories, hiddenItems) = libraryItems
LibraryData(
categories = categories,
@ -855,6 +860,7 @@ class LibraryPresenter(
sortingMode: Int,
isAscending: Boolean,
showAll: Boolean,
collapsedCategories: Set<String>,
defaultCategory: Category,
): Triple<List<LibraryItem>, List<Category>, List<LibraryItem>> {
val categories = allCategories.toMutableList()
@ -878,6 +884,11 @@ class LibraryPresenter(
} + (-1 to catItemAll) + (0 to LibraryHeaderItem({ categories.getOrDefault(0) }, 0))
).toMap()
// TODO
val map = libraryManga.groupBy {
categories.getOrDefault(it.category)
}
val items = if (libraryIsGrouped) {
libraryManga
} else {
@ -897,16 +908,14 @@ class LibraryPresenter(
val categoriesHidden = if (forceShowAllCategories || controllerIsSubClass) {
emptySet()
} else {
preferences.collapsedCategories().get().mapNotNull { it.toIntOrNull() }.toSet()
collapsedCategories.mapNotNull { it.toIntOrNull() }.toSet()
}
if (categorySet.contains(0)) categories.add(0, defaultCategory)
if (libraryIsGrouped) {
categories.forEach { category ->
val catId = category.id ?: return@forEach
if (catId > 0 && !categorySet.contains(catId) &&
(catId !in categoriesHidden || !showAll)
) {
if (catId > 0 && !categorySet.contains(catId) && (catId !in categoriesHidden || !showAll)) {
val headerItem = headerItems[catId]
if (headerItem != null) {
items.add(
@ -959,6 +968,7 @@ class LibraryPresenter(
sortingMode: Int,
isAscending: Boolean,
groupType: Int,
collapsedDynamicCategories: Set<String>,
): Triple<List<LibraryItem>, List<Category>, List<LibraryItem>> {
val tagItems: MutableMap<String, LibraryHeaderItem> = mutableMapOf()
@ -1061,7 +1071,7 @@ class LibraryPresenter(
val hiddenDynamics = if (controllerIsSubClass) {
emptySet()
} else {
preferences.collapsedDynamicCategories().get()
collapsedDynamicCategories
}
val headers = tagItems.map { item ->
Category.createCustom(
@ -1217,7 +1227,6 @@ class LibraryPresenter(
.mapNotNull { if (it.id != null) MangaUpdate(it.id!!, favorite = false) else null }
withIOContext { updateManga.awaitAll(mangaToDelete) }
getLibrary()
}
}
@ -1240,8 +1249,11 @@ class LibraryPresenter(
}
}
/** Called when Library Service updates a manga, update the item as well */
fun updateManga() = getLibrary()
/** Force update the library */
fun updateLibrary() = presenterScope.launch {
forceUpdateEvent.send(Unit)
}
/** Undo the removal of the manga once in library */
fun reAddMangas(mangas: List<Manga>) {
@ -1251,12 +1263,12 @@ class LibraryPresenter(
withIOContext { updateManga.awaitAll(mangaToAdd) }
(view as? FilteredLibraryController)?.updateStatsPage()
getLibrary()
}
}
/** Returns first unread chapter of a manga */
fun getFirstUnread(manga: Manga): Chapter? {
// FIXME: Don't do blocking
val chapters = runBlocking { getChapter.awaitAll(manga) }
return ChapterSort(manga, chapterFilter, preferences).getNextUnreadChapter(chapters, false)
}
@ -1308,7 +1320,6 @@ class LibraryPresenter(
}
}
// TODO: Use SQLDelight
/** Shift a manga's category via drag & drop */
fun moveMangaToCategory(
manga: LibraryManga,
@ -1354,7 +1365,7 @@ class LibraryPresenter(
)
}
}
getLibrary()
updateLibrary()
}
}
@ -1389,7 +1400,6 @@ class LibraryPresenter(
}
preferences.collapsedDynamicCategories().set(categoriesHidden)
}
getLibrary()
}
private fun getDynamicCategoryName(category: Category): String =
@ -1420,7 +1430,6 @@ class LibraryPresenter(
}
}
}
getLibrary()
}
fun allCategoriesExpanded(): Boolean {
@ -1462,7 +1471,7 @@ class LibraryPresenter(
mapMangaChapters[manga] = chapters
}
getLibrary()
updateLibrary()
}
return mapMangaChapters
}
@ -1478,7 +1487,7 @@ class LibraryPresenter(
}
}.flatten()
updateChapter.awaitAll(updates)
getLibrary()
updateLibrary()
}
}
@ -1651,6 +1660,9 @@ class LibraryPresenter(
val sortingMode: Int,
val sortAscending: Boolean,
val collapsedCategories: Set<String>,
val collapsedDynamicCategories: Set<String>,
)
data class LibraryData(

View file

@ -2,16 +2,14 @@ package eu.kanade.tachiyomi.ui.library.display
import android.content.Context
import android.util.AttributeSet
import eu.kanade.tachiyomi.R
import yokai.i18n.MR
import yokai.util.lang.getString
import dev.icerock.moko.resources.compose.stringResource
import eu.kanade.tachiyomi.databinding.LibraryCategoryLayoutBinding
import eu.kanade.tachiyomi.util.bindToPreference
import eu.kanade.tachiyomi.util.lang.withSubtitle
import eu.kanade.tachiyomi.util.system.toInt
import eu.kanade.tachiyomi.widget.BaseLibraryDisplayView
import kotlin.math.min
import yokai.i18n.MR
import yokai.util.lang.getString
class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
BaseLibraryDisplayView<LibraryCategoryLayoutBinding>(context, attrs) {
@ -20,7 +18,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
override fun initGeneralPreferences() {
with(binding) {
showAll.bindToPreference(preferences.showAllCategories()) {
controller?.presenter?.getLibrary()
controller?.presenter?.updateLibrary()
binding.categoryShow.isEnabled = it
}
categoryShow.isEnabled = showAll.isChecked
@ -30,7 +28,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
dynamicToBottom.text = context.getString(MR.strings.move_dynamic_to_bottom)
.withSubtitle(context, MR.strings.when_grouping_by_sources_tags)
dynamicToBottom.bindToPreference(preferences.collapsedDynamicAtBottom()) {
controller?.presenter?.getLibrary()
controller?.presenter?.updateLibrary()
}
showEmptyCatsFiltering.bindToPreference(preferences.showEmptyCategoriesWhileFiltering()) {
controller?.presenter?.requestFilterUpdate()

View file

@ -36,6 +36,8 @@ import eu.kanade.tachiyomi.util.view.isCollapsed
import eu.kanade.tachiyomi.util.view.isExpanded
import eu.kanade.tachiyomi.util.view.isHidden
import eu.kanade.tachiyomi.util.view.setText
import kotlin.math.max
import kotlin.math.roundToInt
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.drop
@ -48,8 +50,6 @@ import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.i18n.MR
import yokai.util.lang.getString
import kotlin.math.max
import kotlin.math.roundToInt
class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
LinearLayout(context, attrs),
@ -637,7 +637,7 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
SeriesType('m', MR.strings.series_type),
Bookmarked('b', MR.strings.bookmarked),
Tracked('t', MR.strings.tracking),
ContentType('s', MR.strings.content_type);
ContentType('s', MR.strings.content_type)
;
companion object {