Update explore fragment
This commit is contained in:
@@ -27,13 +27,13 @@ import org.koitharu.kotatsu.core.ui.util.SpanSizeResolver
|
||||
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf
|
||||
import org.koitharu.kotatsu.databinding.FragmentExploreBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.download.ui.list.DownloadsActivity
|
||||
import org.koitharu.kotatsu.explore.ui.adapter.ExploreAdapter
|
||||
import org.koitharu.kotatsu.explore.ui.adapter.ExploreListEventListener
|
||||
import org.koitharu.kotatsu.explore.ui.model.MangaSourceItem
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity
|
||||
import org.koitharu.kotatsu.history.ui.HistoryActivity
|
||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
@@ -65,7 +65,9 @@ class ExploreFragment :
|
||||
|
||||
override fun onViewBindingCreated(binding: FragmentExploreBinding, savedInstanceState: Bundle?) {
|
||||
super.onViewBindingCreated(binding, savedInstanceState)
|
||||
exploreAdapter = ExploreAdapter(coil, viewLifecycleOwner, this, this)
|
||||
exploreAdapter = ExploreAdapter(coil, viewLifecycleOwner, this, this) { manga, view ->
|
||||
startActivity(DetailsActivity.newIntent(view.context, manga), scaleUpActivityOptionsOf(view))
|
||||
}
|
||||
with(binding.recyclerView) {
|
||||
adapter = exploreAdapter
|
||||
setHasFixedSize(true)
|
||||
@@ -103,15 +105,14 @@ class ExploreFragment :
|
||||
|
||||
override fun onClick(v: View) {
|
||||
val intent = when (v.id) {
|
||||
R.id.button_history -> HistoryActivity.newIntent(v.context)
|
||||
R.id.button_local -> MangaListActivity.newIntent(v.context, MangaSource.LOCAL)
|
||||
R.id.button_bookmarks -> BookmarksActivity.newIntent(v.context)
|
||||
R.id.button_more -> SuggestionsActivity.newIntent(v.context)
|
||||
R.id.button_favourites -> FavouriteCategoriesActivity.newIntent(v.context)
|
||||
//R.id.button_random -> {
|
||||
// viewModel.openRandom()
|
||||
// return
|
||||
//}
|
||||
R.id.button_downloads -> DownloadsActivity.newIntent(v.context)
|
||||
R.id.button_random -> {
|
||||
viewModel.openRandom()
|
||||
return
|
||||
}
|
||||
|
||||
else -> return
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package org.koitharu.kotatsu.explore.ui
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@@ -31,7 +33,8 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val TIP_SUGGESTIONS = "suggestions"
|
||||
@@ -39,6 +42,7 @@ private const val TIP_SUGGESTIONS = "suggestions"
|
||||
@HiltViewModel
|
||||
class ExploreViewModel @Inject constructor(
|
||||
private val settings: AppSettings,
|
||||
private val suggestionRepository: SuggestionRepository,
|
||||
private val exploreRepository: ExploreRepository,
|
||||
) : BaseViewModel() {
|
||||
|
||||
@@ -51,6 +55,12 @@ class ExploreViewModel @Inject constructor(
|
||||
val onOpenManga = MutableEventFlow<Manga>()
|
||||
val onActionDone = MutableEventFlow<ReversibleAction>()
|
||||
val onShowSuggestionsTip = MutableEventFlow<Unit>()
|
||||
private val isRandomLoading = MutableStateFlow(false)
|
||||
private val recommendationDeferred = viewModelScope.async(Dispatchers.Default) {
|
||||
runCatchingCancellable {
|
||||
suggestionRepository.getRandom()
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
val content: StateFlow<List<ListModel>> = isLoading.flatMapLatest { loading ->
|
||||
if (loading) {
|
||||
@@ -69,9 +79,17 @@ class ExploreViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun openRandom() {
|
||||
launchLoadingJob(Dispatchers.Default) {
|
||||
val manga = exploreRepository.findRandomManga(tagsLimit = 8)
|
||||
onOpenManga.call(manga)
|
||||
if (isRandomLoading.value) {
|
||||
return
|
||||
}
|
||||
launchJob(Dispatchers.Default) {
|
||||
isRandomLoading.value = true
|
||||
try {
|
||||
val manga = exploreRepository.findRandomManga(tagsLimit = 8)
|
||||
onOpenManga.call(manga)
|
||||
} finally {
|
||||
isRandomLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,39 +112,27 @@ class ExploreViewModel @Inject constructor(
|
||||
settings.closeTip(TIP_SUGGESTIONS)
|
||||
}
|
||||
|
||||
private fun createContentFlow() = settings.observe()
|
||||
.filter {
|
||||
it == AppSettings.KEY_SOURCES_HIDDEN ||
|
||||
it == AppSettings.KEY_SOURCES_ORDER ||
|
||||
it == AppSettings.KEY_SUGGESTIONS
|
||||
}
|
||||
.onStart { emit("") }
|
||||
.map { settings.getMangaSources(includeHidden = false) }
|
||||
.combine(isGrid) { content, grid -> buildList(content, grid) }
|
||||
private fun createContentFlow() = combine(
|
||||
observeSources(),
|
||||
isGrid,
|
||||
isRandomLoading,
|
||||
) { content, grid, randomLoading ->
|
||||
val recommendation = recommendationDeferred.await()
|
||||
buildList(content, recommendation, grid, randomLoading)
|
||||
}
|
||||
|
||||
private fun buildList(sources: List<MangaSource>, isGrid: Boolean): List<ListModel> {
|
||||
private fun buildList(
|
||||
sources: List<MangaSource>,
|
||||
recommendation: Manga?,
|
||||
isGrid: Boolean,
|
||||
randomLoading: Boolean,
|
||||
): List<ListModel> {
|
||||
val result = ArrayList<ListModel>(sources.size + 4)
|
||||
result += ExploreButtons()
|
||||
result += ListHeader(R.string.suggestions, 0, null)
|
||||
result += RecommendationsItem(
|
||||
Manga(
|
||||
0,
|
||||
"Test",
|
||||
"Test",
|
||||
"Test",
|
||||
"Test",
|
||||
0f,
|
||||
false,
|
||||
"http://images.ctfassets.net/yadj1kx9rmg0/wtrHxeu3zEoEce2MokCSi/cf6f68efdcf625fdc060607df0f3baef/quwowooybuqbl6ntboz3.jpg",
|
||||
emptySet(),
|
||||
MangaState.ONGOING,
|
||||
"Test",
|
||||
"http://images.ctfassets.net/yadj1kx9rmg0/wtrHxeu3zEoEce2MokCSi/cf6f68efdcf625fdc060607df0f3baef/quwowooybuqbl6ntboz3.jpg",
|
||||
"Test",
|
||||
emptyList(),
|
||||
MangaSource.DESUME,
|
||||
),
|
||||
) // TODO
|
||||
result += ExploreButtons(randomLoading)
|
||||
if (recommendation != null) {
|
||||
result += ListHeader(R.string.suggestions, 0, null)
|
||||
result += RecommendationsItem(recommendation)
|
||||
}
|
||||
if (sources.isNotEmpty()) {
|
||||
result += ListHeader(R.string.remote_sources, R.string.manage, null)
|
||||
sources.mapTo(result) { MangaSourceItem(it, isGrid) }
|
||||
@@ -141,8 +147,17 @@ class ExploreViewModel @Inject constructor(
|
||||
return result
|
||||
}
|
||||
|
||||
private fun observeSources() = settings.observe()
|
||||
.filter {
|
||||
it == AppSettings.KEY_SOURCES_HIDDEN ||
|
||||
it == AppSettings.KEY_SOURCES_ORDER ||
|
||||
it == AppSettings.KEY_SUGGESTIONS
|
||||
}
|
||||
.onStart { emit("") }
|
||||
.map { settings.getMangaSources(includeHidden = false) }
|
||||
|
||||
private fun getLoadingStateList() = listOf(
|
||||
ExploreButtons(),
|
||||
ExploreButtons(isRandomLoading.value),
|
||||
LoadingState,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,18 +9,23 @@ import org.koitharu.kotatsu.list.ui.adapter.emptyHintAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.listHeaderAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
|
||||
class ExploreAdapter(
|
||||
coil: ImageLoader,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
listener: ExploreListEventListener,
|
||||
clickListener: OnListItemClickListener<MangaSourceItem>,
|
||||
mangaClickListener: OnListItemClickListener<Manga>,
|
||||
) : BaseListAdapter<ListModel>() {
|
||||
|
||||
init {
|
||||
delegatesManager
|
||||
.addDelegate(ITEM_TYPE_BUTTONS, exploreButtonsAD(listener))
|
||||
.addDelegate(ITEM_TYPE_RECOMMENDATION, exploreRecommendationItemAD(coil, listener, lifecycleOwner))
|
||||
.addDelegate(
|
||||
ITEM_TYPE_RECOMMENDATION,
|
||||
exploreRecommendationItemAD(coil, listener, mangaClickListener, lifecycleOwner),
|
||||
)
|
||||
.addDelegate(ITEM_TYPE_HEADER, listHeaderAD(listener))
|
||||
.addDelegate(ITEM_TYPE_SOURCE_LIST, exploreSourceListItemAD(coil, clickListener, lifecycleOwner))
|
||||
.addDelegate(ITEM_TYPE_SOURCE_GRID, exploreSourceGridItemAD(coil, clickListener, lifecycleOwner))
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.koitharu.kotatsu.explore.ui.adapter
|
||||
|
||||
import android.graphics.Color
|
||||
import android.view.View
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -11,7 +13,9 @@ import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.util.ext.disposeImageRequest
|
||||
import org.koitharu.kotatsu.core.util.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||
import org.koitharu.kotatsu.core.util.ext.newImageRequest
|
||||
import org.koitharu.kotatsu.core.util.ext.resolveDp
|
||||
import org.koitharu.kotatsu.core.util.ext.source
|
||||
import org.koitharu.kotatsu.databinding.ItemExploreButtonsBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemExploreSourceGridBinding
|
||||
@@ -21,6 +25,8 @@ import org.koitharu.kotatsu.explore.ui.model.ExploreButtons
|
||||
import org.koitharu.kotatsu.explore.ui.model.MangaSourceItem
|
||||
import org.koitharu.kotatsu.explore.ui.model.RecommendationsItem
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
fun exploreButtonsAD(
|
||||
clickListener: View.OnClickListener,
|
||||
@@ -29,26 +35,37 @@ fun exploreButtonsAD(
|
||||
) {
|
||||
|
||||
binding.buttonBookmarks.setOnClickListener(clickListener)
|
||||
binding.buttonHistory.setOnClickListener(clickListener)
|
||||
binding.buttonDownloads.setOnClickListener(clickListener)
|
||||
binding.buttonLocal.setOnClickListener(clickListener)
|
||||
//binding.buttonSuggestions.setOnClickListener(clickListener)
|
||||
binding.buttonFavourites.setOnClickListener(clickListener)
|
||||
//binding.buttonRandom.setOnClickListener(clickListener)
|
||||
binding.buttonRandom.setOnClickListener(clickListener)
|
||||
|
||||
//bind {
|
||||
// binding.buttonSuggestions.isVisible = item.isSuggestionsEnabled
|
||||
//}
|
||||
bind {
|
||||
if (item.isRandomLoading) {
|
||||
val icon = CircularProgressDrawable(context)
|
||||
icon.strokeWidth = context.resources.resolveDp(2f)
|
||||
icon.setColorSchemeColors(context.getThemeColor(materialR.attr.colorPrimary, Color.DKGRAY))
|
||||
binding.buttonRandom.icon = icon
|
||||
icon.start()
|
||||
} else {
|
||||
binding.buttonRandom.setIconResource(R.drawable.ic_dice)
|
||||
}
|
||||
binding.buttonRandom.isClickable = !item.isRandomLoading
|
||||
}
|
||||
}
|
||||
|
||||
fun exploreRecommendationItemAD(
|
||||
coil: ImageLoader,
|
||||
clickListener: View.OnClickListener,
|
||||
itemClickListener: OnListItemClickListener<Manga>,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
) = adapterDelegateViewBinding<RecommendationsItem, ListModel, ItemRecommendationBinding>(
|
||||
{ layoutInflater, parent -> ItemRecommendationBinding.inflate(layoutInflater, parent, false) }
|
||||
{ layoutInflater, parent -> ItemRecommendationBinding.inflate(layoutInflater, parent, false) },
|
||||
) {
|
||||
|
||||
binding.buttonMore.setOnClickListener(clickListener)
|
||||
binding.root.setOnClickListener { v ->
|
||||
itemClickListener.onItemClick(item.manga, v)
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.textViewTitle.text = item.manga.title
|
||||
|
||||
@@ -2,7 +2,9 @@ package org.koitharu.kotatsu.explore.ui.model
|
||||
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
|
||||
class ExploreButtons : ListModel {
|
||||
class ExploreButtons(
|
||||
val isRandomLoading: Boolean,
|
||||
) : ListModel {
|
||||
|
||||
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||
return other is ExploreButtons
|
||||
@@ -10,10 +12,14 @@ class ExploreButtons : ListModel {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
return javaClass == other?.javaClass
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ExploreButtons
|
||||
|
||||
return isRandomLoading == other.isRandomLoading
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return javaClass.hashCode()
|
||||
return isRandomLoading.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import org.koitharu.kotatsu.core.db.entity.toMangaTags
|
||||
import org.koitharu.kotatsu.core.util.ext.mapItems
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.suggestions.data.SuggestionEntity
|
||||
import org.koitharu.kotatsu.suggestions.data.SuggestionWithManga
|
||||
import javax.inject.Inject
|
||||
|
||||
class SuggestionRepository @Inject constructor(
|
||||
@@ -29,8 +28,10 @@ class SuggestionRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getRandom(): SuggestionWithManga? {
|
||||
return db.suggestionDao.getRandom()
|
||||
suspend fun getRandom(): Manga? {
|
||||
return db.suggestionDao.getRandom()?.let {
|
||||
it.manga.toManga(it.tags.toMangaTags())
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clear() {
|
||||
|
||||
@@ -72,7 +72,6 @@ class FeedFragment :
|
||||
addMenuProvider(
|
||||
FeedMenuProvider(
|
||||
binding.recyclerView,
|
||||
(activity as? BottomNavOwner)?.bottomNav,
|
||||
viewModel,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -8,12 +8,10 @@ import android.view.View
|
||||
import androidx.core.view.MenuProvider
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.dialog.CheckBoxAlertDialog
|
||||
import org.koitharu.kotatsu.settings.SettingsActivity
|
||||
import org.koitharu.kotatsu.tracker.work.TrackWorker
|
||||
|
||||
class FeedMenuProvider(
|
||||
private val snackbarHost: View,
|
||||
private val anchorView: View?,
|
||||
private val viewModel: FeedViewModel,
|
||||
) : MenuProvider {
|
||||
|
||||
@@ -43,12 +41,6 @@ class FeedMenuProvider(
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_settings -> {
|
||||
val intent = SettingsActivity.newTrackerSettingsIntent(context)
|
||||
context.startActivity(intent)
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,22 +7,6 @@
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingVertical="4dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_history"
|
||||
style="@style/Widget.Kotatsu.ExploreButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/history"
|
||||
app:icon="@drawable/ic_history" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_favourites"
|
||||
style="@style/Widget.Kotatsu.ExploreButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/favourites"
|
||||
app:icon="@drawable/ic_heart_outline" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_local"
|
||||
style="@style/Widget.Kotatsu.ExploreButton"
|
||||
@@ -39,11 +23,27 @@
|
||||
android:text="@string/bookmarks"
|
||||
app:icon="@drawable/ic_bookmark" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_random"
|
||||
style="@style/Widget.Kotatsu.ExploreButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/random"
|
||||
app:icon="@drawable/ic_dice" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_downloads"
|
||||
style="@style/Widget.Kotatsu.ExploreButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/downloads"
|
||||
app:icon="@drawable/ic_download" />
|
||||
|
||||
<androidx.constraintlayout.helper.widget.Flow
|
||||
android:id="@+id/flow"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="button_history,button_favourites,button_local,button_bookmarks"
|
||||
app:constraint_referenced_ids="button_local,button_bookmarks,button_random,button_downloads"
|
||||
app:flow_horizontalGap="12dp"
|
||||
app:flow_maxElementsWrap="@integer/explore_buttons_columns"
|
||||
app:flow_verticalGap="8dp"
|
||||
|
||||
@@ -15,10 +15,4 @@
|
||||
android:title="@string/clear_feed"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:orderInCategory="50"
|
||||
android:title="@string/settings"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
</menu>
|
||||
|
||||
Reference in New Issue
Block a user