parent
5b3f9e082e
commit
d5b4bb49b1
9 changed files with 72 additions and 81 deletions
|
@ -25,7 +25,7 @@ class MangaRepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getMangaByUrlAndSourceId(url: String, sourceId: Long): Manga? {
|
override suspend fun getMangaByUrlAndSourceId(url: String, sourceId: Long): Manga? {
|
||||||
return handler.awaitOneOrNull { mangasQueries.getMangaByUrlAndSource(url, sourceId, mangaMapper) }
|
return handler.awaitOneOrNull(inTransaction = true) { mangasQueries.getMangaByUrlAndSource(url, sourceId, mangaMapper) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMangaByUrlAndSourceIdAsFlow(url: String, sourceId: Long): Flow<Manga?> {
|
override fun getMangaByUrlAndSourceIdAsFlow(url: String, sourceId: Long): Flow<Manga?> {
|
||||||
|
|
|
@ -44,7 +44,7 @@ import eu.kanade.domain.manga.interactor.GetFavorites
|
||||||
import eu.kanade.domain.manga.interactor.GetLibraryManga
|
import eu.kanade.domain.manga.interactor.GetLibraryManga
|
||||||
import eu.kanade.domain.manga.interactor.GetManga
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.manga.interactor.GetMangaWithChapters
|
import eu.kanade.domain.manga.interactor.GetMangaWithChapters
|
||||||
import eu.kanade.domain.manga.interactor.InsertManga
|
import eu.kanade.domain.manga.interactor.NetworkToLocalManga
|
||||||
import eu.kanade.domain.manga.interactor.ResetViewerFlags
|
import eu.kanade.domain.manga.interactor.ResetViewerFlags
|
||||||
import eu.kanade.domain.manga.interactor.SetMangaChapterFlags
|
import eu.kanade.domain.manga.interactor.SetMangaChapterFlags
|
||||||
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
||||||
|
@ -98,7 +98,7 @@ class DomainModule : InjektModule {
|
||||||
addFactory { SetMangaChapterFlags(get()) }
|
addFactory { SetMangaChapterFlags(get()) }
|
||||||
addFactory { SetMangaDefaultChapterFlags(get(), get(), get()) }
|
addFactory { SetMangaDefaultChapterFlags(get(), get(), get()) }
|
||||||
addFactory { SetMangaViewerFlags(get()) }
|
addFactory { SetMangaViewerFlags(get()) }
|
||||||
addFactory { InsertManga(get()) }
|
addFactory { NetworkToLocalManga(get()) }
|
||||||
addFactory { UpdateManga(get()) }
|
addFactory { UpdateManga(get()) }
|
||||||
addFactory { SetMangaCategories(get()) }
|
addFactory { SetMangaCategories(get()) }
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,6 @@ class GetManga(
|
||||||
return mangaRepository.getMangaByIdAsFlow(id)
|
return mangaRepository.getMangaByIdAsFlow(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun await(url: String, sourceId: Long): Manga? {
|
|
||||||
return mangaRepository.getMangaByUrlAndSourceId(url, sourceId)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun subscribe(url: String, sourceId: Long): Flow<Manga?> {
|
fun subscribe(url: String, sourceId: Long): Flow<Manga?> {
|
||||||
return mangaRepository.getMangaByUrlAndSourceIdAsFlow(url, sourceId)
|
return mangaRepository.getMangaByUrlAndSourceIdAsFlow(url, sourceId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
package eu.kanade.domain.manga.interactor
|
|
||||||
|
|
||||||
import eu.kanade.domain.manga.model.Manga
|
|
||||||
import eu.kanade.domain.manga.repository.MangaRepository
|
|
||||||
|
|
||||||
class InsertManga(
|
|
||||||
private val mangaRepository: MangaRepository,
|
|
||||||
) {
|
|
||||||
|
|
||||||
suspend fun await(manga: Manga): Long? {
|
|
||||||
return mangaRepository.insert(manga)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package eu.kanade.domain.manga.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
|
import eu.kanade.domain.manga.repository.MangaRepository
|
||||||
|
|
||||||
|
class NetworkToLocalManga(
|
||||||
|
private val mangaRepository: MangaRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(manga: Manga, sourceId: Long): Manga {
|
||||||
|
val localManga = getManga(manga.url, sourceId)
|
||||||
|
return when {
|
||||||
|
localManga == null -> {
|
||||||
|
val id = insertManga(manga)
|
||||||
|
manga.copy(id = id!!)
|
||||||
|
}
|
||||||
|
!localManga.favorite -> {
|
||||||
|
// if the manga isn't a favorite, set its display title from source
|
||||||
|
// if it later becomes a favorite, updated title will go to db
|
||||||
|
localManga.copy(title = manga.title)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
localManga
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getManga(url: String, sourceId: Long): Manga? {
|
||||||
|
return mangaRepository.getMangaByUrlAndSourceId(url, sourceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun insertManga(manga: Manga): Long? {
|
||||||
|
return mangaRepository.insert(manga)
|
||||||
|
}
|
||||||
|
}
|
|
@ -232,6 +232,21 @@ fun Manga.toMangaUpdate(): MangaUpdate {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun SManga.toDomainManga(): Manga {
|
||||||
|
return Manga.create().copy(
|
||||||
|
url = url,
|
||||||
|
title = title,
|
||||||
|
artist = artist,
|
||||||
|
author = author,
|
||||||
|
description = description,
|
||||||
|
genre = getGenres(),
|
||||||
|
status = status.toLong(),
|
||||||
|
thumbnailUrl = thumbnail_url,
|
||||||
|
updateStrategy = update_strategy,
|
||||||
|
initialized = initialized,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun Manga.isLocal(): Boolean = source == LocalSource.ID
|
fun Manga.isLocal(): Boolean = source == LocalSource.ID
|
||||||
|
|
||||||
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
|
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
|
||||||
|
|
|
@ -79,11 +79,10 @@ class SearchPresenter(
|
||||||
return GlobalSearchItem(source, results, source.id == manga.source)
|
return GlobalSearchItem(source, results, source.id == manga.source)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun networkToLocalManga(sManga: SManga, sourceId: Long): eu.kanade.tachiyomi.data.database.models.Manga {
|
override suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
|
||||||
val localManga = super.networkToLocalManga(sManga, sourceId)
|
val localManga = super.networkToLocalManga(sManga, sourceId)
|
||||||
// For migration, displayed title should always match source rather than local DB
|
// For migration, displayed title should always match source rather than local DB
|
||||||
localManga.title = sManga.title
|
return localManga.copy(title = sManga.title)
|
||||||
return localManga
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean) {
|
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean) {
|
||||||
|
|
|
@ -28,9 +28,10 @@ import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
|
||||||
import eu.kanade.domain.library.service.LibraryPreferences
|
import eu.kanade.domain.library.service.LibraryPreferences
|
||||||
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
|
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
|
||||||
import eu.kanade.domain.manga.interactor.GetManga
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.manga.interactor.InsertManga
|
import eu.kanade.domain.manga.interactor.NetworkToLocalManga
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
|
import eu.kanade.domain.manga.model.toDomainManga
|
||||||
import eu.kanade.domain.manga.model.toMangaUpdate
|
import eu.kanade.domain.manga.model.toMangaUpdate
|
||||||
import eu.kanade.domain.source.interactor.GetRemoteManga
|
import eu.kanade.domain.source.interactor.GetRemoteManga
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
|
@ -39,8 +40,6 @@ import eu.kanade.domain.track.model.toDomainTrack
|
||||||
import eu.kanade.presentation.browse.BrowseSourceState
|
import eu.kanade.presentation.browse.BrowseSourceState
|
||||||
import eu.kanade.presentation.browse.BrowseSourceStateImpl
|
import eu.kanade.presentation.browse.BrowseSourceStateImpl
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
|
||||||
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
|
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
|
@ -49,7 +48,6 @@ import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.filter.CheckboxItem
|
import eu.kanade.tachiyomi.ui.browse.source.filter.CheckboxItem
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.filter.CheckboxSectionItem
|
import eu.kanade.tachiyomi.ui.browse.source.filter.CheckboxSectionItem
|
||||||
|
@ -97,7 +95,7 @@ open class BrowseSourcePresenter(
|
||||||
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
||||||
private val setMangaCategories: SetMangaCategories = Injekt.get(),
|
private val setMangaCategories: SetMangaCategories = Injekt.get(),
|
||||||
private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(),
|
private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(),
|
||||||
private val insertManga: InsertManga = Injekt.get(),
|
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
|
||||||
private val updateManga: UpdateManga = Injekt.get(),
|
private val updateManga: UpdateManga = Injekt.get(),
|
||||||
private val insertTrack: InsertTrack = Injekt.get(),
|
private val insertTrack: InsertTrack = Injekt.get(),
|
||||||
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(),
|
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(),
|
||||||
|
@ -131,9 +129,9 @@ open class BrowseSourcePresenter(
|
||||||
getRemoteManga.subscribe(sourceId, currentFilter.query, currentFilter.filters)
|
getRemoteManga.subscribe(sourceId, currentFilter.query, currentFilter.filters)
|
||||||
}.flow
|
}.flow
|
||||||
.map {
|
.map {
|
||||||
it.map {
|
it.map { sManga ->
|
||||||
withIOContext {
|
withIOContext {
|
||||||
networkToLocalManga(it, sourceId).toDomainManga()!!
|
networkToLocalManga.await(sManga.toDomainManga(), sourceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,30 +181,6 @@ open class BrowseSourcePresenter(
|
||||||
state.filters = source!!.getFilterList()
|
state.filters = source!!.getFilterList()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a manga from the database for the given manga from network. It creates a new entry
|
|
||||||
* if the manga is not yet in the database.
|
|
||||||
*
|
|
||||||
* @param sManga the manga from the source.
|
|
||||||
* @return a manga from the database.
|
|
||||||
*/
|
|
||||||
private suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
|
|
||||||
var localManga = getManga.await(sManga.url, sourceId)
|
|
||||||
if (localManga == null) {
|
|
||||||
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
|
|
||||||
newManga.copyFrom(sManga)
|
|
||||||
newManga.id = -1
|
|
||||||
val id = insertManga.await(newManga.toDomainManga()!!)
|
|
||||||
val result = getManga.await(id!!)
|
|
||||||
localManga = result
|
|
||||||
} else if (!localManga.favorite) {
|
|
||||||
// if the manga isn't a favorite, set its display title from source
|
|
||||||
// if it later becomes a favorite, updated title will go to db
|
|
||||||
localManga = localManga.copy(title = sManga.title)
|
|
||||||
}
|
|
||||||
return localManga?.toDbManga()!!
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a manga.
|
* Initialize a manga.
|
||||||
*
|
*
|
||||||
|
|
|
@ -2,10 +2,10 @@ package eu.kanade.tachiyomi.ui.browse.source.globalsearch
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import eu.kanade.domain.base.BasePreferences
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.domain.manga.interactor.GetManga
|
import eu.kanade.domain.manga.interactor.NetworkToLocalManga
|
||||||
import eu.kanade.domain.manga.interactor.InsertManga
|
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
|
import eu.kanade.domain.manga.model.toDomainManga
|
||||||
import eu.kanade.domain.manga.model.toMangaUpdate
|
import eu.kanade.domain.manga.model.toMangaUpdate
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
@ -30,6 +30,7 @@ import rx.subjects.PublishSubject
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import eu.kanade.domain.manga.model.Manga as DomainManga
|
||||||
|
|
||||||
open class GlobalSearchPresenter(
|
open class GlobalSearchPresenter(
|
||||||
private val initialQuery: String? = "",
|
private val initialQuery: String? = "",
|
||||||
|
@ -37,8 +38,7 @@ open class GlobalSearchPresenter(
|
||||||
val sourceManager: SourceManager = Injekt.get(),
|
val sourceManager: SourceManager = Injekt.get(),
|
||||||
val preferences: BasePreferences = Injekt.get(),
|
val preferences: BasePreferences = Injekt.get(),
|
||||||
val sourcePreferences: SourcePreferences = Injekt.get(),
|
val sourcePreferences: SourcePreferences = Injekt.get(),
|
||||||
private val getManga: GetManga = Injekt.get(),
|
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
|
||||||
private val insertManga: InsertManga = Injekt.get(),
|
|
||||||
private val updateManga: UpdateManga = Injekt.get(),
|
private val updateManga: UpdateManga = Injekt.get(),
|
||||||
) : BasePresenter<GlobalSearchController>() {
|
) : BasePresenter<GlobalSearchController>() {
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ open class GlobalSearchPresenter(
|
||||||
/**
|
/**
|
||||||
* Subject which fetches image of given manga.
|
* Subject which fetches image of given manga.
|
||||||
*/
|
*/
|
||||||
private val fetchImageSubject = PublishSubject.create<Pair<List<Manga>, Source>>()
|
private val fetchImageSubject = PublishSubject.create<Pair<List<DomainManga>, Source>>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription for fetching images of manga.
|
* Subscription for fetching images of manga.
|
||||||
|
@ -168,9 +168,9 @@ open class GlobalSearchPresenter(
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.onErrorReturn { MangasPage(emptyList(), false) } // Ignore timeouts or other exceptions
|
.onErrorReturn { MangasPage(emptyList(), false) } // Ignore timeouts or other exceptions
|
||||||
.map { it.mangas }
|
.map { it.mangas }
|
||||||
.map { list -> list.map { networkToLocalManga(it, source.id) } } // Convert to local manga
|
.map { list -> list.map { runBlocking { networkToLocalManga(it, source.id) } } } // Convert to local manga
|
||||||
.doOnNext { fetchImage(it, source) } // Load manga covers
|
.doOnNext { fetchImage(it, source) } // Load manga covers
|
||||||
.map { list -> createCatalogueSearchItem(source, list.map { GlobalSearchCardItem(it.toDomainManga()!!) }) }
|
.map { list -> createCatalogueSearchItem(source, list.map { GlobalSearchCardItem(it) }) }
|
||||||
},
|
},
|
||||||
5,
|
5,
|
||||||
)
|
)
|
||||||
|
@ -208,7 +208,7 @@ open class GlobalSearchPresenter(
|
||||||
*
|
*
|
||||||
* @param manga the list of manga to initialize.
|
* @param manga the list of manga to initialize.
|
||||||
*/
|
*/
|
||||||
private fun fetchImage(manga: List<Manga>, source: Source) {
|
private fun fetchImage(manga: List<DomainManga>, source: Source) {
|
||||||
fetchImageSubject.onNext(Pair(manga, source))
|
fetchImageSubject.onNext(Pair(manga, source))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,9 +220,9 @@ open class GlobalSearchPresenter(
|
||||||
fetchImageSubscription = fetchImageSubject.observeOn(Schedulers.io())
|
fetchImageSubscription = fetchImageSubject.observeOn(Schedulers.io())
|
||||||
.flatMap { (first, source) ->
|
.flatMap { (first, source) ->
|
||||||
Observable.from(first)
|
Observable.from(first)
|
||||||
.filter { it.thumbnail_url == null && !it.initialized }
|
.filter { it.thumbnailUrl == null && !it.initialized }
|
||||||
.map { Pair(it, source) }
|
.map { Pair(it, source) }
|
||||||
.concatMap { runAsObservable { getMangaDetails(it.first, it.second) } }
|
.concatMap { runAsObservable { getMangaDetails(it.first.toDbManga(), it.second) } }
|
||||||
.map { Pair(source as CatalogueSource, it) }
|
.map { Pair(source as CatalogueSource, it) }
|
||||||
}
|
}
|
||||||
.onBackpressureBuffer()
|
.onBackpressureBuffer()
|
||||||
|
@ -259,22 +259,7 @@ open class GlobalSearchPresenter(
|
||||||
* @param sManga the manga from the source.
|
* @param sManga the manga from the source.
|
||||||
* @return a manga from the database.
|
* @return a manga from the database.
|
||||||
*/
|
*/
|
||||||
protected open fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
|
protected open suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): DomainManga {
|
||||||
var localManga = runBlocking { getManga.await(sManga.url, sourceId) }
|
return networkToLocalManga.await(sManga.toDomainManga(), sourceId)
|
||||||
if (localManga == null) {
|
|
||||||
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
|
|
||||||
newManga.copyFrom(sManga)
|
|
||||||
newManga.id = -1
|
|
||||||
val result = runBlocking {
|
|
||||||
val id = insertManga.await(newManga.toDomainManga()!!)
|
|
||||||
getManga.await(id!!)
|
|
||||||
}
|
|
||||||
localManga = result
|
|
||||||
} else if (!localManga.favorite) {
|
|
||||||
// if the manga isn't a favorite, set its display title from source
|
|
||||||
// if it later becomes a favorite, updated title will go to db
|
|
||||||
localManga = localManga.copy(title = sManga.title)
|
|
||||||
}
|
|
||||||
return localManga!!.toDbManga()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue