From 809da49301cceacd433f38354fb04fc616efcc5f Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 28 May 2022 19:09:27 +0600 Subject: [PATCH] Reader: Save reading progress with SQLDelight (#7185) * Use SQLDelight in reader to update history * Move chapter progress to sqldelight * Review Changes Co-Authored-By: inorichi * Review Changes 2 Co-authored-by: FourTOne5 <59261191+FourTOne5@users.noreply.github.com> Co-authored-by: inorichi --- .../main/java/eu/kanade/data/DatabaseUtils.kt | 3 + .../data/chapter/ChapterRepositoryImpl.kt | 36 +++++++++ .../eu/kanade/data/history/HistoryMapper.kt | 8 +- .../data/history/HistoryRepositoryImpl.kt | 25 ++++++ .../java/eu/kanade/domain/DomainModule.kt | 8 ++ .../chapter/interactor/UpdateChapter.kt | 13 +++ .../domain/chapter/model/ChapterUpdate.kt | 16 ++++ .../chapter/repository/ChapterRepository.kt | 8 ++ .../history/interactor/UpsertHistory.kt | 13 +++ .../eu/kanade/domain/history/model/History.kt | 3 +- .../domain/history/model/HistoryUpdate.kt | 9 +++ .../history/model/HistoryWithRelations.kt | 1 + .../history/repository/HistoryRepository.kt | 3 + .../java/eu/kanade/tachiyomi/AppModule.kt | 3 +- .../tachiyomi/data/database/models/History.kt | 2 +- .../data/database/models/HistoryImpl.kt | 2 +- .../tachiyomi/ui/reader/ReaderActivity.kt | 3 +- .../tachiyomi/ui/reader/ReaderPresenter.kt | 81 ++++++++++++------- app/src/main/sqldelight/data/chapters.sq | 17 +++- app/src/main/sqldelight/data/history.sq | 36 ++++++--- app/src/main/sqldelight/migrations/15.sqm | 52 ++++++++++++ app/src/main/sqldelight/view/historyView.sq | 34 ++++---- 22 files changed, 309 insertions(+), 67 deletions(-) create mode 100644 app/src/main/java/eu/kanade/data/DatabaseUtils.kt create mode 100644 app/src/main/java/eu/kanade/data/chapter/ChapterRepositoryImpl.kt create mode 100644 app/src/main/java/eu/kanade/domain/chapter/interactor/UpdateChapter.kt create mode 100644 app/src/main/java/eu/kanade/domain/chapter/model/ChapterUpdate.kt create mode 100644 app/src/main/java/eu/kanade/domain/chapter/repository/ChapterRepository.kt create mode 100644 app/src/main/java/eu/kanade/domain/history/interactor/UpsertHistory.kt create mode 100644 app/src/main/java/eu/kanade/domain/history/model/HistoryUpdate.kt create mode 100644 app/src/main/sqldelight/migrations/15.sqm diff --git a/app/src/main/java/eu/kanade/data/DatabaseUtils.kt b/app/src/main/java/eu/kanade/data/DatabaseUtils.kt new file mode 100644 index 0000000000..d46f4b1f8b --- /dev/null +++ b/app/src/main/java/eu/kanade/data/DatabaseUtils.kt @@ -0,0 +1,3 @@ +package eu.kanade.data + +fun Boolean.toLong() = if (this) 1L else 0L diff --git a/app/src/main/java/eu/kanade/data/chapter/ChapterRepositoryImpl.kt b/app/src/main/java/eu/kanade/data/chapter/ChapterRepositoryImpl.kt new file mode 100644 index 0000000000..0d7ae610a4 --- /dev/null +++ b/app/src/main/java/eu/kanade/data/chapter/ChapterRepositoryImpl.kt @@ -0,0 +1,36 @@ +package eu.kanade.data.chapter + +import eu.kanade.data.DatabaseHandler +import eu.kanade.data.toLong +import eu.kanade.domain.chapter.model.ChapterUpdate +import eu.kanade.domain.chapter.repository.ChapterRepository +import eu.kanade.tachiyomi.util.system.logcat +import logcat.LogPriority + +class ChapterRepositoryImpl( + private val databaseHandler: DatabaseHandler, +) : ChapterRepository { + + override suspend fun update(chapterUpdate: ChapterUpdate) { + try { + databaseHandler.await { + chaptersQueries.update( + chapterUpdate.mangaId, + chapterUpdate.url, + chapterUpdate.name, + chapterUpdate.scanlator, + chapterUpdate.read?.toLong(), + chapterUpdate.bookmark?.toLong(), + chapterUpdate.lastPageRead, + chapterUpdate.chapterNumber?.toDouble(), + chapterUpdate.sourceOrder, + chapterUpdate.dateFetch, + chapterUpdate.dateUpload, + chapterId = chapterUpdate.id, + ) + } + } catch (e: Exception) { + logcat(LogPriority.ERROR, e) + } + } +} diff --git a/app/src/main/java/eu/kanade/data/history/HistoryMapper.kt b/app/src/main/java/eu/kanade/data/history/HistoryMapper.kt index c4b7d4b312..8164c5e0b4 100644 --- a/app/src/main/java/eu/kanade/data/history/HistoryMapper.kt +++ b/app/src/main/java/eu/kanade/data/history/HistoryMapper.kt @@ -4,16 +4,17 @@ import eu.kanade.domain.history.model.History import eu.kanade.domain.history.model.HistoryWithRelations import java.util.Date -val historyMapper: (Long, Long, Date?, Date?) -> History = { id, chapterId, readAt, _ -> +val historyMapper: (Long, Long, Date?, Long) -> History = { id, chapterId, readAt, readDuration -> History( id = id, chapterId = chapterId, readAt = readAt, + readDuration = readDuration, ) } -val historyWithRelationsMapper: (Long, Long, Long, String, String?, Float, Date?) -> HistoryWithRelations = { - historyId, mangaId, chapterId, title, thumbnailUrl, chapterNumber, readAt -> +val historyWithRelationsMapper: (Long, Long, Long, String, String?, Float, Date?, Long) -> HistoryWithRelations = { + historyId, mangaId, chapterId, title, thumbnailUrl, chapterNumber, readAt, readDuration -> HistoryWithRelations( id = historyId, chapterId = chapterId, @@ -22,5 +23,6 @@ val historyWithRelationsMapper: (Long, Long, Long, String, String?, Float, Date? thumbnailUrl = thumbnailUrl ?: "", chapterNumber = chapterNumber, readAt = readAt, + readDuration = readDuration, ) } diff --git a/app/src/main/java/eu/kanade/data/history/HistoryRepositoryImpl.kt b/app/src/main/java/eu/kanade/data/history/HistoryRepositoryImpl.kt index 388a11bd1a..50223330b7 100644 --- a/app/src/main/java/eu/kanade/data/history/HistoryRepositoryImpl.kt +++ b/app/src/main/java/eu/kanade/data/history/HistoryRepositoryImpl.kt @@ -5,6 +5,7 @@ import eu.kanade.data.DatabaseHandler import eu.kanade.data.chapter.chapterMapper import eu.kanade.data.manga.mangaMapper import eu.kanade.domain.chapter.model.Chapter +import eu.kanade.domain.history.model.HistoryUpdate import eu.kanade.domain.history.model.HistoryWithRelations import eu.kanade.domain.history.repository.HistoryRepository import eu.kanade.domain.manga.model.Manga @@ -89,4 +90,28 @@ class HistoryRepositoryImpl( false } } + + override suspend fun upsertHistory(historyUpdate: HistoryUpdate) { + try { + try { + handler.await { + historyQueries.insert( + historyUpdate.chapterId, + historyUpdate.readAt, + historyUpdate.sessionReadDuration, + ) + } + } catch (e: Exception) { + handler.await { + historyQueries.update( + historyUpdate.readAt, + historyUpdate.sessionReadDuration, + historyUpdate.chapterId, + ) + } + } + } catch (e: Exception) { + logcat(LogPriority.ERROR, throwable = e) + } + } } diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 99da63a5fa..2d70278a01 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -1,8 +1,11 @@ package eu.kanade.domain +import eu.kanade.data.chapter.ChapterRepositoryImpl import eu.kanade.data.history.HistoryRepositoryImpl import eu.kanade.data.manga.MangaRepositoryImpl import eu.kanade.data.source.SourceRepositoryImpl +import eu.kanade.domain.chapter.interactor.UpdateChapter +import eu.kanade.domain.chapter.repository.ChapterRepository import eu.kanade.domain.extension.interactor.GetExtensionLanguages import eu.kanade.domain.extension.interactor.GetExtensionSources import eu.kanade.domain.extension.interactor.GetExtensionUpdates @@ -12,6 +15,7 @@ import eu.kanade.domain.history.interactor.GetHistory import eu.kanade.domain.history.interactor.GetNextChapterForManga import eu.kanade.domain.history.interactor.RemoveHistoryById import eu.kanade.domain.history.interactor.RemoveHistoryByMangaId +import eu.kanade.domain.history.interactor.UpsertHistory import eu.kanade.domain.history.repository.HistoryRepository import eu.kanade.domain.manga.interactor.GetFavoritesBySourceId import eu.kanade.domain.manga.interactor.ResetViewerFlags @@ -38,9 +42,13 @@ class DomainModule : InjektModule { addFactory { GetNextChapterForManga(get()) } addFactory { ResetViewerFlags(get()) } + addSingletonFactory { ChapterRepositoryImpl(get()) } + addFactory { UpdateChapter(get()) } + addSingletonFactory { HistoryRepositoryImpl(get()) } addFactory { DeleteHistoryTable(get()) } addFactory { GetHistory(get()) } + addFactory { UpsertHistory(get()) } addFactory { RemoveHistoryById(get()) } addFactory { RemoveHistoryByMangaId(get()) } diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/UpdateChapter.kt b/app/src/main/java/eu/kanade/domain/chapter/interactor/UpdateChapter.kt new file mode 100644 index 0000000000..2d9d0d052a --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/chapter/interactor/UpdateChapter.kt @@ -0,0 +1,13 @@ +package eu.kanade.domain.chapter.interactor + +import eu.kanade.domain.chapter.model.ChapterUpdate +import eu.kanade.domain.chapter.repository.ChapterRepository + +class UpdateChapter( + private val chapterRepository: ChapterRepository, +) { + + suspend fun await(chapterUpdate: ChapterUpdate) { + chapterRepository.update(chapterUpdate) + } +} diff --git a/app/src/main/java/eu/kanade/domain/chapter/model/ChapterUpdate.kt b/app/src/main/java/eu/kanade/domain/chapter/model/ChapterUpdate.kt new file mode 100644 index 0000000000..2c9042c477 --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/chapter/model/ChapterUpdate.kt @@ -0,0 +1,16 @@ +package eu.kanade.domain.chapter.model + +data class ChapterUpdate( + val id: Long, + val mangaId: Long? = null, + val read: Boolean? = null, + val bookmark: Boolean? = null, + val lastPageRead: Long? = null, + val dateFetch: Long? = null, + val sourceOrder: Long? = null, + val url: String? = null, + val name: String? = null, + val dateUpload: Long? = null, + val chapterNumber: Float? = null, + val scanlator: String? = null, +) diff --git a/app/src/main/java/eu/kanade/domain/chapter/repository/ChapterRepository.kt b/app/src/main/java/eu/kanade/domain/chapter/repository/ChapterRepository.kt new file mode 100644 index 0000000000..9f3bb73d74 --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/chapter/repository/ChapterRepository.kt @@ -0,0 +1,8 @@ +package eu.kanade.domain.chapter.repository + +import eu.kanade.domain.chapter.model.ChapterUpdate + +interface ChapterRepository { + + suspend fun update(chapterUpdate: ChapterUpdate) +} diff --git a/app/src/main/java/eu/kanade/domain/history/interactor/UpsertHistory.kt b/app/src/main/java/eu/kanade/domain/history/interactor/UpsertHistory.kt new file mode 100644 index 0000000000..31c46b2999 --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/history/interactor/UpsertHistory.kt @@ -0,0 +1,13 @@ +package eu.kanade.domain.history.interactor + +import eu.kanade.domain.history.model.HistoryUpdate +import eu.kanade.domain.history.repository.HistoryRepository + +class UpsertHistory( + private val historyRepository: HistoryRepository, +) { + + suspend fun await(historyUpdate: HistoryUpdate) { + historyRepository.upsertHistory(historyUpdate) + } +} diff --git a/app/src/main/java/eu/kanade/domain/history/model/History.kt b/app/src/main/java/eu/kanade/domain/history/model/History.kt index 5c42c98525..b8a1cf3c10 100644 --- a/app/src/main/java/eu/kanade/domain/history/model/History.kt +++ b/app/src/main/java/eu/kanade/domain/history/model/History.kt @@ -3,7 +3,8 @@ package eu.kanade.domain.history.model import java.util.Date data class History( - val id: Long?, + val id: Long, val chapterId: Long, val readAt: Date?, + val readDuration: Long, ) diff --git a/app/src/main/java/eu/kanade/domain/history/model/HistoryUpdate.kt b/app/src/main/java/eu/kanade/domain/history/model/HistoryUpdate.kt new file mode 100644 index 0000000000..8cd7f5a2ef --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/history/model/HistoryUpdate.kt @@ -0,0 +1,9 @@ +package eu.kanade.domain.history.model + +import java.util.Date + +data class HistoryUpdate( + val chapterId: Long, + val readAt: Date, + val sessionReadDuration: Long, +) diff --git a/app/src/main/java/eu/kanade/domain/history/model/HistoryWithRelations.kt b/app/src/main/java/eu/kanade/domain/history/model/HistoryWithRelations.kt index 827cfda540..2871b80bec 100644 --- a/app/src/main/java/eu/kanade/domain/history/model/HistoryWithRelations.kt +++ b/app/src/main/java/eu/kanade/domain/history/model/HistoryWithRelations.kt @@ -10,4 +10,5 @@ data class HistoryWithRelations( val thumbnailUrl: String, val chapterNumber: Float, val readAt: Date?, + val readDuration: Long, ) diff --git a/app/src/main/java/eu/kanade/domain/history/repository/HistoryRepository.kt b/app/src/main/java/eu/kanade/domain/history/repository/HistoryRepository.kt index 38e0f4192f..b50c8c6405 100644 --- a/app/src/main/java/eu/kanade/domain/history/repository/HistoryRepository.kt +++ b/app/src/main/java/eu/kanade/domain/history/repository/HistoryRepository.kt @@ -2,6 +2,7 @@ package eu.kanade.domain.history.repository import androidx.paging.PagingSource import eu.kanade.domain.chapter.model.Chapter +import eu.kanade.domain.history.model.HistoryUpdate import eu.kanade.domain.history.model.HistoryWithRelations interface HistoryRepository { @@ -15,4 +16,6 @@ interface HistoryRepository { suspend fun resetHistoryByMangaId(mangaId: Long) suspend fun deleteAllHistory(): Boolean + + suspend fun upsertHistory(historyUpdate: HistoryUpdate) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt index 0d365840c3..515df53a48 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt @@ -55,8 +55,7 @@ class AppModule(val app: Application) : InjektModule { Database( driver = get(), historyAdapter = History.Adapter( - history_last_readAdapter = dateAdapter, - history_time_readAdapter = dateAdapter, + last_readAdapter = dateAdapter, ), mangasAdapter = Mangas.Adapter( genreAdapter = listOfStringsAdapter, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/History.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/History.kt index dff3bcb155..10429f8b1d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/History.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/History.kt @@ -23,7 +23,7 @@ interface History : Serializable { var last_read: Long /** - * Total time chapter was read - todo not yet implemented + * Total time chapter was read */ var time_read: Long diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/HistoryImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/HistoryImpl.kt index 94efcf2666..8b9dbe7662 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/HistoryImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/HistoryImpl.kt @@ -21,7 +21,7 @@ class HistoryImpl : History { override var last_read: Long = 0 /** - * Total time chapter was read - todo not yet implemented + * Total time chapter was read */ override var time_read: Long = 0 } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 157ee3c96c..17c8fa1776 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -232,7 +232,7 @@ class ReaderActivity : BaseRxActivity() { } override fun onPause() { - presenter.saveProgress() + presenter.saveCurrentChapterReadingProgress() super.onPause() } @@ -242,6 +242,7 @@ class ReaderActivity : BaseRxActivity() { */ override fun onResume() { super.onResume() + presenter.setReadStartTime() setMenuVisibility(menuVisible, animate = false) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index ba3b1b9afb..9a1e5c1bf7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -4,9 +4,12 @@ import android.app.Application import android.net.Uri import android.os.Bundle import com.jakewharton.rxrelay.BehaviorRelay +import eu.kanade.domain.chapter.interactor.UpdateChapter +import eu.kanade.domain.chapter.model.ChapterUpdate +import eu.kanade.domain.history.interactor.UpsertHistory +import eu.kanade.domain.history.model.HistoryUpdate import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.DatabaseHelper -import eu.kanade.tachiyomi.data.database.models.History import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.preference.PreferencesHelper @@ -62,6 +65,8 @@ class ReaderPresenter( private val coverCache: CoverCache = Injekt.get(), private val preferences: PreferencesHelper = Injekt.get(), private val delayedTrackingStore: DelayedTrackingStore = Injekt.get(), + private val upsertHistory: UpsertHistory = Injekt.get(), + private val updateChapter: UpdateChapter = Injekt.get(), ) : BasePresenter() { /** @@ -80,6 +85,11 @@ class ReaderPresenter( */ private var loader: ChapterLoader? = null + /** + * The time the chapter was started reading + */ + private var chapterReadStartTime: Long? = null + /** * Subscription to prevent setting chapters as active from multiple threads. */ @@ -168,8 +178,7 @@ class ReaderPresenter( val currentChapters = viewerChaptersRelay.value if (currentChapters != null) { currentChapters.unref() - saveChapterProgress(currentChapters.currChapter) - saveChapterHistory(currentChapters.currChapter) + saveReadingProgress(currentChapters.currChapter) } } @@ -200,7 +209,9 @@ class ReaderPresenter( */ fun onSaveInstanceStateNonConfigurationChange() { val currentChapter = getCurrentChapter() ?: return - saveChapterProgress(currentChapter) + launchIO { + saveChapterProgress(currentChapter) + } } /** @@ -397,7 +408,7 @@ class ReaderPresenter( if (selectedChapter != currentChapters.currChapter) { logcat { "Setting ${selectedChapter.chapter.url} as active" } - onChapterChanged(currentChapters.currChapter) + saveReadingProgress(currentChapters.currChapter) loadNewChapter(selectedChapter) } } @@ -429,43 +440,57 @@ class ReaderPresenter( } } - /** - * Called when a chapter changed from [fromChapter] to [toChapter]. It updates [fromChapter] - * on the database. - */ - private fun onChapterChanged(fromChapter: ReaderChapter) { - saveChapterProgress(fromChapter) - saveChapterHistory(fromChapter) + fun saveCurrentChapterReadingProgress() { + getCurrentChapter()?.let { saveReadingProgress(it) } } /** - * Saves this [chapter] progress (last read page and whether it's read). + * Called when reader chapter is changed in reader or when activity is paused. + */ + private fun saveReadingProgress(readerChapter: ReaderChapter) { + launchIO { + saveChapterProgress(readerChapter) + saveChapterHistory(readerChapter) + } + } + + /** + * Saves this [readerChapter] progress (last read page and whether it's read). * If incognito mode isn't on or has at least 1 tracker */ - private fun saveChapterProgress(chapter: ReaderChapter) { + private suspend fun saveChapterProgress(readerChapter: ReaderChapter) { if (!incognitoMode || hasTrackers) { - db.updateChapterProgress(chapter.chapter).asRxCompletable() - .onErrorComplete() - .subscribeOn(Schedulers.io()) - .subscribe() + val chapter = readerChapter.chapter + updateChapter.await( + ChapterUpdate( + id = chapter.id!!, + read = chapter.read, + bookmark = chapter.bookmark, + lastPageRead = chapter.last_page_read.toLong(), + ), + ) } } /** - * Saves this [chapter] last read history if incognito mode isn't on. + * Saves this [readerChapter] last read history if incognito mode isn't on. */ - private fun saveChapterHistory(chapter: ReaderChapter) { + private suspend fun saveChapterHistory(readerChapter: ReaderChapter) { if (!incognitoMode) { - val history = History.create(chapter.chapter).apply { last_read = Date().time } - db.upsertHistoryLastRead(history).asRxCompletable() - .onErrorComplete() - .subscribeOn(Schedulers.io()) - .subscribe() + val chapterId = readerChapter.chapter.id!! + val readAt = Date() + val sessionReadDuration = chapterReadStartTime?.let { readAt.time - it } ?: 0 + + upsertHistory.await( + HistoryUpdate(chapterId, readAt, sessionReadDuration), + ).also { + chapterReadStartTime = null + } } } - fun saveProgress() { - getCurrentChapter()?.let { onChapterChanged(it) } + fun setReadStartTime() { + chapterReadStartTime = Date().time } /** @@ -633,7 +658,7 @@ class ReaderPresenter( * Shares the image of this [page] and notifies the UI with the path of the file to share. * The image must be first copied to the internal partition because there are many possible * formats it can come from, like a zipped chapter, in which case it's not possible to directly - * get a path to the file and it has to be decompresssed somewhere first. Only the last shared + * get a path to the file and it has to be decompressed somewhere first. Only the last shared * image will be kept so it won't be taking lots of internal disk space. */ fun shareImage(page: ReaderPage) { diff --git a/app/src/main/sqldelight/data/chapters.sq b/app/src/main/sqldelight/data/chapters.sq index 337b1163cc..226d9b6bdf 100644 --- a/app/src/main/sqldelight/data/chapters.sq +++ b/app/src/main/sqldelight/data/chapters.sq @@ -26,4 +26,19 @@ WHERE _id = :id; getChapterByMangaId: SELECT * FROM chapters -WHERE manga_id = :mangaId; \ No newline at end of file +WHERE manga_id = :mangaId; + +update: +UPDATE chapters +SET manga_id = coalesce(:mangaId, manga_id), + url = coalesce(:url, url), + name = coalesce(:name, name), + scanlator = coalesce(:scanlator, scanlator), + read = coalesce(:read, read), + bookmark = coalesce(:bookmark, bookmark), + last_page_read = coalesce(:lastPageRead, last_page_read), + chapter_number = coalesce(:chapterNumber, chapter_number), + source_order = coalesce(:sourceOrder, source_order), + date_fetch = coalesce(:dateFetch, date_fetch), + date_upload = coalesce(:dateUpload, date_upload) +WHERE _id = :chapterId; \ No newline at end of file diff --git a/app/src/main/sqldelight/data/history.sq b/app/src/main/sqldelight/data/history.sq index a798b325a4..a6d6b912fc 100644 --- a/app/src/main/sqldelight/data/history.sq +++ b/app/src/main/sqldelight/data/history.sq @@ -1,31 +1,31 @@ import java.util.Date; CREATE TABLE history( - history_id INTEGER NOT NULL PRIMARY KEY, - history_chapter_id INTEGER NOT NULL UNIQUE, - history_last_read INTEGER AS Date, - history_time_read INTEGER AS Date, - FOREIGN KEY(history_chapter_id) REFERENCES chapters (_id) + _id INTEGER NOT NULL PRIMARY KEY, + chapter_id INTEGER NOT NULL UNIQUE, + last_read INTEGER AS Date, + time_read INTEGER NOT NULL, + FOREIGN KEY(chapter_id) REFERENCES chapters (_id) ON DELETE CASCADE ); -CREATE INDEX history_history_chapter_id_index ON history(history_chapter_id); +CREATE INDEX history_history_chapter_id_index ON history(chapter_id); resetHistoryById: UPDATE history -SET history_last_read = 0 -WHERE history_id = :historyId; +SET last_read = 0 +WHERE _id = :historyId; resetHistoryByMangaId: UPDATE history -SET history_last_read = 0 -WHERE history_id IN ( - SELECT H.history_id +SET last_read = 0 +WHERE _id IN ( + SELECT H._id FROM mangas M INNER JOIN chapters C ON M._id = C.manga_id INNER JOIN history H - ON C._id = H.history_chapter_id + ON C._id = H.chapter_id WHERE M._id = :mangaId ); @@ -34,4 +34,14 @@ DELETE FROM history; removeResettedHistory: DELETE FROM history -WHERE history_last_read = 0; +WHERE last_read = 0; + +insert: +INSERT INTO history(chapter_id, last_read, time_read) +VALUES (:chapterId, :readAt, :readDuration); + +update: +UPDATE history +SET last_read = :readAt, + time_read = time_read + :sessionReadDuration +WHERE chapter_id = :chapterId; diff --git a/app/src/main/sqldelight/migrations/15.sqm b/app/src/main/sqldelight/migrations/15.sqm new file mode 100644 index 0000000000..6c3c60b9b0 --- /dev/null +++ b/app/src/main/sqldelight/migrations/15.sqm @@ -0,0 +1,52 @@ +import java.util.Date; + +DROP INDEX IF EXISTS history_history_chapter_id_index; +DROP VIEW IF EXISTS historyView; + +/** + * [last_read] was made not-null + * [time_read] was kept as long and made non-null + * `history` prefix was removed from table name + */ +ALTER TABLE history RENAME TO history_temp; +CREATE TABLE history( + _id INTEGER NOT NULL PRIMARY KEY, + chapter_id INTEGER NOT NULL UNIQUE, + last_read INTEGER AS Date NOT NULL, + time_read INTEGER NOT NULL, + FOREIGN KEY(chapter_id) REFERENCES chapters (_id) + ON DELETE CASCADE +); +INSERT INTO history +SELECT history_id, history_chapter_id, coalesce(history_last_read, 0), coalesce(history_time_read, 0) +FROM history_temp; + +/** + * [history.time_read] was added as a column in [historyView] + */ +CREATE VIEW historyView AS +SELECT + history._id AS id, + mangas._id AS mangaId, + chapters._id AS chapterId, + mangas.title, + mangas.thumbnail_url AS thumbnailUrl, + chapters.chapter_number AS chapterNumber, + history.last_read AS readAt, + history.time_read AS readDuration, + max_last_read.last_read AS maxReadAt, + max_last_read.chapter_id AS maxReadAtChapterId +FROM mangas +JOIN chapters +ON mangas._id = chapters.manga_id +JOIN history +ON chapters._id = history.chapter_id +JOIN ( + SELECT chapters.manga_id,chapters._id AS chapter_id, MAX(history.last_read) AS last_read + FROM chapters JOIN history + ON chapters._id = history.chapter_id + GROUP BY chapters.manga_id +) AS max_last_read +ON chapters.manga_id = max_last_read.manga_id; + +CREATE INDEX history_history_chapter_id_index ON history(chapter_id); \ No newline at end of file diff --git a/app/src/main/sqldelight/view/historyView.sq b/app/src/main/sqldelight/view/historyView.sq index 2471f85aad..e47a347143 100644 --- a/app/src/main/sqldelight/view/historyView.sq +++ b/app/src/main/sqldelight/view/historyView.sq @@ -1,24 +1,25 @@ CREATE VIEW historyView AS SELECT -history.history_id AS id, -mangas._id AS mangaId, -chapters._id AS chapterId, -mangas.title, -mangas.thumbnail_url AS thumnailUrl, -chapters.chapter_number AS chapterNumber, -history.history_last_read AS readAt, -max_last_read.history_last_read AS maxReadAt, -max_last_read.history_chapter_id AS maxReadAtChapterId + history._id AS id, + mangas._id AS mangaId, + chapters._id AS chapterId, + mangas.title, + mangas.thumbnail_url AS thumbnailUrl, + chapters.chapter_number AS chapterNumber, + history.last_read AS readAt, + history.time_read AS readDuration, + max_last_read.last_read AS maxReadAt, + max_last_read.chapter_id AS maxReadAtChapterId FROM mangas JOIN chapters ON mangas._id = chapters.manga_id JOIN history -ON chapters._id = history.history_chapter_id +ON chapters._id = history.chapter_id JOIN ( -SELECT chapters.manga_id,chapters._id AS history_chapter_id, MAX(history.history_last_read) AS history_last_read -FROM chapters JOIN history -ON chapters._id = history.history_chapter_id -GROUP BY chapters.manga_id + SELECT chapters.manga_id,chapters._id AS chapter_id, MAX(history.last_read) AS last_read + FROM chapters JOIN history + ON chapters._id = history.chapter_id + GROUP BY chapters.manga_id ) AS max_last_read ON chapters.manga_id = max_last_read.manga_id; @@ -35,9 +36,10 @@ id, mangaId, chapterId, title, -thumnailUrl, +thumbnailUrl, chapterNumber, -readAt +readAt, +readDuration FROM historyView WHERE historyView.readAt > 0 AND maxReadAtChapterId = historyView.chapterId