Option to show only downloaded chapters

master
Koitharu 9 months ago
parent d8efe374a8
commit d641e7933d
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -21,7 +21,6 @@ import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.flow.transformWhile import kotlinx.coroutines.flow.transformWhile
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.withTimeout
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.parsers.util.suspendlazy.SuspendLazy import org.koitharu.kotatsu.parsers.util.suspendlazy.SuspendLazy
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -135,6 +134,28 @@ fun <T1, T2, T3, T4, T5, T6, R> combine(
) )
} }
@Suppress("UNCHECKED_CAST")
fun <T1, T2, T3, T4, T5, T6, T7, R> combine(
flow: Flow<T1>,
flow2: Flow<T2>,
flow3: Flow<T3>,
flow4: Flow<T4>,
flow5: Flow<T5>,
flow6: Flow<T6>,
flow7: Flow<T7>,
transform: suspend (T1, T2, T3, T4, T5, T6, T7) -> R,
): Flow<R> = combine(flow, flow2, flow3, flow4, flow5, flow6, flow7) { args: Array<*> ->
transform(
args[0] as T1,
args[1] as T2,
args[2] as T3,
args[3] as T4,
args[4] as T5,
args[5] as T6,
args[6] as T7,
)
}
suspend fun <T : Any> Flow<T?>.firstNotNull(): T = checkNotNull(first { x -> x != null }) suspend fun <T : Any> Flow<T?>.firstNotNull(): T = checkNotNull(first { x -> x != null })
suspend fun <T : Any> Flow<T?>.firstNotNullOrNull(): T? = firstOrNull { x -> x != null } suspend fun <T : Any> Flow<T?>.firstNotNullOrNull(): T? = firstOrNull { x -> x != null }

@ -17,7 +17,6 @@ data class MangaDetails(
private val localManga: LocalManga?, private val localManga: LocalManga?,
private val override: MangaOverride?, private val override: MangaOverride?,
val description: CharSequence?, val description: CharSequence?,
@Deprecated("Caller should decide if manga is loaded enough by itself")
val isLoaded: Boolean, val isLoaded: Boolean,
) { ) {

@ -16,6 +16,7 @@ fun MangaDetails.mapChapters(
branch: String?, branch: String?,
bookmarks: List<Bookmark>, bookmarks: List<Bookmark>,
isGrid: Boolean, isGrid: Boolean,
isDownloadedOnly: Boolean,
): List<ChapterListItem> { ): List<ChapterListItem> {
val remoteChapters = chapters[branch].orEmpty() val remoteChapters = chapters[branch].orEmpty()
val localChapters = local?.manga?.getChapters(branch).orEmpty() val localChapters = local?.manga?.getChapters(branch).orEmpty()
@ -35,19 +36,21 @@ fun MangaDetails.mapChapters(
null null
} }
var isUnread = currentChapterId !in ids var isUnread = currentChapterId !in ids
for (chapter in remoteChapters) { if (!isDownloadedOnly || local?.manga?.chapters == null) {
val local = localMap?.remove(chapter.id) for (chapter in remoteChapters) {
if (chapter.id == currentChapterId) { val local = localMap?.remove(chapter.id)
isUnread = true if (chapter.id == currentChapterId) {
isUnread = true
}
result += (local ?: chapter).toListItem(
isCurrent = chapter.id == currentChapterId,
isUnread = isUnread,
isNew = isUnread && result.size >= newFrom,
isDownloaded = local != null,
isBookmarked = chapter.id in bookmarked,
isGrid = isGrid,
)
} }
result += (local ?: chapter).toListItem(
isCurrent = chapter.id == currentChapterId,
isUnread = isUnread,
isNew = isUnread && result.size >= newFrom,
isDownloaded = local != null,
isBookmarked = chapter.id in bookmarked,
isGrid = isGrid,
)
} }
if (!localMap.isNullOrEmpty()) { if (!localMap.isNullOrEmpty()) {
for (chapter in localMap.values) { for (chapter in localMap.values) {

@ -9,6 +9,7 @@ import androidx.core.view.MenuProvider
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.slider.LabelFormatter import com.google.android.material.slider.LabelFormatter
import com.google.android.material.slider.Slider import com.google.android.material.slider.Slider
import com.google.android.material.slider.TickVisibilityMode
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
@ -41,6 +42,10 @@ class ChapterPagesMenuProvider(
menu.findItem(R.id.action_search)?.isVisible = viewModel.isChaptersEmpty.value == false menu.findItem(R.id.action_search)?.isVisible = viewModel.isChaptersEmpty.value == false
menu.findItem(R.id.action_reversed)?.isChecked = viewModel.isChaptersReversed.value == true menu.findItem(R.id.action_reversed)?.isChecked = viewModel.isChaptersReversed.value == true
menu.findItem(R.id.action_grid_view)?.isChecked = viewModel.isChaptersInGridView.value == true menu.findItem(R.id.action_grid_view)?.isChecked = viewModel.isChaptersInGridView.value == true
menu.findItem(R.id.action_downloaded)?.let { menuItem ->
menuItem.isVisible = viewModel.mangaDetails.value?.local != null
menuItem.isChecked = viewModel.isDownloadedOnly.value == true
}
} }
TAB_PAGES, TAB_BOOKMARKS -> { TAB_PAGES, TAB_BOOKMARKS -> {
@ -64,6 +69,11 @@ class ChapterPagesMenuProvider(
true true
} }
R.id.action_downloaded -> {
viewModel.isDownloadedOnly.value = !menuItem.isChecked
true
}
else -> false else -> false
} }
@ -110,7 +120,7 @@ class ChapterPagesMenuProvider(
valueFrom = 50f valueFrom = 50f
valueTo = 150f valueTo = 150f
stepSize = 5f stepSize = 5f
isTickVisible = false tickVisibilityMode = TickVisibilityMode.TICK_VISIBILITY_HIDDEN
labelBehavior = LabelFormatter.LABEL_FLOATING labelBehavior = LabelFormatter.LABEL_FLOATING
setLabelFormatter(IntPercentLabelFormatter(context)) setLabelFormatter(IntPercentLabelFormatter(context))
setValueRounded(settings.gridSizePages.toFloat()) setValueRounded(settings.gridSizePages.toFloat())

@ -81,6 +81,7 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(),
val menuInvalidator = MenuInvalidator(binding.toolbar) val menuInvalidator = MenuInvalidator(binding.toolbar)
viewModel.isChaptersReversed.observe(viewLifecycleOwner, menuInvalidator) viewModel.isChaptersReversed.observe(viewLifecycleOwner, menuInvalidator)
viewModel.isChaptersInGridView.observe(viewLifecycleOwner, menuInvalidator) viewModel.isChaptersInGridView.observe(viewLifecycleOwner, menuInvalidator)
viewModel.isDownloadedOnly.observe(viewLifecycleOwner, menuInvalidator)
actionModeDelegate?.addListener(this, viewLifecycleOwner) actionModeDelegate?.addListener(this, viewLifecycleOwner)
addSheetCallback(this, viewLifecycleOwner) addSheetCallback(this, viewLifecycleOwner)

@ -87,6 +87,8 @@ abstract class ChaptersPagesViewModel(
valueProducer = { isChaptersGridView }, valueProducer = { isChaptersGridView },
) )
val isDownloadedOnly = MutableStateFlow(false)
val newChaptersCount = mangaDetails.flatMapLatest { d -> val newChaptersCount = mangaDetails.flatMapLatest { d ->
if (d?.isLocal == false) { if (d?.isLocal == false) {
interactor.observeNewChapters(d.id) interactor.observeNewChapters(d.id)
@ -115,13 +117,15 @@ abstract class ChaptersPagesViewModel(
newChaptersCount, newChaptersCount,
bookmarks, bookmarks,
isChaptersInGridView, isChaptersInGridView,
) { manga, currentChapterId, branch, news, bookmarks, grid -> isDownloadedOnly,
) { manga, currentChapterId, branch, news, bookmarks, grid, downloadedOnly ->
manga?.mapChapters( manga?.mapChapters(
currentChapterId, currentChapterId = currentChapterId,
news, newCount = news,
branch, branch = branch,
bookmarks, bookmarks = bookmarks,
grid, isGrid = grid,
isDownloadedOnly = downloadedOnly,
).orEmpty() ).orEmpty()
}, },
isChaptersReversed, isChaptersReversed,

@ -11,6 +11,13 @@
app:actionViewClass="androidx.appcompat.widget.SearchView" app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" /> app:showAsAction="ifRoom|collapseActionView" />
<item
android:id="@+id/action_downloaded"
android:checkable="true"
android:orderInCategory="18"
android:title="@string/on_device"
app:showAsAction="never" />
<item <item
android:id="@+id/action_reversed" android:id="@+id/action_reversed"
android:checkable="true" android:checkable="true"

Loading…
Cancel
Save