diff --git a/app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt b/app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt new file mode 100644 index 0000000000..753beea5d4 --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt @@ -0,0 +1,82 @@ +package eu.kanade.domain.chapter.model + +import eu.kanade.domain.manga.model.Manga +import eu.kanade.domain.manga.model.TriStateFilter +import eu.kanade.domain.manga.model.isLocal +import eu.kanade.tachiyomi.data.download.DownloadManager +import eu.kanade.tachiyomi.data.download.model.Download +import eu.kanade.tachiyomi.ui.manga.ChapterItem +import eu.kanade.tachiyomi.util.chapter.getChapterSort + +/** + * Applies the view filters to the list of chapters obtained from the database. + * @return an observable of the list of chapters filtered and sorted. + */ +fun List.applyFilters(manga: Manga, downloadManager: DownloadManager): List { + val isLocalManga = manga.isLocal() + val unreadFilter = manga.unreadFilter + val downloadedFilter = manga.downloadedFilter + val bookmarkedFilter = manga.bookmarkedFilter + + return filter { chapter -> + when (unreadFilter) { + TriStateFilter.DISABLED -> true + TriStateFilter.ENABLED_IS -> !chapter.read + TriStateFilter.ENABLED_NOT -> chapter.read + } + } + .filter { chapter -> + when (bookmarkedFilter) { + TriStateFilter.DISABLED -> true + TriStateFilter.ENABLED_IS -> chapter.bookmark + TriStateFilter.ENABLED_NOT -> !chapter.bookmark + } + } + .filter { chapter -> + val downloaded = downloadManager.isChapterDownloaded(chapter.name, chapter.scanlator, manga.title, manga.source) + val downloadState = when { + downloaded -> Download.State.DOWNLOADED + else -> Download.State.NOT_DOWNLOADED + } + when (downloadedFilter) { + TriStateFilter.DISABLED -> true + TriStateFilter.ENABLED_IS -> downloadState == Download.State.DOWNLOADED || isLocalManga + TriStateFilter.ENABLED_NOT -> downloadState != Download.State.DOWNLOADED && !isLocalManga + } + } + .sortedWith(getChapterSort(manga)) +} + +/** + * Applies the view filters to the list of chapters obtained from the database. + * @return an observable of the list of chapters filtered and sorted. + */ +fun List.applyFilters(manga: Manga): Sequence { + val isLocalManga = manga.isLocal() + val unreadFilter = manga.unreadFilter + val downloadedFilter = manga.downloadedFilter + val bookmarkedFilter = manga.bookmarkedFilter + return asSequence() + .filter { (chapter) -> + when (unreadFilter) { + TriStateFilter.DISABLED -> true + TriStateFilter.ENABLED_IS -> !chapter.read + TriStateFilter.ENABLED_NOT -> chapter.read + } + } + .filter { (chapter) -> + when (bookmarkedFilter) { + TriStateFilter.DISABLED -> true + TriStateFilter.ENABLED_IS -> chapter.bookmark + TriStateFilter.ENABLED_NOT -> !chapter.bookmark + } + } + .filter { + when (downloadedFilter) { + TriStateFilter.DISABLED -> true + TriStateFilter.ENABLED_IS -> it.isDownloaded || isLocalManga + TriStateFilter.ENABLED_NOT -> !it.isDownloaded && !isLocalManga + } + } + .sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) } +} diff --git a/app/src/main/java/eu/kanade/domain/library/service/LibraryPreferences.kt b/app/src/main/java/eu/kanade/domain/library/service/LibraryPreferences.kt index 18a8aac070..d78d980fca 100644 --- a/app/src/main/java/eu/kanade/domain/library/service/LibraryPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/library/service/LibraryPreferences.kt @@ -32,6 +32,8 @@ class LibraryPreferences( fun autoUpdateTrackers() = preferenceStore.getBoolean("auto_update_trackers", false) + fun showContinueReadingButton() = preferenceStore.getBoolean("display_continue_reading_button", false) + // region Filter fun filterDownloaded() = preferenceStore.getInt("pref_filter_library_downloaded", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value) diff --git a/app/src/main/java/eu/kanade/presentation/components/CommonMangaItem.kt b/app/src/main/java/eu/kanade/presentation/components/CommonMangaItem.kt index 27f2480fd0..f18d166f51 100644 --- a/app/src/main/java/eu/kanade/presentation/components/CommonMangaItem.kt +++ b/app/src/main/java/eu/kanade/presentation/components/CommonMangaItem.kt @@ -12,10 +12,17 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment @@ -41,6 +48,7 @@ object CommonMangaItemDefaults { const val BrowseFavoriteCoverAlpha = 0.34f } +private val ContinueReadingButtonSize = 38.dp private const val GridSelectedCoverAlpha = 0.76f /** @@ -55,8 +63,10 @@ fun MangaCompactGridItem( coverAlpha: Float = 1f, coverBadgeStart: (@Composable RowScope.() -> Unit)? = null, coverBadgeEnd: (@Composable RowScope.() -> Unit)? = null, + showContinueReadingButton: Boolean = false, onLongClick: () -> Unit, onClick: () -> Unit, + onClickContinueReading: (() -> Unit)? = null, ) { GridItemSelectable( isSelected = isSelected, @@ -76,7 +86,12 @@ fun MangaCompactGridItem( badgesEnd = coverBadgeEnd, content = { if (title != null) { - CoverTextOverlay(title = title) + CoverTextOverlay(title = title, showContinueReadingButton) + } + }, + continueReadingButton = { + if (showContinueReadingButton && onClickContinueReading != null) { + ContinueReadingButton(onClickContinueReading) } }, ) @@ -87,7 +102,10 @@ fun MangaCompactGridItem( * Title overlay for [MangaCompactGridItem] */ @Composable -private fun BoxScope.CoverTextOverlay(title: String) { +private fun BoxScope.CoverTextOverlay( + title: String, + showContinueReadingButton: Boolean = false, +) { Box( modifier = Modifier .clip(RoundedCornerShape(bottomStart = 4.dp, bottomEnd = 4.dp)) @@ -101,9 +119,10 @@ private fun BoxScope.CoverTextOverlay(title: String) { .fillMaxWidth() .align(Alignment.BottomCenter), ) + val endPadding = if (showContinueReadingButton) ContinueReadingButtonSize else 8.dp GridItemTitle( modifier = Modifier - .padding(8.dp) + .padding(start = 8.dp, top = 8.dp, end = endPadding, bottom = 8.dp) .align(Alignment.BottomStart), title = title, style = MaterialTheme.typography.titleSmall.copy( @@ -127,8 +146,10 @@ fun MangaComfortableGridItem( coverAlpha: Float = 1f, coverBadgeStart: (@Composable RowScope.() -> Unit)? = null, coverBadgeEnd: (@Composable RowScope.() -> Unit)? = null, + showContinueReadingButton: Boolean = false, onLongClick: () -> Unit, onClick: () -> Unit, + onClickContinueReading: (() -> Unit)? = null, ) { GridItemSelectable( isSelected = isSelected, @@ -147,6 +168,11 @@ fun MangaComfortableGridItem( }, badgesStart = coverBadgeStart, badgesEnd = coverBadgeEnd, + continueReadingButton = { + if (showContinueReadingButton && onClickContinueReading != null) { + ContinueReadingButton(onClickContinueReading) + } + }, ) GridItemTitle( modifier = Modifier.padding(4.dp), @@ -166,6 +192,7 @@ private fun MangaGridCover( cover: @Composable BoxScope.() -> Unit = {}, badgesStart: (@Composable RowScope.() -> Unit)? = null, badgesEnd: (@Composable RowScope.() -> Unit)? = null, + continueReadingButton: (@Composable BoxScope.() -> Unit)? = null, content: @Composable (BoxScope.() -> Unit)? = null, ) { Box( @@ -192,6 +219,7 @@ private fun MangaGridCover( content = badgesEnd, ) } + continueReadingButton?.invoke(this) } } @@ -283,8 +311,10 @@ fun MangaListItem( coverData: eu.kanade.domain.manga.model.MangaCover, coverAlpha: Float = 1f, badge: @Composable RowScope.() -> Unit, + showContinueReadingButton: Boolean = false, onLongClick: () -> Unit, onClick: () -> Unit, + onClickContinueReading: (() -> Unit)? = null, ) { Row( modifier = Modifier @@ -313,5 +343,37 @@ fun MangaListItem( style = MaterialTheme.typography.bodyMedium, ) BadgeGroup(content = badge) + if (showContinueReadingButton && onClickContinueReading != null) { + Box { + ContinueReadingButton(onClickContinueReading) + } + } + } +} + +@Composable +private fun BoxScope.ContinueReadingButton( + onClickContinueReading: () -> Unit, +) { + FilledIconButton( + onClick = { + onClickContinueReading() + }, + modifier = Modifier + .size(ContinueReadingButtonSize) + .padding(3.dp) + .align(Alignment.BottomEnd), + shape = MaterialTheme.shapes.small, + colors = IconButtonDefaults.filledIconButtonColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + contentColor = contentColorFor(MaterialTheme.colorScheme.primaryContainer), + ), + ) { + Icon( + imageVector = Icons.Filled.PlayArrow, + contentDescription = "", + modifier = Modifier + .size(15.dp), + ) } } diff --git a/app/src/main/java/eu/kanade/presentation/library/LibraryScreen.kt b/app/src/main/java/eu/kanade/presentation/library/LibraryScreen.kt index 1fc4799943..84bc2ca695 100644 --- a/app/src/main/java/eu/kanade/presentation/library/LibraryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/library/LibraryScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.util.fastAll import eu.kanade.domain.category.model.Category +import eu.kanade.domain.library.model.LibraryManga import eu.kanade.domain.library.model.display import eu.kanade.domain.manga.model.isLocal import eu.kanade.presentation.components.EmptyScreen @@ -29,6 +30,7 @@ import eu.kanade.tachiyomi.widget.TachiyomiBottomNavigationView fun LibraryScreen( presenter: LibraryPresenter, onMangaClicked: (Long) -> Unit, + onContinueReadingClicked: (LibraryManga) -> Unit, onGlobalSearchClicked: () -> Unit, onChangeCategoryClicked: () -> Unit, onMarkAsReadClicked: () -> Unit, @@ -104,6 +106,7 @@ fun LibraryScreen( showMangaCount = presenter.mangaCountVisibility, onChangeCurrentPage = { presenter.activeCategory = it }, onMangaClicked = onMangaClicked, + onContinueReadingClicked = onContinueReadingClicked, onToggleSelection = { presenter.toggleSelection(it) }, onToggleRangeSelection = { presenter.toggleRangeSelection(it) @@ -119,6 +122,7 @@ fun LibraryScreen( showUnreadBadges = presenter.showUnreadBadges, showLocalBadges = presenter.showLocalBadges, showLanguageBadges = presenter.showLanguageBadges, + showContinueReadingButton = presenter.showContinueReadingButton, isIncognitoMode = presenter.isIncognitoMode, isDownloadOnly = presenter.isDownloadOnly, ) diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt index bfbf2373f8..beb9ba51ab 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt @@ -18,11 +18,13 @@ fun LibraryComfortableGrid( showUnreadBadges: Boolean, showLocalBadges: Boolean, showLanguageBadges: Boolean, + showContinueReadingButton: Boolean, columns: Int, contentPadding: PaddingValues, selection: List, onClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit, + onClickContinueReading: (LibraryManga) -> Unit, searchQuery: String?, onGlobalSearchClicked: () -> Unit, ) { @@ -65,8 +67,10 @@ fun LibraryComfortableGrid( item = libraryItem, ) }, + showContinueReadingButton = showContinueReadingButton, onLongClick = { onLongClick(libraryItem.libraryManga) }, onClick = { onClick(libraryItem.libraryManga) }, + onClickContinueReading = { onClickContinueReading(libraryItem.libraryManga) }, ) } } diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryCompactGrid.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryCompactGrid.kt index 6291bee285..6dd62fda37 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryCompactGrid.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryCompactGrid.kt @@ -19,11 +19,13 @@ fun LibraryCompactGrid( showUnreadBadges: Boolean, showLocalBadges: Boolean, showLanguageBadges: Boolean, + showContinueReadingButton: Boolean, columns: Int, contentPadding: PaddingValues, selection: List, onClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit, + onClickContinueReading: (LibraryManga) -> Unit, searchQuery: String?, onGlobalSearchClicked: () -> Unit, ) { @@ -66,8 +68,10 @@ fun LibraryCompactGrid( item = libraryItem, ) }, + showContinueReadingButton = showContinueReadingButton, onLongClick = { onLongClick(libraryItem.libraryManga) }, onClick = { onClick(libraryItem.libraryManga) }, + onClickContinueReading = { onClickContinueReading(libraryItem.libraryManga) }, ) } } diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt index b36186df07..b12774ce96 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt @@ -37,6 +37,7 @@ fun LibraryContent( showMangaCount: Boolean, onChangeCurrentPage: (Int) -> Unit, onMangaClicked: (Long) -> Unit, + onContinueReadingClicked: (LibraryManga) -> Unit, onToggleSelection: (LibraryManga) -> Unit, onToggleRangeSelection: (LibraryManga) -> Unit, onRefresh: (Category?) -> Boolean, @@ -49,6 +50,7 @@ fun LibraryContent( showUnreadBadges: Boolean, showLocalBadges: Boolean, showLanguageBadges: Boolean, + showContinueReadingButton: Boolean, isDownloadOnly: Boolean, isIncognitoMode: Boolean, ) { @@ -88,6 +90,9 @@ fun LibraryContent( val onLongClickManga = { manga: LibraryManga -> onToggleRangeSelection(manga) } + val onClickContinueReading = { manga: LibraryManga -> + onContinueReadingClicked(manga) + } SwipeRefresh( refreshing = isRefreshing, @@ -115,8 +120,10 @@ fun LibraryContent( showUnreadBadges = showUnreadBadges, showLocalBadges = showLocalBadges, showLanguageBadges = showLanguageBadges, + showContinueReadingButton = showContinueReadingButton, onClickManga = onClickManga, onLongClickManga = onLongClickManga, + onClickContinueReading = onClickContinueReading, onGlobalSearchClicked = onGlobalSearchClicked, searchQuery = state.searchQuery, ) diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt index 97ec5b4129..d4f04d453e 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryList.kt @@ -27,10 +27,12 @@ fun LibraryList( showUnreadBadges: Boolean, showLocalBadges: Boolean, showLanguageBadges: Boolean, + showContinueReadingButton: Boolean, contentPadding: PaddingValues, selection: List, onClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit, + onClickContinueReading: (LibraryManga) -> Unit, searchQuery: String?, onGlobalSearchClicked: () -> Unit, ) { @@ -72,8 +74,10 @@ fun LibraryList( UnreadBadge(enabled = showUnreadBadges, item = libraryItem) LanguageBadge(showLanguage = showLanguageBadges, showLocal = showLocalBadges, item = libraryItem) }, + showContinueReadingButton = showContinueReadingButton, onLongClick = { onLongClick(libraryItem.libraryManga) }, onClick = { onClick(libraryItem.libraryManga) }, + onClickContinueReading = { onClickContinueReading(libraryItem.libraryManga) }, ) } } diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt index 292ca462a6..40618a6893 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt @@ -32,8 +32,10 @@ fun LibraryPager( showUnreadBadges: Boolean, showLocalBadges: Boolean, showLanguageBadges: Boolean, + showContinueReadingButton: Boolean, onClickManga: (LibraryManga) -> Unit, onLongClickManga: (LibraryManga) -> Unit, + onClickContinueReading: (LibraryManga) -> Unit, ) { HorizontalPager( count = pageCount, @@ -64,10 +66,12 @@ fun LibraryPager( showUnreadBadges = showUnreadBadges, showLocalBadges = showLocalBadges, showLanguageBadges = showLanguageBadges, + showContinueReadingButton = showContinueReadingButton, contentPadding = contentPadding, selection = selectedManga, onClick = onClickManga, onLongClick = onLongClickManga, + onClickContinueReading = onClickContinueReading, searchQuery = searchQuery, onGlobalSearchClicked = onGlobalSearchClicked, ) @@ -80,11 +84,13 @@ fun LibraryPager( showUnreadBadges = showUnreadBadges, showLocalBadges = showLocalBadges, showLanguageBadges = showLanguageBadges, + showContinueReadingButton = showContinueReadingButton, columns = columns, contentPadding = contentPadding, selection = selectedManga, onClick = onClickManga, onLongClick = onLongClickManga, + onClickContinueReading = onClickContinueReading, searchQuery = searchQuery, onGlobalSearchClicked = onGlobalSearchClicked, ) @@ -96,10 +102,12 @@ fun LibraryPager( showUnreadBadges = showUnreadBadges, showLocalBadges = showLocalBadges, showLanguageBadges = showLanguageBadges, + showContinueReadingButton = showContinueReadingButton, columns = columns, contentPadding = contentPadding, selection = selectedManga, onClick = onClickManga, + onClickContinueReading = onClickContinueReading, onLongClick = onLongClickManga, searchQuery = searchQuery, onGlobalSearchClicked = onGlobalSearchClicked, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index b30b87c140..6f0d19b128 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -9,6 +9,8 @@ import androidx.compose.ui.platform.LocalContext import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import eu.kanade.core.prefs.CheckboxState +import eu.kanade.domain.chapter.model.Chapter +import eu.kanade.domain.library.model.LibraryManga import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.isLocal import eu.kanade.domain.manga.model.toDbManga @@ -26,6 +28,7 @@ import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController import eu.kanade.tachiyomi.ui.category.CategoryController import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.manga.MangaController +import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.system.toast @@ -50,6 +53,7 @@ class LibraryController( LibraryScreen( presenter = presenter, onMangaClicked = ::openManga, + onContinueReadingClicked = ::continueReading, onGlobalSearchClicked = { router.pushController(GlobalSearchController(presenter.searchQuery)) }, @@ -196,6 +200,19 @@ class LibraryController( router.pushController(MangaController(mangaId)) } + private fun continueReading(libraryManga: LibraryManga) { + viewScope.launchIO { + val chapter = presenter.getNextUnreadChapter(libraryManga.manga) + if (chapter != null) openChapter(chapter) + } + } + + private fun openChapter(chapter: Chapter) { + activity?.run { + startActivity(ReaderActivity.newIntent(this, chapter.mangaId, chapter.id)) + } + } + /** * Clear all of the manga currently selected, and * invalidate the action mode to revert the top toolbar diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index 3022493606..559d875afb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -17,7 +17,9 @@ import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.category.interactor.GetCategories import eu.kanade.domain.category.interactor.SetMangaCategories import eu.kanade.domain.category.model.Category +import eu.kanade.domain.chapter.interactor.GetChapterByMangaId import eu.kanade.domain.chapter.interactor.SetReadStatus +import eu.kanade.domain.chapter.model.Chapter import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.history.interactor.GetNextChapters import eu.kanade.domain.library.model.LibraryManga @@ -44,6 +46,7 @@ import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter +import eu.kanade.tachiyomi.util.chapter.getNextUnread import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchNonCancellable import eu.kanade.tachiyomi.util.lang.withIOContext @@ -76,9 +79,10 @@ typealias LibraryMap = Map> class LibraryPresenter( private val state: LibraryStateImpl = LibraryState() as LibraryStateImpl, private val getLibraryManga: GetLibraryManga = Injekt.get(), - private val getTracksPerManga: GetTracksPerManga = Injekt.get(), private val getCategories: GetCategories = Injekt.get(), + private val getTracksPerManga: GetTracksPerManga = Injekt.get(), private val getNextChapters: GetNextChapters = Injekt.get(), + private val getChaptersByMangaId: GetChapterByMangaId = Injekt.get(), private val setReadStatus: SetReadStatus = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(), private val setMangaCategories: SetMangaCategories = Injekt.get(), @@ -105,6 +109,8 @@ class LibraryPresenter( var activeCategory: Int by libraryPreferences.lastUsedCategory().asState() + val showContinueReadingButton by libraryPreferences.showContinueReadingButton().asState() + val isDownloadOnly: Boolean by preferences.downloadedOnly().asState() val isIncognitoMode: Boolean by preferences.incognitoMode().asState() @@ -389,6 +395,10 @@ class LibraryPresenter( .reduce { set1, set2 -> set1.intersect(set2) } } + suspend fun getNextUnreadChapter(manga: Manga): Chapter? { + return getChaptersByMangaId.await(manga.id).getNextUnread(manga, downloadManager) + } + /** * Returns the mix (non-common) categories for the given list of manga. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt index 1ed4a31fe0..91da562a97 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt @@ -282,12 +282,14 @@ class LibrarySettingsSheet( private val displayGroup: DisplayGroup private val badgeGroup: BadgeGroup private val tabsGroup: TabsGroup + private val otherGroup: OtherGroup init { displayGroup = DisplayGroup() badgeGroup = BadgeGroup() tabsGroup = TabsGroup() - setGroups(listOf(displayGroup, badgeGroup, tabsGroup)) + otherGroup = OtherGroup() + setGroups(listOf(displayGroup, badgeGroup, tabsGroup, otherGroup)) } // Refreshes Display Setting selections @@ -408,6 +410,28 @@ class LibrarySettingsSheet( adapter.notifyItemChanged(item) } } + + inner class OtherGroup : Group { + private val showContinueReadingButton = Item.CheckboxGroup(R.string.action_display_show_continue_reading_button, this) + + override val header = Item.Header(R.string.other_header) + override val items = listOf(showContinueReadingButton) + override val footer = null + + override fun initModels() { + showContinueReadingButton.checked = libraryPreferences.showContinueReadingButton().get() + } + + override fun onItemClicked(item: Item) { + item as Item.CheckboxGroup + item.checked = !item.checked + when (item) { + showContinueReadingButton -> libraryPreferences.showContinueReadingButton().set(item.checked) + else -> {} + } + adapter.notifyItemChanged(item) + } + } } open inner class Settings(context: Context, attrs: AttributeSet?) : diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt index 0f43454bfd..de345ae0af 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt @@ -16,6 +16,7 @@ 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.applyFilters import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.download.service.DownloadPreferences import eu.kanade.domain.library.service.LibraryPreferences @@ -23,8 +24,6 @@ import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga import eu.kanade.domain.manga.interactor.GetMangaWithChapters import eu.kanade.domain.manga.interactor.SetMangaChapterFlags import eu.kanade.domain.manga.interactor.UpdateManga -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.track.interactor.DeleteTrack import eu.kanade.domain.track.interactor.GetTracks @@ -45,6 +44,7 @@ import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.tachiyomi.util.chapter.getChapterSort +import eu.kanade.tachiyomi.util.chapter.getNextUnread import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchNonCancellable import eu.kanade.tachiyomi.util.lang.toRelativeString @@ -579,13 +579,7 @@ class MangaPresenter( */ fun getNextUnreadChapter(): DomainChapter? { val successState = successState ?: return null - return successState.processedChapters.map { it.chapter }.let { chapters -> - if (successState.manga.sortDescending()) { - chapters.findLast { !it.read } - } else { - chapters.find { !it.read } - } - } + return successState.chapters.getNextUnread(successState.manga) } fun getUnreadChapters(): List { @@ -1092,40 +1086,6 @@ sealed class MangaScreenState { val processedChapters: Sequence get() = chapters.applyFilters(manga) - - /** - * Applies the view filters to the list of chapters obtained from the database. - * @return an observable of the list of chapters filtered and sorted. - */ - private fun List.applyFilters(manga: DomainManga): Sequence { - val isLocalManga = manga.isLocal() - val unreadFilter = manga.unreadFilter - val downloadedFilter = manga.downloadedFilter - val bookmarkedFilter = manga.bookmarkedFilter - return asSequence() - .filter { (chapter) -> - when (unreadFilter) { - TriStateFilter.DISABLED -> true - TriStateFilter.ENABLED_IS -> !chapter.read - TriStateFilter.ENABLED_NOT -> chapter.read - } - } - .filter { (chapter) -> - when (bookmarkedFilter) { - TriStateFilter.DISABLED -> true - TriStateFilter.ENABLED_IS -> chapter.bookmark - TriStateFilter.ENABLED_NOT -> !chapter.bookmark - } - } - .filter { - when (downloadedFilter) { - TriStateFilter.DISABLED -> true - TriStateFilter.ENABLED_IS -> it.isDownloaded || isLocalManga - TriStateFilter.ENABLED_NOT -> !it.isDownloaded && !isLocalManga - } - } - .sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) } - } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterGetNextUnread.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterGetNextUnread.kt new file mode 100644 index 0000000000..296c7e6d43 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterGetNextUnread.kt @@ -0,0 +1,33 @@ +package eu.kanade.tachiyomi.util.chapter + +import eu.kanade.domain.chapter.model.Chapter +import eu.kanade.domain.chapter.model.applyFilters +import eu.kanade.domain.manga.model.Manga +import eu.kanade.tachiyomi.data.download.DownloadManager +import eu.kanade.tachiyomi.ui.manga.ChapterItem + +/** + * Gets next unread chapter with filters and sorting applied + */ +fun List.getNextUnread(manga: Manga, downloadManager: DownloadManager): Chapter? { + return applyFilters(manga, downloadManager).let { chapters -> + if (manga.sortDescending()) { + chapters.findLast { !it.read } + } else { + chapters.find { !it.read } + } + } +} + +/** + * Gets next unread chapter with filters and sorting applied + */ +fun List.getNextUnread(manga: Manga): Chapter? { + return applyFilters(manga).let { chapters -> + if (manga.sortDescending()) { + chapters.findLast { !it.chapter.read } + } else { + chapters.find { !it.chapter.read } + } + }?.chapter +} diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 67d5a8dfd3..c878d71087 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -108,6 +108,7 @@ Language Show category tabs Show number of items + Show continue reading button Disable Pin Unpin @@ -579,6 +580,7 @@ Downloaded chapters Badges Tabs + Other