Option to show only downloaded chapters
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user