Use sqldelight for direct db calls in MangaPresenter
(#7366)
(cherry picked from commit 04f0ca78469573707e14742226fd8f14445853d4) # Conflicts: # app/src/main/java/eu/kanade/domain/manga/model/Manga.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt
This commit is contained in:
parent
1084e7fe9d
commit
6f407f6e0a
@ -3,6 +3,7 @@ package eu.kanade.data.track
|
||||
import eu.kanade.data.DatabaseHandler
|
||||
import eu.kanade.domain.track.model.Track
|
||||
import eu.kanade.domain.track.repository.TrackRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class TrackRepositoryImpl(
|
||||
private val handler: DatabaseHandler,
|
||||
@ -14,11 +15,45 @@ class TrackRepositoryImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun subscribeTracksByMangaId(mangaId: Long): Flow<List<Track>> {
|
||||
return handler.subscribeToList {
|
||||
manga_syncQueries.getTracksByMangaId(mangaId, trackMapper)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun delete(mangaId: Long, syncId: Long) {
|
||||
handler.await {
|
||||
manga_syncQueries.delete(
|
||||
mangaId = mangaId,
|
||||
syncId = syncId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun insert(track: Track) {
|
||||
handler.await {
|
||||
manga_syncQueries.insert(
|
||||
mangaId = track.mangaId,
|
||||
syncId = track.syncId,
|
||||
remoteId = track.remoteId,
|
||||
libraryId = track.libraryId,
|
||||
title = track.title,
|
||||
lastChapterRead = track.lastChapterRead,
|
||||
totalChapters = track.totalChapters,
|
||||
status = track.status,
|
||||
score = track.score,
|
||||
remoteUrl = track.remoteUrl,
|
||||
startDate = track.startDate,
|
||||
finishDate = track.finishDate,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun insertAll(tracks: List<Track>) {
|
||||
handler.await(inTransaction = true) {
|
||||
tracks.forEach { mangaTrack ->
|
||||
manga_syncQueries.insert(
|
||||
mangaId = mangaTrack.id,
|
||||
mangaId = mangaTrack.mangaId,
|
||||
syncId = mangaTrack.syncId,
|
||||
remoteId = mangaTrack.remoteId,
|
||||
libraryId = mangaTrack.libraryId,
|
||||
|
@ -15,6 +15,7 @@ import eu.kanade.domain.category.repository.CategoryRepository
|
||||
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
||||
import eu.kanade.domain.chapter.interactor.ShouldUpdateDbChapter
|
||||
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.repository.ChapterRepository
|
||||
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
|
||||
@ -47,6 +48,7 @@ import eu.kanade.domain.source.interactor.ToggleSource
|
||||
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
||||
import eu.kanade.domain.source.interactor.UpsertSourceData
|
||||
import eu.kanade.domain.source.repository.SourceRepository
|
||||
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.repository.TrackRepository
|
||||
@ -77,6 +79,7 @@ class DomainModule : InjektModule {
|
||||
addFactory { MoveMangaToCategories(get()) }
|
||||
|
||||
addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) }
|
||||
addFactory { DeleteTrack(get()) }
|
||||
addFactory { GetTracks(get()) }
|
||||
addFactory { InsertTrack(get()) }
|
||||
|
||||
@ -85,6 +88,7 @@ class DomainModule : InjektModule {
|
||||
addFactory { UpdateChapter(get()) }
|
||||
addFactory { ShouldUpdateDbChapter() }
|
||||
addFactory { SyncChaptersWithSource(get(), get(), get(), get()) }
|
||||
addFactory { SyncChaptersWithTrackServiceTwoWay(get(), get()) }
|
||||
|
||||
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
|
||||
addFactory { DeleteHistoryTable(get()) }
|
||||
|
@ -0,0 +1,41 @@
|
||||
package eu.kanade.domain.chapter.interactor
|
||||
|
||||
import eu.kanade.domain.chapter.model.Chapter
|
||||
import eu.kanade.domain.chapter.model.toChapterUpdate
|
||||
import eu.kanade.domain.track.interactor.InsertTrack
|
||||
import eu.kanade.domain.track.model.Track
|
||||
import eu.kanade.domain.track.model.toDbTrack
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import logcat.LogPriority
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class SyncChaptersWithTrackServiceTwoWay(
|
||||
private val updateChapter: UpdateChapter = Injekt.get(),
|
||||
private val insertTrack: InsertTrack = Injekt.get(),
|
||||
) {
|
||||
|
||||
suspend fun await(
|
||||
chapters: List<Chapter>,
|
||||
remoteTrack: Track,
|
||||
service: TrackService,
|
||||
) {
|
||||
val sortedChapters = chapters.sortedBy { it.chapterNumber }
|
||||
val chapterUpdates = sortedChapters
|
||||
.filter { chapter -> chapter.chapterNumber <= remoteTrack.lastChapterRead && !chapter.read }
|
||||
.map { it.copy(read = true).toChapterUpdate() }
|
||||
|
||||
// only take into account continuous reading
|
||||
val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapterNumber ?: 0F
|
||||
val updatedTrack = remoteTrack.copy(lastChapterRead = localLastRead.toDouble())
|
||||
|
||||
try {
|
||||
service.update(updatedTrack.toDbTrack())
|
||||
updateChapter.awaitAll(chapterUpdates)
|
||||
insertTrack.await(updatedTrack)
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.WARN, e)
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,9 @@ import eu.kanade.domain.manga.model.Manga
|
||||
import eu.kanade.domain.manga.model.MangaUpdate
|
||||
import eu.kanade.domain.manga.repository.MangaRepository
|
||||
|
||||
class SetMangaChapterFlags(private val mangaRepository: MangaRepository) {
|
||||
class SetMangaChapterFlags(
|
||||
private val mangaRepository: MangaRepository,
|
||||
) {
|
||||
|
||||
suspend fun awaitSetDownloadedFilter(manga: Manga, flag: Long): Boolean {
|
||||
return mangaRepository.update(
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.domain.manga.model
|
||||
|
||||
import eu.kanade.data.listOfStringsAdapter
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.library.CustomMangaManager
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
@ -178,7 +179,7 @@ fun TriStateFilter.toTriStateGroupState(): ExtendedNavigationView.Item.TriStateG
|
||||
}
|
||||
|
||||
// TODO: Remove when all deps are migrated
|
||||
fun Manga.toDbManga(): DbManga = DbManga.create(url, ogTitle, source).also {
|
||||
fun Manga.toDbManga(): DbManga = DbManga.create(source).also {
|
||||
it.id = id
|
||||
it.favorite = favorite
|
||||
it.last_update = lastUpdate
|
||||
@ -186,7 +187,17 @@ fun Manga.toDbManga(): DbManga = DbManga.create(url, ogTitle, source).also {
|
||||
it.viewer_flags = viewerFlags.toInt()
|
||||
it.chapter_flags = chapterFlags.toInt()
|
||||
it.cover_last_modified = coverLastModified
|
||||
it.url = url
|
||||
// SY -->
|
||||
it.title = ogTitle
|
||||
it.artist = ogArtist
|
||||
it.author = ogAuthor
|
||||
it.description = ogDescription
|
||||
it.genre = ogGenre?.let(listOfStringsAdapter::encode)
|
||||
it.status = ogStatus.toInt()
|
||||
// SY <--
|
||||
it.thumbnail_url = thumbnailUrl
|
||||
it.initialized = initialized
|
||||
}
|
||||
|
||||
fun Manga.toMangaInfo(): MangaInfo = MangaInfo(
|
||||
|
@ -0,0 +1,18 @@
|
||||
package eu.kanade.domain.track.interactor
|
||||
|
||||
import eu.kanade.domain.track.repository.TrackRepository
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import logcat.LogPriority
|
||||
|
||||
class DeleteTrack(
|
||||
private val trackRepository: TrackRepository,
|
||||
) {
|
||||
|
||||
suspend fun await(mangaId: Long, syncId: Long) {
|
||||
try {
|
||||
trackRepository.delete(mangaId, syncId)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package eu.kanade.domain.track.interactor
|
||||
import eu.kanade.domain.track.model.Track
|
||||
import eu.kanade.domain.track.repository.TrackRepository
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import logcat.LogPriority
|
||||
|
||||
class GetTracks(
|
||||
@ -17,4 +18,8 @@ class GetTracks(
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun subscribe(mangaId: Long): Flow<List<Track>> {
|
||||
return trackRepository.subscribeTracksByMangaId(mangaId)
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,14 @@ class InsertTrack(
|
||||
private val trackRepository: TrackRepository,
|
||||
) {
|
||||
|
||||
suspend fun await(track: Track) {
|
||||
try {
|
||||
trackRepository.insert(track)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun awaitAll(tracks: List<Track>) {
|
||||
try {
|
||||
trackRepository.insertAll(tracks)
|
||||
|
@ -1,5 +1,7 @@
|
||||
package eu.kanade.domain.track.model
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Track as DbTrack
|
||||
|
||||
data class Track(
|
||||
val id: Long,
|
||||
val mangaId: Long,
|
||||
@ -25,3 +27,37 @@ data class Track(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun Track.toDbTrack(): DbTrack = DbTrack.create(syncId.toInt()).also {
|
||||
it.id = id
|
||||
it.manga_id = mangaId
|
||||
it.media_id = remoteId
|
||||
it.library_id = libraryId
|
||||
it.title = title
|
||||
it.last_chapter_read = lastChapterRead.toFloat()
|
||||
it.total_chapters = totalChapters.toInt()
|
||||
it.status = status.toInt()
|
||||
it.score = score
|
||||
it.tracking_url = remoteUrl
|
||||
it.started_reading_date = startDate
|
||||
it.finished_reading_date = finishDate
|
||||
}
|
||||
|
||||
fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
|
||||
val trackId = id ?: if (idRequired.not()) -1 else return null
|
||||
return Track(
|
||||
id = trackId,
|
||||
mangaId = manga_id,
|
||||
syncId = sync_id.toLong(),
|
||||
remoteId = media_id,
|
||||
libraryId = library_id,
|
||||
title = title,
|
||||
lastChapterRead = last_chapter_read.toDouble(),
|
||||
totalChapters = total_chapters.toLong(),
|
||||
status = status.toLong(),
|
||||
score = score,
|
||||
remoteUrl = tracking_url,
|
||||
startDate = started_reading_date,
|
||||
finishDate = finished_reading_date,
|
||||
)
|
||||
}
|
||||
|
@ -1,10 +1,17 @@
|
||||
package eu.kanade.domain.track.repository
|
||||
|
||||
import eu.kanade.domain.track.model.Track
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface TrackRepository {
|
||||
|
||||
suspend fun getTracksByMangaId(mangaId: Long): List<Track>
|
||||
|
||||
suspend fun subscribeTracksByMangaId(mangaId: Long): Flow<List<Track>>
|
||||
|
||||
suspend fun delete(mangaId: Long, syncId: Long)
|
||||
|
||||
suspend fun insert(track: Track)
|
||||
|
||||
suspend fun insertAll(tracks: List<Track>)
|
||||
}
|
||||
|
@ -3,8 +3,11 @@ package eu.kanade.tachiyomi.ui.manga
|
||||
import android.content.Context
|
||||
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.GetMergedChapterByMangaId
|
||||
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
|
||||
@ -20,6 +23,11 @@ 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.R
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
@ -45,7 +53,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
|
||||
@ -72,6 +79,7 @@ import exh.source.isEhBasedSource
|
||||
import exh.source.mangaDexSourceIds
|
||||
import exh.util.nullIfEmpty
|
||||
import exh.util.trimOrNull
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.async
|
||||
@ -79,15 +87,17 @@ import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
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
|
||||
@ -122,6 +132,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)
|
||||
@ -152,7 +168,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
|
||||
|
||||
@ -279,17 +294,7 @@ class MangaPresenter(
|
||||
mergedData = mergedData,
|
||||
showRecommendationsInOverflow = preferences.recommendsInOverflow().get(),
|
||||
showMergeWithAnother = smartSearched,
|
||||
).also {
|
||||
getTrackingObservable(manga)
|
||||
.subscribeLatestCache(
|
||||
{ _, count ->
|
||||
successState?.let {
|
||||
_state.value = it.copy(trackingCount = count)
|
||||
}
|
||||
},
|
||||
{ _, error -> logcat(LogPriority.ERROR, error) },
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Update state
|
||||
@ -304,7 +309,8 @@ class MangaPresenter(
|
||||
}
|
||||
}
|
||||
|
||||
fetchTrackers()
|
||||
observeTrackers()
|
||||
observeTrackingCount()
|
||||
observeDownloads()
|
||||
|
||||
if (!manga.initialized) {
|
||||
@ -332,24 +338,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
|
||||
// SY -->
|
||||
.filterNot { it.sync_id == TrackManager.MDLIST && it.status == FollowStatus.UNFOLLOWED.int }
|
||||
// SY <--
|
||||
.filter { it.sync_id in loggedServices }
|
||||
}
|
||||
.map { it.size }
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch manga information from source.
|
||||
*/
|
||||
@ -688,8 +676,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>) {
|
||||
@ -706,8 +694,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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -720,6 +711,27 @@ 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 }
|
||||
// SY -->
|
||||
.filterNot { it.syncId == TrackManager.MDLIST.toLong() && it.status == FollowStatus.UNFOLLOWED.int.toLong() }
|
||||
// SY <--
|
||||
.size
|
||||
}
|
||||
.collectLatest { trackingCount ->
|
||||
updateSuccessState { it.copy(trackingCount = trackingCount) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Manga info - end
|
||||
|
||||
// Chapters list - start
|
||||
@ -891,7 +903,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)
|
||||
}
|
||||
@ -924,7 +936,7 @@ class MangaPresenter(
|
||||
chapters
|
||||
.filterNot { it.bookmark == bookmarked }
|
||||
.map { ChapterUpdate(id = it.id, bookmark = bookmarked) }
|
||||
.forEach { updateChapter.await(it) }
|
||||
.let { updateChapter.awaitAll(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -972,6 +984,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
|
||||
@ -988,11 +1001,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)
|
||||
}
|
||||
@ -1004,11 +1019,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)
|
||||
}
|
||||
@ -1029,6 +1046,7 @@ class MangaPresenter(
|
||||
*/
|
||||
fun setDisplayMode(mode: Long) {
|
||||
val manga = successState?.manga ?: return
|
||||
|
||||
presenterScope.launchIO {
|
||||
setMangaChapterFlags.awaitSetDisplayMode(manga, mode)
|
||||
}
|
||||
@ -1040,6 +1058,7 @@ class MangaPresenter(
|
||||
*/
|
||||
fun setSorting(sort: Long) {
|
||||
val manga = successState?.manga ?: return
|
||||
|
||||
presenterScope.launchIO {
|
||||
setMangaChapterFlags.awaitSetSortingModeOrFlipOrder(manga, sort)
|
||||
}
|
||||
@ -1049,36 +1068,42 @@ class MangaPresenter(
|
||||
|
||||
// Track sheet - start
|
||||
|
||||
private fun fetchTrackers() {
|
||||
val state = successState ?: return
|
||||
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)
|
||||
}
|
||||
}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
// SY -->
|
||||
.map { trackItems ->
|
||||
if (manga.source in mangaDexSourceIds || state.mergedData?.manga?.values.orEmpty().any { it.source in mangaDexSourceIds }) {
|
||||
val mdTrack = trackItems.firstOrNull { it.service.id == TrackManager.MDLIST }
|
||||
when {
|
||||
mdTrack == null -> {
|
||||
trackItems
|
||||
}
|
||||
mdTrack.track == null -> {
|
||||
trackItems - mdTrack + createMdListTrack()
|
||||
}
|
||||
else -> trackItems
|
||||
private fun observeTrackers() {
|
||||
val state = successState
|
||||
val manga = state?.manga ?: return
|
||||
|
||||
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)
|
||||
}
|
||||
} else trackItems
|
||||
}
|
||||
// SY <--
|
||||
.doOnNext { _trackList = it }
|
||||
.subscribeLatestCache(MangaController::onNextTrackers)
|
||||
}
|
||||
// SY -->
|
||||
.map { trackItems ->
|
||||
if (manga.source in mangaDexSourceIds || state.mergedData?.manga?.values.orEmpty().any { it.source in mangaDexSourceIds }) {
|
||||
val mdTrack = trackItems.firstOrNull { it.service.id == TrackManager.MDLIST }
|
||||
when {
|
||||
mdTrack == null -> {
|
||||
trackItems
|
||||
}
|
||||
mdTrack.track == null -> {
|
||||
trackItems - mdTrack + createMdListTrack()
|
||||
}
|
||||
else -> trackItems
|
||||
}
|
||||
} else trackItems
|
||||
}
|
||||
// SY <--
|
||||
.collectLatest { trackItems ->
|
||||
_trackList = trackItems
|
||||
withContext(Dispatchers.Main) {
|
||||
view?.onNextTrackers(trackItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SY -->
|
||||
@ -1099,16 +1124,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1146,10 +1176,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) {
|
||||
this@MangaPresenter.xLogD("Error registering tracking", e)
|
||||
@ -1163,21 +1200,28 @@ 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) {
|
||||
this@MangaPresenter.xLogD("Error updating tracking", e)
|
||||
withUIContext { view?.onTrackingRefreshError(e) }
|
||||
|
||||
// Restart on error to set old values
|
||||
fetchTrackers()
|
||||
observeTrackers()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,10 @@ CREATE TABLE manga_sync(
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
delete:
|
||||
DELETE FROM manga_sync
|
||||
WHERE manga_id = :mangaId AND sync_id = :syncId;
|
||||
|
||||
getTracksByMangaId:
|
||||
SELECT *
|
||||
FROM manga_sync
|
||||
|
Loading…
x
Reference in New Issue
Block a user