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:
AntsyLich 2022-06-27 01:54:34 +06:00 committed by Jobobby04
parent 1084e7fe9d
commit 6f407f6e0a
12 changed files with 298 additions and 83 deletions

View File

@ -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,

View File

@ -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()) }

View File

@ -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)
}
}
}

View File

@ -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(

View File

@ -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(

View File

@ -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)
}
}
}

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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,
)
}

View File

@ -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>)
}

View File

@ -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()
}
}
}

View File

@ -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