From 173087ee1949901524b01e4feda39e259ca2e49b Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 6 Jun 2024 10:07:14 +0300 Subject: [PATCH] Sources catalog improvements --- app/build.gradle | 2 +- .../kotatsu/core/model/MangaSource.kt | 4 +-- .../list/fastscroll/FastScrollRecyclerView.kt | 10 +++++++ .../kotatsu/core/ui/widgets/ChipsView.kt | 4 ++- .../kotatsu/core/util/LocaleComparator.kt | 21 ++++++++++++--- .../kotatsu/core/util/ext/LocaleList.kt | 9 +++---- .../explore/data/MangaSourcesRepository.kt | 6 ++++- .../filter/ui/sheet/FilterSheetFragment.kt | 7 ++--- .../kotatsu/main/ui/welcome/WelcomeSheet.kt | 8 +++--- .../main/ui/welcome/WelcomeViewModel.kt | 24 +++++++++-------- .../sources/catalog/SourceCatalogItemAD.kt | 12 +++++++-- .../sources/catalog/SourcesCatalogActivity.kt | 3 +++ .../sources/catalog/SourcesCatalogAdapter.kt | 5 +++- .../catalog/SourcesCatalogPagerAdapter.kt | 25 ----------------- .../catalog/SourcesCatalogViewModel.kt | 27 +++++++++++++------ app/src/main/res/drawable/ic_off_small.xml | 13 +++++++++ .../res/layout/activity_sources_catalog.xml | 7 ++--- .../main/res/layout/item_source_catalog.xml | 3 +++ app/src/main/res/values/strings.xml | 1 + 19 files changed, 117 insertions(+), 74 deletions(-) delete mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogPagerAdapter.kt create mode 100644 app/src/main/res/drawable/ic_off_small.xml diff --git a/app/build.gradle b/app/build.gradle index 6e36dd8df..a1e69a113 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -82,7 +82,7 @@ afterEvaluate { } dependencies { //noinspection GradleDependency - implementation('com.github.KotatsuApp:kotatsu-parsers:77a733a062') { + implementation('com.github.KotatsuApp:kotatsu-parsers:0b2bf607f7') { exclude group: 'org.json', module: 'json' } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt index dc736c8b9..5fee8ec2f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt @@ -15,8 +15,6 @@ import org.koitharu.kotatsu.core.util.ext.getThemeColor import org.koitharu.kotatsu.core.util.ext.toLocale import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaSource -import org.koitharu.kotatsu.parsers.util.toTitleCase -import java.util.Locale import com.google.android.material.R as materialR fun MangaSource(name: String): MangaSource { @@ -39,7 +37,7 @@ val ContentType.titleResId fun MangaSource.getSummary(context: Context): String { val type = context.getString(contentType.titleResId) - val locale = locale?.toLocale().getDisplayName(context) + val locale = locale.toLocale().getDisplayName(context) return context.getString(R.string.source_summary_pattern, type, locale) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/list/fastscroll/FastScrollRecyclerView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/list/fastscroll/FastScrollRecyclerView.kt index 4374dc6d2..3a75b4af6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/list/fastscroll/FastScrollRecyclerView.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/list/fastscroll/FastScrollRecyclerView.kt @@ -52,6 +52,16 @@ class FastScrollRecyclerView @JvmOverloads constructor( fastScroller.visibility = if (isFastScrollerEnabled) visibility else GONE } + override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) { + super.setPadding(left, top, right, bottom) + fastScroller.setPadding(left, top, right, bottom) + } + + override fun setPaddingRelative(start: Int, top: Int, end: Int, bottom: Int) { + super.setPaddingRelative(start, top, end, bottom) + fastScroller.setPaddingRelative(start, top, end, bottom) + } + override fun onAttachedToWindow() { super.onAttachedToWindow() fastScroller.attachRecyclerView(this) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ChipsView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ChipsView.kt index 0a3ad0e13..fd332ca5b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ChipsView.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ChipsView.kt @@ -26,7 +26,9 @@ class ChipsView @JvmOverloads constructor( onChipClickListener?.onChipClick(it as Chip, it.tag) } private val chipOnCloseListener = OnClickListener { - onChipCloseClickListener?.onChipCloseClick(it as Chip, it.tag) + val chip = it as Chip + val data = it.tag + onChipCloseClickListener?.onChipCloseClick(chip, data) ?: onChipClickListener?.onChipClick(chip, data) } private val chipStyle: Int var onChipClickListener: OnChipClickListener? = null diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/LocaleComparator.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/LocaleComparator.kt index eac57d93d..3eb2770ec 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/LocaleComparator.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/LocaleComparator.kt @@ -1,14 +1,27 @@ package org.koitharu.kotatsu.core.util import androidx.core.os.LocaleListCompat -import org.koitharu.kotatsu.core.util.ext.map +import org.koitharu.kotatsu.core.util.ext.iterator import java.util.Locale class LocaleComparator : Comparator { - private val deviceLocales = LocaleListCompat.getAdjustedDefault()//LocaleManagerCompat.getSystemLocales(context) - .map { it.language } - .distinct() + private val deviceLocales: List + + init { + val localeList = LocaleListCompat.getAdjustedDefault() + deviceLocales = buildList(localeList.size() + 1) { + add("") + val set = HashSet(localeList.size() + 1) + set.add("") + for (locale in localeList) { + val lang = locale.language + if (set.add(lang)) { + add(lang) + } + } + } + } override fun compare(a: Locale, b: Locale): Int { val indexA = deviceLocales.indexOf(a.language) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/LocaleList.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/LocaleList.kt index 7d817c6c1..e9b69a6c7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/LocaleList.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/LocaleList.kt @@ -22,11 +22,10 @@ fun LocaleListCompat.getOrThrow(index: Int) = get(index) ?: throw NoSuchElementE fun String.toLocale() = Locale(this) -fun Locale?.getDisplayName(context: Context): String { - if (this == null) { - return context.getString(R.string.various_languages) - } - return getDisplayLanguage(this).toTitleCase(this) +fun Locale?.getDisplayName(context: Context): String = when (this) { + null -> context.getString(R.string.all_languages) + Locale.ROOT -> context.getString(R.string.various_languages) + else -> getDisplayLanguage(this).toTitleCase(this) } private class LocaleListCompatIterator(private val list: LocaleListCompat) : ListIterator { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt index 17b0462f2..fe9ae9ca1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt @@ -67,6 +67,7 @@ class MangaSourcesRepository @Inject constructor( excludeBroken: Boolean, types: Set, query: String?, + locale: String?, sortOrder: SourcesSortOrder?, ): List { assimilateNewSources() @@ -81,6 +82,9 @@ class MangaSourcesRepository @Inject constructor( skipNsfwSources = settings.isNsfwContentDisabled, sortOrder = sortOrder, ) + if (locale != null) { + sources.retainAll { it.locale == locale } + } if (excludeBroken) { sources.removeAll { it.isBroken } } @@ -175,7 +179,7 @@ class MangaSourcesRepository @Inject constructor( fun observeHasNewSources(): Flow = observeIsNsfwDisabled().map { skipNsfw -> val sources = dao.findAllFromVersion(BuildConfig.VERSION_CODE).toSources(skipNsfw, null) - sources.isNotEmpty() + sources.isNotEmpty() && sources.size != remoteSources.size }.onStart { assimilateNewSources() } fun observeHasNewSourcesForBadge(): Flow = combine( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt index 724f77101..f677603b4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt @@ -17,6 +17,7 @@ import org.koitharu.kotatsu.core.ui.model.titleRes import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.core.util.ext.getDisplayMessage +import org.koitharu.kotatsu.core.util.ext.getDisplayName import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.parentView import org.koitharu.kotatsu.core.util.ext.showDistinct @@ -29,7 +30,6 @@ import org.koitharu.kotatsu.parsers.model.ContentRating import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder -import org.koitharu.kotatsu.parsers.util.toTitleCase import java.util.Locale import com.google.android.material.R as materialR @@ -122,10 +122,7 @@ class FilterSheetFragment : BaseAdaptiveSheet(), b.spinnerLocale.context, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1, - value.availableItems.map { - it?.getDisplayLanguage(it)?.toTitleCase(it) - ?: b.spinnerLocale.context.getString(R.string.various_languages) - }, + value.availableItems.map { it.getDisplayName(b.spinnerLocale.context) }, ) val selectedIndex = value.availableItems.indexOf(selected) if (selectedIndex >= 0) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeSheet.kt index b31c9b012..e8f0b95fd 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeSheet.kt @@ -18,13 +18,13 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.titleResId import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.ui.widgets.ChipsView +import org.koitharu.kotatsu.core.util.ext.getDisplayName import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.showDistinct import org.koitharu.kotatsu.core.util.ext.tryLaunch import org.koitharu.kotatsu.databinding.SheetWelcomeBinding import org.koitharu.kotatsu.filter.ui.model.FilterProperty import org.koitharu.kotatsu.parsers.model.ContentType -import org.koitharu.kotatsu.parsers.util.toTitleCase import org.koitharu.kotatsu.settings.backup.RestoreDialogFragment import java.util.Locale @@ -58,7 +58,7 @@ class WelcomeSheet : BaseAdaptiveSheet(), ChipsView.OnChipC override fun onChipClick(chip: Chip, data: Any?) { when (data) { is ContentType -> viewModel.setTypeChecked(data, chip.isChecked) - is Locale? -> viewModel.setLocaleChecked(data, chip.isChecked) + is Locale -> viewModel.setLocaleChecked(data, chip.isChecked) } } @@ -86,12 +86,12 @@ class WelcomeSheet : BaseAdaptiveSheet(), ChipsView.OnChipC } } - private fun onLocalesChanged(value: FilterProperty) { + private fun onLocalesChanged(value: FilterProperty) { val chips = viewBinding?.chipsLocales ?: return chips.setChips( value.availableItems.map { ChipsView.ChipModel( - title = it?.getDisplayLanguage(it)?.toTitleCase(it) ?: getString(R.string.various_languages), + title = it.getDisplayName(chips.context), isCheckable = true, isChecked = it in value.selectedItems, data = it, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeViewModel.kt index c3c6800f8..e97d7950a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeViewModel.kt @@ -11,6 +11,7 @@ import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.LocaleComparator import org.koitharu.kotatsu.core.util.ext.sortedWithSafe import org.koitharu.kotatsu.core.util.ext.toList +import org.koitharu.kotatsu.core.util.ext.toLocale import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.filter.ui.model.FilterProperty import org.koitharu.kotatsu.parsers.model.ContentType @@ -27,14 +28,14 @@ class WelcomeViewModel @Inject constructor( ) : BaseViewModel() { private val allSources = repository.allMangaSources - private val localesGroups by lazy { allSources.groupBy { it.locale?.let { x -> Locale(x) } } } + private val localesGroups by lazy { allSources.groupBy { it.locale.toLocale() } } private var updateJob: Job val locales = MutableStateFlow( - FilterProperty( - availableItems = listOf(null), - selectedItems = setOf(null), + FilterProperty( + availableItems = listOf(Locale.ROOT), + selectedItems = setOf(Locale.ROOT), isLoading = true, error = null, ), @@ -51,13 +52,14 @@ class WelcomeViewModel @Inject constructor( init { updateJob = launchJob(Dispatchers.Default) { - val languages = localesGroups.keys.associateBy { x -> x?.language } - val selectedLocales = HashSet(2) - selectedLocales += ConfigurationCompat.getLocales(context.resources.configuration).toList() + val languages = localesGroups.keys.associateBy { x -> x.language } + val selectedLocales = HashSet(2) + ConfigurationCompat.getLocales(context.resources.configuration).toList() .firstNotNullOfOrNull { lc -> languages[lc.language] } - selectedLocales += null + ?.let { selectedLocales += it } + selectedLocales += Locale.ROOT locales.value = locales.value.copy( - availableItems = localesGroups.keys.sortedWithSafe(nullsFirst(LocaleComparator())), + availableItems = localesGroups.keys.sortedWithSafe(LocaleComparator()), selectedItems = selectedLocales, isLoading = false, ) @@ -66,7 +68,7 @@ class WelcomeViewModel @Inject constructor( } } - fun setLocaleChecked(locale: Locale?, isChecked: Boolean) { + fun setLocaleChecked(locale: Locale, isChecked: Boolean) { val snapshot = locales.value locales.value = snapshot.copy( selectedItems = if (isChecked) { @@ -99,7 +101,7 @@ class WelcomeViewModel @Inject constructor( } private suspend fun commit() { - val languages = locales.value.selectedItems.mapToSet { it?.language } + val languages = locales.value.selectedItems.mapToSet { it.language } val types = types.value.selectedItems val enabledSources = allSources.filterTo(EnumSet.noneOf(MangaSource::class.java)) { x -> x.contentType in types && x.locale in languages diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItemAD.kt index 360f33d61..fa7d0441c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItemAD.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.settings.sources.catalog +import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.core.view.isVisible import androidx.core.view.updatePadding @@ -15,6 +16,7 @@ import org.koitharu.kotatsu.core.ui.image.FaviconDrawable import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.util.WindowInsetsDelegate import org.koitharu.kotatsu.core.util.ext.crossfade +import org.koitharu.kotatsu.core.util.ext.drawableStart import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.newImageRequest import org.koitharu.kotatsu.core.util.ext.setTextAndVisible @@ -22,12 +24,13 @@ import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemCatalogPageBinding import org.koitharu.kotatsu.databinding.ItemEmptyHintBinding import org.koitharu.kotatsu.databinding.ItemSourceCatalogBinding +import org.koitharu.kotatsu.list.ui.model.ListModel fun sourceCatalogItemSourceAD( coil: ImageLoader, lifecycleOwner: LifecycleOwner, listener: OnListItemClickListener -) = adapterDelegateViewBinding( +) = adapterDelegateViewBinding( { layoutInflater, parent -> ItemSourceCatalogBinding.inflate(layoutInflater, parent, false) }, @@ -43,6 +46,11 @@ fun sourceCatalogItemSourceAD( bind { binding.textViewTitle.text = item.source.getTitle(context) binding.textViewDescription.text = item.source.getSummary(context) + binding.textViewDescription.drawableStart = if (item.source.isBroken) { + ContextCompat.getDrawable(context, R.drawable.ic_off_small) + } else { + null + } val fallbackIcon = FaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name) binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run { crossfade(context) @@ -59,7 +67,7 @@ fun sourceCatalogItemSourceAD( fun sourceCatalogItemHintAD( coil: ImageLoader, lifecycleOwner: LifecycleOwner, -) = adapterDelegateViewBinding( +) = adapterDelegateViewBinding( { inflater, parent -> ItemEmptyHintBinding.inflate(inflater, parent, false) }, ) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt index 9896ddd76..a99cccbeb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt @@ -73,6 +73,9 @@ class SourcesCatalogActivity : BaseActivity(), left = insets.left, right = insets.right, ) + viewBinding.recyclerView.updatePadding( + bottom = insets.bottom, + ) } override fun onChipClick(chip: Chip, data: Any?) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt index 281371a45..030a0f661 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt @@ -7,16 +7,19 @@ import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.list.fastscroll.FastScroller import org.koitharu.kotatsu.list.ui.adapter.ListItemType +import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD +import org.koitharu.kotatsu.list.ui.model.ListModel class SourcesCatalogAdapter( listener: OnListItemClickListener, coil: ImageLoader, lifecycleOwner: LifecycleOwner, -) : BaseListAdapter(), FastScroller.SectionIndexer { +) : BaseListAdapter(), FastScroller.SectionIndexer { init { addDelegate(ListItemType.CHAPTER_LIST, sourceCatalogItemSourceAD(coil, lifecycleOwner, listener)) addDelegate(ListItemType.HINT_EMPTY, sourceCatalogItemHintAD(coil, lifecycleOwner)) + addDelegate(ListItemType.STATE_LOADING, loadingStateAD()) } override fun getSectionText(context: Context, position: Int): CharSequence? { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogPagerAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogPagerAdapter.kt deleted file mode 100644 index 32f76fe15..000000000 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogPagerAdapter.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.koitharu.kotatsu.settings.sources.catalog - -import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader -import com.google.android.material.tabs.TabLayout -import com.google.android.material.tabs.TabLayoutMediator -import org.koitharu.kotatsu.core.model.titleResId -import org.koitharu.kotatsu.core.ui.BaseListAdapter -import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener - -class SourcesCatalogPagerAdapter( - listener: OnListItemClickListener, - coil: ImageLoader, - lifecycleOwner: LifecycleOwner, -) : BaseListAdapter(), TabLayoutMediator.TabConfigurationStrategy { - - init { - delegatesManager.addDelegate(sourceCatalogPageAD(listener, coil, lifecycleOwner)) - } - - override fun onConfigureTab(tab: TabLayout.Tab, position: Int) { - val item = items.getOrNull(position) ?: return - tab.setText(item.type.titleResId) - } -} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt index a451cad34..227e70412 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.settings.sources.catalog import androidx.lifecycle.viewModelScope +import androidx.room.invalidationTrackerFlow import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow @@ -10,6 +11,8 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.db.MangaDatabase +import org.koitharu.kotatsu.core.db.TABLE_SOURCES import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.ui.util.ReversibleAction @@ -17,9 +20,10 @@ import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.explore.data.SourcesSortOrder +import org.koitharu.kotatsu.list.ui.model.ListModel +import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaSource -import org.koitharu.kotatsu.parsers.util.mapToSet import java.util.EnumSet import java.util.Locale import javax.inject.Inject @@ -27,11 +31,14 @@ import javax.inject.Inject @HiltViewModel class SourcesCatalogViewModel @Inject constructor( private val repository: MangaSourcesRepository, - private val settings: AppSettings, + db: MangaDatabase, + settings: AppSettings, ) : BaseViewModel() { val onActionDone = MutableEventFlow() - val locales = repository.allMangaSources.mapToSet { it.locale } + val locales: Set = repository.allMangaSources.mapTo(HashSet()) { it.locale }.also { + it.add(null) + } private val searchQuery = MutableStateFlow(null) val appliedFilter = MutableStateFlow( @@ -47,12 +54,13 @@ class SourcesCatalogViewModel @Inject constructor( val hasNewSources = repository.observeHasNewSources() .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, false) - val content: StateFlow> = combine( + val content: StateFlow> = combine( searchQuery, appliedFilter, - ) { q, f -> + db.invalidationTrackerFlow(TABLE_SOURCES), + ) { q, f, _ -> buildSourcesList(f, q) - }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList()) + }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState)) init { repository.clearNewSourcesBadge() @@ -96,8 +104,9 @@ class SourcesCatalogViewModel @Inject constructor( excludeBroken = false, types = filter.types, query = query, + locale = filter.locale, sortOrder = SourcesSortOrder.ALPHABETIC, - ).filter { it.locale == filter.locale } + ) return if (sources.isEmpty()) { listOf( if (query == null) { @@ -115,7 +124,9 @@ class SourcesCatalogViewModel @Inject constructor( }, ) } else { - sources.map { + sources.sortedBy { + it.isBroken + }.map { SourceCatalogItem.Source(source = it) } } diff --git a/app/src/main/res/drawable/ic_off_small.xml b/app/src/main/res/drawable/ic_off_small.xml new file mode 100644 index 000000000..c2a8720a9 --- /dev/null +++ b/app/src/main/res/drawable/ic_off_small.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/layout/activity_sources_catalog.xml b/app/src/main/res/layout/activity_sources_catalog.xml index 528ad1107..2bdeb16a0 100644 --- a/app/src/main/res/layout/activity_sources_catalog.xml +++ b/app/src/main/res/layout/activity_sources_catalog.xml @@ -17,7 +17,8 @@ + android:layout_height="?attr/actionBarSize" + app:layout_scrollFlags="scroll|enterAlways|snap" /> - diff --git a/app/src/main/res/layout/item_source_catalog.xml b/app/src/main/res/layout/item_source_catalog.xml index 2c99ffc8f..e770233b4 100644 --- a/app/src/main/res/layout/item_source_catalog.xml +++ b/app/src/main/res/layout/item_source_catalog.xml @@ -45,9 +45,12 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="2dp" + android:drawablePadding="4dp" android:ellipsize="end" + android:gravity="center_vertical" android:singleLine="true" android:textAppearance="?attr/textAppearanceBodySmall" + tools:drawableStart="@drawable/ic_off_small" tools:text="English" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b0ea5149e..722f09e2b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -652,4 +652,5 @@ Debug information about background checks for new chapters New + All languages