Option to hide nsfw content
This commit is contained in:
@@ -9,8 +9,10 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.core.model.getLocaleTitle
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
|
||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
||||
import org.koitharu.kotatsu.parsers.util.SuspendLazy
|
||||
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
||||
import javax.inject.Inject
|
||||
@@ -18,6 +20,7 @@ import javax.inject.Inject
|
||||
@HiltViewModel
|
||||
class NewSourcesViewModel @Inject constructor(
|
||||
private val repository: MangaSourcesRepository,
|
||||
private val settings: AppSettings,
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val newSources = SuspendLazy {
|
||||
@@ -26,9 +29,16 @@ class NewSourcesViewModel @Inject constructor(
|
||||
val content: StateFlow<List<SourceConfigItem>> = repository.observeAll()
|
||||
.map { sources ->
|
||||
val new = newSources.get()
|
||||
val skipNsfw = settings.isNsfwContentDisabled
|
||||
sources.mapNotNull { (source, enabled) ->
|
||||
if (source in new) {
|
||||
SourceConfigItem.SourceItem(source, enabled, source.getLocaleTitle(), false)
|
||||
SourceConfigItem.SourceItem(
|
||||
source = source,
|
||||
isEnabled = enabled,
|
||||
summary = source.getLocaleTitle(),
|
||||
isDraggable = false,
|
||||
isAvailable = !skipNsfw || source.contentType != ContentType.HENTAI,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.ImageLoader
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.ui.BaseFragment
|
||||
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
||||
@@ -41,6 +42,9 @@ class SourcesManageFragment :
|
||||
@Inject
|
||||
lateinit var coil: ImageLoader
|
||||
|
||||
@Inject
|
||||
lateinit var settings: AppSettings
|
||||
|
||||
private var reorderHelper: ItemTouchHelper? = null
|
||||
private val viewModel by viewModels<SourcesManageViewModel>()
|
||||
|
||||
@@ -128,9 +132,19 @@ class SourcesManageFragment :
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_no_nsfw -> {
|
||||
settings.isNsfwContentDisabled = !menuItem.isChecked
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun onPrepareMenu(menu: Menu) {
|
||||
super.onPrepareMenu(menu)
|
||||
menu.findItem(R.id.action_no_nsfw).isChecked = settings.isNsfwContentDisabled
|
||||
}
|
||||
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
(activity as? AppBarOwner)?.appBar?.setExpanded(false, true)
|
||||
return true
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.koitharu.kotatsu.core.util.ext.call
|
||||
import org.koitharu.kotatsu.core.util.ext.map
|
||||
import org.koitharu.kotatsu.core.util.ext.toEnumSet
|
||||
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
|
||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.toTitleCase
|
||||
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
||||
@@ -54,8 +55,9 @@ class SourcesManageViewModel @Inject constructor(
|
||||
expandedGroups,
|
||||
searchQuery,
|
||||
observeTip(),
|
||||
) { sources, groups, query, tip ->
|
||||
buildList(sources, groups, query, tip)
|
||||
settings.observeAsFlow(AppSettings.KEY_DISABLE_NSFW) { isNsfwContentDisabled },
|
||||
) { sources, groups, query, tip, noNsfw ->
|
||||
buildList(sources, groups, query, tip, noNsfw)
|
||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
val onActionDone = MutableEventFlow<ReversibleAction>()
|
||||
@@ -125,6 +127,7 @@ class SourcesManageViewModel @Inject constructor(
|
||||
expanded: Set<String?>,
|
||||
query: String?,
|
||||
withTip: Boolean,
|
||||
isNsfwDisabled: Boolean,
|
||||
): List<SourceConfigItem> {
|
||||
val allSources = repository.allMangaSources
|
||||
val enabledSet = enabledSources.toEnumSet()
|
||||
@@ -138,6 +141,7 @@ class SourcesManageViewModel @Inject constructor(
|
||||
summary = it.getLocaleTitle(),
|
||||
isEnabled = it in enabledSet,
|
||||
isDraggable = false,
|
||||
isAvailable = !isNsfwDisabled || !it.isNsfw(),
|
||||
)
|
||||
}.ifEmpty {
|
||||
listOf(SourceConfigItem.EmptySearchResult)
|
||||
@@ -163,6 +167,7 @@ class SourcesManageViewModel @Inject constructor(
|
||||
summary = it.getLocaleTitle(),
|
||||
isEnabled = true,
|
||||
isDraggable = true,
|
||||
isAvailable = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -184,6 +189,7 @@ class SourcesManageViewModel @Inject constructor(
|
||||
summary = null,
|
||||
isEnabled = false,
|
||||
isDraggable = false,
|
||||
isAvailable = !isNsfwDisabled || !it.isNsfw(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -210,6 +216,8 @@ class SourcesManageViewModel @Inject constructor(
|
||||
isTipEnabled(TIP_REORDER)
|
||||
}
|
||||
|
||||
private fun MangaSource.isNsfw() = contentType == ContentType.HENTAI
|
||||
|
||||
private class LocaleKeyComparator : Comparator<String?> {
|
||||
|
||||
private val deviceLocales = LocaleListCompat.getAdjustedDefault()
|
||||
|
||||
@@ -72,8 +72,17 @@ fun sourceConfigItemCheckableDelegate(
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.textViewTitle.text = item.source.title
|
||||
binding.textViewTitle.text = if (item.isNsfw) {
|
||||
buildSpannedString {
|
||||
append(item.source.title)
|
||||
append(' ')
|
||||
appendNsfwLabel(context)
|
||||
}
|
||||
} else {
|
||||
item.source.title
|
||||
}
|
||||
binding.switchToggle.isChecked = item.isEnabled
|
||||
binding.switchToggle.isEnabled = item.isAvailable
|
||||
binding.textViewDescription.textAndVisible = item.summary
|
||||
val fallbackIcon = FaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)
|
||||
binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run {
|
||||
@@ -120,7 +129,7 @@ fun sourceConfigItemDelegate2(
|
||||
} else {
|
||||
item.source.title
|
||||
}
|
||||
binding.imageViewAdd.isGone = item.isEnabled
|
||||
binding.imageViewAdd.isGone = item.isEnabled || !item.isAvailable
|
||||
binding.imageViewRemove.isVisible = item.isEnabled
|
||||
binding.imageViewConfig.isVisible = item.isEnabled
|
||||
binding.textViewDescription.textAndVisible = item.summary
|
||||
|
||||
@@ -9,25 +9,16 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
|
||||
sealed interface SourceConfigItem : ListModel {
|
||||
|
||||
class Header(
|
||||
data class Header(
|
||||
@StringRes val titleResId: Int,
|
||||
) : SourceConfigItem {
|
||||
|
||||
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||
return other is Header && other.titleResId == titleResId
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
other as Header
|
||||
return titleResId == other.titleResId
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = titleResId
|
||||
}
|
||||
|
||||
class LocaleGroup(
|
||||
data class LocaleGroup(
|
||||
val localeId: String?,
|
||||
val title: String?,
|
||||
val isExpanded: Boolean,
|
||||
@@ -44,31 +35,14 @@ sealed interface SourceConfigItem : ListModel {
|
||||
super.getChangePayload(previousState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as LocaleGroup
|
||||
|
||||
if (localeId != other.localeId) return false
|
||||
if (title != other.title) return false
|
||||
return isExpanded == other.isExpanded
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = localeId?.hashCode() ?: 0
|
||||
result = 31 * result + (title?.hashCode() ?: 0)
|
||||
result = 31 * result + isExpanded.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class SourceItem(
|
||||
data class SourceItem(
|
||||
val source: MangaSource,
|
||||
val isEnabled: Boolean,
|
||||
val summary: String?,
|
||||
val isDraggable: Boolean,
|
||||
val isAvailable: Boolean,
|
||||
) : SourceConfigItem {
|
||||
|
||||
val isNsfw: Boolean
|
||||
@@ -85,29 +59,9 @@ sealed interface SourceConfigItem : ListModel {
|
||||
super.getChangePayload(previousState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as SourceItem
|
||||
|
||||
if (source != other.source) return false
|
||||
if (summary != other.summary) return false
|
||||
if (isEnabled != other.isEnabled) return false
|
||||
return isDraggable == other.isDraggable
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = source.hashCode()
|
||||
result = 31 * result + summary.hashCode()
|
||||
result = 31 * result + isEnabled.hashCode()
|
||||
result = 31 * result + isDraggable.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class Tip(
|
||||
data class Tip(
|
||||
val key: String,
|
||||
@DrawableRes val iconResId: Int,
|
||||
@StringRes val textResId: Int,
|
||||
@@ -116,24 +70,6 @@ sealed interface SourceConfigItem : ListModel {
|
||||
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||
return other is Tip && other.key == key
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Tip
|
||||
|
||||
if (key != other.key) return false
|
||||
if (iconResId != other.iconResId) return false
|
||||
return textResId == other.textResId
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = key.hashCode()
|
||||
result = 31 * result + iconResId
|
||||
result = 31 * result + textResId
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
object EmptySearchResult : SourceConfigItem {
|
||||
|
||||
Reference in New Issue
Block a user