refactor: Attempt 2 on getting library manga using flow

This commit is contained in:
Ahmad Ansori Palembani 2024-06-19 16:36:35 +07:00
parent 535fcc81dd
commit 7b765a5fc2
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
9 changed files with 82 additions and 52 deletions

View file

@ -75,6 +75,7 @@ import kotlinx.coroutines.sync.withPermit
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import yokai.domain.manga.interactor.GetLibraryManga import yokai.domain.manga.interactor.GetLibraryManga
import yokai.domain.manga.interactor.UpdateManga
import java.io.File import java.io.File
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.* import java.util.*
@ -92,6 +93,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
private val trackManager: TrackManager = Injekt.get() private val trackManager: TrackManager = Injekt.get()
private val mangaShortcutManager: MangaShortcutManager = Injekt.get() private val mangaShortcutManager: MangaShortcutManager = Injekt.get()
private val getLibraryManga: GetLibraryManga = Injekt.get() private val getLibraryManga: GetLibraryManga = Injekt.get()
private val updateManga: UpdateManga = Injekt.get()
private var extraDeferredJobs = mutableListOf<Deferred<Any>>() private var extraDeferredJobs = mutableListOf<Deferred<Any>>()
@ -277,7 +279,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
.build() .build()
} }
context.imageLoader.execute(request) context.imageLoader.execute(request)
db.insertManga(manga).executeAsBlocking() updateManga.await(manga.toMangaUpdate())
} }
} }
} }

View file

@ -1384,7 +1384,7 @@ open class LibraryController(
private fun onRefresh() { private fun onRefresh() {
showCategories(false) showCategories(false)
presenter.getLibrary() presenter.getLibrary(true)
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
} }
@ -1796,7 +1796,7 @@ open class LibraryController(
val category = (adapter.getItem(position) as? LibraryHeaderItem)?.category ?: return val category = (adapter.getItem(position) as? LibraryHeaderItem)?.category ?: return
if (!category.isDynamic) { if (!category.isDynamic) {
ManageCategoryDialog(category) { ManageCategoryDialog(category) {
presenter.getLibrary() presenter.getLibrary(true)
}.showDialog(router) }.showDialog(router)
} }
} }

View file

@ -50,6 +50,7 @@ import eu.kanade.tachiyomi.util.system.withIOContext
import eu.kanade.tachiyomi.util.system.withUIContext import eu.kanade.tachiyomi.util.system.withUIContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -59,7 +60,10 @@ import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import yokai.domain.category.interactor.GetCategories import yokai.domain.category.interactor.GetCategories
import yokai.domain.chapter.interactor.GetChapters import yokai.domain.chapter.interactor.GetChapters
import yokai.domain.chapter.interactor.UpdateChapters
import yokai.domain.chapter.models.ChapterUpdate
import yokai.domain.manga.interactor.GetLibraryManga import yokai.domain.manga.interactor.GetLibraryManga
import yokai.domain.manga.interactor.GetManga
import yokai.domain.manga.interactor.UpdateManga import yokai.domain.manga.interactor.UpdateManga
import yokai.domain.manga.models.MangaUpdate import yokai.domain.manga.models.MangaUpdate
import yokai.util.isLewd import yokai.util.isLewd
@ -88,9 +92,10 @@ class LibraryPresenter(
private val getCategories: GetCategories by injectLazy() private val getCategories: GetCategories by injectLazy()
private val getLibraryManga: GetLibraryManga by injectLazy() private val getLibraryManga: GetLibraryManga by injectLazy()
private val getChapters: GetChapters by injectLazy() private val getChapters: GetChapters by injectLazy()
private val updateChapter: UpdateChapters by injectLazy()
private val updateManga: UpdateManga by injectLazy() private val updateManga: UpdateManga by injectLazy()
private val libraryManga: List<LibraryManga> = emptyList() private var libraryManga: List<LibraryManga> = emptyList()
private val context = preferences.context private val context = preferences.context
private val viewContext private val viewContext
@ -176,7 +181,12 @@ class LibraryPresenter(
lastLibraryItems = null lastLibraryItems = null
lastAllLibraryItems = null lastAllLibraryItems = null
} }
getLibrary() presenterScope.launch {
getLibraryManga.subscribe().collectLatest {
libraryManga = it.apply { if (groupType > BY_DEFAULT) { distinctBy { it.id } } }
getLibrary()
}
}
if (!preferences.showLibrarySearchSuggestions().isSet()) { if (!preferences.showLibrarySearchSuggestions().isSet()) {
DelayedLibrarySuggestionsJob.setupTask(context, true) DelayedLibrarySuggestionsJob.setupTask(context, true)
} else if (preferences.showLibrarySearchSuggestions().get() && } else if (preferences.showLibrarySearchSuggestions().get() &&
@ -200,7 +210,7 @@ class LibraryPresenter(
} }
/** Get favorited manga for library and sort and filter it */ /** Get favorited manga for library and sort and filter it */
fun getLibrary() { fun getLibrary(forceFetch: Boolean = false) {
presenterScope.launch { presenterScope.launch {
if (categories.isEmpty()) { if (categories.isEmpty()) {
val dbCategories = getCategories.await() val dbCategories = getCategories.await()
@ -210,7 +220,7 @@ class LibraryPresenter(
categories = lastCategories ?: getCategories.await().toMutableList() categories = lastCategories ?: getCategories.await().toMutableList()
} }
val (library, hiddenItems) = withIOContext { getLibraryFromDB() } val (library, hiddenItems) = withIOContext { getLibraryFromDB(forceFetch) }
setDownloadCount(library) setDownloadCount(library)
setUnreadBadge(library) setUnreadBadge(library)
setSourceLanguage(library) setSourceLanguage(library)
@ -766,14 +776,12 @@ class LibraryPresenter(
* *
* @return an list of all the manga in a itemized form. * @return an list of all the manga in a itemized form.
*/ */
private suspend fun getLibraryFromDB(): Pair<List<LibraryItem>, List<LibraryItem>> { private suspend fun getLibraryFromDB(forceFetch: Boolean = false): Pair<List<LibraryItem>, List<LibraryItem>> {
removeArticles = preferences.removeArticles().get() removeArticles = preferences.removeArticles().get()
val categories = getCategories.await().toMutableList() val categories = getCategories.await().toMutableList()
var libraryManga = getLibraryManga.await() if (forceFetch)
libraryManga = getLibraryManga.await().apply { if (groupType > BY_DEFAULT) { distinctBy { it.id } } }
val showAll = showAllCategories val showAll = showAllCategories
if (groupType > BY_DEFAULT) {
libraryManga = libraryManga.distinctBy { it.id }
}
val hiddenItems = mutableListOf<LibraryItem>() val hiddenItems = mutableListOf<LibraryItem>()
val items = if (groupType <= BY_DEFAULT || !libraryIsGrouped) { val items = if (groupType <= BY_DEFAULT || !libraryIsGrouped) {
@ -1222,6 +1230,7 @@ class LibraryPresenter(
} }
} }
// TODO: Use SQLDelight
/** Shift a manga's category via drag & drop */ /** Shift a manga's category via drag & drop */
fun moveMangaToCategory( fun moveMangaToCategory(
manga: LibraryManga, manga: LibraryManga,
@ -1360,15 +1369,14 @@ class LibraryPresenter(
val mapMangaChapters = HashMap<Manga, List<Chapter>>() val mapMangaChapters = HashMap<Manga, List<Chapter>>()
presenterScope.launchIO { presenterScope.launchIO {
mangaList.forEach { manga -> mangaList.forEach { manga ->
val oldChapters = db.getChapters(manga).executeAsBlocking() val chapters = getChapters.await(manga)
val chapters = oldChapters.copy() val updates = chapters.copy().mapNotNull {
chapters.forEach { if (it.id == null) return@mapNotNull null
it.read = markRead ChapterUpdate(it.id!!, read = markRead, lastPageRead = 0)
it.last_page_read = 0
} }
db.updateChaptersProgress(chapters).executeAsBlocking() updateChapter.awaitAll(updates)
mapMangaChapters[manga] = oldChapters mapMangaChapters[manga] = chapters
} }
getLibrary() getLibrary()
} }
@ -1379,9 +1387,13 @@ class LibraryPresenter(
mangaList: HashMap<Manga, List<Chapter>>, mangaList: HashMap<Manga, List<Chapter>>,
) { ) {
launchIO { launchIO {
mangaList.forEach { (_, chapters) -> val updates = mangaList.values.map { chapters ->
db.updateChaptersProgress(chapters).executeAsBlocking() chapters.mapNotNull {
} if (it.id == null) return@mapNotNull null
ChapterUpdate(it.id!!, read = it.read, lastPageRead = it.last_page_read.toLong())
}
}.flatten()
updateChapter.awaitAll(updates)
getLibrary() getLibrary()
} }
} }
@ -1501,46 +1513,48 @@ class LibraryPresenter(
} }
/** Give library manga to a date added based on min chapter fetch */ /** Give library manga to a date added based on min chapter fetch */
fun updateDB() { suspend fun updateDB(
val db: DatabaseHelper = Injekt.get() getChapters: GetChapters = Injekt.get(),
val getLibraryManga: GetLibraryManga by injectLazy() getLibraryManga: GetLibraryManga = Injekt.get(),
val libraryManga = runBlocking { getLibraryManga.await() } updateManga: UpdateManga = Injekt.get(),
db.inTransaction { ) {
libraryManga.forEach { manga -> val libraryManga = getLibraryManga.await()
if (manga.date_added == 0L) { libraryManga.forEach { manga ->
val chapters = db.getChapters(manga).executeAsBlocking() if (manga.id == null) return@forEach
manga.date_added = chapters.minByOrNull { it.date_fetch }?.date_fetch ?: 0L if (manga.date_added == 0L) {
db.insertManga(manga).executeAsBlocking() val chapters = getChapters.await(manga)
} manga.date_added = chapters.minByOrNull { it.date_fetch }?.date_fetch ?: 0L
updateManga.await(MangaUpdate(manga.id!!, dateAdded = manga.date_added))
} }
} }
} }
suspend fun updateRatiosAndColors() { suspend fun updateRatiosAndColors(
val db: DatabaseHelper = Injekt.get() getManga: GetManga = Injekt.get(),
val libraryManga = db.getFavoriteMangas().executeOnIO() ) {
val libraryManga = getManga.awaitFavorites()
libraryManga.forEach { manga -> libraryManga.forEach { manga ->
try { withUIContext { MangaCoverMetadata.setRatioAndColors(manga) } } catch (_: Exception) { } try { withUIContext { MangaCoverMetadata.setRatioAndColors(manga) } } catch (_: Exception) { }
} }
MangaCoverMetadata.savePrefs() MangaCoverMetadata.savePrefs()
} }
fun updateCustoms() { suspend fun updateCustoms(
val db: DatabaseHelper = Injekt.get() cc: CoverCache = Injekt.get(),
val cc: CoverCache = Injekt.get() updateManga: UpdateManga = Injekt.get(),
) {
val getLibraryManga: GetLibraryManga by injectLazy() val getLibraryManga: GetLibraryManga by injectLazy()
val libraryManga = runBlocking { getLibraryManga.await() } val libraryManga = getLibraryManga.await()
db.inTransaction { libraryManga.forEach { manga ->
libraryManga.forEach { manga -> if (manga.id == null) return@forEach
if (manga.thumbnail_url?.startsWith("custom", ignoreCase = true) == true) { if (manga.thumbnail_url?.startsWith("custom", ignoreCase = true) == true) {
val file = cc.getCoverFile(manga) val file = cc.getCoverFile(manga)
if (file.exists()) { if (file.exists()) {
file.renameTo(cc.getCustomCoverFile(manga)) file.renameTo(cc.getCustomCoverFile(manga))
}
manga.thumbnail_url =
manga.thumbnail_url!!.lowercase(Locale.ROOT).substringAfter("custom-")
db.insertManga(manga).executeAsBlocking()
} }
manga.thumbnail_url =
manga.thumbnail_url!!.lowercase(Locale.ROOT).substringAfter("custom-")
updateManga.await(MangaUpdate(manga.id!!, thumbnailUrl = manga.thumbnail_url))
} }
} }
} }

View file

@ -1,6 +1,7 @@
package yokai.core.migration.migrations package yokai.core.migration.migrations
import eu.kanade.tachiyomi.ui.library.LibraryPresenter import eu.kanade.tachiyomi.ui.library.LibraryPresenter
import eu.kanade.tachiyomi.util.system.withIOContext
import yokai.core.migration.Migration import yokai.core.migration.Migration
import yokai.core.migration.MigrationContext import yokai.core.migration.MigrationContext
@ -9,7 +10,7 @@ class CustomInfoMigration : Migration {
override suspend fun invoke(migrationContext: MigrationContext): Boolean { override suspend fun invoke(migrationContext: MigrationContext): Boolean {
try { try {
LibraryPresenter.updateCustoms() withIOContext { LibraryPresenter.updateCustoms() }
} catch (e: Exception) { } catch (e: Exception) {
return false return false
} }

View file

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.updater.AppUpdateJob import eu.kanade.tachiyomi.data.updater.AppUpdateJob
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.ui.library.LibraryPresenter import eu.kanade.tachiyomi.ui.library.LibraryPresenter
import eu.kanade.tachiyomi.util.system.withIOContext
import yokai.core.migration.Migration import yokai.core.migration.Migration
import yokai.core.migration.MigrationContext import yokai.core.migration.MigrationContext
@ -18,7 +19,9 @@ class WorkManagerMigration : Migration {
override suspend fun invoke(migrationContext: MigrationContext): Boolean { override suspend fun invoke(migrationContext: MigrationContext): Boolean {
val context: App = migrationContext.get() ?: return false val context: App = migrationContext.get() ?: return false
LibraryPresenter.updateDB() withIOContext {
LibraryPresenter.updateDB()
}
if (BuildConfig.INCLUDE_UPDATER) { if (BuildConfig.INCLUDE_UPDATER) {
AppUpdateJob.setupTask(context) AppUpdateJob.setupTask(context)
} }

View file

@ -20,6 +20,9 @@ class MangaRepositoryImpl(private val handler: DatabaseHandler) : MangaRepositor
override suspend fun getMangaById(id: Long): Manga? = override suspend fun getMangaById(id: Long): Manga? =
handler.awaitOneOrNull { mangasQueries.findById(id, Manga::mapper) } handler.awaitOneOrNull { mangasQueries.findById(id, Manga::mapper) }
override suspend fun getFavorites(): List<Manga> =
handler.awaitList { mangasQueries.findFavorites(Manga::mapper) }
override fun getMangaListAsFlow(): Flow<List<Manga>> = override fun getMangaListAsFlow(): Flow<List<Manga>> =
handler.subscribeToList { mangasQueries.findAll(Manga::mapper) } handler.subscribeToList { mangasQueries.findAll(Manga::mapper) }

View file

@ -9,6 +9,7 @@ interface MangaRepository {
suspend fun getMangaList(): List<Manga> suspend fun getMangaList(): List<Manga>
suspend fun getMangaByUrlAndSource(url: String, source: Long): Manga? suspend fun getMangaByUrlAndSource(url: String, source: Long): Manga?
suspend fun getMangaById(id: Long): Manga? suspend fun getMangaById(id: Long): Manga?
suspend fun getFavorites(): List<Manga>
fun getMangaListAsFlow(): Flow<List<Manga>> fun getMangaListAsFlow(): Flow<List<Manga>>
suspend fun getLibraryManga(): List<LibraryManga> suspend fun getLibraryManga(): List<LibraryManga>
fun getLibraryMangaAsFlow(): Flow<List<LibraryManga>> fun getLibraryMangaAsFlow(): Flow<List<LibraryManga>>

View file

@ -10,4 +10,5 @@ class GetManga (
suspend fun awaitByUrlAndSource(url: String, source: Long) = mangaRepository.getMangaByUrlAndSource(url, source) suspend fun awaitByUrlAndSource(url: String, source: Long) = mangaRepository.getMangaByUrlAndSource(url, source)
suspend fun awaitById(id: Long) = mangaRepository.getMangaById(id) suspend fun awaitById(id: Long) = mangaRepository.getMangaById(id)
suspend fun awaitFavorites() = mangaRepository.getFavorites()
} }

View file

@ -45,6 +45,11 @@ SELECT *
FROM mangas FROM mangas
WHERE favorite = 1 AND lower(title) = :title AND source = :source; WHERE favorite = 1 AND lower(title) = :title AND source = :source;
findFavorites:
SELECT *
FROM mangas
WHERE favorite = 1;
insert: insert:
INSERT INTO mangas (source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, initialized, viewer, hide_title, chapter_flags, date_added, filtered_scanlators, update_strategy) INSERT INTO mangas (source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, initialized, viewer, hide_title, chapter_flags, date_added, filtered_scanlators, update_strategy)
VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :initialized, :viewer, :hideTitle, :chapterFlags, :dateAdded, :filteredScanlators, :updateStrategy); VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :initialized, :viewer, :hideTitle, :chapterFlags, :dateAdded, :filteredScanlators, :updateStrategy);