Fix pages thumbnails loading

pull/362/merge
Koitharu 3 years ago
parent 5b53f8c27d
commit 97524d66f2
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -2,9 +2,9 @@ package org.koitharu.kotatsu.core.model
import android.os.Parcelable import android.os.Parcelable
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.koitharu.kotatsu.list.domain.ListSortOrder
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.SortOrder
import java.util.Date import java.util.Date
@Parcelize @Parcelize
@ -12,7 +12,7 @@ data class FavouriteCategory(
val id: Long, val id: Long,
val title: String, val title: String,
val sortKey: Int, val sortKey: Int,
val order: SortOrder, val order: ListSortOrder,
val createdAt: Date, val createdAt: Date,
val isTrackingEnabled: Boolean, val isTrackingEnabled: Boolean,
val isVisibleInLibrary: Boolean, val isVisibleInLibrary: Boolean,

@ -22,7 +22,7 @@ import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.putEnumValue import org.koitharu.kotatsu.core.util.ext.putEnumValue
import org.koitharu.kotatsu.core.util.ext.takeIfReadable import org.koitharu.kotatsu.core.util.ext.takeIfReadable
import org.koitharu.kotatsu.core.util.ext.toUriOrNull import org.koitharu.kotatsu.core.util.ext.toUriOrNull
import org.koitharu.kotatsu.history.domain.model.HistoryOrder import org.koitharu.kotatsu.list.domain.ListSortOrder
import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.util.find import org.koitharu.kotatsu.parsers.util.find
import org.koitharu.kotatsu.parsers.util.mapNotNullToSet import org.koitharu.kotatsu.parsers.util.mapNotNullToSet
@ -76,6 +76,10 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
get() = prefs.getEnumValue(KEY_LIST_MODE_HISTORY, listMode) get() = prefs.getEnumValue(KEY_LIST_MODE_HISTORY, listMode)
set(value) = prefs.edit { putEnumValue(KEY_LIST_MODE_HISTORY, value) } set(value) = prefs.edit { putEnumValue(KEY_LIST_MODE_HISTORY, value) }
var suggestionsListMode: ListMode
get() = prefs.getEnumValue(KEY_LIST_MODE_SUGGESTIONS, listMode)
set(value) = prefs.edit { putEnumValue(KEY_LIST_MODE_SUGGESTIONS, value) }
var favoritesListMode: ListMode var favoritesListMode: ListMode
get() = prefs.getEnumValue(KEY_LIST_MODE_FAVORITES, listMode) get() = prefs.getEnumValue(KEY_LIST_MODE_FAVORITES, listMode)
set(value) = prefs.edit { putEnumValue(KEY_LIST_MODE_FAVORITES, value) } set(value) = prefs.edit { putEnumValue(KEY_LIST_MODE_FAVORITES, value) }
@ -315,8 +319,8 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
get() = prefs.getEnumValue(KEY_LOCAL_LIST_ORDER, SortOrder.NEWEST) get() = prefs.getEnumValue(KEY_LOCAL_LIST_ORDER, SortOrder.NEWEST)
set(value) = prefs.edit { putEnumValue(KEY_LOCAL_LIST_ORDER, value) } set(value) = prefs.edit { putEnumValue(KEY_LOCAL_LIST_ORDER, value) }
var historySortOrder: HistoryOrder var historySortOrder: ListSortOrder
get() = prefs.getEnumValue(KEY_HISTORY_ORDER, HistoryOrder.UPDATED) get() = prefs.getEnumValue(KEY_HISTORY_ORDER, ListSortOrder.UPDATED)
set(value) = prefs.edit { putEnumValue(KEY_HISTORY_ORDER, value) } set(value) = prefs.edit { putEnumValue(KEY_HISTORY_ORDER, value) }
val isRelatedMangaEnabled: Boolean val isRelatedMangaEnabled: Boolean
@ -417,6 +421,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_LIST_MODE = "list_mode_2" const val KEY_LIST_MODE = "list_mode_2"
const val KEY_LIST_MODE_HISTORY = "list_mode_history" const val KEY_LIST_MODE_HISTORY = "list_mode_history"
const val KEY_LIST_MODE_FAVORITES = "list_mode_favorites" const val KEY_LIST_MODE_FAVORITES = "list_mode_favorites"
const val KEY_LIST_MODE_SUGGESTIONS = "list_mode_suggestions"
const val KEY_THEME = "theme" const val KEY_THEME = "theme"
const val KEY_COLOR_THEME = "color_theme" const val KEY_COLOR_THEME = "color_theme"
const val KEY_THEME_AMOLED = "amoled_theme" const val KEY_THEME_AMOLED = "amoled_theme"

@ -55,3 +55,5 @@ inline fun <reified E : Enum<E>> Collection<E>.toEnumSet(): EnumSet<E> = if (isE
} else { } else {
EnumSet.copyOf(this) EnumSet.copyOf(this)
} }
fun <E : Enum<E>> Collection<E>.sortedByOrdinal() = sortedBy { it.ordinal }

@ -1,17 +1,16 @@
package org.koitharu.kotatsu.favourites.data package org.koitharu.kotatsu.favourites.data
import org.koitharu.kotatsu.core.db.entity.SortOrder
import org.koitharu.kotatsu.core.db.entity.toManga import org.koitharu.kotatsu.core.db.entity.toManga
import org.koitharu.kotatsu.core.db.entity.toMangaTags import org.koitharu.kotatsu.core.db.entity.toMangaTags
import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.list.domain.ListSortOrder
import java.util.Date import java.util.Date
fun FavouriteCategoryEntity.toFavouriteCategory(id: Long = categoryId.toLong()) = FavouriteCategory( fun FavouriteCategoryEntity.toFavouriteCategory(id: Long = categoryId.toLong()) = FavouriteCategory(
id = id, id = id,
title = title, title = title,
sortKey = sortKey, sortKey = sortKey,
order = SortOrder(order, SortOrder.NEWEST), order = ListSortOrder(order, ListSortOrder.NEWEST),
createdAt = Date(createdAt), createdAt = Date(createdAt),
isTrackingEnabled = track, isTrackingEnabled = track,
isVisibleInLibrary = isVisibleInLibrary, isVisibleInLibrary = isVisibleInLibrary,

@ -1,13 +1,19 @@
package org.koitharu.kotatsu.favourites.data package org.koitharu.kotatsu.favourites.data
import androidx.room.* import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.RawQuery
import androidx.room.Transaction
import androidx.room.Upsert
import androidx.sqlite.db.SimpleSQLiteQuery import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.sqlite.db.SupportSQLiteQuery import androidx.sqlite.db.SupportSQLiteQuery
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import org.intellij.lang.annotations.Language import org.intellij.lang.annotations.Language
import org.koitharu.kotatsu.core.db.entity.MangaEntity import org.koitharu.kotatsu.core.db.entity.MangaEntity
import org.koitharu.kotatsu.favourites.domain.model.Cover import org.koitharu.kotatsu.favourites.domain.model.Cover
import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.list.domain.ListSortOrder
@Dao @Dao
abstract class FavouritesDao { abstract class FavouritesDao {
@ -22,7 +28,7 @@ abstract class FavouritesDao {
@Query("SELECT * FROM favourites WHERE deleted_at = 0 GROUP BY manga_id ORDER BY created_at DESC LIMIT :limit") @Query("SELECT * FROM favourites WHERE deleted_at = 0 GROUP BY manga_id ORDER BY created_at DESC LIMIT :limit")
abstract suspend fun findLast(limit: Int): List<FavouriteManga> abstract suspend fun findLast(limit: Int): List<FavouriteManga>
fun observeAll(order: SortOrder): Flow<List<FavouriteManga>> { fun observeAll(order: ListSortOrder): Flow<List<FavouriteManga>> {
val orderBy = getOrderBy(order) val orderBy = getOrderBy(order)
@Language("RoomSql") @Language("RoomSql")
@ -47,7 +53,7 @@ abstract class FavouritesDao {
) )
abstract suspend fun findAll(categoryId: Long): List<FavouriteManga> abstract suspend fun findAll(categoryId: Long): List<FavouriteManga>
fun observeAll(categoryId: Long, order: SortOrder): Flow<List<FavouriteManga>> { fun observeAll(categoryId: Long, order: ListSortOrder): Flow<List<FavouriteManga>> {
val orderBy = getOrderBy(order) val orderBy = getOrderBy(order)
@Language("RoomSql") @Language("RoomSql")
@ -72,7 +78,7 @@ abstract class FavouritesDao {
) )
abstract suspend fun findAllManga(categoryId: Int): List<MangaEntity> abstract suspend fun findAllManga(categoryId: Int): List<MangaEntity>
suspend fun findCovers(categoryId: Long, order: SortOrder): List<Cover> { suspend fun findCovers(categoryId: Long, order: ListSortOrder): List<Cover> {
val orderBy = getOrderBy(order) val orderBy = getOrderBy(order)
@Language("RoomSql") @Language("RoomSql")
@ -157,13 +163,13 @@ abstract class FavouritesDao {
@Query("UPDATE favourites SET deleted_at = :deletedAt WHERE category_id = :categoryId AND deleted_at = 0") @Query("UPDATE favourites SET deleted_at = :deletedAt WHERE category_id = :categoryId AND deleted_at = 0")
protected abstract suspend fun setDeletedAtAll(categoryId: Long, deletedAt: Long) protected abstract suspend fun setDeletedAtAll(categoryId: Long, deletedAt: Long)
private fun getOrderBy(sortOrder: SortOrder) = when (sortOrder) { private fun getOrderBy(sortOrder: ListSortOrder) = when (sortOrder) {
SortOrder.RATING -> "rating DESC" ListSortOrder.RATING -> "rating DESC"
SortOrder.NEWEST, ListSortOrder.NEWEST,
SortOrder.UPDATED, ListSortOrder.UPDATED,
-> "created_at DESC" -> "created_at DESC"
SortOrder.ALPHABETICAL -> "title ASC" ListSortOrder.ALPHABETIC -> "title ASC"
else -> throw IllegalArgumentException("Sort order $sortOrder is not supported") else -> throw IllegalArgumentException("Sort order $sortOrder is not supported")
} }
} }

@ -8,7 +8,6 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.db.entity.SortOrder
import org.koitharu.kotatsu.core.db.entity.toEntities import org.koitharu.kotatsu.core.db.entity.toEntities
import org.koitharu.kotatsu.core.db.entity.toEntity import org.koitharu.kotatsu.core.db.entity.toEntity
import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.FavouriteCategory
@ -20,8 +19,8 @@ import org.koitharu.kotatsu.favourites.data.toFavouriteCategory
import org.koitharu.kotatsu.favourites.data.toManga import org.koitharu.kotatsu.favourites.data.toManga
import org.koitharu.kotatsu.favourites.data.toMangaList import org.koitharu.kotatsu.favourites.data.toMangaList
import org.koitharu.kotatsu.favourites.domain.model.Cover import org.koitharu.kotatsu.favourites.domain.model.Cover
import org.koitharu.kotatsu.list.domain.ListSortOrder
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels
import javax.inject.Inject import javax.inject.Inject
@ -41,7 +40,7 @@ class FavouritesRepository @Inject constructor(
return entities.toMangaList() return entities.toMangaList()
} }
fun observeAll(order: SortOrder): Flow<List<Manga>> { fun observeAll(order: ListSortOrder): Flow<List<Manga>> {
return db.favouritesDao.observeAll(order) return db.favouritesDao.observeAll(order)
.mapItems { it.toManga() } .mapItems { it.toManga() }
} }
@ -51,7 +50,7 @@ class FavouritesRepository @Inject constructor(
return entities.toMangaList() return entities.toMangaList()
} }
fun observeAll(categoryId: Long, order: SortOrder): Flow<List<Manga>> { fun observeAll(categoryId: Long, order: ListSortOrder): Flow<List<Manga>> {
return db.favouritesDao.observeAll(categoryId, order) return db.favouritesDao.observeAll(categoryId, order)
.mapItems { it.toManga() } .mapItems { it.toManga() }
} }
@ -105,7 +104,7 @@ class FavouritesRepository @Inject constructor(
suspend fun createCategory( suspend fun createCategory(
title: String, title: String,
sortOrder: SortOrder, sortOrder: ListSortOrder,
isTrackerEnabled: Boolean, isTrackerEnabled: Boolean,
isVisibleOnShelf: Boolean, isVisibleOnShelf: Boolean,
): FavouriteCategory { ): FavouriteCategory {
@ -128,7 +127,7 @@ class FavouritesRepository @Inject constructor(
suspend fun updateCategory( suspend fun updateCategory(
id: Long, id: Long,
title: String, title: String,
sortOrder: SortOrder, sortOrder: ListSortOrder,
isTrackerEnabled: Boolean, isTrackerEnabled: Boolean,
isVisibleOnShelf: Boolean, isVisibleOnShelf: Boolean,
) { ) {
@ -156,7 +155,7 @@ class FavouritesRepository @Inject constructor(
} }
} }
suspend fun setCategoryOrder(id: Long, order: SortOrder) { suspend fun setCategoryOrder(id: Long, order: ListSortOrder) {
db.favouriteCategoriesDao.updateOrder(id, order.name) db.favouriteCategoriesDao.updateOrder(id, order.name)
} }
@ -205,10 +204,10 @@ class FavouritesRepository @Inject constructor(
return ReversibleHandle { recoverToCategory(categoryId, ids) } return ReversibleHandle { recoverToCategory(categoryId, ids) }
} }
private fun observeOrder(categoryId: Long): Flow<SortOrder> { private fun observeOrder(categoryId: Long): Flow<ListSortOrder> {
return db.favouriteCategoriesDao.observe(categoryId) return db.favouriteCategoriesDao.observe(categoryId)
.filterNotNull() .filterNotNull()
.map { x -> SortOrder(x.order, SortOrder.NEWEST) } .map { x -> ListSortOrder(x.order, ListSortOrder.NEWEST) }
.distinctUntilChanged() .distinctUntilChanged()
} }

@ -27,7 +27,6 @@ import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEdit
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.SortOrder
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -176,12 +175,6 @@ class FavouriteCategoriesActivity :
companion object { companion object {
val SORT_ORDERS = arrayOf(
SortOrder.ALPHABETICAL,
SortOrder.NEWEST,
SortOrder.RATING,
)
fun newIntent(context: Context) = Intent(context, FavouriteCategoriesActivity::class.java) fun newIntent(context: Context) = Intent(context, FavouriteCategoriesActivity::class.java)
} }
} }

@ -18,16 +18,15 @@ import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.ui.model.titleRes
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.getSerializableCompat import org.koitharu.kotatsu.core.util.ext.getSerializableCompat
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.setChecked import org.koitharu.kotatsu.core.util.ext.setChecked
import org.koitharu.kotatsu.core.util.ext.sortedByOrdinal
import org.koitharu.kotatsu.databinding.ActivityCategoryEditBinding import org.koitharu.kotatsu.databinding.ActivityCategoryEditBinding
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity import org.koitharu.kotatsu.list.domain.ListSortOrder
import org.koitharu.kotatsu.parsers.model.SortOrder
import com.google.android.material.R as materialR import com.google.android.material.R as materialR
@AndroidEntryPoint @AndroidEntryPoint
@ -38,7 +37,8 @@ class FavouritesCategoryEditActivity :
DefaultTextWatcher { DefaultTextWatcher {
private val viewModel by viewModels<FavouritesCategoryEditViewModel>() private val viewModel by viewModels<FavouritesCategoryEditViewModel>()
private var selectedSortOrder: SortOrder? = null private var selectedSortOrder: ListSortOrder? = null
private val sortOrders = ListSortOrder.FAVORITES.sortedByOrdinal()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -68,7 +68,7 @@ class FavouritesCategoryEditActivity :
override fun onRestoreInstanceState(savedInstanceState: Bundle) { override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState) super.onRestoreInstanceState(savedInstanceState)
val order = savedInstanceState.getSerializableCompat<SortOrder>(KEY_SORT_ORDER) val order = savedInstanceState.getSerializableCompat<ListSortOrder>(KEY_SORT_ORDER)
if (order != null) { if (order != null) {
selectedSortOrder = order selectedSortOrder = order
} }
@ -103,7 +103,7 @@ class FavouritesCategoryEditActivity :
} }
override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
selectedSortOrder = FavouriteCategoriesActivity.SORT_ORDERS.getOrNull(position) selectedSortOrder = sortOrders.getOrNull(position)
} }
private fun onCategoryChanged(category: FavouriteCategory?) { private fun onCategoryChanged(category: FavouriteCategory?) {
@ -113,7 +113,7 @@ class FavouritesCategoryEditActivity :
} }
viewBinding.editName.setText(category?.title) viewBinding.editName.setText(category?.title)
selectedSortOrder = category?.order selectedSortOrder = category?.order
val sortText = getString((category?.order ?: SortOrder.NEWEST).titleRes) val sortText = getString((category?.order ?: ListSortOrder.NEWEST).titleResId)
viewBinding.editSort.setText(sortText, false) viewBinding.editSort.setText(sortText, false)
viewBinding.switchTracker.setChecked(category?.isTrackingEnabled ?: true, false) viewBinding.switchTracker.setChecked(category?.isTrackingEnabled ?: true, false)
viewBinding.switchShelf.setChecked(category?.isVisibleInLibrary ?: true, false) viewBinding.switchShelf.setChecked(category?.isVisibleInLibrary ?: true, false)
@ -135,17 +135,17 @@ class FavouritesCategoryEditActivity :
} }
private fun initSortSpinner() { private fun initSortSpinner() {
val entries = FavouriteCategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) } val entries = sortOrders.map { getString(it.titleResId) }
val adapter = SortAdapter(this, entries) val adapter = SortAdapter(this, entries)
viewBinding.editSort.setAdapter(adapter) viewBinding.editSort.setAdapter(adapter)
viewBinding.editSort.onItemClickListener = this viewBinding.editSort.onItemClickListener = this
} }
private fun getSelectedSortOrder(): SortOrder { private fun getSelectedSortOrder(): ListSortOrder {
selectedSortOrder?.let { return it } selectedSortOrder?.let { return it }
val entries = FavouriteCategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) } val entries = sortOrders.map { getString(it.titleResId) }
val index = entries.indexOf(viewBinding.editSort.text.toString()) val index = entries.indexOf(viewBinding.editSort.text.toString())
return FavouriteCategoriesActivity.SORT_ORDERS.getOrNull(index) ?: SortOrder.NEWEST return sortOrders.getOrNull(index) ?: ListSortOrder.NEWEST
} }
private class SortAdapter( private class SortAdapter(

@ -17,7 +17,7 @@ import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity.Companion.EXTRA_ID import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity.Companion.EXTRA_ID
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity.Companion.NO_ID import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity.Companion.NO_ID
import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.list.domain.ListSortOrder
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@ -48,7 +48,7 @@ class FavouritesCategoryEditViewModel @Inject constructor(
fun save( fun save(
title: String, title: String,
sortOrder: SortOrder, sortOrder: ListSortOrder,
isTrackerEnabled: Boolean, isTrackerEnabled: Boolean,
isVisibleOnShelf: Boolean, isVisibleOnShelf: Boolean,
) { ) {

@ -10,13 +10,11 @@ import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.list.ListSelectionController
import org.koitharu.kotatsu.core.ui.model.titleRes
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
import org.koitharu.kotatsu.core.util.ext.addMenuProvider import org.koitharu.kotatsu.core.util.ext.addMenuProvider
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.sortedByOrdinal
import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.core.util.ext.withArgs
import org.koitharu.kotatsu.databinding.FragmentListBinding import org.koitharu.kotatsu.databinding.FragmentListBinding
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity import org.koitharu.kotatsu.list.domain.ListSortOrder
import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
@ -27,12 +25,14 @@ class FavouritesListFragment : MangaListFragment(), PopupMenu.OnMenuItemClickLis
override val isSwipeRefreshEnabled = false override val isSwipeRefreshEnabled = false
val categoryId
get() = viewModel.categoryId
override fun onViewBindingCreated(binding: FragmentListBinding, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: FragmentListBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
if (viewModel.categoryId != NO_ID) { if (viewModel.categoryId != NO_ID) {
addMenuProvider(FavouritesListMenuProvider(binding.root.context, viewModel)) addMenuProvider(FavouritesListMenuProvider(binding.root.context, viewModel))
} }
viewModel.sortOrder.observe(viewLifecycleOwner, MenuInvalidator(requireActivity()))
} }
override fun onScrolledToEnd() = Unit override fun onScrolledToEnd() = Unit
@ -40,14 +40,15 @@ class FavouritesListFragment : MangaListFragment(), PopupMenu.OnMenuItemClickLis
override fun onFilterClick(view: View?) { override fun onFilterClick(view: View?) {
val menu = PopupMenu(view?.context ?: return, view) val menu = PopupMenu(view?.context ?: return, view)
menu.setOnMenuItemClickListener(this) menu.setOnMenuItemClickListener(this)
for ((i, item) in FavouriteCategoriesActivity.SORT_ORDERS.withIndex()) { val orders = ListSortOrder.FAVORITES.sortedByOrdinal()
menu.menu.add(Menu.NONE, Menu.NONE, i, item.titleRes) for ((i, item) in orders.withIndex()) {
menu.menu.add(Menu.NONE, Menu.NONE, i, item.titleResId)
} }
menu.show() menu.show()
} }
override fun onMenuItemClick(item: MenuItem): Boolean { override fun onMenuItemClick(item: MenuItem): Boolean {
val order = FavouriteCategoriesActivity.SORT_ORDERS.getOrNull(item.order) ?: return false val order = ListSortOrder.FAVORITES.sortedByOrdinal().getOrNull(item.order) ?: return false
viewModel.setSortOrder(order) viewModel.setSortOrder(order)
return true return true
} }

@ -5,12 +5,8 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.core.view.forEach
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.model.titleRes
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity
import org.koitharu.kotatsu.parsers.model.SortOrder
class FavouritesListMenuProvider( class FavouritesListMenuProvider(
private val context: Context, private val context: Context,
@ -19,29 +15,9 @@ class FavouritesListMenuProvider(
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_favourites, menu) menuInflater.inflate(R.menu.opt_favourites, menu)
val subMenu = menu.findItem(R.id.action_order)?.subMenu ?: return
for (order in FavouriteCategoriesActivity.SORT_ORDERS) {
subMenu.add(R.id.group_order, Menu.NONE, order.ordinal, order.titleRes)
}
subMenu.setGroupCheckable(R.id.group_order, true, true)
}
override fun onPrepareMenu(menu: Menu) {
super.onPrepareMenu(menu)
val order = viewModel.sortOrder.value ?: return
menu.findItem(R.id.action_order)?.subMenu?.forEach { item ->
if (item.order == order.ordinal) {
item.isChecked = true
}
}
} }
override fun onMenuItemSelected(menuItem: MenuItem): Boolean { override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
if (menuItem.groupId == R.id.group_order) {
val order = SortOrder.entries[menuItem.order]
viewModel.setSortOrder(order)
return true
}
return when (menuItem.itemId) { return when (menuItem.itemId) {
R.id.action_edit -> { R.id.action_edit -> {
context.startActivity(FavouritesCategoryEditActivity.newIntent(context, viewModel.categoryId)) context.startActivity(FavouritesCategoryEditActivity.newIntent(context, viewModel.categoryId))

@ -22,12 +22,12 @@ import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.ARG_CATEGORY_ID import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.ARG_CATEGORY_ID
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID
import org.koitharu.kotatsu.list.domain.ListExtraProvider import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.domain.ListSortOrder
import org.koitharu.kotatsu.list.ui.MangaListViewModel import org.koitharu.kotatsu.list.ui.MangaListViewModel
import org.koitharu.kotatsu.list.ui.model.EmptyState import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorState import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.list.ui.model.toUi import org.koitharu.kotatsu.list.ui.model.toUi
import org.koitharu.kotatsu.parsers.model.SortOrder
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@ -44,7 +44,7 @@ class FavouritesListViewModel @Inject constructor(
override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_FAVORITES) { favoritesListMode } override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_FAVORITES) { favoritesListMode }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.favoritesListMode) .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.favoritesListMode)
val sortOrder: StateFlow<SortOrder?> = if (categoryId == NO_ID) { val sortOrder: StateFlow<ListSortOrder?> = if (categoryId == NO_ID) {
MutableStateFlow(null) MutableStateFlow(null)
} else { } else {
repository.observeCategory(categoryId) repository.observeCategory(categoryId)
@ -54,7 +54,7 @@ class FavouritesListViewModel @Inject constructor(
override val content = combine( override val content = combine(
if (categoryId == NO_ID) { if (categoryId == NO_ID) {
repository.observeAll(SortOrder.NEWEST) repository.observeAll(ListSortOrder.NEWEST)
} else { } else {
repository.observeAll(categoryId) repository.observeAll(categoryId)
}, },
@ -98,7 +98,7 @@ class FavouritesListViewModel @Inject constructor(
} }
} }
fun setSortOrder(order: SortOrder) { fun setSortOrder(order: ListSortOrder) {
if (categoryId == NO_ID) { if (categoryId == NO_ID) {
return return
} }

@ -25,6 +25,7 @@ import org.koitharu.kotatsu.core.ui.widgets.ChipsView
import org.koitharu.kotatsu.core.util.ext.lifecycleScope import org.koitharu.kotatsu.core.util.ext.lifecycleScope
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.require import org.koitharu.kotatsu.core.util.ext.require
import org.koitharu.kotatsu.core.util.ext.sortedByOrdinal
import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel
import org.koitharu.kotatsu.filter.ui.model.FilterItem import org.koitharu.kotatsu.filter.ui.model.FilterItem
import org.koitharu.kotatsu.filter.ui.model.FilterState import org.koitharu.kotatsu.filter.ui.model.FilterState
@ -207,7 +208,7 @@ class FilterCoordinator @Inject constructor(
state: FilterState, state: FilterState,
query: String, query: String,
): List<ListModel> { ): List<ListModel> {
val sortOrders = repository.sortOrders.sortedBy { it.ordinal } val sortOrders = repository.sortOrders.sortedByOrdinal()
val tags = mergeTags(state.tags, allTags.tags).toList() val tags = mergeTags(state.tags, allTags.tags).toList()
val list = ArrayList<ListModel>(tags.size + sortOrders.size + 3) val list = ArrayList<ListModel>(tags.size + sortOrders.size + 3)
if (query.isEmpty()) { if (query.isEmpty()) {

@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.Flow
import org.intellij.lang.annotations.Language import org.intellij.lang.annotations.Language
import org.koitharu.kotatsu.core.db.entity.MangaEntity import org.koitharu.kotatsu.core.db.entity.MangaEntity
import org.koitharu.kotatsu.core.db.entity.TagEntity import org.koitharu.kotatsu.core.db.entity.TagEntity
import org.koitharu.kotatsu.history.domain.model.HistoryOrder import org.koitharu.kotatsu.list.domain.ListSortOrder
@Dao @Dao
abstract class HistoryDao { abstract class HistoryDao {
@ -33,12 +33,13 @@ abstract class HistoryDao {
@Query("SELECT * FROM history WHERE deleted_at = 0 ORDER BY updated_at DESC LIMIT :limit") @Query("SELECT * FROM history WHERE deleted_at = 0 ORDER BY updated_at DESC LIMIT :limit")
abstract fun observeAll(limit: Int): Flow<List<HistoryWithManga>> abstract fun observeAll(limit: Int): Flow<List<HistoryWithManga>>
fun observeAll(order: HistoryOrder): Flow<List<HistoryWithManga>> { fun observeAll(order: ListSortOrder): Flow<List<HistoryWithManga>> {
val orderBy = when (order) { val orderBy = when (order) {
HistoryOrder.UPDATED -> "history.updated_at DESC" ListSortOrder.UPDATED -> "history.updated_at DESC"
HistoryOrder.CREATED -> "history.created_at DESC" ListSortOrder.NEWEST -> "history.created_at DESC"
HistoryOrder.PROGRESS -> "history.percent DESC" ListSortOrder.PROGRESS -> "history.percent DESC"
HistoryOrder.ALPHABETIC -> "manga.title" ListSortOrder.ALPHABETIC -> "manga.title"
else -> throw IllegalArgumentException("Sort order $order is not supported")
} }
@Language("RoomSql") @Language("RoomSql")

@ -18,8 +18,8 @@ import org.koitharu.kotatsu.core.model.findById
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.util.ReversibleHandle import org.koitharu.kotatsu.core.ui.util.ReversibleHandle
import org.koitharu.kotatsu.core.util.ext.mapItems import org.koitharu.kotatsu.core.util.ext.mapItems
import org.koitharu.kotatsu.history.domain.model.HistoryOrder
import org.koitharu.kotatsu.history.domain.model.MangaWithHistory import org.koitharu.kotatsu.history.domain.model.MangaWithHistory
import org.koitharu.kotatsu.list.domain.ListSortOrder
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler
@ -66,7 +66,7 @@ class HistoryRepository @Inject constructor(
} }
} }
fun observeAllWithHistory(order: HistoryOrder): Flow<List<MangaWithHistory>> { fun observeAllWithHistory(order: ListSortOrder): Flow<List<MangaWithHistory>> {
return db.historyDao.observeAll(order).mapItems { return db.historyDao.observeAll(order).mapItems {
MangaWithHistory( MangaWithHistory(
it.manga.toManga(it.tags.toMangaTags()), it.manga.toManga(it.tags.toMangaTags()),

@ -1,16 +0,0 @@
package org.koitharu.kotatsu.history.domain.model
import androidx.annotation.StringRes
import org.koitharu.kotatsu.R
enum class HistoryOrder(
@StringRes val titleResId: Int,
) {
UPDATED(R.string.updated),
CREATED(R.string.order_added),
PROGRESS(R.string.progress),
ALPHABETIC(R.string.by_name);
fun isGroupingSupported() = this == UPDATED || this == CREATED || this == PROGRESS
}

@ -26,9 +26,9 @@ import org.koitharu.kotatsu.core.util.ext.daysDiff
import org.koitharu.kotatsu.core.util.ext.onFirst import org.koitharu.kotatsu.core.util.ext.onFirst
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.history.data.HistoryRepository import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.history.domain.model.HistoryOrder
import org.koitharu.kotatsu.history.domain.model.MangaWithHistory import org.koitharu.kotatsu.history.domain.model.MangaWithHistory
import org.koitharu.kotatsu.list.domain.ListExtraProvider import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.domain.ListSortOrder
import org.koitharu.kotatsu.list.ui.MangaListViewModel import org.koitharu.kotatsu.list.ui.MangaListViewModel
import org.koitharu.kotatsu.list.ui.model.EmptyHint import org.koitharu.kotatsu.list.ui.model.EmptyHint
import org.koitharu.kotatsu.list.ui.model.EmptyState import org.koitharu.kotatsu.list.ui.model.EmptyState
@ -54,7 +54,7 @@ class HistoryListViewModel @Inject constructor(
downloadScheduler: DownloadWorker.Scheduler, downloadScheduler: DownloadWorker.Scheduler,
) : MangaListViewModel(settings, downloadScheduler) { ) : MangaListViewModel(settings, downloadScheduler) {
private val sortOrder: StateFlow<HistoryOrder> = settings.observeAsStateFlow( private val sortOrder: StateFlow<ListSortOrder> = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.IO, scope = viewModelScope + Dispatchers.IO,
key = AppSettings.KEY_HISTORY_ORDER, key = AppSettings.KEY_HISTORY_ORDER,
valueProducer = { historySortOrder }, valueProducer = { historySortOrder },
@ -123,10 +123,6 @@ class HistoryListViewModel @Inject constructor(
} }
} }
fun setGrouping(isGroupingEnabled: Boolean) {
settings.isHistoryGroupingEnabled = isGroupingEnabled
}
private suspend fun mapList( private suspend fun mapList(
list: List<MangaWithHistory>, list: List<MangaWithHistory>,
grouped: Boolean, grouped: Boolean,
@ -168,10 +164,10 @@ class HistoryListViewModel @Inject constructor(
return result return result
} }
private fun MangaHistory.header(order: HistoryOrder): ListHeader? = when (order) { private fun MangaHistory.header(order: ListSortOrder): ListHeader? = when (order) {
HistoryOrder.UPDATED -> ListHeader(timeAgo(updatedAt)) ListSortOrder.UPDATED -> ListHeader(timeAgo(updatedAt))
HistoryOrder.CREATED -> ListHeader(timeAgo(createdAt)) ListSortOrder.NEWEST -> ListHeader(timeAgo(createdAt))
HistoryOrder.PROGRESS -> ListHeader( ListSortOrder.PROGRESS -> ListHeader(
when (percent) { when (percent) {
1f -> R.string.status_completed 1f -> R.string.status_completed
in 0f..0.01f -> R.string.status_planned in 0f..0.01f -> R.string.status_planned
@ -180,7 +176,9 @@ class HistoryListViewModel @Inject constructor(
}, },
) )
HistoryOrder.ALPHABETIC -> null ListSortOrder.ALPHABETIC,
ListSortOrder.RELEVANCE,
ListSortOrder.RATING -> null
} }
private fun timeAgo(date: Date): DateTimeAgo { private fun timeAgo(date: Date): DateTimeAgo {

@ -0,0 +1,30 @@
package org.koitharu.kotatsu.list.domain
import androidx.annotation.StringRes
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.parsers.util.find
import java.util.EnumSet
enum class ListSortOrder(
@StringRes val titleResId: Int,
) {
UPDATED(R.string.updated),
NEWEST(R.string.order_added),
PROGRESS(R.string.progress),
ALPHABETIC(R.string.by_name),
RATING(R.string.by_rating),
RELEVANCE(R.string.by_relevance),
;
fun isGroupingSupported() = this == UPDATED || this == NEWEST || this == PROGRESS
companion object {
val HISTORY = EnumSet.of(UPDATED, NEWEST, PROGRESS, ALPHABETIC)
val FAVORITES = EnumSet.of(ALPHABETIC, NEWEST, RATING)
val SUGGESTIONS = EnumSet.of(RELEVANCE)
operator fun invoke(value: String, fallback: ListSortOrder) = entries.find(value) ?: fallback
}
}

@ -6,7 +6,11 @@ import android.view.MenuItem
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment
import org.koitharu.kotatsu.history.ui.HistoryListFragment
import org.koitharu.kotatsu.list.ui.config.ListConfigBottomSheet import org.koitharu.kotatsu.list.ui.config.ListConfigBottomSheet
import org.koitharu.kotatsu.list.ui.config.ListConfigSection
import org.koitharu.kotatsu.suggestions.ui.SuggestionsFragment
class MangaListMenuProvider( class MangaListMenuProvider(
private val fragment: Fragment, private val fragment: Fragment,
@ -18,7 +22,13 @@ class MangaListMenuProvider(
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) { override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_list_mode -> { R.id.action_list_mode -> {
ListConfigBottomSheet.show(fragment.childFragmentManager) val section: ListConfigSection = when (fragment) {
is HistoryListFragment -> ListConfigSection.History
is SuggestionsFragment -> ListConfigSection.Suggestions
is FavouritesListFragment -> ListConfigSection.Favorites(fragment.categoryId)
else -> ListConfigSection.General
}
ListConfigBottomSheet.show(fragment.childFragmentManager, section)
true true
} }

@ -9,6 +9,7 @@ import android.widget.ArrayAdapter
import android.widget.CompoundButton import android.widget.CompoundButton
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.fragment.app.viewModels
import com.google.android.material.button.MaterialButtonToggleGroup import com.google.android.material.button.MaterialButtonToggleGroup
import com.google.android.material.slider.Slider import com.google.android.material.slider.Slider
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -18,11 +19,9 @@ import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
import org.koitharu.kotatsu.core.util.ext.setValueRounded import org.koitharu.kotatsu.core.util.ext.setValueRounded
import org.koitharu.kotatsu.core.util.ext.showDistinct import org.koitharu.kotatsu.core.util.ext.showDistinct
import org.koitharu.kotatsu.core.util.ext.withArgs
import org.koitharu.kotatsu.core.util.progress.IntPercentLabelFormatter import org.koitharu.kotatsu.core.util.progress.IntPercentLabelFormatter
import org.koitharu.kotatsu.databinding.SheetListModeBinding import org.koitharu.kotatsu.databinding.SheetListModeBinding
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment
import org.koitharu.kotatsu.history.domain.model.HistoryOrder
import org.koitharu.kotatsu.history.ui.HistoryListFragment
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -33,8 +32,11 @@ class ListConfigBottomSheet :
AdapterView.OnItemSelectedListener { AdapterView.OnItemSelectedListener {
@Inject @Inject
@Deprecated("")
lateinit var settings: AppSettings lateinit var settings: AppSettings
private val viewModel by viewModels<ListConfigViewModel>()
override fun onCreateViewBinding( override fun onCreateViewBinding(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -42,12 +44,7 @@ class ListConfigBottomSheet :
override fun onViewBindingCreated(binding: SheetListModeBinding, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: SheetListModeBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
val section = getSection() val mode = viewModel.listMode
val mode = when (section) {
Section.GENERAL -> settings.listMode
Section.HISTORY -> settings.historyListMode
Section.FAVORITES -> settings.favoritesListMode
}
binding.buttonList.isChecked = mode == ListMode.LIST binding.buttonList.isChecked = mode == ListMode.LIST
binding.buttonListDetailed.isChecked = mode == ListMode.DETAILED_LIST binding.buttonListDetailed.isChecked = mode == ListMode.DETAILED_LIST
binding.buttonGrid.isChecked = mode == ListMode.GRID binding.buttonGrid.isChecked = mode == ListMode.GRID
@ -55,27 +52,31 @@ class ListConfigBottomSheet :
binding.sliderGrid.isVisible = mode == ListMode.GRID binding.sliderGrid.isVisible = mode == ListMode.GRID
binding.sliderGrid.setLabelFormatter(IntPercentLabelFormatter(binding.root.context)) binding.sliderGrid.setLabelFormatter(IntPercentLabelFormatter(binding.root.context))
binding.sliderGrid.setValueRounded(settings.gridSize.toFloat()) binding.sliderGrid.setValueRounded(viewModel.gridSize.toFloat())
binding.sliderGrid.addOnChangeListener(this) binding.sliderGrid.addOnChangeListener(this)
binding.checkableGroup.addOnButtonCheckedListener(this) binding.checkableGroup.addOnButtonCheckedListener(this)
binding.switchGrouping.isVisible = section == Section.HISTORY binding.switchGrouping.isVisible = viewModel.isGroupingAvailable
if (section == Section.HISTORY) { if (viewModel.isGroupingAvailable) {
binding.switchGrouping.isEnabled = settings.historySortOrder.isGroupingSupported() binding.switchGrouping.isEnabled = settings.historySortOrder.isGroupingSupported()
} }
binding.switchGrouping.isChecked = settings.isHistoryGroupingEnabled binding.switchGrouping.isChecked = settings.isHistoryGroupingEnabled
binding.switchGrouping.setOnCheckedChangeListener(this) binding.switchGrouping.setOnCheckedChangeListener(this)
if (section == Section.HISTORY) { val sortOrders = viewModel.getSortOrders()
if (sortOrders != null) {
binding.textViewOrderTitle.isVisible = true binding.textViewOrderTitle.isVisible = true
binding.spinnerOrder.adapter = ArrayAdapter( binding.spinnerOrder.adapter = ArrayAdapter(
binding.spinnerOrder.context, binding.spinnerOrder.context,
android.R.layout.simple_spinner_dropdown_item, android.R.layout.simple_spinner_dropdown_item,
android.R.id.text1, android.R.id.text1,
HistoryOrder.entries.map { getString(it.titleResId) }, sortOrders.map { binding.spinnerOrder.context.getString(it.titleResId) },
) )
binding.spinnerOrder.setSelection(settings.historySortOrder.ordinal, false) val selected = sortOrders.indexOf(viewModel.getSelectedSortOrder())
if (selected >= 0) {
binding.spinnerOrder.setSelection(selected, false)
}
binding.spinnerOrder.onItemSelectedListener = this binding.spinnerOrder.onItemSelectedListener = this
binding.cardOrder.isVisible = true binding.cardOrder.isVisible = true
} }
@ -93,11 +94,7 @@ class ListConfigBottomSheet :
} }
requireViewBinding().textViewGridTitle.isVisible = mode == ListMode.GRID requireViewBinding().textViewGridTitle.isVisible = mode == ListMode.GRID
requireViewBinding().sliderGrid.isVisible = mode == ListMode.GRID requireViewBinding().sliderGrid.isVisible = mode == ListMode.GRID
when (getSection()) { viewModel.listMode = mode
Section.GENERAL -> settings.listMode = mode
Section.HISTORY -> settings.historyListMode = mode
Section.FAVORITES -> settings.favoritesListMode = mode
}
} }
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
@ -108,36 +105,28 @@ class ListConfigBottomSheet :
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) { override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
if (fromUser) { if (fromUser) {
settings.gridSize = value.toInt() viewModel.gridSize = value.toInt()
} }
} }
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) { override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
when (parent.id) { when (parent.id) {
R.id.spinner_order -> { R.id.spinner_order -> {
val value = HistoryOrder.entries[position] viewModel.setSortOrder(position)
settings.historySortOrder = value viewBinding?.switchGrouping?.isEnabled = settings.historySortOrder.isGroupingSupported()
viewBinding?.switchGrouping?.isEnabled = value.isGroupingSupported()
} }
} }
} }
override fun onNothingSelected(parent: AdapterView<*>?) = Unit override fun onNothingSelected(parent: AdapterView<*>?) = Unit
private fun getSection(): Section = when (parentFragment) {
is HistoryListFragment -> Section.HISTORY
is FavouritesListFragment -> Section.FAVORITES
else -> Section.GENERAL
}
enum class Section {
GENERAL, HISTORY, FAVORITES;
}
companion object { companion object {
private const val TAG = "ListModeSelectDialog" private const val TAG = "ListModeSelectDialog"
const val ARG_SECTION = "section"
fun show(fm: FragmentManager) = ListConfigBottomSheet().showDistinct(fm, TAG) fun show(fm: FragmentManager, section: ListConfigSection) = ListConfigBottomSheet().withArgs(1) {
putParcelable(ARG_SECTION, section)
}.showDistinct(fm, TAG)
} }
} }

@ -0,0 +1,21 @@
package org.koitharu.kotatsu.list.ui.config
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
sealed interface ListConfigSection : Parcelable {
@Parcelize
data object History : ListConfigSection
@Parcelize
data object General : ListConfigSection
@Parcelize
data class Favorites(
val categoryId: Long,
) : ListConfigSection
@Parcelize
data object Suggestions : ListConfigSection
}

@ -0,0 +1,76 @@
package org.koitharu.kotatsu.list.ui.config
import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.runBlocking
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.require
import org.koitharu.kotatsu.core.util.ext.sortedByOrdinal
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.list.domain.ListSortOrder
import javax.inject.Inject
@HiltViewModel
class ListConfigViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val settings: AppSettings,
private val favouritesRepository: FavouritesRepository,
) : BaseViewModel() {
val section = savedStateHandle.require<ListConfigSection>(ListConfigBottomSheet.ARG_SECTION)
var listMode: ListMode
get() = when (section) {
is ListConfigSection.Favorites -> settings.favoritesListMode
ListConfigSection.General -> settings.listMode
ListConfigSection.History -> settings.historyListMode
ListConfigSection.Suggestions -> settings.suggestionsListMode
}
set(value) {
when (section) {
is ListConfigSection.Favorites -> settings.favoritesListMode = value
ListConfigSection.General -> settings.listMode = value
ListConfigSection.History -> settings.historyListMode = value
ListConfigSection.Suggestions -> settings.suggestionsListMode = value
}
}
var gridSize: Int
get() = settings.gridSize
set(value) {
settings.gridSize = value
}
val isGroupingAvailable: Boolean
get() = section == ListConfigSection.History
fun getSortOrders(): List<ListSortOrder>? = when (section) {
is ListConfigSection.Favorites -> ListSortOrder.FAVORITES
ListConfigSection.General -> null
ListConfigSection.History -> ListSortOrder.HISTORY
ListConfigSection.Suggestions -> ListSortOrder.SUGGESTIONS
}?.sortedByOrdinal()
fun getSelectedSortOrder(): ListSortOrder? = when (section) {
is ListConfigSection.Favorites -> runBlocking { favouritesRepository.getCategory(section.categoryId).order }
ListConfigSection.General -> null
ListConfigSection.History -> settings.historySortOrder
ListConfigSection.Suggestions -> ListSortOrder.RELEVANCE // TODO
}
fun setSortOrder(position: Int) {
val value = getSortOrders()?.getOrNull(position) ?: return
when (section) {
is ListConfigSection.Favorites -> launchJob {
favouritesRepository.setCategoryOrder(section.categoryId, value)
}
ListConfigSection.General -> Unit
ListConfigSection.History -> settings.historySortOrder = value
ListConfigSection.Suggestions -> Unit
}
}
}

@ -7,7 +7,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.last import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import org.koitharu.kotatsu.core.model.findById import org.koitharu.kotatsu.core.model.findById
@ -52,7 +52,7 @@ class PagesThumbnailsViewModel @Inject constructor(
init { init {
loadingJob = launchLoadingJob(Dispatchers.Default) { loadingJob = launchLoadingJob(Dispatchers.Default) {
chaptersLoader.init(checkNotNull(mangaDetails.last())) chaptersLoader.init(checkNotNull(mangaDetails.first { x -> x?.isLoaded == true }))
chaptersLoader.loadSingleChapter(initialChapterId) chaptersLoader.loadSingleChapter(initialChapterId)
updateList() updateList()
} }

@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.observeAsFlow
import org.koitharu.kotatsu.core.util.ext.onFirst import org.koitharu.kotatsu.core.util.ext.onFirst
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.list.domain.ListExtraProvider import org.koitharu.kotatsu.list.domain.ListExtraProvider
@ -31,6 +32,9 @@ class SuggestionsViewModel @Inject constructor(
private val suggestionsScheduler: SuggestionsWorker.Scheduler, private val suggestionsScheduler: SuggestionsWorker.Scheduler,
) : MangaListViewModel(settings, downloadScheduler) { ) : MangaListViewModel(settings, downloadScheduler) {
override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_SUGGESTIONS) { suggestionsListMode }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.suggestionsListMode)
override val content = combine( override val content = combine(
repository.observeAll(), repository.observeAll(),
listMode, listMode,

@ -2,22 +2,9 @@
<menu <menu
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_order"
android:orderInCategory="50"
android:title="@string/sort_order">
<menu>
<group android:id="@+id/group_order" />
</menu>
</item>
<item <item
android:id="@+id/action_edit" android:id="@+id/action_edit"
android:orderInCategory="50" android:orderInCategory="20"
android:title="@string/edit_category" android:title="@string/edit_category"
android:titleCondensed="@string/edit" /> android:titleCondensed="@string/edit" />

@ -5,7 +5,7 @@
<item <item
android:id="@+id/action_manage" android:id="@+id/action_manage"
android:orderInCategory="48" android:orderInCategory="48"
android:title="@string/manage_categories" android:title="@string/favourites_categories"
android:titleCondensed="@string/manage" /> android:titleCondensed="@string/categories" />
</menu> </menu>

@ -5,7 +5,7 @@
<item <item
android:id="@+id/action_list_mode" android:id="@+id/action_list_mode"
android:orderInCategory="20" android:orderInCategory="40"
android:title="@string/list_options" android:title="@string/list_options"
app:showAsAction="never" /> app:showAsAction="never" />

@ -497,4 +497,6 @@
<string name="suggest_new_sources">Suggest new sources after app update</string> <string name="suggest_new_sources">Suggest new sources after app update</string>
<string name="suggest_new_sources_summary">Prompt to enable newly added sources after updating the application</string> <string name="suggest_new_sources_summary">Prompt to enable newly added sources after updating the application</string>
<string name="list_options">List options</string> <string name="list_options">List options</string>
<string name="by_relevance">Relevance</string>
<string name="categories">Categories</string>
</resources> </resources>

Loading…
Cancel
Save