Improve favorite categories screen (fix #1047)
This commit is contained in:
@@ -71,5 +71,11 @@ open class ReorderableListAdapter<T : ListModel> : ListDelegationAdapter<List<T>
|
|||||||
val newItem = newList[newItemPosition]
|
val newItem = newList[newItemPosition]
|
||||||
return newItem == oldItem
|
return newItem == oldItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
|
||||||
|
val oldItem = oldList[oldItemPosition]
|
||||||
|
val newItem = newList[newItemPosition]
|
||||||
|
return newItem.getChangePayload(oldItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.os.Bundle
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.core.graphics.Insets
|
import androidx.core.graphics.Insets
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
@@ -100,6 +101,18 @@ class FavouriteCategoriesActivity :
|
|||||||
return item != null && selectionController.onItemLongClick(item.id)
|
return item != null && selectionController.onItemLongClick(item.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSupportActionModeStarted(mode: ActionMode) {
|
||||||
|
super.onSupportActionModeStarted(mode)
|
||||||
|
viewBinding.fabAdd.hide()
|
||||||
|
viewModel.setActionsEnabled(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSupportActionModeFinished(mode: ActionMode) {
|
||||||
|
super.onSupportActionModeFinished(mode)
|
||||||
|
viewBinding.fabAdd.show()
|
||||||
|
viewModel.setActionsEnabled(true)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onShowAllClick(isChecked: Boolean) {
|
override fun onShowAllClick(isChecked: Boolean) {
|
||||||
viewModel.setAllCategoriesVisible(isChecked)
|
viewModel.setAllCategoriesVisible(isChecked)
|
||||||
}
|
}
|
||||||
@@ -138,6 +151,14 @@ class FavouriteCategoriesActivity :
|
|||||||
0,
|
0,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
override fun getDragDirs(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||||
|
return if (actionModeDelegate.isActionModeStarted) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
super.getDragDirs(recyclerView, viewHolder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
|
||||||
|
|
||||||
override fun onMove(
|
override fun onMove(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.cancelAndJoin
|
import kotlinx.coroutines.cancelAndJoin
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
@@ -34,13 +35,15 @@ class FavouritesCategoriesViewModel @Inject constructor(
|
|||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
private var commitJob: Job? = null
|
private var commitJob: Job? = null
|
||||||
|
private val isActionsEnabled = MutableStateFlow(true)
|
||||||
|
|
||||||
val content = combine(
|
val content = combine(
|
||||||
repository.observeCategoriesWithCovers(),
|
repository.observeCategoriesWithCovers(),
|
||||||
observeAllCategories(),
|
observeAllCategories(),
|
||||||
settings.observeAsFlow(AppSettings.KEY_ALL_FAVOURITES_VISIBLE) { isAllFavouritesVisible },
|
settings.observeAsFlow(AppSettings.KEY_ALL_FAVOURITES_VISIBLE) { isAllFavouritesVisible },
|
||||||
) { cats, all, showAll ->
|
isActionsEnabled,
|
||||||
cats.toUiList(all, showAll)
|
) { cats, all, showAll, hasActions ->
|
||||||
|
cats.toUiList(all, showAll, hasActions)
|
||||||
}.withErrorHandling()
|
}.withErrorHandling()
|
||||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState))
|
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState))
|
||||||
|
|
||||||
@@ -77,6 +80,10 @@ class FavouritesCategoriesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setActionsEnabled(value: Boolean) {
|
||||||
|
isActionsEnabled.value = value
|
||||||
|
}
|
||||||
|
|
||||||
fun getCategories(ids: LongSet): ArrayList<FavouriteCategory> {
|
fun getCategories(ids: LongSet): ArrayList<FavouriteCategory> {
|
||||||
val items = content.requireValue()
|
val items = content.requireValue()
|
||||||
return items.mapNotNullTo(ArrayList(ids.size)) { item ->
|
return items.mapNotNullTo(ArrayList(ids.size)) { item ->
|
||||||
@@ -86,7 +93,8 @@ class FavouritesCategoriesViewModel @Inject constructor(
|
|||||||
|
|
||||||
private fun Map<FavouriteCategory, List<Cover>>.toUiList(
|
private fun Map<FavouriteCategory, List<Cover>>.toUiList(
|
||||||
allFavorites: Pair<Int, List<Cover>>,
|
allFavorites: Pair<Int, List<Cover>>,
|
||||||
showAll: Boolean
|
showAll: Boolean,
|
||||||
|
hasActions: Boolean,
|
||||||
): List<ListModel> {
|
): List<ListModel> {
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return listOf(
|
return listOf(
|
||||||
@@ -104,6 +112,7 @@ class FavouritesCategoriesViewModel @Inject constructor(
|
|||||||
mangaCount = allFavorites.first,
|
mangaCount = allFavorites.first,
|
||||||
covers = allFavorites.second,
|
covers = allFavorites.second,
|
||||||
isVisible = showAll,
|
isVisible = showAll,
|
||||||
|
isActionsEnabled = hasActions,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
mapTo(result) { (category, covers) ->
|
mapTo(result) { (category, covers) ->
|
||||||
@@ -111,6 +120,7 @@ class FavouritesCategoriesViewModel @Inject constructor(
|
|||||||
mangaCount = covers.size,
|
mangaCount = covers.size,
|
||||||
covers = covers.take(3),
|
covers = covers.take(3),
|
||||||
category = category,
|
category = category,
|
||||||
|
isActionsEnabled = hasActions,
|
||||||
isTrackerEnabled = settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources,
|
isTrackerEnabled = settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,17 @@ data class AllCategoriesListModel(
|
|||||||
val mangaCount: Int,
|
val mangaCount: Int,
|
||||||
val covers: List<Cover>,
|
val covers: List<Cover>,
|
||||||
val isVisible: Boolean,
|
val isVisible: Boolean,
|
||||||
|
val isActionsEnabled: Boolean,
|
||||||
) : ListModel {
|
) : ListModel {
|
||||||
|
|
||||||
override fun areItemsTheSame(other: ListModel): Boolean {
|
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||||
return other is AllCategoriesListModel
|
return other is AllCategoriesListModel
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getChangePayload(previousState: ListModel): Any? {
|
override fun getChangePayload(previousState: ListModel): Any? = when {
|
||||||
return if (previousState is AllCategoriesListModel && previousState.isVisible != isVisible) {
|
previousState !is AllCategoriesListModel -> super.getChangePayload(previousState)
|
||||||
ListModelDiffCallback.PAYLOAD_CHECKED_CHANGED
|
previousState.isVisible != isVisible -> ListModelDiffCallback.PAYLOAD_CHECKED_CHANGED
|
||||||
} else {
|
previousState.isActionsEnabled != isActionsEnabled -> ListModelDiffCallback.PAYLOAD_ANYTHING_CHANGED
|
||||||
super.getChangePayload(previousState)
|
else -> super.getChangePayload(previousState)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ fun categoryAD(
|
|||||||
binding.imageViewHandle.setOnTouchListener(eventListener)
|
binding.imageViewHandle.setOnTouchListener(eventListener)
|
||||||
|
|
||||||
bind {
|
bind {
|
||||||
|
binding.imageViewHandle.isVisible = item.isActionsEnabled
|
||||||
|
binding.imageViewEdit.isVisible = item.isActionsEnabled
|
||||||
binding.textViewTitle.text = item.category.title
|
binding.textViewTitle.text = item.category.title
|
||||||
binding.textViewSubtitle.text = if (item.mangaCount == 0) {
|
binding.textViewSubtitle.text = if (item.mangaCount == 0) {
|
||||||
getString(R.string.empty)
|
getString(R.string.empty)
|
||||||
@@ -138,6 +140,7 @@ fun allCategoriesAD(
|
|||||||
item.mangaCount,
|
item.mangaCount,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
binding.imageViewVisible.isVisible = item.isActionsEnabled
|
||||||
binding.imageViewVisible.setImageResource(
|
binding.imageViewVisible.setImageResource(
|
||||||
if (item.isVisible) {
|
if (item.isVisible) {
|
||||||
R.drawable.ic_eye
|
R.drawable.ic_eye
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.favourites.ui.categories.adapter
|
|||||||
|
|
||||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
import org.koitharu.kotatsu.favourites.domain.model.Cover
|
import org.koitharu.kotatsu.favourites.domain.model.Cover
|
||||||
|
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
|
|
||||||
class CategoryListModel(
|
class CategoryListModel(
|
||||||
@@ -9,12 +10,19 @@ class CategoryListModel(
|
|||||||
val covers: List<Cover>,
|
val covers: List<Cover>,
|
||||||
val category: FavouriteCategory,
|
val category: FavouriteCategory,
|
||||||
val isTrackerEnabled: Boolean,
|
val isTrackerEnabled: Boolean,
|
||||||
|
val isActionsEnabled: Boolean,
|
||||||
) : ListModel {
|
) : ListModel {
|
||||||
|
|
||||||
override fun areItemsTheSame(other: ListModel): Boolean {
|
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||||
return other is CategoryListModel && other.category.id == category.id
|
return other is CategoryListModel && other.category.id == category.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getChangePayload(previousState: ListModel): Any? = when {
|
||||||
|
previousState !is CategoryListModel -> super.getChangePayload(previousState)
|
||||||
|
previousState.isActionsEnabled != isActionsEnabled -> ListModelDiffCallback.PAYLOAD_ANYTHING_CHANGED
|
||||||
|
else -> super.getChangePayload(previousState)
|
||||||
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
@@ -23,6 +31,7 @@ class CategoryListModel(
|
|||||||
|
|
||||||
if (mangaCount != other.mangaCount) return false
|
if (mangaCount != other.mangaCount) return false
|
||||||
if (isTrackerEnabled != other.isTrackerEnabled) return false
|
if (isTrackerEnabled != other.isTrackerEnabled) return false
|
||||||
|
if (isActionsEnabled != other.isActionsEnabled) return false
|
||||||
if (covers != other.covers) return false
|
if (covers != other.covers) return false
|
||||||
if (category.id != other.category.id) return false
|
if (category.id != other.category.id) return false
|
||||||
if (category.title != other.category.title) return false
|
if (category.title != other.category.title) return false
|
||||||
@@ -36,6 +45,7 @@ class CategoryListModel(
|
|||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = mangaCount
|
var result = mangaCount
|
||||||
result = 31 * result + isTrackerEnabled.hashCode()
|
result = 31 * result + isTrackerEnabled.hashCode()
|
||||||
|
result = 31 * result + isActionsEnabled.hashCode()
|
||||||
result = 31 * result + covers.hashCode()
|
result = 31 * result + covers.hashCode()
|
||||||
result = 31 * result + category.id.hashCode()
|
result = 31 * result + category.id.hashCode()
|
||||||
result = 31 * result + category.title.hashCode()
|
result = 31 * result + category.title.hashCode()
|
||||||
|
|||||||
@@ -179,6 +179,7 @@
|
|||||||
layout="@layout/layout_details_chips"
|
layout="@layout/layout_details_chips"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/screen_padding"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/barrier_header" />
|
app:layout_constraintTop_toBottomOf="@id/barrier_header" />
|
||||||
|
|||||||
@@ -54,18 +54,17 @@
|
|||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||||
tools:listitem="@layout/item_category" />
|
tools:listitem="@layout/item_category" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/fab_add"
|
android:id="@+id/fab_add"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="16dp"
|
android:layout_margin="16dp"
|
||||||
android:contentDescription="@string/add_new_category"
|
android:contentDescription="@string/add_new_category"
|
||||||
android:text="@string/create_category"
|
android:src="@drawable/ic_add"
|
||||||
app:fabSize="normal"
|
app:fabSize="normal"
|
||||||
app:icon="@drawable/ic_add"
|
|
||||||
app:layout_anchor="@id/recyclerView"
|
app:layout_anchor="@id/recyclerView"
|
||||||
app:layout_anchorGravity="bottom|end"
|
app:layout_anchorGravity="bottom|end"
|
||||||
app:layout_behavior="org.koitharu.kotatsu.core.ui.util.ShrinkOnScrollBehavior"
|
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
|
||||||
app:layout_dodgeInsetEdges="bottom" />
|
app:layout_dodgeInsetEdges="bottom" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|||||||
Reference in New Issue
Block a user