From c663d1051525d70aa23dff9125eb93cc40ff2fa2 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 24 Oct 2022 19:32:28 +0300 Subject: [PATCH] Configure shelf sections --- .github/FUNDING.yml | 2 +- .../AdapterDelegateClickListenerAdapter.kt | 4 +- .../koitharu/kotatsu/core/prefs/AppSection.kt | 6 -- .../kotatsu/core/prefs/AppSettings.kt | 20 ++++-- .../kotatsu/shelf/domain/ShelfContent.kt | 35 ++++++++++ .../kotatsu/shelf/domain/ShelfRepository.kt | 11 +++ .../kotatsu/shelf/domain/ShelfSection.kt | 6 ++ .../kotatsu/shelf/ui/ShelfMenuProvider.kt | 8 ++- .../kotatsu/shelf/ui/ShelfViewModel.kt | 48 +++++++------ .../ShelfCategoriesConfigAdapter.kt | 32 --------- .../ShelfCategoriesConfigViewModel.kt | 30 -------- .../ui/config/categories/ShelfCategoryAD.kt | 21 ------ .../ui/config/categories/ShelfConfigAD.kt | 51 ++++++++++++++ .../config/categories/ShelfConfigAdapter.kt | 42 +++++++++++ .../ui/config/categories/ShelfConfigModel.kt | 60 ++++++++++++++++ ...riesConfigSheet.kt => ShelfConfigSheet.kt} | 15 ++-- .../config/categories/ShelfConfigViewModel.kt | 69 +++++++++++++++++++ app/src/main/res/menu/opt_shelf.xml | 2 +- 18 files changed, 330 insertions(+), 132 deletions(-) delete mode 100644 app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSection.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfContent.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfSection.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoriesConfigAdapter.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoriesConfigViewModel.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoryAD.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigAD.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigAdapter.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigModel.kt rename app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/{ShelfCategoriesConfigSheet.kt => ShelfConfigSheet.kt} (73%) create mode 100644 app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigViewModel.kt diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 203df6bfe..03f83f88f 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,2 @@ +ko_fi: xtimms custom: ["https://yoomoney.ru/to/410012543938752"] -ko_fi: koitharu diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/list/AdapterDelegateClickListenerAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/list/AdapterDelegateClickListenerAdapter.kt index 650e816c5..19d1d5661 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/list/AdapterDelegateClickListenerAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/list/AdapterDelegateClickListenerAdapter.kt @@ -6,7 +6,7 @@ import android.view.View.OnLongClickListener import com.hannesdorfmann.adapterdelegates4.dsl.AdapterDelegateViewBindingViewHolder class AdapterDelegateClickListenerAdapter( - private val adapterDelegate: AdapterDelegateViewBindingViewHolder, + private val adapterDelegate: AdapterDelegateViewBindingViewHolder, private val clickListener: OnListItemClickListener, ) : OnClickListener, OnLongClickListener { @@ -17,4 +17,4 @@ class AdapterDelegateClickListenerAdapter( override fun onLongClick(v: View): Boolean { return clickListener.onItemLongClick(adapterDelegate.item, v) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSection.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSection.kt deleted file mode 100644 index 0efa45c92..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSection.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.koitharu.kotatsu.core.prefs - -enum class AppSection { - - LOCAL, FAVOURITES, HISTORY, FEED, SUGGESTIONS -} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt index 20a29269b..6fa3a8803 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -16,6 +16,8 @@ import org.koitharu.kotatsu.core.model.ZoomMode import org.koitharu.kotatsu.core.network.DoHProvider import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.util.mapToSet +import org.koitharu.kotatsu.shelf.domain.ShelfSection import org.koitharu.kotatsu.utils.ext.getEnumValue import org.koitharu.kotatsu.utils.ext.observe import org.koitharu.kotatsu.utils.ext.putEnumValue @@ -44,14 +46,23 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { val remoteMangaSources: Set get() = Collections.unmodifiableSet(remoteSources) + var shelfSections: Set + get() { + val raw = prefs.getStringSet(KEY_SHELF_SECTIONS, null) + if (raw == null) { + return EnumSet.allOf(ShelfSection::class.java) + } + return raw.mapTo(EnumSet.noneOf(ShelfSection::class.java)) { ShelfSection.valueOf(it) } + } + set(value) { + val raw = value.mapToSet { it.name } + prefs.edit { putStringSet(KEY_SHELF_SECTIONS, raw) } + } + var listMode: ListMode get() = prefs.getEnumValue(KEY_LIST_MODE, ListMode.GRID) set(value) = prefs.edit { putEnumValue(KEY_LIST_MODE, value) } - var defaultSection: AppSection - get() = prefs.getEnumValue(KEY_APP_SECTION, AppSection.HISTORY) - set(value) = prefs.edit { putEnumValue(KEY_APP_SECTION, value) } - val theme: Int get() = prefs.getString(KEY_THEME, null)?.toIntOrNull() ?: AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM @@ -341,6 +352,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { const val KEY_READER_TAPS_LTR = "reader_taps_ltr" const val KEY_LOCAL_LIST_ORDER = "local_order" const val KEY_WEBTOON_ZOOM = "webtoon_zoom" + const val KEY_SHELF_SECTIONS = "shelf_sections" // About const val KEY_APP_UPDATE = "app_update" diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfContent.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfContent.kt new file mode 100644 index 000000000..c8089e431 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfContent.kt @@ -0,0 +1,35 @@ +package org.koitharu.kotatsu.shelf.domain + +import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.history.domain.MangaWithHistory +import org.koitharu.kotatsu.parsers.model.Manga + +class ShelfContent( + val history: List, + val favourites: Map>, + val updated: Map, + val local: List, +) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ShelfContent + + if (history != other.history) return false + if (favourites != other.favourites) return false + if (updated != other.updated) return false + if (local != other.local) return false + + return true + } + + override fun hashCode(): Int { + var result = history.hashCode() + result = 31 * result + favourites.hashCode() + result = 31 * result + updated.hashCode() + result = 31 * result + local.hashCode() + return result + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfRepository.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfRepository.kt index c56a04a35..7ecfa7dbf 100644 --- a/app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfRepository.kt @@ -21,15 +21,26 @@ import org.koitharu.kotatsu.history.domain.HistoryRepository import org.koitharu.kotatsu.local.domain.LocalMangaRepository import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.utils.ext.runCatchingCancellable import javax.inject.Inject class ShelfRepository @Inject constructor( private val localMangaRepository: LocalMangaRepository, private val historyRepository: HistoryRepository, + private val trackingRepository: TrackingRepository, private val db: MangaDatabase, ) { + fun observeShelfContent(): Flow = combine( + historyRepository.observeAllWithHistory(), + observeLocalManga(SortOrder.UPDATED), + observeFavourites(), + trackingRepository.observeUpdatedManga(), + ) { history, local, favorites, updated -> + ShelfContent(history, favorites, updated, local) + } + fun observeLocalManga(sortOrder: SortOrder): Flow> { return flow { emit(null) diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfSection.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfSection.kt new file mode 100644 index 000000000..de24a9583 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/shelf/domain/ShelfSection.kt @@ -0,0 +1,6 @@ +package org.koitharu.kotatsu.shelf.domain + +enum class ShelfSection { + + HISTORY, LOCAL, UPDATED, FAVORITES; +} diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/ShelfMenuProvider.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/ShelfMenuProvider.kt index 589432ed7..b4d8fb5a2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/ShelfMenuProvider.kt +++ b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/ShelfMenuProvider.kt @@ -12,7 +12,7 @@ import java.util.* import java.util.concurrent.TimeUnit import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.dialog.RememberSelectionDialogListener -import org.koitharu.kotatsu.shelf.ui.config.categories.ShelfCategoriesConfigSheet +import org.koitharu.kotatsu.shelf.ui.config.categories.ShelfConfigSheet import org.koitharu.kotatsu.shelf.ui.config.size.ShelfSizeBottomSheet import org.koitharu.kotatsu.local.ui.ImportDialogFragment import org.koitharu.kotatsu.utils.ext.startOfDay @@ -33,18 +33,22 @@ class ShelfMenuProvider( showClearHistoryDialog() true } + R.id.action_grid_size -> { ShelfSizeBottomSheet.show(fragmentManager) true } + R.id.action_import -> { ImportDialogFragment.show(fragmentManager) true } + R.id.action_categories -> { - ShelfCategoriesConfigSheet.show(fragmentManager) + ShelfConfigSheet.show(fragmentManager) true } + else -> false } } diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/ShelfViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/ShelfViewModel.kt index 2e88331b2..53ca50f88 100644 --- a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/ShelfViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/ShelfViewModel.kt @@ -15,6 +15,7 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.os.NetworkStateObserver import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ListMode +import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.history.domain.HistoryRepository import org.koitharu.kotatsu.history.domain.MangaWithHistory @@ -29,8 +30,9 @@ import org.koitharu.kotatsu.list.ui.model.toGridModel import org.koitharu.kotatsu.list.ui.model.toUi import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaSource -import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.shelf.domain.ShelfContent import org.koitharu.kotatsu.shelf.domain.ShelfRepository +import org.koitharu.kotatsu.shelf.domain.ShelfSection import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.utils.SingleLiveEvent @@ -44,19 +46,17 @@ class ShelfViewModel @Inject constructor( private val favouritesRepository: FavouritesRepository, private val trackingRepository: TrackingRepository, private val settings: AppSettings, - private val networkStateObserver: NetworkStateObserver, + networkStateObserver: NetworkStateObserver, ) : BaseViewModel(), ListExtraProvider { val onActionDone = SingleLiveEvent() val content: LiveData> = combine( + settings.observeAsFlow(AppSettings.KEY_SHELF_SECTIONS) { shelfSections }, networkStateObserver, - historyRepository.observeAllWithHistory(), - repository.observeLocalManga(SortOrder.UPDATED), - repository.observeFavourites(), - trackingRepository.observeUpdatedManga(), - ) { isConnected, history, local, favourites, updated -> - mapList(history, favourites, updated, local, isConnected) + repository.observeShelfContent(), + ) { sections, isConnected, content -> + mapList(content, sections, isConnected) }.debounce(500) .catch { e -> emit(listOf(e.toErrorState(canRetry = false))) @@ -134,25 +134,23 @@ class ShelfViewModel @Inject constructor( } private suspend fun mapList( - history: List, - favourites: Map>, - updated: Map, - local: List, + content: ShelfContent, + sections: Set, isNetworkAvailable: Boolean, ): List { - val result = ArrayList(favourites.keys.size + 3) + val result = ArrayList(content.favourites.keys.size + 3) if (isNetworkAvailable) { - if (history.isNotEmpty()) { - mapHistory(result, history) + if (content.history.isNotEmpty() && ShelfSection.HISTORY in sections) { + mapHistory(result, content.history) } - if (local.isNotEmpty()) { - mapLocal(result, local) + if (content.local.isNotEmpty() && ShelfSection.LOCAL in sections) { + mapLocal(result, content.local) } - if (updated.isNotEmpty()) { - mapUpdated(result, updated) + if (content.updated.isNotEmpty() && ShelfSection.UPDATED in sections) { + mapUpdated(result, content.updated) } - if (favourites.isNotEmpty()) { - mapFavourites(result, favourites) + if (content.favourites.isNotEmpty() && ShelfSection.FAVORITES in sections) { + mapFavourites(result, content.favourites) } } else { result += EmptyHint( @@ -161,12 +159,12 @@ class ShelfViewModel @Inject constructor( textSecondary = R.string.network_unavailable_hint, actionStringRes = R.string.manage, ) - val offlineHistory = history.filter { it.manga.source == MangaSource.LOCAL } - if (offlineHistory.isNotEmpty()) { + val offlineHistory = content.history.filter { it.manga.source == MangaSource.LOCAL } + if (offlineHistory.isNotEmpty() && ShelfSection.HISTORY in sections) { mapHistory(result, offlineHistory) } - if (local.isNotEmpty()) { - mapLocal(result, local) + if (content.local.isNotEmpty() && ShelfSection.LOCAL in sections) { + mapLocal(result, content.local) } } if (result.isEmpty()) { diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoriesConfigAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoriesConfigAdapter.kt deleted file mode 100644 index 12d0b4a5d..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoriesConfigAdapter.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.koitharu.kotatsu.shelf.ui.config.categories - -import androidx.recyclerview.widget.DiffUtil -import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter -import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener -import org.koitharu.kotatsu.core.model.FavouriteCategory - -class ShelfCategoriesConfigAdapter( - listener: OnListItemClickListener, -) : AsyncListDifferDelegationAdapter(DiffCallback()) { - - init { - delegatesManager.addDelegate(shelfCategoryAD(listener)) - } - - class DiffCallback : DiffUtil.ItemCallback() { - - override fun areItemsTheSame(oldItem: FavouriteCategory, newItem: FavouriteCategory): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame(oldItem: FavouriteCategory, newItem: FavouriteCategory): Boolean { - return oldItem.isVisibleInLibrary == newItem.isVisibleInLibrary && oldItem.title == newItem.title - } - - override fun getChangePayload(oldItem: FavouriteCategory, newItem: FavouriteCategory): Any? { - return if (oldItem.isVisibleInLibrary == newItem.isVisibleInLibrary) { - super.getChangePayload(oldItem, newItem) - } else Unit - } - } -} diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoriesConfigViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoriesConfigViewModel.kt deleted file mode 100644 index 1c77b243e..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoriesConfigViewModel.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.koitharu.kotatsu.shelf.ui.config.categories - -import androidx.lifecycle.viewModelScope -import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import org.koitharu.kotatsu.base.ui.BaseViewModel -import org.koitharu.kotatsu.core.model.FavouriteCategory -import org.koitharu.kotatsu.favourites.domain.FavouritesRepository -import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct - -@HiltViewModel -class ShelfCategoriesConfigViewModel @Inject constructor( - private val favouritesRepository: FavouritesRepository, -) : BaseViewModel() { - - val content = favouritesRepository.observeCategories() - .asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, emptyList()) - - private var updateJob: Job? = null - - fun toggleItem(category: FavouriteCategory) { - val prevJob = updateJob - updateJob = launchJob(Dispatchers.Default) { - prevJob?.join() - favouritesRepository.updateCategory(category.id, !category.isVisibleInLibrary) - } - } -} diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoryAD.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoryAD.kt deleted file mode 100644 index 9aa529210..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoryAD.kt +++ /dev/null @@ -1,21 +0,0 @@ -package org.koitharu.kotatsu.shelf.ui.config.categories - -import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding -import org.koitharu.kotatsu.base.ui.list.AdapterDelegateClickListenerAdapter -import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener -import org.koitharu.kotatsu.core.model.FavouriteCategory -import org.koitharu.kotatsu.databinding.ItemCategoryCheckableMultipleBinding - -fun shelfCategoryAD( - listener: OnListItemClickListener, -) = adapterDelegateViewBinding( - { layoutInflater, parent -> ItemCategoryCheckableMultipleBinding.inflate(layoutInflater, parent, false) }, -) { - val eventListener = AdapterDelegateClickListenerAdapter(this, listener) - itemView.setOnClickListener(eventListener) - - bind { - binding.root.text = item.title - binding.root.isChecked = item.isVisibleInLibrary - } -} diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigAD.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigAD.kt new file mode 100644 index 000000000..a83180efb --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigAD.kt @@ -0,0 +1,51 @@ +package org.koitharu.kotatsu.shelf.ui.config.categories + +import androidx.core.view.updatePaddingRelative +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.ItemCategoryCheckableMultipleBinding +import org.koitharu.kotatsu.shelf.domain.ShelfSection + +fun shelfSectionAD( + listener: OnListItemClickListener, +) = adapterDelegateViewBinding( + { layoutInflater, parent -> ItemCategoryCheckableMultipleBinding.inflate(layoutInflater, parent, false) }, +) { + + val eventListener = AdapterDelegateClickListenerAdapter(this, listener) + itemView.setOnClickListener(eventListener) + + bind { + binding.root.setText(item.section.titleResId) + binding.root.isChecked = item.isChecked + } +} + +fun shelfCategoryAD( + listener: OnListItemClickListener, +) = + adapterDelegateViewBinding( + { layoutInflater, parent -> ItemCategoryCheckableMultipleBinding.inflate(layoutInflater, parent, false) }, + ) { + val eventListener = AdapterDelegateClickListenerAdapter(this, listener) + itemView.setOnClickListener(eventListener) + binding.root.updatePaddingRelative( + start = binding.root.paddingStart * 2, + end = binding.root.paddingStart, + ) + + bind { + binding.root.text = item.title + binding.root.isChecked = item.isChecked + } + } + +private val ShelfSection.titleResId: Int + get() = when (this) { + ShelfSection.HISTORY -> R.string.history + ShelfSection.LOCAL -> R.string.local_storage + ShelfSection.UPDATED -> R.string.updated + ShelfSection.FAVORITES -> R.string.favourites + } diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigAdapter.kt new file mode 100644 index 000000000..166dcf680 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigAdapter.kt @@ -0,0 +1,42 @@ +package org.koitharu.kotatsu.shelf.ui.config.categories + +import androidx.recyclerview.widget.DiffUtil +import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter +import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener + +class ShelfConfigAdapter( + listener: OnListItemClickListener, +) : AsyncListDifferDelegationAdapter(DiffCallback()) { + + init { + delegatesManager.addDelegate(shelfCategoryAD(listener)) + .addDelegate(shelfSectionAD(listener)) + } + + class DiffCallback : DiffUtil.ItemCallback() { + + override fun areItemsTheSame(oldItem: ShelfConfigModel, newItem: ShelfConfigModel): Boolean { + return when { + oldItem is ShelfConfigModel.Section && newItem is ShelfConfigModel.Section -> { + oldItem.section == newItem.section + } + + oldItem is ShelfConfigModel.FavouriteCategory && newItem is ShelfConfigModel.FavouriteCategory -> { + oldItem.id == newItem.id + } + + else -> false + } + } + + override fun areContentsTheSame(oldItem: ShelfConfigModel, newItem: ShelfConfigModel): Boolean { + return oldItem == newItem + } + + override fun getChangePayload(oldItem: ShelfConfigModel, newItem: ShelfConfigModel): Any? { + return if (oldItem.isChecked == newItem.isChecked) { + super.getChangePayload(oldItem, newItem) + } else Unit + } + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigModel.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigModel.kt new file mode 100644 index 000000000..f385b84be --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigModel.kt @@ -0,0 +1,60 @@ +package org.koitharu.kotatsu.shelf.ui.config.categories + +import org.koitharu.kotatsu.list.ui.model.ListModel +import org.koitharu.kotatsu.shelf.domain.ShelfSection + +sealed interface ShelfConfigModel : ListModel { + + val isChecked: Boolean + + class Section( + val section: ShelfSection, + override val isChecked: Boolean, + ) : ShelfConfigModel { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Section + + if (section != other.section) return false + if (isChecked != other.isChecked) return false + + return true + } + + override fun hashCode(): Int { + var result = section.hashCode() + result = 31 * result + isChecked.hashCode() + return result + } + } + + class FavouriteCategory( + val id: Long, + val title: String, + override val isChecked: Boolean, + ) : ShelfConfigModel { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as FavouriteCategory + + if (id != other.id) return false + if (title != other.title) return false + if (isChecked != other.isChecked) return false + + return true + } + + override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + title.hashCode() + result = 31 * result + isChecked.hashCode() + return result + } + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoriesConfigSheet.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigSheet.kt similarity index 73% rename from app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoriesConfigSheet.kt rename to app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigSheet.kt index 5df721191..0f2b7ab88 100644 --- a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfCategoriesConfigSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigSheet.kt @@ -11,16 +11,15 @@ import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener -import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.databinding.SheetBaseBinding @AndroidEntryPoint -class ShelfCategoriesConfigSheet : +class ShelfConfigSheet : BaseBottomSheet(), - OnListItemClickListener, + OnListItemClickListener, View.OnClickListener { - private val viewModel by viewModels() + private val viewModel by viewModels() override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetBaseBinding { return SheetBaseBinding.inflate(inflater, container, false) @@ -28,16 +27,16 @@ class ShelfCategoriesConfigSheet : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.headerBar.toolbar.setTitle(R.string.favourites_categories) + binding.headerBar.setTitle(R.string.settings) binding.buttonDone.isVisible = true binding.buttonDone.setOnClickListener(this) - val adapter = ShelfCategoriesConfigAdapter(this) + val adapter = ShelfConfigAdapter(this) binding.recyclerView.adapter = adapter viewModel.content.observe(viewLifecycleOwner) { adapter.items = it } } - override fun onItemClick(item: FavouriteCategory, view: View) { + override fun onItemClick(item: ShelfConfigModel, view: View) { viewModel.toggleItem(item) } @@ -49,6 +48,6 @@ class ShelfCategoriesConfigSheet : private const val TAG = "ShelfCategoriesConfigSheet" - fun show(fm: FragmentManager) = ShelfCategoriesConfigSheet().show(fm, TAG) + fun show(fm: FragmentManager) = ShelfConfigSheet().show(fm, TAG) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigViewModel.kt new file mode 100644 index 000000000..5e9cb38da --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/shelf/ui/config/categories/ShelfConfigViewModel.kt @@ -0,0 +1,69 @@ +package org.koitharu.kotatsu.shelf.ui.config.categories + +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.combine +import org.koitharu.kotatsu.base.ui.BaseViewModel +import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.core.prefs.observeAsFlow +import org.koitharu.kotatsu.favourites.domain.FavouritesRepository +import org.koitharu.kotatsu.shelf.domain.ShelfSection +import org.koitharu.kotatsu.utils.asFlowLiveData +import javax.inject.Inject + +@HiltViewModel +class ShelfConfigViewModel @Inject constructor( + private val favouritesRepository: FavouritesRepository, + private val settings: AppSettings, +) : BaseViewModel() { + + val content = combine( + settings.observeAsFlow(AppSettings.KEY_SHELF_SECTIONS) { shelfSections }, + favouritesRepository.observeCategories(), + ) { sections, categories -> + buildList(sections, categories) + }.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, emptyList()) + + private var updateJob: Job? = null + + fun toggleItem(item: ShelfConfigModel) { + val prevJob = updateJob + updateJob = launchJob(Dispatchers.Default) { + prevJob?.join() + when (item) { + is ShelfConfigModel.FavouriteCategory -> { + favouritesRepository.updateCategory(item.id, !item.isChecked) + } + + is ShelfConfigModel.Section -> { + if (item.isChecked) { + settings.shelfSections -= item.section + } else { + settings.shelfSections += item.section + } + } + } + } + } + + private fun buildList(sections: Set, categories: List): List { + val result = ArrayList() + for (section in ShelfSection.values()) { + val isEnabled = section in sections + result.add(ShelfConfigModel.Section(section, isEnabled)) + if (section == ShelfSection.FAVORITES && isEnabled) { + categories.mapTo(result) { + ShelfConfigModel.FavouriteCategory( + id = it.id, + title = it.title, + isChecked = it.isVisibleInLibrary, + ) + } + } + } + return result + } +} diff --git a/app/src/main/res/menu/opt_shelf.xml b/app/src/main/res/menu/opt_shelf.xml index 3cefebcb3..3a88eac5c 100644 --- a/app/src/main/res/menu/opt_shelf.xml +++ b/app/src/main/res/menu/opt_shelf.xml @@ -12,7 +12,7 @@