Show reason why manga has no chapters
This commit is contained in:
@@ -104,14 +104,14 @@ class CaptchaHandler @Inject constructor(
|
||||
val dao = databaseProvider.get().getSourcesDao()
|
||||
dao.setCfState(source.name, exception?.state ?: CloudFlareHelper.PROTECTION_NOT_DETECTED)
|
||||
|
||||
val exceptions = dao.findAllCaptchaRequired().mapNotNull {
|
||||
it.source.toMangaSourceOrNull()
|
||||
}.filterNot {
|
||||
SourceSettings(context, it).isCaptchaNotificationsDisabled
|
||||
}.mapNotNull {
|
||||
exceptionMap[it]
|
||||
}
|
||||
if (notify && context.checkNotificationPermission(CHANNEL_ID)) {
|
||||
val exceptions = dao.findAllCaptchaRequired().mapNotNull {
|
||||
it.source.toMangaSourceOrNull()
|
||||
}.filterNot {
|
||||
SourceSettings(context, it).isCaptchaNotificationsDisabled
|
||||
}.mapNotNull {
|
||||
exceptionMap[it]
|
||||
}
|
||||
if (removedException != null) {
|
||||
NotificationManagerCompat.from(context).cancel(TAG, removedException.source.hashCode())
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class ChapterPagesMenuProvider(
|
||||
setOnActionExpandListener(this@ChapterPagesMenuProvider)
|
||||
(actionView as? SearchView)?.setupChaptersSearchView()
|
||||
}
|
||||
menu.findItem(R.id.action_search)?.isVisible = viewModel.isChaptersEmpty.value == false
|
||||
menu.findItem(R.id.action_search)?.isVisible = viewModel.emptyReason.value == null
|
||||
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_downloaded)?.let { menuItem ->
|
||||
|
||||
@@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.plus
|
||||
@@ -43,6 +44,7 @@ import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.local.domain.DeleteLocalMangaUseCase
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderViewModel
|
||||
@@ -97,9 +99,19 @@ abstract class ChaptersPagesViewModel(
|
||||
}
|
||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, 0)
|
||||
|
||||
val isChaptersEmpty: StateFlow<Boolean> = mangaDetails.map {
|
||||
it != null && it.isLoaded && it.allChapters.isEmpty()
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
val emptyReason: StateFlow<EmptyMangaReason?> = combine(
|
||||
mangaDetails,
|
||||
isLoading,
|
||||
onError.onStart { emit(null) },
|
||||
) { details, loading, error ->
|
||||
when {
|
||||
details == null || loading -> null
|
||||
details.chapters.isNotEmpty() -> null
|
||||
details.toManga().state == MangaState.RESTRICTED -> EmptyMangaReason.RESTRICTED
|
||||
error != null -> EmptyMangaReason.LOADING_ERROR
|
||||
else -> EmptyMangaReason.NO_CHAPTERS
|
||||
}
|
||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val bookmarks = mangaDetails.flatMapLatest {
|
||||
if (it != null) {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.koitharu.kotatsu.details.ui.pager
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
enum class EmptyMangaReason(
|
||||
@StringRes val msgResId: Int,
|
||||
) {
|
||||
|
||||
NO_CHAPTERS(R.string.no_chapters_in_manga),
|
||||
LOADING_ERROR(R.string.chapters_load_failed),
|
||||
RESTRICTED(R.string.manga_restricted_description),
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import org.koitharu.kotatsu.core.util.RecyclerViewScrollCallback
|
||||
import org.koitharu.kotatsu.core.util.ext.findAppCompatDelegate
|
||||
import org.koitharu.kotatsu.core.util.ext.findParentCallback
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.setTextAndVisible
|
||||
import org.koitharu.kotatsu.databinding.FragmentChaptersBinding
|
||||
import org.koitharu.kotatsu.details.ui.adapter.ChaptersAdapter
|
||||
import org.koitharu.kotatsu.details.ui.adapter.ChaptersSelectionDecoration
|
||||
@@ -96,8 +97,8 @@ class ChaptersFragment :
|
||||
.flowOn(Dispatchers.Default)
|
||||
.observe(viewLifecycleOwner, this::onChaptersChanged)
|
||||
viewModel.quickFilter.observe(viewLifecycleOwner, this::onFilterChanged)
|
||||
viewModel.isChaptersEmpty.observe(viewLifecycleOwner) {
|
||||
binding.textViewHolder.isVisible = it
|
||||
viewModel.emptyReason.observe(viewLifecycleOwner) {
|
||||
binding.textViewHolder.setTextAndVisible(it?.msgResId ?: 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import androidx.appcompat.view.ActionMode
|
||||
import androidx.collection.ArraySet
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@@ -37,9 +36,11 @@ import org.koitharu.kotatsu.core.util.ext.findAppCompatDelegate
|
||||
import org.koitharu.kotatsu.core.util.ext.findParentCallback
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.core.util.ext.setTextAndVisible
|
||||
import org.koitharu.kotatsu.core.util.ext.showOrHide
|
||||
import org.koitharu.kotatsu.databinding.FragmentPagesBinding
|
||||
import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesViewModel
|
||||
import org.koitharu.kotatsu.details.ui.pager.EmptyMangaReason
|
||||
import org.koitharu.kotatsu.list.ui.GridSpanResolver
|
||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
||||
import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
|
||||
@@ -125,11 +126,18 @@ class PagesFragment :
|
||||
it.spanCount = checkNotNull(spanResolver).spanCount
|
||||
}
|
||||
}
|
||||
parentViewModel.isChaptersEmpty.observe(viewLifecycleOwner, ::onNoChaptersChanged)
|
||||
parentViewModel.emptyReason.observe(viewLifecycleOwner, ::onNoChaptersChanged)
|
||||
viewModel.thumbnails.observe(viewLifecycleOwner, ::onThumbnailsChanged)
|
||||
viewModel.onPageSaved.observeEvent(this, PagesSavedObserver(binding.recyclerView))
|
||||
viewModel.onError.observeEvent(viewLifecycleOwner, SnackbarErrorObserver(binding.recyclerView, this))
|
||||
viewModel.isLoading.observe(viewLifecycleOwner) { binding.progressBar.showOrHide(it) }
|
||||
combine(
|
||||
viewModel.isLoading,
|
||||
viewModel.thumbnails,
|
||||
) { loading, content ->
|
||||
loading && content.isEmpty()
|
||||
}.observe(viewLifecycleOwner) {
|
||||
binding.progressBar.showOrHide(it)
|
||||
}
|
||||
viewModel.isLoadingUp.observe(viewLifecycleOwner) { binding.progressBarTop.showOrHide(it) }
|
||||
viewModel.isLoadingDown.observe(viewLifecycleOwner) { binding.progressBarBottom.showOrHide(it) }
|
||||
}
|
||||
@@ -237,10 +245,10 @@ class PagesFragment :
|
||||
spanResolver?.setGridSize(scale, requireViewBinding().recyclerView)
|
||||
}
|
||||
|
||||
private fun onNoChaptersChanged(isNoChapters: Boolean) {
|
||||
private fun onNoChaptersChanged(reason: EmptyMangaReason?) {
|
||||
with(viewBinding ?: return) {
|
||||
textViewHolder.isVisible = isNoChapters
|
||||
recyclerView.isInvisible = isNoChapters
|
||||
textViewHolder.setTextAndVisible(reason?.msgResId ?: 0)
|
||||
recyclerView.isInvisible = reason != null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
|
||||
@@ -47,16 +48,15 @@ class PagesViewModel @Inject constructor(
|
||||
)
|
||||
|
||||
init {
|
||||
loadingJob = launchLoadingJob(Dispatchers.Default) {
|
||||
val firstState = state.firstNotNull()
|
||||
doInit(firstState)
|
||||
launchJob(Dispatchers.Default) {
|
||||
state.collectLatest {
|
||||
if (it != null) {
|
||||
launchJob(Dispatchers.Default) {
|
||||
state.filterNotNull()
|
||||
.collect {
|
||||
val prevJob = loadingJob
|
||||
loadingJob = launchLoadingJob(Dispatchers.Default) {
|
||||
prevJob?.cancelAndJoin()
|
||||
doInit(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -875,4 +875,7 @@
|
||||
<string name="invalid_token">Invalid token: %s</string>
|
||||
<string name="show_floating_control_button">Show floating control button</string>
|
||||
<string name="unavailable">Unavailable</string>
|
||||
<string name="manga_restricted_description">This manga is not available to read in this source. Try searching for it in other sources or opening it in a browser for more information</string>
|
||||
<string name="no_chapters_in_manga">This manga does not contain any chapters</string>
|
||||
<string name="chapters_load_failed">Failed to load chapter list</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user