refactor(recents): Fully migrate recents to use SQLDelight

This commit is contained in:
Ahmad Ansori Palembani 2024-08-27 08:16:04 +07:00
parent 79929b395e
commit 354ed7ce8a
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
10 changed files with 354 additions and 86 deletions

View file

@ -39,16 +39,18 @@ interface History : Serializable {
this.chapter_id = chapter.id!!
}
fun create(): History = HistoryImpl()
fun mapper(
id: Long,
chapterId: Long,
lastRead: Long,
timeRead: Long
) = HistoryImpl().apply {
lastRead: Long?,
timeRead: Long?,
): History = HistoryImpl().apply {
this.id = id
this.chapter_id = chapterId
this.last_read = lastRead
this.time_read = timeRead
this.last_read = lastRead ?: 0L
this.time_read = timeRead ?: 0L
}
}
}

View file

@ -13,6 +13,100 @@ data class MangaChapterHistory(val manga: Manga, val chapter: Chapter, val histo
companion object {
fun createBlank() = MangaChapterHistory(MangaImpl(), ChapterImpl(), HistoryImpl())
fun mapper(
// manga
mangaId: Long,
source: Long,
mangaUrl: String,
artist: String?,
author: String?,
description: String?,
genre: String?,
title: String,
status: Long,
thumbnailUrl: String?,
favorite: Boolean,
lastUpdate: Long?,
initialized: Boolean,
viewer: Long,
hideTitle: Boolean,
chapterFlags: Long,
dateAdded: Long?,
filteredScanlators: String?,
updateStrategy: Long,
coverLastModified: Long,
// chapter
chapterId: Long?,
_mangaId: Long?,
chapterUrl: String?,
name: String?,
scanlator: String?,
read: Boolean?,
bookmark: Boolean?,
lastPageRead: Long?,
pagesLeft: Long?,
chapterNumber: Double?,
sourceOrder: Long?,
dateFetch: Long?,
dateUpload: Long?,
// history
historyId: Long?,
historyChapterId: Long?,
historyLastRead: Long?,
historyTimeRead: Long?,
) = MangaChapterHistory(
Manga.mapper(
id = mangaId,
source = source,
url = mangaUrl,
artist = artist,
author = author,
description = description,
genre = genre,
title = title,
status = status,
thumbnailUrl = thumbnailUrl,
favorite = favorite,
lastUpdate = lastUpdate,
initialized = initialized,
viewerFlags = viewer,
hideTitle = hideTitle,
chapterFlags = chapterFlags,
dateAdded = dateAdded,
filteredScanlators = filteredScanlators,
updateStrategy = updateStrategy,
coverLastModified = coverLastModified,
),
chapterId?.let {
Chapter.mapper(
id = chapterId,
mangaId = _mangaId!!,
url = chapterUrl!!,
name = name!!,
scanlator = scanlator!!,
read = read!!,
bookmark = bookmark!!,
lastPageRead = lastPageRead!!,
pagesLeft = pagesLeft!!,
chapterNumber = chapterNumber!!,
sourceOrder = sourceOrder!!,
dateFetch = dateFetch!!,
dateUpload = dateUpload!!,
)
} ?: Chapter.create(),
historyId?.let {
History.mapper(
id = historyId,
chapterId = historyChapterId!!,
lastRead = historyLastRead,
timeRead = historyTimeRead,
)
} ?: History.create().apply {
historyLastRead?.let { last_read = it }
historyTimeRead?.let { time_read = it }
},
)
}
}

View file

@ -52,22 +52,6 @@ interface HistoryQueries : DbProvider {
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
/**
* Returns history of recent manga containing last read chapter in 25s
* @param date recent date range
* @offset offset the db by
*/
fun getRecentMangaLimit(search: String = "", offset: Int, isResuming: Boolean) = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(
RawQuery.builder()
.query(getRecentMangasLimitQuery(search.sqLite, offset, isResuming))
.observesTables(HistoryTable.TABLE)
.build(),
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
/**
* Returns history of manga read during period
* @param startDate start date of the period
@ -85,37 +69,6 @@ interface HistoryQueries : DbProvider {
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
/**
* Returns history of recent manga containing last read chapter in 25s
* @param date recent date range
* @offset offset the db by
*/
fun getAllRecentsTypes(
search: String = "",
includeRead: Boolean,
endless: Boolean,
offset: Int,
isResuming: Boolean,
) = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(
RawQuery.builder()
.query(
getAllRecentsType(
search.sqLite,
includeRead,
endless,
offset,
isResuming,
),
)
// .args(date.time, startDate.time)
.observesTables(HistoryTable.TABLE)
.build(),
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
fun getHistoryByMangaId(mangaId: Long) = db.get()
.listOfObjects(History::class.java)
.withQuery(

View file

@ -17,7 +17,6 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.presenter.BaseCoroutinePresenter
import eu.kanade.tachiyomi.util.chapter.ChapterFilter
import eu.kanade.tachiyomi.util.chapter.ChapterSort
import eu.kanade.tachiyomi.util.system.executeOnIO
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.system.toast
@ -41,9 +40,9 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.RecentChapter
import yokai.domain.chapter.interactor.UpdateChapter
import yokai.domain.recents.RecentsPreferences
import yokai.domain.recents.interactor.GetRecents
import yokai.domain.ui.UiPreferences
import yokai.i18n.MR
@ -56,7 +55,7 @@ class RecentsPresenter(
private val chapterFilter: ChapterFilter = Injekt.get(),
) : BaseCoroutinePresenter<RecentsController>(), DownloadQueue.DownloadListener {
private val getChapter: GetChapter by injectLazy()
private val recentChapter: RecentChapter by injectLazy()
private val getRecents: GetRecents by injectLazy()
private val updateChapter: UpdateChapter by injectLazy()
private var recentsJob: Job? = null
@ -172,26 +171,29 @@ class RecentsPresenter(
var extraCount = 0
val cReading: List<MangaChapterHistory> = when (viewType) {
RecentsViewType.GroupedAll, RecentsViewType.UngroupedAll -> {
db.getAllRecentsTypes(
query,
getRecents.awaitAll(
showRead,
true,
isEndless,
if (isCustom) ENDLESS_LIMIT else pageOffset,
!updatePageCount && !isOnFirstPage,
).executeOnIO()
query,
(if (isCustom) ENDLESS_LIMIT else pageOffset).toLong(),
)
}
RecentsViewType.History -> {
val items = if (groupChaptersHistory == GroupType.BySeries) {
db.getRecentMangaLimit(
query,
if (isCustom) ENDLESS_LIMIT else pageOffset,
getRecents.awaitBySeries(
true,
!updatePageCount && !isOnFirstPage,
query,
(if (isCustom) ENDLESS_LIMIT else pageOffset).toLong(),
)
} else {
db.getHistoryUngrouped(
query,
if (isCustom) ENDLESS_LIMIT else pageOffset,
getRecents.awaitUngrouped(
true,
!updatePageCount && !isOnFirstPage,
query,
(if (isCustom) ENDLESS_LIMIT else pageOffset).toLong(),
)
}
if (groupChaptersHistory.isByTime) {
@ -203,7 +205,7 @@ class RecentsPresenter(
)
val dayOfWeek = Calendar.getInstance().get(Calendar.DAY_OF_WEEK) % 7 + 1
dateFormat.calendar.firstDayOfWeek = dayOfWeek
items.executeOnIO().groupBy {
items.groupBy {
val date = it.history.last_read
it.manga.id to if (date <= 0L) "-1" else dateFormat.format(Date(date))
}
@ -235,14 +237,14 @@ class RecentsPresenter(
}
}
} else {
items.executeOnIO()
items
}
}
RecentsViewType.Updates -> {
dateFormat.applyPattern("yyyy-MM-dd")
dateFormat.calendar.firstDayOfWeek =
Calendar.getInstance().get(Calendar.DAY_OF_WEEK)
recentChapter.await(
getRecents.awaitUpdates(
true,
!updatePageCount && !isOnFirstPage,
query,

View file

@ -8,6 +8,7 @@ import uy.kohesive.injekt.api.get
import yokai.data.category.CategoryRepositoryImpl
import yokai.data.chapter.ChapterRepositoryImpl
import yokai.data.extension.repo.ExtensionRepoRepositoryImpl
import yokai.data.history.HistoryRepositoryImpl
import yokai.data.library.custom.CustomMangaRepositoryImpl
import yokai.data.manga.MangaRepositoryImpl
import yokai.domain.category.CategoryRepository
@ -17,7 +18,7 @@ import yokai.domain.chapter.interactor.DeleteChapter
import yokai.domain.chapter.interactor.GetAvailableScanlators
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.InsertChapter
import yokai.domain.chapter.interactor.RecentChapter
import yokai.domain.recents.interactor.GetRecents
import yokai.domain.chapter.interactor.UpdateChapter
import yokai.domain.extension.interactor.TrustExtension
import yokai.domain.extension.repo.ExtensionRepoRepository
@ -27,6 +28,7 @@ import yokai.domain.extension.repo.interactor.GetExtensionRepo
import yokai.domain.extension.repo.interactor.GetExtensionRepoCount
import yokai.domain.extension.repo.interactor.ReplaceExtensionRepo
import yokai.domain.extension.repo.interactor.UpdateExtensionRepo
import yokai.domain.history.HistoryRepository
import yokai.domain.library.custom.CustomMangaRepository
import yokai.domain.library.custom.interactor.CreateCustomManga
import yokai.domain.library.custom.interactor.DeleteCustomManga
@ -67,9 +69,12 @@ class DomainModule : InjektModule {
addFactory { GetAvailableScanlators(get()) }
addFactory { GetChapter(get()) }
addFactory { InsertChapter(get()) }
addFactory { RecentChapter(get()) }
addFactory { UpdateChapter(get()) }
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
addFactory { GetRecents(get(), get()) }
addSingletonFactory<CategoryRepository> { CategoryRepositoryImpl(get()) }
addFactory { GetCategories(get()) }
}

View file

@ -0,0 +1,33 @@
package yokai.data.history
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
import eu.kanade.tachiyomi.util.system.toInt
import yokai.data.DatabaseHandler
import yokai.domain.history.HistoryRepository
class HistoryRepositoryImpl(private val handler: DatabaseHandler) : HistoryRepository {
override suspend fun getRecentsUngrouped(
filterScanlators: Boolean,
search: String,
limit: Long,
offset: Long,
): List<MangaChapterHistory> =
handler.awaitList { historyQueries.getRecentsUngrouped(search, filterScanlators.toInt().toLong(), limit, offset, MangaChapterHistory::mapper) }
override suspend fun getRecentsBySeries(
filterScanlators: Boolean,
search: String,
limit: Long,
offset: Long,
): List<MangaChapterHistory> =
handler.awaitList { historyQueries.getRecentsBySeries(search, filterScanlators.toInt().toLong(), limit, offset, MangaChapterHistory::mapper) }
override suspend fun getRecentsAll(
includeRead: Boolean,
filterScanlators: Boolean,
search: String,
limit: Long,
offset: Long
): List<MangaChapterHistory> =
handler.awaitList { historyQueries.getRecentsAll(includeRead.toInt().toLong(), search, filterScanlators.toInt().toLong(), limit, offset, MangaChapterHistory::mapper) }
}

View file

@ -1,15 +0,0 @@
package yokai.domain.chapter.interactor
import eu.kanade.tachiyomi.data.database.models.MangaChapter
import yokai.domain.chapter.ChapterRepository
import yokai.util.limitAndOffset
class RecentChapter(
private val chapterRepository: ChapterRepository,
) {
suspend fun await(filterScanlators: Boolean, isResuming: Boolean, search: String = "", offset: Long = 0L): List<MangaChapter> {
val (limit, actualOffset) = limitAndOffset(true, isResuming, offset)
return chapterRepository.getRecents(filterScanlators, search, limit, actualOffset)
}
}

View file

@ -0,0 +1,9 @@
package yokai.domain.history
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
interface HistoryRepository {
suspend fun getRecentsUngrouped(filterScanlators: Boolean, search: String = "", limit: Long = 25L, offset: Long = 0L): List<MangaChapterHistory>
suspend fun getRecentsBySeries(filterScanlators: Boolean, search: String = "", limit: Long = 25L, offset: Long = 0L): List<MangaChapterHistory>
suspend fun getRecentsAll(includeRead: Boolean, filterScanlators: Boolean, search: String = "", limit: Long = 25L, offset: Long = 0L): List<MangaChapterHistory>
}

View file

@ -0,0 +1,58 @@
package yokai.domain.recents.interactor
import eu.kanade.tachiyomi.data.database.models.MangaChapter
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
import yokai.domain.chapter.ChapterRepository
import yokai.domain.history.HistoryRepository
import yokai.util.limitAndOffset
class GetRecents(
private val chapterRepository: ChapterRepository,
private val historyRepository: HistoryRepository,
) {
suspend fun awaitUpdates(
filterScanlators: Boolean,
isResuming: Boolean,
search: String = "",
offset: Long = 0L,
): List<MangaChapter> {
val (limit, actualOffset) = limitAndOffset(true, isResuming, offset)
return chapterRepository.getRecents(filterScanlators, search, limit, actualOffset)
}
suspend fun awaitUngrouped(
filterScanlators: Boolean,
isResuming: Boolean,
search: String = "",
offset: Long = 0L,
): List<MangaChapterHistory> {
val (limit, actualOffset) = limitAndOffset(true, isResuming, offset)
return historyRepository.getRecentsUngrouped(filterScanlators, search, limit, actualOffset)
}
suspend fun awaitBySeries(
filterScanlators: Boolean,
isResuming: Boolean,
search: String = "",
offset: Long = 0L,
): List<MangaChapterHistory> {
val (limit, actualOffset) = limitAndOffset(true, isResuming, offset)
return historyRepository.getRecentsBySeries(filterScanlators, search, limit, actualOffset)
}
suspend fun awaitAll(
includeRead: Boolean,
filterScanlators: Boolean,
isEndless: Boolean,
isResuming: Boolean,
search: String = "",
offset: Long = 0L,
): List<MangaChapterHistory> {
val (limit, actualOffset) = limitAndOffset(isEndless, isResuming, offset)
return historyRepository.getRecentsAll(includeRead, filterScanlators, search, limit, actualOffset)
}
}

View file

@ -32,7 +32,6 @@ LIMIT :limit OFFSET :offset;
getRecentsBySeries:
SELECT
M.url AS mangaUrl,
M.*,
C.*,
H.*
@ -62,3 +61,131 @@ AND (
)
ORDER BY max_last_read.history_last_read DESC
LIMIT :limit OFFSET :offset;
getRecentsAll: -- AKA insanity
SELECT * FROM (
SELECT
M.*,
C.*,
H.*
FROM (
SELECT mangas.*
FROM mangas
LEFT JOIN (
SELECT manga_id, COUNT(*) AS unread
FROM chapters
WHERE read = 0
GROUP BY manga_id
) AS C
ON _id = C.manga_id
WHERE (
:include_read = 0 OR C.unread > 0
)
GROUP BY _id
ORDER BY title
) AS M
JOIN chapters AS C
ON M._id = C.manga_id
JOIN history AS H
ON C._id = H.history_chapter_id
JOIN (
SELECT
chapters.manga_id AS 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
) AS max_last_read
ON C.manga_id = max_last_read.manga_id
AND max_last_read.history_chapter_id = H.history_chapter_id
AND max_last_read.history_last_read > 0
LEFT JOIN scanlators_view AS S
ON C.manga_id = S.manga_id
AND ifnull(C.scanlator, 'N/A') = ifnull(S.name, '/<INVALID>/') -- I assume if it's N/A it shouldn't be filtered
WHERE lower(M.title) LIKE '%' || :search || '%'
AND (
:apply_filter = 0 OR S.name IS NULL
)
)
UNION --
SELECT * FROM (
SELECT
M.*,
C.*,
NULL AS history_id,
NULL AS history_chapter_id,
C.date_fetch AS history_last_read,
NULL AS history_time_read
FROM (
SELECT mangas.*
FROM mangas
LEFT JOIN (
SELECT manga_id, COUNT(*) AS unread
FROM chapters
WHERE read = 0
GROUP BY manga_id
) AS C2
ON _id = C2.manga_id
WHERE (
:include_read = 0 OR C2.unread > 0
)
GROUP BY _id
ORDER BY title
) AS M
JOIN chapters AS C
ON M._id = C.manga_id
JOIN history AS H
ON C._id = H.history_chapter_id
JOIN (
SELECT
chapters.manga_id,
chapters._id AS history_chapter_id,
max(chapters.date_upload)
FROM chapters JOIN mangas
ON mangas._id = chapters.manga_id
WHERE chapters.read = 0
GROUP BY chapters.manga_id
) AS newest_chapter
LEFT JOIN scanlators_view AS S
ON C.manga_id = S.manga_id
AND ifnull(C.scanlator, 'N/A') = ifnull(S.name, '/<INVALID>/') -- I assume if it's N/A it shouldn't be filtered
WHERE M.favorite = 1
AND newest_chapter.history_chapter_id = H.history_chapter_id
AND C.date_fetch > M.date_added
AND lower(M.title) LIKE '%' || :search || '%'
AND (
:apply_filter = 0 OR S.name IS NULL
)
)
UNION --
SELECT * FROM (
SELECT
M.*,
NULL AS _id,
NULL AS manga_id,
NULL AS url,
NULL AS name,
NULL AS read,
NULL AS scanlator,
NULL AS bookmark,
NULL AS date_fetch,
NULL AS date_upload,
NULL AS last_page_read,
NULL AS pages_left,
NULL AS chapter_number,
NULL AS source_order,
NULL AS history_id,
NULL AS history_chapter_id,
M.date_added AS history_last_read,
NULL AS history_time_read
FROM mangas AS M
WHERE M.favorite = 1
AND lower(M.title) LIKE '%' || :search || '%'
)
ORDER BY history_last_read DESC
LIMIT :limit OFFSET :offset;