mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Add "Play" button on manga in library (#8218)
* resume manga button in libarary * work on resume button * Backup * work on opening the last read chapter * backup * renaming * fab instead of image * done with logic * cleanup * cleanup * import cleanup * cleanup... * refactoring * fixing logic * fixing scopes * Reworking design * adding ability to turn on/off the feature * cleanup * refactoring, fixing logic, adding filter logic (partial) * backup * backup * logic done * backup before merge fix * merge conflict.... * merge conflict... * reworking ui logic * removing unnecessary file * refactoring * refactoring * review changes + minor parameter position movement * commiting suggestion Co-authored-by: arkon <arkon@users.noreply.github.com> * fixing minor mistake * moving ChapterFilter.kt Co-authored-by: arkon <arkon@users.noreply.github.com>
This commit is contained in:
		| @@ -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<Chapter>.applyFilters(manga: Manga, downloadManager: DownloadManager): List<Chapter> { | ||||
|     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<ChapterItem>.applyFilters(manga: Manga): Sequence<ChapterItem> { | ||||
|     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) } | ||||
| } | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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, | ||||
|         ) | ||||
|   | ||||
| @@ -18,11 +18,13 @@ fun LibraryComfortableGrid( | ||||
|     showUnreadBadges: Boolean, | ||||
|     showLocalBadges: Boolean, | ||||
|     showLanguageBadges: Boolean, | ||||
|     showContinueReadingButton: Boolean, | ||||
|     columns: Int, | ||||
|     contentPadding: PaddingValues, | ||||
|     selection: List<LibraryManga>, | ||||
|     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) }, | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -19,11 +19,13 @@ fun LibraryCompactGrid( | ||||
|     showUnreadBadges: Boolean, | ||||
|     showLocalBadges: Boolean, | ||||
|     showLanguageBadges: Boolean, | ||||
|     showContinueReadingButton: Boolean, | ||||
|     columns: Int, | ||||
|     contentPadding: PaddingValues, | ||||
|     selection: List<LibraryManga>, | ||||
|     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) }, | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -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, | ||||
|             ) | ||||
|   | ||||
| @@ -27,10 +27,12 @@ fun LibraryList( | ||||
|     showUnreadBadges: Boolean, | ||||
|     showLocalBadges: Boolean, | ||||
|     showLanguageBadges: Boolean, | ||||
|     showContinueReadingButton: Boolean, | ||||
|     contentPadding: PaddingValues, | ||||
|     selection: List<LibraryManga>, | ||||
|     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) }, | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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<Long, List<LibraryItem>> | ||||
| 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. | ||||
|      * | ||||
|   | ||||
| @@ -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?) : | ||||
|   | ||||
| @@ -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<DomainChapter> { | ||||
| @@ -1092,40 +1086,6 @@ sealed class MangaScreenState { | ||||
|  | ||||
|         val processedChapters: Sequence<ChapterItem> | ||||
|             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<ChapterItem>.applyFilters(manga: DomainManga): Sequence<ChapterItem> { | ||||
|             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) } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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<Chapter>.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<ChapterItem>.getNextUnread(manga: Manga): Chapter? { | ||||
|     return applyFilters(manga).let { chapters -> | ||||
|         if (manga.sortDescending()) { | ||||
|             chapters.findLast { !it.chapter.read } | ||||
|         } else { | ||||
|             chapters.find { !it.chapter.read } | ||||
|         } | ||||
|     }?.chapter | ||||
| } | ||||
		Reference in New Issue
	
	Block a user