Compare commits

...

1 Commits

Author SHA1 Message Date
Koitharu
ce455d4cb6 Refactor chapters grid mode 2024-03-13 14:43:35 +02:00
15 changed files with 34 additions and 32 deletions

1
.idea/.name generated
View File

@@ -1 +0,0 @@
Kotatsu

View File

@@ -178,11 +178,11 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
get() = prefs.getBoolean(KEY_INCOGNITO_MODE, false) get() = prefs.getBoolean(KEY_INCOGNITO_MODE, false)
set(value) = prefs.edit { putBoolean(KEY_INCOGNITO_MODE, value) } set(value) = prefs.edit { putBoolean(KEY_INCOGNITO_MODE, value) }
var chaptersReverse: Boolean var isChaptersReverse: Boolean
get() = prefs.getBoolean(KEY_REVERSE_CHAPTERS, false) get() = prefs.getBoolean(KEY_REVERSE_CHAPTERS, false)
set(value) = prefs.edit { putBoolean(KEY_REVERSE_CHAPTERS, value) } set(value) = prefs.edit { putBoolean(KEY_REVERSE_CHAPTERS, value) }
var chaptersGridView: Boolean var isChaptersGridView: Boolean
get() = prefs.getBoolean(KEY_GRID_VIEW_CHAPTERS, false) get() = prefs.getBoolean(KEY_GRID_VIEW_CHAPTERS, false)
set(value) = prefs.edit { putBoolean(KEY_GRID_VIEW_CHAPTERS, value) } set(value) = prefs.edit { putBoolean(KEY_GRID_VIEW_CHAPTERS, value) }

View File

@@ -16,6 +16,7 @@ fun MangaDetails.mapChapters(
newCount: Int, newCount: Int,
branch: String?, branch: String?,
bookmarks: List<Bookmark>, bookmarks: List<Bookmark>,
isGrid: Boolean,
): List<ChapterListItem> { ): List<ChapterListItem> {
val remoteChapters = chapters[branch].orEmpty() val remoteChapters = chapters[branch].orEmpty()
val localChapters = local?.manga?.getChapters(branch).orEmpty() val localChapters = local?.manga?.getChapters(branch).orEmpty()
@@ -47,6 +48,7 @@ fun MangaDetails.mapChapters(
isNew = isUnread && result.size >= newFrom, isNew = isUnread && result.size >= newFrom,
isDownloaded = local != null, isDownloaded = local != null,
isBookmarked = chapter.id in bookmarked, isBookmarked = chapter.id in bookmarked,
isGrid = isGrid,
) )
} }
if (!localMap.isNullOrEmpty()) { if (!localMap.isNullOrEmpty()) {
@@ -60,6 +62,7 @@ fun MangaDetails.mapChapters(
isNew = false, isNew = false,
isDownloaded = !isLocal, isDownloaded = !isLocal,
isBookmarked = chapter.id in bookmarked, isBookmarked = chapter.id in bookmarked,
isGrid = isGrid,
) )
} }
} }

View File

@@ -36,6 +36,7 @@ import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.ui.util.ReversibleAction import org.koitharu.kotatsu.core.ui.util.ReversibleAction
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.core.util.ext.combine
import org.koitharu.kotatsu.core.util.ext.computeSize import org.koitharu.kotatsu.core.util.ext.computeSize
import org.koitharu.kotatsu.core.util.ext.onEachWhile import org.koitharu.kotatsu.core.util.ext.onEachWhile
import org.koitharu.kotatsu.core.util.ext.requireValue import org.koitharu.kotatsu.core.util.ext.requireValue
@@ -122,13 +123,13 @@ class DetailsViewModel @Inject constructor(
val isChaptersReversed = settings.observeAsStateFlow( val isChaptersReversed = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.Default, scope = viewModelScope + Dispatchers.Default,
key = AppSettings.KEY_REVERSE_CHAPTERS, key = AppSettings.KEY_REVERSE_CHAPTERS,
valueProducer = { chaptersReverse }, valueProducer = { isChaptersReverse },
) )
val isChaptersInGridView = settings.observeAsStateFlow( val isChaptersInGridView = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.Default, scope = viewModelScope + Dispatchers.Default,
key = AppSettings.KEY_GRID_VIEW_CHAPTERS, key = AppSettings.KEY_GRID_VIEW_CHAPTERS,
valueProducer = { chaptersGridView }, valueProducer = { isChaptersGridView },
) )
val historyInfo: StateFlow<HistoryInfo> = combine( val historyInfo: StateFlow<HistoryInfo> = combine(
@@ -213,12 +214,14 @@ class DetailsViewModel @Inject constructor(
selectedBranch, selectedBranch,
newChaptersCount, newChaptersCount,
bookmarks, bookmarks,
) { manga, history, branch, news, bookmarks -> isChaptersInGridView,
) { manga, history, branch, news, bookmarks, grid ->
manga?.mapChapters( manga?.mapChapters(
history, history,
news, news,
branch, branch,
bookmarks, bookmarks,
grid,
).orEmpty() ).orEmpty()
}, },
isChaptersReversed, isChaptersReversed,
@@ -288,11 +291,11 @@ class DetailsViewModel @Inject constructor(
} }
fun setChaptersReversed(newValue: Boolean) { fun setChaptersReversed(newValue: Boolean) {
settings.chaptersReverse = newValue settings.isChaptersReverse = newValue
} }
fun setChaptersInGridView(newValue: Boolean) { fun setChaptersInGridView(newValue: Boolean) {
settings.chaptersGridView = newValue settings.isChaptersGridView = newValue
} }
fun setSelectedBranch(branch: String?) { fun setSelectedBranch(branch: String?) {

View File

@@ -17,7 +17,8 @@ import kotlin.math.roundToInt
fun chapterGridItemAD( fun chapterGridItemAD(
clickListener: OnListItemClickListener<ChapterListItem>, clickListener: OnListItemClickListener<ChapterListItem>,
) = adapterDelegateViewBinding<ChapterListItem, ListModel, ItemChapterGridBinding>( ) = adapterDelegateViewBinding<ChapterListItem, ListModel, ItemChapterGridBinding>(
{ inflater, parent -> ItemChapterGridBinding.inflate(inflater, parent, false) }, viewBinding = { inflater, parent -> ItemChapterGridBinding.inflate(inflater, parent, false) },
on = { item, _, _ -> item is ChapterListItem && item.isGrid }
) { ) {
val eventListener = AdapterDelegateClickListenerAdapter(this, clickListener) val eventListener = AdapterDelegateClickListenerAdapter(this, clickListener)

View File

@@ -18,7 +18,8 @@ import com.google.android.material.R as MR
fun chapterListItemAD( fun chapterListItemAD(
clickListener: OnListItemClickListener<ChapterListItem>, clickListener: OnListItemClickListener<ChapterListItem>,
) = adapterDelegateViewBinding<ChapterListItem, ListModel, ItemChapterBinding>( ) = adapterDelegateViewBinding<ChapterListItem, ListModel, ItemChapterBinding>(
{ inflater, parent -> ItemChapterBinding.inflate(inflater, parent, false) }, viewBinding = { inflater, parent -> ItemChapterBinding.inflate(inflater, parent, false) },
on = { item, _, _ -> item is ChapterListItem && !item.isGrid }
) { ) {
val eventListener = AdapterDelegateClickListenerAdapter(this, clickListener) val eventListener = AdapterDelegateClickListenerAdapter(this, clickListener)

View File

@@ -11,21 +11,12 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
class ChaptersAdapter( class ChaptersAdapter(
private val onItemClickListener: OnListItemClickListener<ChapterListItem>, private val onItemClickListener: OnListItemClickListener<ChapterListItem>,
chaptersInGridView: Boolean,
) : BaseListAdapter<ListModel>(), FastScroller.SectionIndexer { ) : BaseListAdapter<ListModel>(), FastScroller.SectionIndexer {
init { init {
setChapterAdapterDelegate(chaptersInGridView)
addDelegate(ListItemType.HEADER, listHeaderAD(null)) addDelegate(ListItemType.HEADER, listHeaderAD(null))
} addDelegate(ListItemType.CHAPTER_LIST, chapterListItemAD(onItemClickListener))
addDelegate(ListItemType.CHAPTER_GRID, chapterGridItemAD(onItemClickListener))
fun setChapterAdapterDelegate(chaptersInGridView: Boolean) {
delegatesManager.removeDelegate(ListItemType.CHAPTER.ordinal)
if (chaptersInGridView) {
addDelegate(ListItemType.CHAPTER, chapterGridItemAD(onItemClickListener))
} else {
addDelegate(ListItemType.CHAPTER, chapterListItemAD(onItemClickListener))
}
} }
override fun getSectionText(context: Context, position: Int): CharSequence? { override fun getSectionText(context: Context, position: Int): CharSequence? {

View File

@@ -10,6 +10,7 @@ data class ChapterListItem(
val chapter: MangaChapter, val chapter: MangaChapter,
val flags: Int, val flags: Int,
private val uploadDateMs: Long, private val uploadDateMs: Long,
val isGrid: Boolean,
) : ListModel { ) : ListModel {
var description: String? = null var description: String? = null

View File

@@ -13,6 +13,7 @@ fun MangaChapter.toListItem(
isNew: Boolean, isNew: Boolean,
isDownloaded: Boolean, isDownloaded: Boolean,
isBookmarked: Boolean, isBookmarked: Boolean,
isGrid: Boolean,
): ChapterListItem { ): ChapterListItem {
var flags = 0 var flags = 0
if (isCurrent) flags = flags or FLAG_CURRENT if (isCurrent) flags = flags or FLAG_CURRENT
@@ -24,5 +25,6 @@ fun MangaChapter.toListItem(
chapter = this, chapter = this,
flags = flags, flags = flags,
uploadDateMs = uploadDate, uploadDateMs = uploadDate,
isGrid = isGrid,
) )
} }

View File

@@ -58,7 +58,7 @@ class ChaptersFragment :
override fun onViewBindingCreated(binding: FragmentChaptersBinding, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: FragmentChaptersBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
chaptersAdapter = ChaptersAdapter(this, viewModel.isChaptersInGridView.value) chaptersAdapter = ChaptersAdapter(this)
selectionController = ListSelectionController( selectionController = ListSelectionController(
activity = requireActivity(), activity = requireActivity(),
decoration = ChaptersSelectionDecoration(binding.root.context), decoration = ChaptersSelectionDecoration(binding.root.context),
@@ -70,10 +70,9 @@ class ChaptersFragment :
checkNotNull(selectionController).attachToRecyclerView(this) checkNotNull(selectionController).attachToRecyclerView(this)
setHasFixedSize(true) setHasFixedSize(true)
isNestedScrollingEnabled = false isNestedScrollingEnabled = false
adapter = chaptersAdapter
} }
viewModel.isChaptersInGridView.observe(viewLifecycleOwner) { chaptersInGridView -> viewModel.isChaptersInGridView.observe(viewLifecycleOwner) { chaptersInGridView ->
chaptersAdapter?.setChapterAdapterDelegate(chaptersInGridView)
binding.recyclerViewChapters.adapter = chaptersAdapter
binding.recyclerViewChapters.layoutManager = if (chaptersInGridView) { binding.recyclerViewChapters.layoutManager = if (chaptersInGridView) {
GridLayoutManager(context, 4) GridLayoutManager(context, 4)
} else { } else {

View File

@@ -59,7 +59,7 @@ fun downloadItemAD(
} }
} }
val chaptersAdapter = BaseListAdapter<DownloadChapter>() val chaptersAdapter = BaseListAdapter<DownloadChapter>()
.addDelegate(ListItemType.CHAPTER, downloadChapterAD()) .addDelegate(ListItemType.CHAPTER_LIST, downloadChapterAD())
binding.recyclerViewChapters.addItemDecoration(DividerItemDecoration(context, RecyclerView.VERTICAL)) binding.recyclerViewChapters.addItemDecoration(DividerItemDecoration(context, RecyclerView.VERTICAL))
binding.recyclerViewChapters.adapter = chaptersAdapter binding.recyclerViewChapters.adapter = chaptersAdapter

View File

@@ -29,5 +29,6 @@ enum class ListItemType {
CATEGORY_LARGE, CATEGORY_LARGE,
MANGA_SCROBBLING, MANGA_SCROBBLING,
NAV_ITEM, NAV_ITEM,
CHAPTER, CHAPTER_LIST,
CHAPTER_GRID,
} }

View File

@@ -62,7 +62,8 @@ class TypedListSpacingDecoration(
ListItemType.MANGA_NESTED_GROUP, ListItemType.MANGA_NESTED_GROUP,
ListItemType.CATEGORY_LARGE, ListItemType.CATEGORY_LARGE,
ListItemType.NAV_ITEM, ListItemType.NAV_ITEM,
ListItemType.CHAPTER, ListItemType.CHAPTER_LIST,
ListItemType.CHAPTER_GRID,
null, null,
-> outRect.set(0) -> outRect.set(0)
@@ -83,5 +84,5 @@ class TypedListSpacingDecoration(
private fun ListItemType?.isEdgeToEdge() = this == ListItemType.MANGA_NESTED_GROUP private fun ListItemType?.isEdgeToEdge() = this == ListItemType.MANGA_NESTED_GROUP
|| this == ListItemType.FILTER_SORT || this == ListItemType.FILTER_SORT
|| this == ListItemType.FILTER_TAG || this == ListItemType.FILTER_TAG
|| this == ListItemType.CHAPTER || this == ListItemType.CHAPTER_LIST
} }

View File

@@ -66,6 +66,7 @@ class ChaptersSheet : BaseAdaptiveSheet<SheetChaptersBinding>(),
newCount = 0, newCount = 0,
branch = currentChapter?.branch, branch = currentChapter?.branch,
bookmarks = listOf(), bookmarks = listOf(),
isGrid = settings.isChaptersGridView,
).withVolumeHeaders(binding.root.context) ).withVolumeHeaders(binding.root.context)
if (chapters.isEmpty()) { if (chapters.isEmpty()) {
dismissAllowingStateLoss() dismissAllowingStateLoss()
@@ -77,8 +78,7 @@ class ChaptersSheet : BaseAdaptiveSheet<SheetChaptersBinding>(),
-1 -1
} }
binding.recyclerView.addItemDecoration(TypedListSpacingDecoration(binding.recyclerView.context, true)) binding.recyclerView.addItemDecoration(TypedListSpacingDecoration(binding.recyclerView.context, true))
val chaptersInGridView = settings.chaptersGridView binding.recyclerView.adapter = ChaptersAdapter(this).also { adapter ->
binding.recyclerView.adapter = ChaptersAdapter(this, chaptersInGridView).also { adapter ->
if (currentPosition >= 0) { if (currentPosition >= 0) {
val targetPosition = (currentPosition - 1).coerceAtLeast(0) val targetPosition = (currentPosition - 1).coerceAtLeast(0)
val offset = val offset =
@@ -90,7 +90,7 @@ class ChaptersSheet : BaseAdaptiveSheet<SheetChaptersBinding>(),
adapter.items = chapters adapter.items = chapters
} }
} }
binding.recyclerView.layoutManager = if (chaptersInGridView) { binding.recyclerView.layoutManager = if (settings.isChaptersGridView) {
GridLayoutManager(context, 4) GridLayoutManager(context, 4)
} else { } else {
LinearLayoutManager(context) LinearLayoutManager(context)

View File

@@ -15,7 +15,7 @@ class SourcesCatalogAdapter(
) : BaseListAdapter<SourceCatalogItem>(), FastScroller.SectionIndexer { ) : BaseListAdapter<SourceCatalogItem>(), FastScroller.SectionIndexer {
init { init {
addDelegate(ListItemType.CHAPTER, sourceCatalogItemSourceAD(coil, lifecycleOwner, listener)) addDelegate(ListItemType.CHAPTER_LIST, sourceCatalogItemSourceAD(coil, lifecycleOwner, listener))
addDelegate(ListItemType.HINT_EMPTY, sourceCatalogItemHintAD(coil, lifecycleOwner)) addDelegate(ListItemType.HINT_EMPTY, sourceCatalogItemHintAD(coil, lifecycleOwner))
} }