Use sqldelight for direct db calls in MangaPresenter
(#7366)
This commit is contained in:
parent
61a44101a2
commit
04f0ca7846
12 changed files with 274 additions and 58 deletions
|
@ -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.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
|
@ -143,7 +144,7 @@ fun TriStateFilter.toTriStateGroupState(): ExtendedNavigationView.Item.TriStateG
|
|||
}
|
||||
|
||||
// TODO: Remove when all deps are migrated
|
||||
fun Manga.toDbManga(): DbManga = DbManga.create(url, title, source).also {
|
||||
fun Manga.toDbManga(): DbManga = DbManga.create(source).also {
|
||||
it.id = id
|
||||
it.favorite = favorite
|
||||
it.last_update = lastUpdate
|
||||
|
@ -151,7 +152,15 @@ fun Manga.toDbManga(): DbManga = DbManga.create(url, title, source).also {
|
|||
it.viewer_flags = viewerFlags.toInt()
|
||||
it.chapter_flags = chapterFlags.toInt()
|
||||
it.cover_last_modified = coverLastModified
|
||||
it.url = url
|
||||
it.title = title
|
||||
it.artist = artist
|
||||
it.author = author
|
||||
it.description = description
|
||||
it.genre = genre?.let(listOfStringsAdapter::encode)
|
||||
it.status = status.toInt()
|
||||
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>)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Reference in a new issue