From d00822a6c3d8c39e5a23ccb49dcbc41ec23d2412 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 3 Aug 2024 16:22:46 +0300 Subject: [PATCH] Quick filter in history draft implementation --- .../kotatsu/core/ui/widgets/ChipsView.kt | 10 ++- .../kotatsu/history/data/HistoryDao.kt | 21 ++++- .../kotatsu/history/data/HistoryRepository.kt | 9 ++- .../kotatsu/history/ui/HistoryListFragment.kt | 5 ++ .../history/ui/HistoryListViewModel.kt | 80 +++++++++++++++---- .../kotatsu/list/domain/ListFilterOption.kt | 28 +++++++ .../kotatsu/list/ui/MangaListFragment.kt | 3 + .../kotatsu/list/ui/adapter/ListItemType.kt | 1 + .../list/ui/adapter/MangaListAdapter.kt | 1 + .../ui/adapter/MangaListDetailedItemAD.kt | 2 +- .../list/ui/adapter/MangaListListener.kt | 2 +- .../kotatsu/list/ui/adapter/QuickFilterAD.kt | 25 ++++++ .../ui/adapter/QuickFilterClickListener.kt | 8 ++ .../ui/adapter/TypedListSpacingDecoration.kt | 1 + .../kotatsu/list/ui/model/QuickFilter.kt | 13 +++ .../search/ui/multi/MultiSearchActivity.kt | 3 + .../kotatsu/tracker/ui/feed/FeedFragment.kt | 3 + app/src/main/res/layout/item_quick_filter.xml | 24 ++++++ app/src/main/res/values/strings.xml | 1 + 19 files changed, 218 insertions(+), 22 deletions(-) create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/list/domain/ListFilterOption.kt create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/QuickFilterAD.kt create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/QuickFilterClickListener.kt create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/QuickFilter.kt create mode 100644 app/src/main/res/layout/item_quick_filter.xml 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 fd332ca5b..66a5e9227 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 @@ -5,6 +5,7 @@ import android.util.AttributeSet import android.view.View.OnClickListener import androidx.annotation.ColorRes import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.core.view.children import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipDrawable @@ -92,7 +93,11 @@ class ChipsView @JvmOverloads constructor( } private fun bindChip(chip: Chip, model: ChipModel) { - chip.text = model.title + if (model.titleResId == 0) { + chip.text = model.title + } else { + chip.setText(model.titleResId) + } chip.isClickable = onChipClickListener != null || model.isCheckable chip.isCheckable = model.isCheckable if (model.icon == 0) { @@ -139,7 +144,8 @@ class ChipsView @JvmOverloads constructor( } data class ChipModel( - val title: CharSequence, + val title: CharSequence? = null, + @StringRes val titleResId: Int = 0, @DrawableRes val icon: Int = 0, val isCheckable: Boolean = false, @ColorRes val tint: Int = 0, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryDao.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryDao.kt index 93b3b8db2..b54ddb1e4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryDao.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryDao.kt @@ -10,6 +10,7 @@ import androidx.sqlite.db.SimpleSQLiteQuery import androidx.sqlite.db.SupportSQLiteQuery import kotlinx.coroutines.flow.Flow import org.koitharu.kotatsu.core.db.entity.TagEntity +import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.domain.ListSortOrder @Dao @@ -27,7 +28,11 @@ abstract class HistoryDao { @Query("SELECT * FROM history WHERE deleted_at = 0 ORDER BY updated_at DESC LIMIT :limit") abstract fun observeAll(limit: Int): Flow> - fun observeAll(order: ListSortOrder, limit: Int): Flow> { + fun observeAll( + order: ListSortOrder, + filterOptions: Set, + limit: Int + ): Flow> { val orderBy = when (order) { ListSortOrder.LAST_READ -> "history.updated_at DESC" ListSortOrder.LONG_AGO_READ -> "history.updated_at ASC" @@ -44,8 +49,13 @@ abstract class HistoryDao { val query = buildString { append( "SELECT * FROM history LEFT JOIN manga ON history.manga_id = manga.manga_id " + - "WHERE history.deleted_at = 0 GROUP BY history.manga_id ORDER BY ", + "WHERE history.deleted_at = 0", ) + for (option in filterOptions) { + append(" AND ") + append(option.getCondition()) + } + append(" GROUP BY history.manga_id ORDER BY ") append(orderBy) if (limit > 0) { append(" LIMIT ") @@ -147,4 +157,11 @@ abstract class HistoryDao { @Transaction @RawQuery(observedEntities = [HistoryEntity::class]) protected abstract fun observeAllImpl(query: SupportSQLiteQuery): Flow> + + private fun ListFilterOption.getCondition(): String = when (this) { + ListFilterOption.NEW_CHAPTERS -> "(SELECT chapters_new FROM tracks WHERE tracks.manga_id = history.manga_id) > 0" + ListFilterOption.FAVORITE -> "EXISTS(SELECT * FROM favourites WHERE history.manga_id = favourites.manga_id)" + ListFilterOption.COMPLETED -> "percent >= 0.9999" + ListFilterOption.DOWNLOADED -> throw IllegalArgumentException("Unsupported option $this") + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryRepository.kt index f9a85f9df..c745858eb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/data/HistoryRepository.kt @@ -23,6 +23,7 @@ import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode import org.koitharu.kotatsu.core.ui.util.ReversibleHandle import org.koitharu.kotatsu.core.util.ext.mapItems import org.koitharu.kotatsu.history.domain.model.MangaWithHistory +import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.domain.ListSortOrder import org.koitharu.kotatsu.list.domain.ReadingProgress import org.koitharu.kotatsu.parsers.model.Manga @@ -76,8 +77,12 @@ class HistoryRepository @Inject constructor( } } - fun observeAllWithHistory(order: ListSortOrder, limit: Int): Flow> { - return db.getHistoryDao().observeAll(order, limit).mapItems { + fun observeAllWithHistory( + order: ListSortOrder, + filterOptions: Set, + limit: Int + ): Flow> { + return db.getHistoryDao().observeAll(order, filterOptions, limit).mapItems { MangaWithHistory( it.manga.toManga(it.tags.toMangaTags()), it.history.toMangaHistory(), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt index 03c2ee975..feadf3018 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt @@ -16,6 +16,7 @@ import org.koitharu.kotatsu.core.ui.util.MenuInvalidator import org.koitharu.kotatsu.core.util.ext.addMenuProvider import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.databinding.FragmentListBinding +import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.list.ui.size.DynamicItemSizeResolver @@ -34,6 +35,10 @@ class HistoryListFragment : MangaListFragment() { override fun onScrolledToEnd() = viewModel.requestMoreItems() + override fun onFilterOptionClick(option: ListFilterOption) { + viewModel.onFilterOptionClick(option) + } + override fun onEmptyActionClick() { startActivity(NetworkManageIntent()) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt index 6485c98f3..b027e9f81 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt @@ -21,13 +21,16 @@ import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.core.prefs.observeAsStateFlow import org.koitharu.kotatsu.core.ui.util.ReversibleAction +import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.core.util.ext.calculateTimeAgo import org.koitharu.kotatsu.core.util.ext.call +import org.koitharu.kotatsu.core.util.ext.combine import org.koitharu.kotatsu.core.util.ext.onFirst import org.koitharu.kotatsu.download.ui.worker.DownloadWorker import org.koitharu.kotatsu.history.data.HistoryRepository import org.koitharu.kotatsu.history.domain.MarkAsReadUseCase import org.koitharu.kotatsu.history.domain.model.MangaWithHistory +import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.domain.ListSortOrder import org.koitharu.kotatsu.list.domain.MangaListMapper import org.koitharu.kotatsu.list.ui.MangaListViewModel @@ -36,11 +39,13 @@ import org.koitharu.kotatsu.list.ui.model.EmptyState import org.koitharu.kotatsu.list.ui.model.ListHeader import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.LoadingState +import org.koitharu.kotatsu.list.ui.model.QuickFilter import org.koitharu.kotatsu.list.ui.model.TipModel import org.koitharu.kotatsu.list.ui.model.toErrorState import org.koitharu.kotatsu.local.data.LocalMangaRepository import org.koitharu.kotatsu.parsers.model.Manga import java.time.Instant +import java.util.EnumSet import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject @@ -63,6 +68,8 @@ class HistoryListViewModel @Inject constructor( valueProducer = { historySortOrder }, ) + private val filterOptions = MutableStateFlow>(EnumSet.noneOf(ListFilterOption::class.java)) + override val listMode = settings.observeAsStateFlow( scope = viewModelScope + Dispatchers.Default, key = AppSettings.KEY_LIST_MODE_HISTORY, @@ -86,25 +93,25 @@ class HistoryListViewModel @Inject constructor( ) override val content = combine( + filterOptions, observeHistory(), isGroupingEnabled, observeListModeWithTriggers(), networkState, settings.observeAsFlow(AppSettings.KEY_INCOGNITO_MODE) { isIncognitoModeEnabled }, - ) { list, grouped, mode, online, incognito -> + ) { filters, list, grouped, mode, online, incognito -> when { - list.isEmpty() -> listOf( - EmptyState( - icon = R.drawable.ic_empty_history, - textPrimary = R.string.text_history_holder_primary, - textSecondary = R.string.text_history_holder_secondary, - actionStringRes = 0, - ), - ) + list.isEmpty() -> { + if (filters.isEmpty()) { + listOf(getEmptyState(hasFilters = false)) + } else { + listOf(filterItem(filters), getEmptyState(hasFilters = true)) + } + } else -> { isReady.set(true) - mapList(list, grouped, mode, online, incognito) + mapList(filters, list, grouped, mode, online, incognito) } } }.onStart { @@ -154,17 +161,29 @@ class HistoryListViewModel @Inject constructor( } } - private fun observeHistory() = combine(sortOrder, limit, ::Pair) - .flatMapLatest { repository.observeAllWithHistory(it.first, it.second) } + fun onFilterOptionClick(option: ListFilterOption) { + filterOptions.value = EnumSet.copyOf(filterOptions.value).also { + if (option in it) { + it.remove(option) + } else { + it.add(option) + } + } + } + + private fun observeHistory() = combine(sortOrder, filterOptions, limit, ::Triple) + .flatMapLatest { repository.observeAllWithHistory(it.first, it.second - ListFilterOption.DOWNLOADED, it.third) } private suspend fun mapList( + filters: Set, list: List, grouped: Boolean, mode: ListMode, isOnline: Boolean, isIncognito: Boolean, ): List { - val result = ArrayList(if (grouped) (list.size * 1.4).toInt() else list.size + 2) + val result = ArrayList((if (grouped) (list.size * 1.4).toInt() else list.size) + 3) + result += filterItem(filters) if (isIncognito) { result += TipModel( key = AppSettings.KEY_INCOGNITO_MODE, @@ -185,12 +204,14 @@ class HistoryListViewModel @Inject constructor( actionStringRes = R.string.manage, ) } + var isEmpty = true for ((m, history) in list) { - val manga = if (!isOnline && !m.isLocal) { + val manga = if ((!isOnline && !m.isLocal) || ListFilterOption.DOWNLOADED in filters) { localMangaRepository.findSavedManga(m)?.manga ?: continue } else { m } + isEmpty = false if (grouped) { val header = history.header(order) if (header != prevHeader) { @@ -202,6 +223,9 @@ class HistoryListViewModel @Inject constructor( } result += mangaListMapper.toListModel(manga, mode) } + if (filters.isNotEmpty() && isEmpty) { + result += getEmptyState(hasFilters = true) + } return result } @@ -229,4 +253,32 @@ class HistoryListViewModel @Inject constructor( ListSortOrder.UPDATED, ListSortOrder.RATING -> null } + + private fun filterItem(selected: Set) = QuickFilter( + items = ListFilterOption.HISTORY.map { option -> + ChipsView.ChipModel( + titleResId = option.titleResId, + icon = option.iconResId, + isCheckable = true, + isChecked = option in selected, + data = option, + ) + }, + ) + + private fun getEmptyState(hasFilters: Boolean) = if (hasFilters) { + EmptyState( + icon = R.drawable.ic_empty_history, + textPrimary = R.string.nothing_found, + textSecondary = R.string.text_history_holder_secondary_filtered, + actionStringRes = 0, + ) + } else { + EmptyState( + icon = R.drawable.ic_empty_history, + textPrimary = R.string.text_history_holder_primary, + textSecondary = R.string.text_history_holder_secondary, + actionStringRes = 0, + ) + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/domain/ListFilterOption.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/domain/ListFilterOption.kt new file mode 100644 index 000000000..5d138bf01 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/domain/ListFilterOption.kt @@ -0,0 +1,28 @@ +package org.koitharu.kotatsu.list.domain + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import org.koitharu.kotatsu.R +import java.util.EnumSet + +enum class ListFilterOption( + @StringRes val titleResId: Int, + @DrawableRes val iconResId: Int, +) { + + DOWNLOADED(R.string.on_device, R.drawable.ic_storage), + COMPLETED(R.string.status_completed, R.drawable.ic_state_finished), + NEW_CHAPTERS(R.string.new_chapters, R.drawable.ic_updated), + FAVORITE(R.string.favourites, R.drawable.ic_heart_outline), + ; + + companion object { + + val HISTORY: Set = EnumSet.of( + DOWNLOADED, + NEW_CHAPTERS, + FAVORITE, + COMPLETED, + ) + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 03d46fe2c..e2cb67a02 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -44,6 +44,7 @@ import org.koitharu.kotatsu.databinding.FragmentListBinding import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteSheet +import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter import org.koitharu.kotatsu.list.ui.adapter.MangaListListener @@ -226,6 +227,8 @@ abstract class MangaListFragment : } } + override fun onFilterOptionClick(option: ListFilterOption) = Unit + override fun onFilterClick(view: View?) = Unit override fun onEmptyActionClick() = Unit diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ListItemType.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ListItemType.kt index b5d9eeae1..5ced570e7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ListItemType.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/ListItemType.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.list.ui.adapter enum class ListItemType { + FILTER_HEADER, FILTER_SORT, FILTER_TAG, FILTER_TAG_MULTI, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt index 4719a42a4..4589d3467 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt @@ -24,6 +24,7 @@ open class MangaListAdapter( addDelegate(ListItemType.STATE_EMPTY, emptyStateListAD(coil, lifecycleOwner, listener)) addDelegate(ListItemType.HINT_EMPTY, emptyHintAD(coil, lifecycleOwner, listener)) addDelegate(ListItemType.HEADER, listHeaderAD(listener)) + addDelegate(ListItemType.FILTER_HEADER, quickFilterAD(listener)) addDelegate(ListItemType.TIP, tipAD(listener)) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt index aa8449e47..05dac38a8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt @@ -52,7 +52,7 @@ fun mangaListDetailedItemAD( source(item.source) enqueueWith(coil) } - binding.textViewTags.text = item.tags.joinToString(separator = ", ") { it.title } + binding.textViewTags.text = item.tags.joinToString(separator = ", ") { it.title ?: "" } badge = itemView.bindBadge(badge, item.counter) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListListener.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListListener.kt index 5da2f168e..609126836 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListListener.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListListener.kt @@ -5,7 +5,7 @@ import org.koitharu.kotatsu.core.ui.widgets.TipView import org.koitharu.kotatsu.parsers.model.MangaTag interface MangaListListener : MangaDetailsClickListener, ListStateHolderListener, ListHeaderClickListener, - TipView.OnButtonClickListener { + TipView.OnButtonClickListener, QuickFilterClickListener { fun onUpdateFilter(tags: Set) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/QuickFilterAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/QuickFilterAD.kt new file mode 100644 index 000000000..de5743dca --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/QuickFilterAD.kt @@ -0,0 +1,25 @@ +package org.koitharu.kotatsu.list.ui.adapter + +import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding +import org.koitharu.kotatsu.core.ui.widgets.ChipsView +import org.koitharu.kotatsu.databinding.ItemQuickFilterBinding +import org.koitharu.kotatsu.list.domain.ListFilterOption +import org.koitharu.kotatsu.list.ui.model.ListModel +import org.koitharu.kotatsu.list.ui.model.QuickFilter + +fun quickFilterAD( + listener: QuickFilterClickListener, +) = adapterDelegateViewBinding( + { layoutInflater, parent -> ItemQuickFilterBinding.inflate(layoutInflater, parent, false) } +) { + + binding.chipsTags.onChipClickListener = ChipsView.OnChipClickListener { chip, data -> + if (data is ListFilterOption) { + listener.onFilterOptionClick(data) + } + } + + bind { + binding.chipsTags.setChips(item.items) + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/QuickFilterClickListener.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/QuickFilterClickListener.kt new file mode 100644 index 000000000..8f447f3ef --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/QuickFilterClickListener.kt @@ -0,0 +1,8 @@ +package org.koitharu.kotatsu.list.ui.adapter + +import org.koitharu.kotatsu.list.domain.ListFilterOption + +interface QuickFilterClickListener { + + fun onFilterOptionClick(option: ListFilterOption) +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/TypedListSpacingDecoration.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/TypedListSpacingDecoration.kt index a69464c25..39e158d6d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/TypedListSpacingDecoration.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/TypedListSpacingDecoration.kt @@ -32,6 +32,7 @@ class TypedListSpacingDecoration( ListItemType.FILTER_TAG_MULTI, ListItemType.FILTER_STATE, ListItemType.FILTER_LANGUAGE, + ListItemType.FILTER_HEADER, -> outRect.set(0) ListItemType.HEADER, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/QuickFilter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/QuickFilter.kt new file mode 100644 index 000000000..fba190dbb --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/QuickFilter.kt @@ -0,0 +1,13 @@ +package org.koitharu.kotatsu.list.ui.model + +import org.koitharu.kotatsu.core.ui.widgets.ChipsView +import org.koitharu.kotatsu.list.ui.ListModelDiffCallback + +data class QuickFilter( + val items: List, +) : ListModel { + + override fun areItemsTheSame(other: ListModel): Boolean = other is QuickFilter + + override fun getChangePayload(previousState: ListModel) = ListModelDiffCallback.PAYLOAD_NESTED_LIST_CHANGED +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt index 2e4ecf344..e121b274c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/MultiSearchActivity.kt @@ -27,6 +27,7 @@ import org.koitharu.kotatsu.databinding.ActivitySearchMultiBinding import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteSheet +import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration import org.koitharu.kotatsu.list.ui.adapter.MangaListListener import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration @@ -133,6 +134,8 @@ class MultiSearchActivity : viewModel.retry() } + override fun onFilterOptionClick(option: ListFilterOption) = Unit + override fun onUpdateFilter(tags: Set) = Unit override fun onFilterClick(view: View?) = Unit diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt index 6d16293ff..6e9e112d8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt @@ -24,6 +24,7 @@ import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.databinding.FragmentFeedBinding import org.koitharu.kotatsu.details.ui.DetailsActivity +import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.ui.adapter.MangaListListener import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.model.ListHeader @@ -94,6 +95,8 @@ class FeedFragment : viewModel.update() } + override fun onFilterOptionClick(option: ListFilterOption) = Unit + override fun onRetryClick(error: Throwable) = Unit override fun onUpdateFilter(tags: Set) = Unit diff --git a/app/src/main/res/layout/item_quick_filter.xml b/app/src/main/res/layout/item_quick_filter.xml new file mode 100644 index 000000000..fa301a8d5 --- /dev/null +++ b/app/src/main/res/layout/item_quick_filter.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a3ed043af..c7b6b25a6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -96,6 +96,7 @@ Try to reformulate the query. What you read will be displayed here Find what to read in the «Explore» section + There are no manga matching the filters you selected Save something first Save something from an online catalog or import it from a file. Shelf