Can now expand history grouped chapters

just like the update sections, the preferences for history its either always collapsed by default, or its separated out
This commit is contained in:
Jays2Kings 2023-03-03 01:10:46 -05:00
parent f1c5f65090
commit addabd70bb
12 changed files with 159 additions and 154 deletions

View file

@ -21,6 +21,9 @@ interface Chapter : SChapter, Serializable {
var source_order: Int
var dateRead: Long?
var history: History?
val isRecognizedNumber: Boolean
get() = chapter_number >= 0f

View file

@ -24,6 +24,13 @@ class ChapterImpl : Chapter {
override var date_upload: Long = 0
override var dateRead: Long? = null
override var history: History? = null
set(value) {
field = value
dateRead = history?.last_read
}
override var chapter_number: Float = 0f
override var source_order: Int = 0

View file

@ -35,23 +35,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))
// .args(date.time, startDate.time)
.observesTables(HistoryTable.TABLE)
.build(),
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
/**
* Returns history of recent manga containing last read chapter in 25s
* @param date recent date range
@ -62,7 +45,6 @@ interface HistoryQueries : DbProvider {
.withQuery(
RawQuery.builder()
.query(getRecentHistoryUngrouped(search.sqLite, offset, isResuming))
// .args(date.time, startDate.time)
.observesTables(HistoryTable.TABLE)
.build(),
)

View file

@ -61,18 +61,6 @@ fun getRecentsQuery(search: String, offset: Int, isResuming: Boolean) =
${limitAndOffset(true, isResuming, offset)}
"""
/**
* Query to get the recently added manga
*/
fun getRecentAdditionsQuery(search: String, endless: Boolean, offset: Int, isResuming: Boolean) =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, * FROM ${Manga.TABLE}
WHERE ${Manga.COL_FAVORITE} = 1
AND lower(${Manga.COL_TITLE}) LIKE '%$search%'
ORDER BY ${Manga.COL_DATE_ADDED} DESC
${limitAndOffset(endless, isResuming, offset)}
"""
fun limitAndOffset(endless: Boolean, isResuming: Boolean, offset: Int): String {
return when {
isResuming && endless && offset > 0 -> "LIMIT $offset"
@ -81,37 +69,6 @@ fun limitAndOffset(endless: Boolean, isResuming: Boolean, offset: Int): String {
}
}
/**
* Query to get the recently read chapters of manga from the library up to a date.
* The max_last_read table contains the most recent chapters grouped by manga
* The select statement returns all information of chapters that have the same id as the chapter in max_last_read
* and are read after the given time period
*/
fun getRecentMangasLimitQuery(
search: String = "",
offset: Int = 0,
isResuming: Boolean,
) =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
JOIN ${History.TABLE}
ON ${Chapter.TABLE}.${Chapter.COL_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}
JOIN (
SELECT ${Chapter.TABLE}.${Chapter.COL_MANGA_ID},${Chapter.TABLE}.${Chapter.COL_ID} as ${History.COL_CHAPTER_ID}, MAX(${History.TABLE}.${History.COL_LAST_READ}) as ${History.COL_LAST_READ}
FROM ${Chapter.TABLE} JOIN ${History.TABLE}
ON ${Chapter.TABLE}.${Chapter.COL_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}
GROUP BY ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}) AS max_last_read
ON ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = max_last_read.${Chapter.COL_MANGA_ID}
AND max_last_read.${History.COL_CHAPTER_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}
AND max_last_read.${History.COL_LAST_READ} > 0
AND lower(${Manga.TABLE}.${Manga.COL_TITLE}) LIKE '%$search%'
ORDER BY max_last_read.${History.COL_LAST_READ} DESC
${limitAndOffset(true, isResuming, offset)}
"""
/**
* Query to get the recently read chapters of manga from the library up to a date.
* The max_last_read table contains the most recent chapters grouped by manga

View file

@ -88,6 +88,7 @@ class RecentMangaAdapter(val delegate: RecentsInterface) :
fun isSearching(): Boolean
fun scope(): CoroutineScope
fun getViewType(): Int
fun onItemLongClick(position: Int, chapter: Chapter): Boolean
}
override fun onItemSwiped(position: Int, direction: Int) {

View file

@ -39,9 +39,11 @@ class RecentMangaHolder(
private val binding = RecentMangaItemBinding.bind(view)
var chapterId: Long? = null
private val isUpdates
get() = adapter.viewType == RecentsPresenter.VIEW_TYPE_ONLY_UPDATES
private val isSmallUpdates
get() = adapter.viewType == RecentsPresenter.VIEW_TYPE_ONLY_UPDATES &&
!adapter.showUpdatedTime
get() = isUpdates && !adapter.showUpdatedTime
init {
binding.cardLayout.setOnClickListener { adapter.delegate.onCoverClick(flexibleAdapterPosition) }
@ -62,7 +64,7 @@ class RecentMangaHolder(
RecentSubChapterItemBinding.bind(view).updateDivider()
}
}
if (binding.moreChaptersLayout.children.any { view ->
if (isUpdates && binding.moreChaptersLayout.children.any { view ->
!RecentSubChapterItemBinding.bind(view).subtitle.text.isNullOrBlank()
}
) {
@ -230,7 +232,7 @@ class RecentMangaHolder(
RecentSubChapterItemBinding.bind(binding.moreChaptersLayout.getChildAt(index))
.configureView(chapter, item)
}
if (binding.moreChaptersLayout.children.any { view ->
if (isUpdates && binding.moreChaptersLayout.children.any { view ->
!RecentSubChapterItemBinding.bind(view).subtitle.text.isNullOrBlank()
}
) {
@ -247,7 +249,7 @@ class RecentMangaHolder(
true,
)
binding.configureView(chapter, item)
if (chapter.isRecognizedNumber &&
if (isUpdates && chapter.isRecognizedNumber &&
chapter.chapter_number == item.chapter.chapter_number &&
!chapter.scanlator.isNullOrBlank()
) {
@ -309,13 +311,20 @@ class RecentMangaHolder(
val showDLs = adapter.showDownloads
title.text = chapter.preferredChapterName(context, item.mch.manga, adapter.preferences)
title.setTextColor(ChapterUtil.readColor(context, chapter))
chapter.dateRead?.let { dateRead ->
subtitle.text = context.timeSpanFromNow(R.string.read_, dateRead)
subtitle.isVisible = true
}
root.setOnClickListener {
adapter.delegate.onSubChapterClicked(
flexibleAdapterPosition,
bindingAdapterPosition,
chapter,
it,
)
}
root.setOnLongClickListener {
adapter.delegate.onItemLongClick(bindingAdapterPosition, chapter)
}
listOf(root, downloadButton.root).forEach {
it.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {

View file

@ -652,7 +652,7 @@ class RecentsController(bundle: Bundle? = null) :
override fun areExtraChaptersExpanded(position: Int): Boolean {
val item = (adapter.getItem(position) as? RecentMangaItem) ?: return false
val date = presenter.dateFormat.format(item.chapter.date_fetch)
val date = presenter.dateFormat.format(item.chapter.dateRead ?: item.chapter.date_fetch)
val invertDefault = !adapter.collapseGroupedUpdates
return presenter.expandedSectionsMap["${item.mch.manga} - $date"]?.xor(invertDefault)
?: invertDefault
@ -660,7 +660,7 @@ class RecentsController(bundle: Bundle? = null) :
override fun updateExpandedExtraChapters(position: Int, expanded: Boolean) {
val item = (adapter.getItem(position) as? RecentMangaItem) ?: return
val date = presenter.dateFormat.format(item.chapter.date_fetch)
val date = presenter.dateFormat.format(item.chapter.dateRead ?: item.chapter.date_fetch)
val invertDefault = !adapter.collapseGroupedUpdates
presenter.expandedSectionsMap["${item.mch.manga} - $date"] = expanded.xor(invertDefault)
}
@ -727,6 +727,16 @@ class RecentsController(bundle: Bundle? = null) :
}
}
override fun onItemLongClick(position: Int, chapter: Chapter): Boolean {
val history = chapter.history ?: return false
val item = adapter.getItem(position) as? RecentMangaItem ?: return false
val manga = item.mch.manga
if (history.id != null) {
RemoveHistoryDialog(this, manga, history, chapter).showDialog(router)
}
return history.id != null
}
override fun removeHistory(manga: Manga, history: History, all: Boolean) {
if (all) {
// Reset last read of chapter to 0L

View file

@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.HistoryImpl
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaChapter
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadService
@ -19,6 +18,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
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.ChapterFilter.Companion.filterChaptersByScanlators
import eu.kanade.tachiyomi.util.chapter.ChapterSort
import eu.kanade.tachiyomi.util.system.executeOnIO
import eu.kanade.tachiyomi.util.system.launchIO
@ -60,12 +60,9 @@ class RecentsPresenter(
}
private val newAdditionsHeader = RecentMangaHeaderItem(RecentMangaHeaderItem.NEWLY_ADDED)
private val newChaptersHeader = RecentMangaHeaderItem(RecentMangaHeaderItem.NEW_CHAPTERS)
private val continueReadingHeader = RecentMangaHeaderItem(
RecentMangaHeaderItem
.CONTINUE_READING,
)
private val continueReadingHeader =
RecentMangaHeaderItem(RecentMangaHeaderItem.CONTINUE_READING)
var finished = false
var heldItems: HashMap<Int, List<RecentMangaItem>> = hashMapOf()
private var shouldMoveToTop = false
var viewType: Int = preferences.recentsViewType().get()
private set
@ -173,19 +170,40 @@ class RecentsPresenter(
).executeOnIO()
}
viewType == VIEW_TYPE_ONLY_HISTORY -> {
val items = db.getHistoryUngrouped(
query,
if (isCustom) ENDLESS_LIMIT else pageOffset,
!updatePageCount && !isOnFirstPage,
)
if (groupChaptersHistory) {
db.getRecentMangaLimit(
query,
if (isCustom) ENDLESS_LIMIT else pageOffset,
!updatePageCount && !isOnFirstPage,
)
items.executeOnIO().groupBy {
val date = it.history.last_read
it.manga.id to if (date <= 0L) "-1" else dateFormat.format(Date(date))
}
.mapNotNull { (key, mchs) ->
val manga = mchs.first().manga
val chapters = mchs.map { mch ->
mch.chapter.also { it.history = mch.history }
}.filterChaptersByScanlators(manga)
if (chapters.isEmpty()) return@mapNotNull null
val existingItem = recentItems.find {
val date = Date(it.mch.history.last_read)
key == it.manga_id to dateFormat.format(date)
}?.takeIf { updatePageCount }
val sort = Comparator<Chapter> { c1, c2 ->
c2.dateRead!!.compareTo(c1.dateRead!!)
}
val (sortedChapters, firstChapter, subCount) =
setupExtraChapters(existingItem, chapters, sort)
?: return@mapNotNull null
extraCount += subCount
mchs.find { firstChapter.id == it.chapter.id }?.also {
it.extraChapters = sortedChapters
}
}
} else {
db.getHistoryUngrouped(
query,
if (isCustom) ENDLESS_LIMIT else pageOffset,
!updatePageCount && !isOnFirstPage,
)
}.executeOnIO()
items.executeOnIO()
}
}
viewType == VIEW_TYPE_ONLY_UPDATES -> {
db.getRecentChapters(
@ -196,49 +214,21 @@ class RecentsPresenter(
val date = it.chapter.date_fetch
it.manga.id to if (date <= 0L) "-1" else dateFormat.format(Date(date))
}
.mapNotNull { entry ->
val manga = entry.value.first().manga
val chapters = chapterFilter.filterChaptersByScanlators(
entry.value.map(MangaChapter::chapter),
manga,
)
if (chapters.isEmpty()) { return@mapNotNull null }
val firstChapter: Chapter
var sortedChapters: MutableList<Chapter>
.mapNotNull { (key, mcs) ->
val manga = mcs.first().manga
val chapters = mcs.map { it.chapter }.filterChaptersByScanlators(manga)
if (chapters.isEmpty()) return@mapNotNull null
val existingItem = recentItems.find {
val date = Date(it.chapter.date_fetch)
entry.key == it.manga_id to dateFormat.format(date)
key == it.manga_id to dateFormat.format(date)
}?.takeIf { updatePageCount }
if (existingItem != null) {
extraCount += chapters.size
val newChapters = existingItem.mch.extraChapters + chapters
val sort: Comparator<Chapter> =
ChapterSort(manga, chapterFilter, preferences)
.sortComparator(true)
sortedChapters = newChapters.sortedWith(sort).toMutableList()
sortedChapters = (
sortedChapters.filter { !it.read } +
sortedChapters.filter { it.read }.reversed()
).toMutableList()
existingItem.mch.extraChapters = sortedChapters
return@mapNotNull null
}
if (chapters.size == 1) {
firstChapter = chapters.first()
sortedChapters = mutableListOf()
} else {
val sort: Comparator<Chapter> =
ChapterSort(manga, chapterFilter, preferences)
.sortComparator(true)
sortedChapters = chapters.sortedWith(sort).toMutableList()
firstChapter =
sortedChapters.firstOrNull { !it.read } ?: sortedChapters.last()
sortedChapters.remove(firstChapter)
sortedChapters = (
sortedChapters.filter { !it.read } +
sortedChapters.filter { it.read }.reversed()
).toMutableList()
}
val (sortedChapters, firstChapter, subCount) =
setupExtraChapters(existingItem, chapters, sort)
?: return@mapNotNull null
extraCount += subCount
MangaChapterHistory(
manga,
firstChapter,
@ -281,8 +271,15 @@ class RecentsPresenter(
it.chapter
}
(it.chapter.read && viewType != VIEW_TYPE_ONLY_UPDATES) || it.chapter.id == null -> {
getNextChapter(it.manga)
val nextChapter = getNextChapter(it.manga)
?: if (showRead && it.chapter.id != null) it.chapter else null
if (viewType == VIEW_TYPE_ONLY_HISTORY && nextChapter?.id != null &&
nextChapter.id != it.chapter.id
) {
nextChapter.dateRead = it.chapter.dateRead
it.extraChapters = listOf(it.chapter) + it.extraChapters
}
nextChapter
}
it.history.id == null && viewType != VIEW_TYPE_ONLY_UPDATES -> {
getFirstUpdatedChapter(it.manga, it.chapter)
@ -369,8 +366,6 @@ class RecentsPresenter(
} else {
recentItems + newItems
}
} else {
heldItems[customViewType] = newItems
}
val newCount = itemCount + newItems.size + newItems.sumOf { it.mch.extraChapters.size } + extraCount
val hasNewItems = newItems.isNotEmpty()
@ -393,6 +388,45 @@ class RecentsPresenter(
}
}
private fun setupExtraChapters(
existingItem: RecentMangaItem?,
chapters: List<Chapter>,
sort: Comparator<Chapter>,
): Triple<MutableList<Chapter>, Chapter, Int>? {
var extraCount = 0
val firstChapter: Chapter
var sortedChapters: MutableList<Chapter>
val reverseRead = viewType != VIEW_TYPE_ONLY_HISTORY
if (existingItem != null) {
extraCount += chapters.size
val newChapters = existingItem.mch.extraChapters + chapters
sortedChapters = newChapters.sortedWith(sort).toMutableList()
sortedChapters = (
sortedChapters.filter { !it.read } +
sortedChapters.filter { it.read }
.run { if (reverseRead) reversed() else this }
).toMutableList()
existingItem.mch.extraChapters = sortedChapters
return null
}
if (chapters.size == 1) {
firstChapter = chapters.first()
sortedChapters = mutableListOf()
} else {
sortedChapters = chapters.sortedWith(sort).toMutableList()
firstChapter = sortedChapters.firstOrNull { !it.read }
?: sortedChapters.run { if (reverseRead) last() else first() }
sortedChapters.last()
sortedChapters.remove(firstChapter)
sortedChapters = (
sortedChapters.filter { !it.read } +
sortedChapters.filter { it.read }
.run { if (reverseRead) reversed() else this }
).toMutableList()
}
return Triple(sortedChapters, firstChapter, extraCount)
}
private fun getNextChapter(manga: Manga): Chapter? {
val chapters = db.getChapters(manga).executeAsBlocking()
return ChapterSort(manga, chapterFilter, preferences).getNextUnreadChapter(chapters, false)
@ -564,7 +598,6 @@ class RecentsPresenter(
/**
* Mark the selected chapter list as read/unread.
* @param selectedChapters the list of selected chapters.
* @param read whether to mark chapters as read or unread.
*/
fun markChapterRead(

View file

@ -19,7 +19,7 @@ class ChapterFilter(val preferences: PreferencesHelper = Injekt.get(), val downl
val notBookmarkEnabled = manga.bookmarkedFilter(preferences) == Manga.CHAPTER_SHOW_NOT_BOOKMARKED
// if none of the filters are enabled skip the filtering of them
val filteredChapters = filterChaptersByScanlators(chapters, manga)
val filteredChapters = chapters.filterChaptersByScanlators(manga)
return if (readEnabled || unreadEnabled || downloadEnabled || notDownloadEnabled || bookmarkEnabled || notBookmarkEnabled) {
filteredChapters.filter {
if (readEnabled && it.read.not() ||
@ -40,7 +40,7 @@ class ChapterFilter(val preferences: PreferencesHelper = Injekt.get(), val downl
/** filter chapters for the reader */
fun <T : Chapter> filterChaptersForReader(chapters: List<T>, manga: Manga, selectedChapter: T? = null): List<T> {
var filteredChapters = filterChaptersByScanlators(chapters, manga)
var filteredChapters = chapters.filterChaptersByScanlators(manga)
// if filter prefs aren't enabled don't even filter
if (!preferences.skipRead() && !preferences.skipFiltered() && !preferences.skipDupe().get()) {
return filteredChapters
@ -82,11 +82,13 @@ class ChapterFilter(val preferences: PreferencesHelper = Injekt.get(), val downl
return filteredChapters
}
companion object {
/** filters chapters for scanlators */
fun <T : Chapter> filterChaptersByScanlators(chapters: List<T>, manga: Manga): List<T> {
fun <T : Chapter> List<T>.filterChaptersByScanlators(manga: Manga): List<T> {
return manga.filtered_scanlators?.let { filteredScanlatorString ->
val filteredScanlators = ChapterUtil.getScanlators(filteredScanlatorString)
chapters.filter { ChapterUtil.getScanlators(it.scanlator).none { group -> filteredScanlators.contains(group) } }
} ?: chapters
filter { ChapterUtil.getScanlators(it.scanlator).none { group -> filteredScanlators.contains(group) } }
} ?: this
}
}
}

View file

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.util.chapter
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.chapter.ChapterFilter.Companion.filterChaptersByScanlators
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -31,7 +32,7 @@ class ChapterSort(val manga: Manga, val chapterFilter: ChapterFilter = Injekt.ge
fun <T : Chapter> getNextUnreadChapter(rawChapters: List<T>, andFiltered: Boolean = true): T? {
val chapters = when {
andFiltered -> chapterFilter.filterChapters(rawChapters, manga)
else -> chapterFilter.filterChaptersByScanlators(rawChapters, manga)
else -> rawChapters.filterChaptersByScanlators(manga)
}
return chapters.sortedWith(sortComparator(true)).find { !it.read }

View file

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.chapter.ChapterFilter.Companion.filterChaptersByScanlators
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Date
@ -32,7 +33,6 @@ fun syncChaptersWithSource(
}
val downloadManager: DownloadManager = Injekt.get()
val chapterFilter: ChapterFilter = Injekt.get()
// Chapters from db.
val dbChapters = db.getChapters(manga).executeAsBlocking()
@ -169,9 +169,10 @@ fun syncChaptersWithSource(
}
db.updateLastUpdated(manga).executeAsBlocking()
}
val reAddedSet = readded.toSet()
return Pair(
chapterFilter.filterChaptersByScanlators(toAdd.subtract(readded).toList(), manga),
toDelete - readded,
toAdd.subtract(reAddedSet).toList().filterChaptersByScanlators(manga),
toDelete - reAddedSet,
)
}

View file

@ -178,18 +178,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/subtitle">
<ImageButton
android:id="@+id/remove_history"
android:layout_width="45dp"
android:layout_height="match_parent"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/reset_chapter_history"
android:padding="8dp"
android:tooltipText="@string/reset_chapter_history"
app:layout_constraintHorizontal_chainStyle="spread"
app:srcCompat="@drawable/ic_eye_remove_outline_24dp"
app:tint="@color/holo_red" />
<ImageButton
android:id="@+id/show_more_chapters"
android:layout_width="45dp"
@ -205,6 +193,17 @@
app:tint="?attr/colorSecondary"
app:srcCompat="@drawable/ic_expand_more_24dp" />
<ImageButton
android:id="@+id/remove_history"
android:layout_width="45dp"
android:layout_height="match_parent"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/reset_chapter_history"
android:padding="8dp"
android:tooltipText="@string/reset_chapter_history"
app:layout_constraintHorizontal_chainStyle="spread"
app:srcCompat="@drawable/ic_eye_remove_outline_24dp"
app:tint="@color/holo_red" />
<include
android:id="@+id/download_button"
layout="@layout/download_button"