From 1381a7d957e06249413d0d4ab5ad7daa7c7172db Mon Sep 17 00:00:00 2001 From: Koitharu Date: Fri, 8 Jul 2022 15:29:51 +0300 Subject: [PATCH] Random manga dice --- .../koitharu/kotatsu/explore/ExploreModule.kt | 6 ++- .../explore/domain/ExploreRepository.kt | 40 +++++++++++++++ .../kotatsu/explore/ui/ExploreFragment.kt | 25 +++++++++ .../kotatsu/explore/ui/ExploreViewModel.kt | 30 ++++++++--- .../explore/ui/adapter/ExploreAdapter.kt | 1 + .../ui/adapter/ExploreAdapterDelegates.kt | 8 ++- .../explore/ui/adapter/ExploreDiffCallback.kt | 3 +- .../kotatsu/explore/ui/model/ExploreItem.kt | 2 + app/src/main/res/drawable/ic_dice.xml | 12 +++++ .../layout-w600dp/item_explore_buttons.xml | 51 ------------------- .../main/res/layout/item_explore_buttons.xml | 20 +++++++- app/src/main/res/values-w600dp/integers.xml | 4 ++ app/src/main/res/values/integers.xml | 1 + app/src/main/res/values/strings.xml | 1 + 14 files changed, 142 insertions(+), 62 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt create mode 100644 app/src/main/res/drawable/ic_dice.xml delete mode 100644 app/src/main/res/layout-w600dp/item_explore_buttons.xml create mode 100644 app/src/main/res/values-w600dp/integers.xml diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/ExploreModule.kt b/app/src/main/java/org/koitharu/kotatsu/explore/ExploreModule.kt index 54a15a972..32d55740b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/explore/ExploreModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/explore/ExploreModule.kt @@ -2,9 +2,13 @@ package org.koitharu.kotatsu.explore import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.module +import org.koitharu.kotatsu.explore.domain.ExploreRepository import org.koitharu.kotatsu.explore.ui.ExploreViewModel val exploreModule get() = module { - viewModel { ExploreViewModel(get()) } + + factory { ExploreRepository(get(), get()) } + + viewModel { ExploreViewModel(get(), get()) } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt b/app/src/main/java/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt new file mode 100644 index 000000000..730f38a29 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt @@ -0,0 +1,40 @@ +package org.koitharu.kotatsu.explore.domain + +import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.history.domain.HistoryRepository +import org.koitharu.kotatsu.parsers.model.Manga +import org.koitharu.kotatsu.parsers.model.SortOrder + +class ExploreRepository( + private val settings: AppSettings, + private val historyRepository: HistoryRepository, +) { + + suspend fun findRandomManga(tagsLimit: Int): Manga { + val blacklistTagRegex = settings.getSuggestionsTagsBlacklistRegex() + val allTags = historyRepository.getPopularTags(tagsLimit).filterNot { + blacklistTagRegex?.containsMatchIn(it.title) ?: false + } + val tag = allTags.randomOrNull() + val source = checkNotNull(tag?.source ?: settings.getMangaSources(includeHidden = false).randomOrNull()) { + "No sources found" + } + val repo = MangaRepository(source) + val list = repo.getList( + offset = 0, + sortOrder = if (SortOrder.UPDATED in repo.sortOrders) SortOrder.UPDATED else null, + tags = setOfNotNull(tag), + ).shuffled() + for (item in list) { + if (settings.isSuggestionsExcludeNsfw && item.isNsfw) { + continue + } + if (blacklistTagRegex != null && item.tags.any { x -> blacklistTagRegex.containsMatchIn(x.title) }) { + continue + } + return item + } + return list.random() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt b/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt index 330a62e7e..4cbd5acc6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt @@ -7,6 +7,7 @@ import android.view.ViewGroup import androidx.core.graphics.Insets import androidx.core.view.updatePadding import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.snackbar.Snackbar import org.koin.android.ext.android.get import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R @@ -15,14 +16,18 @@ import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner import org.koitharu.kotatsu.bookmarks.ui.BookmarksActivity import org.koitharu.kotatsu.databinding.FragmentExploreBinding +import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.explore.ui.adapter.ExploreAdapter import org.koitharu.kotatsu.explore.ui.adapter.ExploreListEventListener import org.koitharu.kotatsu.explore.ui.model.ExploreItem +import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity import org.koitharu.kotatsu.history.ui.HistoryActivity +import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.search.ui.MangaListActivity import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.suggestions.ui.SuggestionsActivity +import org.koitharu.kotatsu.utils.ext.getDisplayMessage class ExploreFragment : BaseFragment(), RecyclerViewOwner, @@ -52,6 +57,8 @@ class ExploreFragment : BaseFragment(), viewModel.content.observe(viewLifecycleOwner) { exploreAdapter?.items = it } + viewModel.onError.observe(viewLifecycleOwner, ::onError) + viewModel.onOpenManga.observe(viewLifecycleOwner, ::onOpenManga) } override fun onDestroyView() { @@ -81,6 +88,11 @@ class ExploreFragment : BaseFragment(), R.id.button_local -> MangaListActivity.newIntent(v.context, MangaSource.LOCAL) R.id.button_bookmarks -> BookmarksActivity.newIntent(v.context) R.id.button_suggestions -> SuggestionsActivity.newIntent(v.context) + R.id.button_favourites -> CategoriesActivity.newIntent(v.context) + R.id.button_random -> { + viewModel.openRandom() + return + } else -> return } startActivity(intent) @@ -95,6 +107,19 @@ class ExploreFragment : BaseFragment(), override fun onEmptyActionClick() = onManageClick(requireView()) + private fun onError(e: Throwable) { + Snackbar.make( + binding.recyclerView, + e.getDisplayMessage(resources), + Snackbar.LENGTH_SHORT + ).show() + } + + private fun onOpenManga(manga: Manga) { + val intent = DetailsActivity.newIntent(context ?: return, manga) + startActivity(intent) + } + companion object { fun newInstance() = ExploreFragment() diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt index 8d5b2c962..b00c63755 100644 --- a/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt @@ -1,24 +1,43 @@ package org.koitharu.kotatsu.explore.ui import androidx.lifecycle.LiveData +import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.* import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.explore.domain.ExploreRepository import org.koitharu.kotatsu.explore.ui.model.ExploreItem +import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct class ExploreViewModel( private val settings: AppSettings, + private val exploreRepository: ExploreRepository, ) : BaseViewModel() { - val content: LiveData> = settings.observe() + val onOpenManga = SingleLiveEvent() + + val content: LiveData> = isLoading.asFlow().flatMapLatest { loading -> + if (loading) { + flowOf(listOf(ExploreItem.Loading)) + } else { + createContentFlow() + } + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(ExploreItem.Loading)) + + fun openRandom() { + launchLoadingJob(Dispatchers.Default) { + val manga = exploreRepository.findRandomManga(tagsLimit = 8) + onOpenManga.postCall(manga) + } + } + + private fun createContentFlow() = settings.observe() .filter { it == AppSettings.KEY_SOURCES_HIDDEN || it == AppSettings.KEY_SOURCES_ORDER || @@ -28,7 +47,6 @@ class ExploreViewModel( .map { settings.getMangaSources(includeHidden = false) } .distinctUntilChanged() .map { buildList(it) } - .asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, emptyList()) private fun buildList(sources: List): List { val result = ArrayList(sources.size + 3) diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapter.kt index e9e0f9aff..10a40c900 100644 --- a/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapter.kt @@ -17,4 +17,5 @@ class ExploreAdapter( exploreSourcesHeaderAD(listener), exploreSourceItemAD(coil, clickListener, lifecycleOwner), exploreEmptyHintListAD(listener), + exploreLoadingAD(), ) \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt b/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt index 7a53d2402..5b870b8cc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt +++ b/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt @@ -6,7 +6,9 @@ import androidx.lifecycle.LifecycleOwner import coil.ImageLoader import coil.request.Disposable import coil.request.ImageRequest +import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding +import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.databinding.ItemEmptyCardBinding @@ -29,6 +31,8 @@ fun exploreButtonsAD( binding.buttonHistory.setOnClickListener(clickListener) binding.buttonLocal.setOnClickListener(clickListener) binding.buttonSuggestions.setOnClickListener(clickListener) + binding.buttonFavourites.setOnClickListener(clickListener) + binding.buttonRandom.setOnClickListener(clickListener) bind { binding.buttonSuggestions.isVisible = item.isSuggestionsEnabled @@ -101,4 +105,6 @@ fun exploreEmptyHintListAD( binding.textSecondary.setTextAndVisible(item.textSecondary) binding.buttonRetry.setTextAndVisible(item.actionStringRes) } -} \ No newline at end of file +} + +fun exploreLoadingAD() = adapterDelegate(R.layout.item_loading_state) {} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreDiffCallback.kt b/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreDiffCallback.kt index 768db9b6b..352edd401 100644 --- a/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreDiffCallback.kt +++ b/app/src/main/java/org/koitharu/kotatsu/explore/ui/adapter/ExploreDiffCallback.kt @@ -9,6 +9,8 @@ class ExploreDiffCallback : DiffUtil.ItemCallback() { return when { oldItem.javaClass != newItem.javaClass -> false oldItem is ExploreItem.Buttons && newItem is ExploreItem.Buttons -> true + oldItem is ExploreItem.Loading && newItem is ExploreItem.Loading -> true + oldItem is ExploreItem.EmptyHint && newItem is ExploreItem.EmptyHint -> true oldItem is ExploreItem.Source && newItem is ExploreItem.Source -> { oldItem.source == newItem.source } @@ -22,5 +24,4 @@ class ExploreDiffCallback : DiffUtil.ItemCallback() { override fun areContentsTheSame(oldItem: ExploreItem, newItem: ExploreItem): Boolean { return oldItem == newItem } - } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/explore/ui/model/ExploreItem.kt b/app/src/main/java/org/koitharu/kotatsu/explore/ui/model/ExploreItem.kt index 2c1a593d7..8ff7c70ba 100644 --- a/app/src/main/java/org/koitharu/kotatsu/explore/ui/model/ExploreItem.kt +++ b/app/src/main/java/org/koitharu/kotatsu/explore/ui/model/ExploreItem.kt @@ -82,4 +82,6 @@ sealed interface ExploreItem : ListModel { @StringRes textSecondary: Int, @StringRes actionStringRes: Int, ) : EmptyState(icon, textPrimary, textSecondary, actionStringRes), ExploreItem + + object Loading : ExploreItem } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_dice.xml b/app/src/main/res/drawable/ic_dice.xml new file mode 100644 index 000000000..66285027b --- /dev/null +++ b/app/src/main/res/drawable/ic_dice.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-w600dp/item_explore_buttons.xml b/app/src/main/res/layout-w600dp/item_explore_buttons.xml deleted file mode 100644 index 38f396541..000000000 --- a/app/src/main/res/layout-w600dp/item_explore_buttons.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_explore_buttons.xml b/app/src/main/res/layout/item_explore_buttons.xml index 3c85420f5..1864b1cd4 100644 --- a/app/src/main/res/layout/item_explore_buttons.xml +++ b/app/src/main/res/layout/item_explore_buttons.xml @@ -15,6 +15,14 @@ android:text="@string/history" app:icon="@drawable/ic_history" /> + + + + + + 3 + \ No newline at end of file diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml index 6bcbce89d..8560f5536 100644 --- a/app/src/main/res/values/integers.xml +++ b/app/src/main/res/values/integers.xml @@ -2,4 +2,5 @@ 3 + 2 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c7579ee53..db37a28b1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -329,4 +329,5 @@ Bookmarks removed No manga sources Enable manga sources to read manga online + Random \ No newline at end of file