Use sqldelight for direct db calls in MangaPresenter (#7366)

This commit is contained in:
AntsyLich
2022-06-27 01:54:34 +06:00
committed by GitHub
parent 61a44101a2
commit 04f0ca7846
12 changed files with 274 additions and 58 deletions

View File

@@ -2,7 +2,10 @@ package eu.kanade.tachiyomi.ui.manga
import android.os.Bundle
import androidx.compose.runtime.Immutable
import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.category.interactor.MoveMangaToCategories
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
import eu.kanade.domain.chapter.interactor.UpdateChapter
import eu.kanade.domain.chapter.model.ChapterUpdate
import eu.kanade.domain.chapter.model.toDbChapter
@@ -14,11 +17,15 @@ import eu.kanade.domain.manga.model.TriStateFilter
import eu.kanade.domain.manga.model.isLocal
import eu.kanade.domain.manga.model.toDbManga
import eu.kanade.domain.manga.model.toMangaInfo
import eu.kanade.domain.track.interactor.DeleteTrack
import eu.kanade.domain.track.interactor.GetTracks
import eu.kanade.domain.track.interactor.InsertTrack
import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.toDomainChapter
import eu.kanade.tachiyomi.data.download.DownloadManager
@@ -34,7 +41,6 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper
import eu.kanade.tachiyomi.util.chapter.getChapterSort
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithTrackServiceTwoWay
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.lang.withUIContext
@@ -44,17 +50,21 @@ import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.State
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext
import logcat.LogPriority
import rx.Observable
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
@@ -77,6 +87,12 @@ class MangaPresenter(
private val updateChapter: UpdateChapter = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(),
private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(),
private val getCategories: GetCategories = Injekt.get(),
private val deleteTrack: DeleteTrack = Injekt.get(),
private val getTracks: GetTracks = Injekt.get(),
private val moveMangaToCategories: MoveMangaToCategories = Injekt.get(),
private val insertTrack: InsertTrack = Injekt.get(),
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(),
) : BasePresenter<MangaController>() {
private val _state: MutableStateFlow<MangaScreenState> = MutableStateFlow(MangaScreenState.Loading)
@@ -107,7 +123,6 @@ class MangaPresenter(
private val loggedServices by lazy { trackManager.services.filter { it.isLogged } }
private var trackSubscription: Subscription? = null
private var searchTrackerJob: Job? = null
private var refreshTrackersJob: Job? = null
@@ -154,20 +169,15 @@ class MangaPresenter(
isFromSource = isFromSource,
trackingAvailable = trackManager.hasLoggedServices(),
chapters = chapterItems,
).also {
getTrackingObservable(manga)
.subscribeLatestCache(
{ _, count -> updateSuccessState { it.copy(trackingCount = count) } },
{ _, error -> logcat(LogPriority.ERROR, error) },
)
}
)
// Update state
is MangaScreenState.Success -> currentState.copy(manga = manga, chapters = chapterItems)
}
}
fetchTrackers()
observeTrackers()
observeTrackingCount()
observeDownloads()
if (!manga.initialized) {
@@ -195,20 +205,6 @@ class MangaPresenter(
}
// Manga info - start
private fun getTrackingObservable(manga: DomainManga): Observable<Int> {
if (!trackManager.hasLoggedServices()) {
return Observable.just(0)
}
return db.getTracks(manga.id).asRxObservable()
.map { tracks ->
val loggedServices = trackManager.services.filter { it.isLogged }.map { it.id }
tracks.filter { it.sync_id in loggedServices }
}
.map { it.size }
}
/**
* Fetch manga information from source.
*/
@@ -341,8 +337,8 @@ class MangaPresenter(
* @return Array of category ids the manga is in, if none returns default id
*/
fun getMangaCategoryIds(manga: DomainManga): Array<Int> {
val categories = db.getCategoriesForManga(manga.toDbManga()).executeAsBlocking()
return categories.mapNotNull { it.id }.toTypedArray()
val categories = runBlocking { getCategories.await(manga.id) }
return categories.map { it.id.toInt() }.toTypedArray()
}
fun moveMangaToCategoriesAndAddToLibrary(manga: Manga, categories: List<Category>) {
@@ -359,8 +355,11 @@ class MangaPresenter(
* @param categories the selected categories.
*/
private fun moveMangaToCategories(manga: Manga, categories: List<Category>) {
val mc = categories.filter { it.id != 0 }.map { MangaCategory.create(manga, it) }
db.setMangaCategories(mc, listOf(manga))
val mangaId = manga.id ?: return
val categoryIds = categories.mapNotNull { it.id?.toLong() }
presenterScope.launchIO {
moveMangaToCategories.await(mangaId, categoryIds)
}
}
/**
@@ -373,6 +372,22 @@ class MangaPresenter(
moveMangaToCategories(manga, listOfNotNull(category))
}
private fun observeTrackingCount() {
val manga = successState?.manga ?: return
presenterScope.launchIO {
getTracks.subscribe(manga.id)
.catch { logcat(LogPriority.ERROR, it) }
.map { tracks ->
val loggedServicesId = loggedServices.map { it.id.toLong() }
tracks.filter { it.syncId in loggedServicesId }.size
}
.collectLatest { trackingCount ->
updateSuccessState { it.copy(trackingCount = trackingCount) }
}
}
}
// Manga info - end
// Chapters list - start
@@ -520,7 +535,7 @@ class MangaPresenter(
val modified = chapters.filterNot { it.read == read }
modified
.map { ChapterUpdate(id = it.id, read = read) }
.forEach { updateChapter.await(it) }
.let { updateChapter.awaitAll(it) }
if (read && preferences.removeAfterMarkedAsRead()) {
deleteChapters(modified)
}
@@ -545,7 +560,7 @@ class MangaPresenter(
chapters
.filterNot { it.bookmark == bookmarked }
.map { ChapterUpdate(id = it.id, bookmark = bookmarked) }
.forEach { updateChapter.await(it) }
.let { updateChapter.awaitAll(it) }
}
}
@@ -593,6 +608,7 @@ class MangaPresenter(
*/
fun setUnreadFilter(state: State) {
val manga = successState?.manga ?: return
val flag = when (state) {
State.IGNORE -> DomainManga.SHOW_ALL
State.INCLUDE -> DomainManga.CHAPTER_SHOW_UNREAD
@@ -609,11 +625,13 @@ class MangaPresenter(
*/
fun setDownloadedFilter(state: State) {
val manga = successState?.manga ?: return
val flag = when (state) {
State.IGNORE -> DomainManga.SHOW_ALL
State.INCLUDE -> DomainManga.CHAPTER_SHOW_DOWNLOADED
State.EXCLUDE -> DomainManga.CHAPTER_SHOW_NOT_DOWNLOADED
}
presenterScope.launchIO {
setMangaChapterFlags.awaitSetDownloadedFilter(manga, flag)
}
@@ -625,11 +643,13 @@ class MangaPresenter(
*/
fun setBookmarkedFilter(state: State) {
val manga = successState?.manga ?: return
val flag = when (state) {
State.IGNORE -> DomainManga.SHOW_ALL
State.INCLUDE -> DomainManga.CHAPTER_SHOW_BOOKMARKED
State.EXCLUDE -> DomainManga.CHAPTER_SHOW_NOT_BOOKMARKED
}
presenterScope.launchIO {
setMangaChapterFlags.awaitSetBookmarkFilter(manga, flag)
}
@@ -641,6 +661,7 @@ class MangaPresenter(
*/
fun setDisplayMode(mode: Long) {
val manga = successState?.manga ?: return
presenterScope.launchIO {
setMangaChapterFlags.awaitSetDisplayMode(manga, mode)
}
@@ -652,6 +673,7 @@ class MangaPresenter(
*/
fun setSorting(sort: Long) {
val manga = successState?.manga ?: return
presenterScope.launchIO {
setMangaChapterFlags.awaitSetSortingModeOrFlipOrder(manga, sort)
}
@@ -661,19 +683,25 @@ class MangaPresenter(
// Track sheet - start
private fun fetchTrackers() {
private fun observeTrackers() {
val manga = successState?.manga ?: return
trackSubscription?.let { remove(it) }
trackSubscription = db.getTracks(manga.id)
.asRxObservable()
.map { tracks ->
loggedServices.map { service ->
TrackItem(tracks.find { it.sync_id == service.id }, service)
presenterScope.launchIO {
getTracks.subscribe(manga.id)
.catch { logcat(LogPriority.ERROR, it) }
.map { tracks ->
val dbTracks = tracks.map { it.toDbTrack() }
loggedServices.map { service ->
TrackItem(dbTracks.find { it.sync_id == service.id }, service)
}
}
}
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { _trackList = it }
.subscribeLatestCache(MangaController::onNextTrackers)
.collectLatest { trackItems ->
_trackList = trackItems
withContext(Dispatchers.Main) {
view?.onNextTrackers(trackItems)
}
}
}
}
fun refreshTrackers() {
@@ -682,16 +710,21 @@ class MangaPresenter(
supervisorScope {
try {
trackList
.filter { it.track != null }
.map {
async {
val track = it.service.refresh(it.track!!)
db.insertTrack(track).executeAsBlocking()
val track = it.track ?: return@async null
if (it.service is EnhancedTrackService) {
val updatedTrack = it.service.refresh(track)
val domainTrack = updatedTrack.toDomainTrack() ?: return@async null
insertTrack.await(domainTrack)
(it.service as? EnhancedTrackService)?.let { _ ->
val allChapters = successState?.chapters
?.map { it.chapter.toDbChapter() } ?: emptyList()
syncChaptersWithTrackServiceTwoWay(db, allChapters, track, it.service)
?.map { it.chapter } ?: emptyList()
syncChaptersWithTrackServiceTwoWay
.await(allChapters, domainTrack, it.service)
}
}
}
@@ -727,10 +760,17 @@ class MangaPresenter(
.map { it.chapter.toDbChapter() }
val hasReadChapters = allChapters.any { it.read }
service.bind(item, hasReadChapters)
db.insertTrack(item).executeAsBlocking()
if (service is EnhancedTrackService) {
syncChaptersWithTrackServiceTwoWay(db, allChapters, item, service)
item.toDomainTrack(idRequired = false)?.let { track ->
insertTrack.await(track)
(service as? EnhancedTrackService)?.let { _ ->
val chapters = successState.chapters
.map { it.chapter }
syncChaptersWithTrackServiceTwoWay
.await(chapters, track, service)
}
}
} catch (e: Throwable) {
withUIContext { view?.applicationContext?.toast(e.message) }
@@ -743,20 +783,27 @@ class MangaPresenter(
fun unregisterTracking(service: TrackService) {
val manga = successState?.manga ?: return
db.deleteTrackForManga(manga.toDbManga(), service).executeAsBlocking()
presenterScope.launchIO {
deleteTrack.await(manga.id, service.id.toLong())
}
}
private fun updateRemote(track: Track, service: TrackService) {
launchIO {
try {
service.update(track)
db.insertTrack(track).executeAsBlocking()
track.toDomainTrack(idRequired = false)?.let {
insertTrack.await(it)
}
withUIContext { view?.onTrackingRefreshDone() }
} catch (e: Throwable) {
withUIContext { view?.onTrackingRefreshError(e) }
// Restart on error to set old values
fetchTrackers()
observeTrackers()
}
}
}