Show related manga

This commit is contained in:
Koitharu
2023-07-04 09:09:56 +03:00
parent 4739da2774
commit ed672feebe
22 changed files with 264 additions and 38 deletions

View File

@@ -36,6 +36,8 @@ interface MangaRepository {
suspend fun getTags(): Set<MangaTag>
suspend fun getRelated(seed: Manga): List<Manga>
@Singleton
class Factory @Inject constructor(
private val localMangaRepository: LocalMangaRepository,

View File

@@ -51,7 +51,7 @@ class RemoteMangaRepository(
getConfig()[parser.configKeyDomain] = value
}
val headers: Headers?
val headers: Headers
get() = parser.headers
override fun intercept(chain: Interceptor.Chain): Response {
@@ -94,6 +94,10 @@ class RemoteMangaRepository(
suspend fun getFavicons(): Favicons = parser.getFavicons()
override suspend fun getRelated(seed: Manga): List<Manga> {
return parser.getRelatedManga(seed).filterNot { it.id == seed.id }
}
fun getAuthProvider(): MangaParserAuthProvider? = parser as? MangaParserAuthProvider
fun getConfigKeys(): List<ConfigKey<*>> = ArrayList<ConfigKey<*>>().also {

View File

@@ -1,6 +1,7 @@
package org.koitharu.kotatsu.core.ui
import androidx.recyclerview.widget.AsyncListDiffer.ListListener
import com.hannesdorfmann.adapterdelegates4.AdapterDelegate
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import kotlinx.coroutines.flow.FlowCollector
import org.koitharu.kotatsu.core.util.ContinuationResumeRunnable
@@ -8,7 +9,9 @@ import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
import org.koitharu.kotatsu.list.ui.model.ListModel
import kotlin.coroutines.suspendCoroutine
abstract class BaseListAdapter : AsyncListDifferDelegationAdapter<ListModel>(ListModelDiffCallback),
open class BaseListAdapter(
vararg delegates: AdapterDelegate<List<ListModel>>,
) : AsyncListDifferDelegationAdapter<ListModel>(ListModelDiffCallback, *delegates),
FlowCollector<List<ListModel>> {
override suspend fun emit(value: List<ListModel>) = suspendCoroutine { cont ->

View File

@@ -0,0 +1,18 @@
package org.koitharu.kotatsu.details.domain
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import javax.inject.Inject
class RelatedMangaUseCase @Inject constructor(
private val mangaRepositoryFactory: MangaRepository.Factory,
) {
suspend operator fun invoke(seed: Manga) = runCatchingCancellable {
mangaRepositoryFactory.create(seed.source).getRelated(seed)
}.onFailure {
it.printStackTraceDebug()
}.getOrNull()
}

View File

@@ -24,6 +24,7 @@ import org.koitharu.kotatsu.bookmarks.domain.Bookmark
import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter
import org.koitharu.kotatsu.core.model.countChaptersByBranch
import org.koitharu.kotatsu.core.ui.BaseFragment
import org.koitharu.kotatsu.core.ui.BaseListAdapter
import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.ui.list.decor.SpacingItemDecoration
@@ -45,6 +46,9 @@ import org.koitharu.kotatsu.details.ui.scrobbling.ScrollingInfoAdapter
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
import org.koitharu.kotatsu.image.ui.ImageActivity
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD
import org.koitharu.kotatsu.list.ui.model.MangaItemModel
import org.koitharu.kotatsu.list.ui.size.StaticItemSizeResolver
import org.koitharu.kotatsu.main.ui.owners.NoModalBottomSheetOwner
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource
@@ -87,6 +91,9 @@ class DetailsFragment :
binding.infoLayout.textViewSource.setOnClickListener(this)
binding.textViewDescription.movementMethod = LinkMovementMethod.getInstance()
binding.chipsTags.onChipClickListener = this
binding.recyclerViewRelated.addItemDecoration(
SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.grid_spacing)),
)
TitleScrollCoordinator(binding.textViewTitle).attach(binding.scrollView)
viewModel.manga.filterNotNull().observe(viewLifecycleOwner, ::onMangaUpdated)
viewModel.isLoading.observe(viewLifecycleOwner, ::onLoadingStateChanged)
@@ -96,6 +103,7 @@ class DetailsFragment :
viewModel.description.observe(viewLifecycleOwner, ::onDescriptionChanged)
viewModel.chapters.observe(viewLifecycleOwner, ::onChaptersChanged)
viewModel.localSize.observe(viewLifecycleOwner, ::onLocalSizeChanged)
viewModel.relatedManga.observe(viewLifecycleOwner, ::onRelatedMangaChanged)
}
override fun onItemClick(item: Bookmark, view: View) {
@@ -193,6 +201,24 @@ class DetailsFragment :
}
}
private fun onRelatedMangaChanged(related: List<MangaItemModel>) {
if (related.isEmpty()) {
requireViewBinding().groupRelated.isVisible = false
return
}
val rv = viewBinding?.recyclerViewRelated ?: return
val adapter = (rv.adapter as? BaseListAdapter) ?: BaseListAdapter(
mangaGridItemAD(
coil, viewLifecycleOwner,
StaticItemSizeResolver(resources.getDimensionPixelSize(R.dimen.smaller_grid_width)),
) { item, view ->
startActivity(DetailsActivity.newIntent(view.context, item), scaleUpActivityOptionsOf(view))
},
).also { rv.adapter = it }
adapter.items = related
requireViewBinding().groupRelated.isVisible = true
}
private fun onHistoryChanged(history: HistoryInfo) {
requireViewBinding().progressView.setPercent(history.history?.percent ?: PROGRESS_NONE, animate = true)
}

View File

@@ -23,6 +23,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.flow.update
@@ -34,6 +35,7 @@ import org.koitharu.kotatsu.core.model.getPreferredBranch
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.parser.MangaIntent
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
@@ -46,12 +48,16 @@ import org.koitharu.kotatsu.core.util.ext.toFileOrNull
import org.koitharu.kotatsu.details.domain.BranchComparator
import org.koitharu.kotatsu.details.domain.DetailsInteractor
import org.koitharu.kotatsu.details.domain.DoubleMangaLoadUseCase
import org.koitharu.kotatsu.details.domain.RelatedMangaUseCase
import org.koitharu.kotatsu.details.domain.model.DoubleManga
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.details.ui.model.HistoryInfo
import org.koitharu.kotatsu.details.ui.model.MangaBranch
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.model.MangaItemModel
import org.koitharu.kotatsu.list.ui.model.toUi
import org.koitharu.kotatsu.local.data.LocalStorageChanges
import org.koitharu.kotatsu.local.domain.DeleteLocalMangaUseCase
import org.koitharu.kotatsu.local.domain.model.LocalManga
@@ -74,6 +80,8 @@ class DetailsViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val deleteLocalMangaUseCase: DeleteLocalMangaUseCase,
private val doubleMangaLoadUseCase: DoubleMangaLoadUseCase,
private val relatedMangaUseCase: RelatedMangaUseCase,
private val extraProvider: ListExtraProvider,
networkState: NetworkState,
) : BaseViewModel() {
@@ -155,6 +163,18 @@ class DetailsViewModel @Inject constructor(
val scrobblingInfo: StateFlow<List<ScrobblingInfo>> = interactor.observeScrobblingInfo(mangaId)
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
val relatedManga: StateFlow<List<MangaItemModel>> = doubleManga.map {
it?.remote
}.distinctUntilChangedBy { it?.id }
.mapLatest {
if (it != null) {
relatedMangaUseCase.invoke(it)?.toUi(ListMode.GRID, extraProvider).orEmpty()
} else {
emptyList()
}
}
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
val branches: StateFlow<List<MangaBranch>> = combine(
doubleManga,
selectedBranch,

View File

@@ -14,16 +14,16 @@ import org.koitharu.kotatsu.core.util.ext.newImageRequest
import org.koitharu.kotatsu.core.util.ext.source
import org.koitharu.kotatsu.databinding.ItemMangaGridBinding
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.MangaGridModel
import org.koitharu.kotatsu.list.ui.size.ItemSizeResolver
import org.koitharu.kotatsu.parsers.model.Manga
fun mangaGridItemAD(
coil: ImageLoader,
lifecycleOwner: LifecycleOwner,
clickListener: OnListItemClickListener<Manga>,
sizeResolver: ItemSizeResolver?,
clickListener: OnListItemClickListener<Manga>,
) = adapterDelegateViewBinding<MangaGridModel, ListModel, ItemMangaGridBinding>(
{ inflater, parent -> ItemMangaGridBinding.inflate(inflater, parent, false) },
) {
@@ -35,7 +35,7 @@ fun mangaGridItemAD(
itemView.setOnLongClickListener {
clickListener.onItemLongClick(item.manga, it)
}
sizeResolver?.attachToView(lifecycleOwner, itemView, binding.textViewTitle)
sizeResolver?.attachToView(lifecycleOwner, itemView, binding.textViewTitle, binding.progressView)
bind { payloads ->
binding.textViewTitle.text = item.title

View File

@@ -14,7 +14,7 @@ open class MangaListAdapter(
delegatesManager
.addDelegate(ITEM_TYPE_MANGA_LIST, mangaListItemAD(coil, lifecycleOwner, listener))
.addDelegate(ITEM_TYPE_MANGA_LIST_DETAILED, mangaListDetailedItemAD(coil, lifecycleOwner, listener))
.addDelegate(ITEM_TYPE_MANGA_GRID, mangaGridItemAD(coil, lifecycleOwner, listener, null))
.addDelegate(ITEM_TYPE_MANGA_GRID, mangaGridItemAD(coil, lifecycleOwner, null, listener))
.addDelegate(ITEM_TYPE_LOADING_FOOTER, loadingFooterAD())
.addDelegate(ITEM_TYPE_LOADING_STATE, loadingStateAD())
.addDelegate(ITEM_TYPE_ERROR_STATE, errorStateListAD(listener))

View File

@@ -61,7 +61,11 @@ suspend fun Manga.toGridModel(
suspend fun List<Manga>.toUi(
mode: ListMode,
extraProvider: ListExtraProvider,
): List<MangaItemModel> = toUi(ArrayList(size), mode, extraProvider)
): List<MangaItemModel> = if (isEmpty()) {
emptyList()
} else {
toUi(ArrayList(size), mode, extraProvider)
}
suspend fun <C : MutableCollection<in MangaItemModel>> List<Manga>.toUi(
destination: C,

View File

@@ -1,4 +1,4 @@
package org.koitharu.kotatsu.list.ui
package org.koitharu.kotatsu.list.ui.size
import android.content.SharedPreferences
import android.content.res.Resources
@@ -9,21 +9,27 @@ import androidx.core.view.updateLayoutParams
import androidx.core.widget.TextViewCompat
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import kotlin.math.roundToInt
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.history.ui.util.ReadingProgressView
import kotlin.math.roundToInt
class ItemSizeResolver(resources: Resources, private val settings: AppSettings) {
class DynamicItemSizeResolver(resources: Resources, private val settings: AppSettings) : ItemSizeResolver {
private val gridWidth = resources.getDimension(R.dimen.preferred_grid_width)
private val scaleFactor: Float
get() = settings.gridSize / 100f
val cellWidth: Int
override val cellWidth: Int
get() = (gridWidth * scaleFactor).roundToInt()
fun attachToView(lifecycleOwner: LifecycleOwner, view: View, textView: TextView?) {
val observer = SizeObserver(view, textView)
override fun attachToView(
lifecycleOwner: LifecycleOwner,
view: View,
textView: TextView?,
progressView: ReadingProgressView?
) {
val observer = SizeObserver(view, textView, progressView)
view.addOnAttachStateChangeListener(observer)
lifecycleOwner.lifecycle.addObserver(observer)
if (view.isAttachedToWindow) {
@@ -34,6 +40,7 @@ class ItemSizeResolver(resources: Resources, private val settings: AppSettings)
private inner class SizeObserver(
private val view: View,
private val textView: TextView?,
private val progressView: ReadingProgressView?,
) : DefaultLifecycleObserver, SharedPreferences.OnSharedPreferenceChangeListener, View.OnAttachStateChangeListener {
private val widthThreshold = view.resources.getDimensionPixelSize(R.dimen.small_grid_width)
@@ -68,6 +75,23 @@ class ItemSizeResolver(resources: Resources, private val settings: AppSettings)
view.updateLayoutParams {
width = newWidth
}
progressView?.adjustSize(newWidth)
}
private fun ReadingProgressView.adjustSize(width: Int) {
val lp = layoutParams
val size = resources.getDimensionPixelSize(
if (width < widthThreshold) {
R.dimen.card_indicator_size_small
} else {
R.dimen.card_indicator_size
},
)
if (lp.width != size || lp.height != size) {
lp.width = size
lp.height = size
layoutParams = lp
}
}
private fun TextView.adjustTextAppearance(width: Int) {

View File

@@ -0,0 +1,18 @@
package org.koitharu.kotatsu.list.ui.size
import android.view.View
import android.widget.TextView
import androidx.lifecycle.LifecycleOwner
import org.koitharu.kotatsu.history.ui.util.ReadingProgressView
interface ItemSizeResolver {
val cellWidth: Int
fun attachToView(
lifecycleOwner: LifecycleOwner,
view: View,
textView: TextView?,
progressView: ReadingProgressView?,
)
}

View File

@@ -0,0 +1,56 @@
package org.koitharu.kotatsu.list.ui.size
import android.view.View
import android.widget.TextView
import androidx.core.view.updateLayoutParams
import androidx.core.widget.TextViewCompat
import androidx.lifecycle.LifecycleOwner
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.history.ui.util.ReadingProgressView
class StaticItemSizeResolver(
override val cellWidth: Int,
) : ItemSizeResolver {
private var widthThreshold: Int = -1
private var textAppearanceResId = R.style.TextAppearance_Kotatsu_GridTitle
override fun attachToView(
lifecycleOwner: LifecycleOwner,
view: View,
textView: TextView?,
progressView: ReadingProgressView?
) {
if (widthThreshold == -1) {
widthThreshold = view.resources.getDimensionPixelSize(R.dimen.small_grid_width)
textAppearanceResId = if (cellWidth < widthThreshold) {
R.style.TextAppearance_Kotatsu_GridTitle_Small
} else {
R.style.TextAppearance_Kotatsu_GridTitle
}
}
if (textView != null) {
TextViewCompat.setTextAppearance(textView, textAppearanceResId)
}
view.updateLayoutParams {
width = cellWidth
}
progressView?.adjustSize()
}
private fun ReadingProgressView.adjustSize() {
val lp = layoutParams
val size = resources.getDimensionPixelSize(
if (cellWidth < widthThreshold) {
R.dimen.card_indicator_size_small
} else {
R.dimen.card_indicator_size
},
)
if (lp.width != size || lp.height != size) {
lp.width = size
lp.height = size
layoutParams = lp
}
}
}

View File

@@ -16,6 +16,7 @@ import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.CompositeMutex
import org.koitharu.kotatsu.core.util.ext.deleteAwait
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.local.data.input.LocalMangaInput
import org.koitharu.kotatsu.local.data.output.LocalMangaOutput
import org.koitharu.kotatsu.local.data.output.LocalMangaUtil
@@ -27,9 +28,7 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import java.io.File
import java.io.FilenameFilter
import java.util.EnumSet
import javax.inject.Inject
import javax.inject.Singleton
@@ -153,6 +152,8 @@ class LocalMangaRepository @Inject constructor(
override suspend fun getTags() = emptySet<MangaTag>()
override suspend fun getRelated(seed: Manga): List<Manga> = emptyList()
suspend fun getOutputDir(manga: Manga): File? {
val defaultDir = storageManager.getDefaultWriteableDir()
if (defaultDir != null && LocalMangaOutput.get(defaultDir, manga) != null) {

View File

@@ -28,10 +28,10 @@ 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.FavouriteCategoriesSheet
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
import org.koitharu.kotatsu.list.ui.model.ListHeader
import org.koitharu.kotatsu.list.ui.size.DynamicItemSizeResolver
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
@@ -64,7 +64,7 @@ class MultiSearchActivity :
val itemCLickListener = OnListItemClickListener<MultiSearchListModel> { item, view ->
startActivity(SearchActivity.newIntent(view.context, item.source, viewModel.query.value))
}
val sizeResolver = ItemSizeResolver(resources, settings)
val sizeResolver = DynamicItemSizeResolver(resources, settings)
val selectionDecoration = MangaSelectionDecoration(this)
selectionController = ListSelectionController(
activity = this,

View File

@@ -6,7 +6,6 @@ import androidx.recyclerview.widget.RecyclerView.RecycledViewPool
import coil.ImageLoader
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD
@@ -15,6 +14,7 @@ import org.koitharu.kotatsu.list.ui.adapter.loadingFooterAD
import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
import org.koitharu.kotatsu.list.ui.size.ItemSizeResolver
import org.koitharu.kotatsu.search.ui.multi.MultiSearchListModel
import kotlin.jvm.internal.Intrinsics

View File

@@ -14,10 +14,10 @@ import org.koitharu.kotatsu.core.ui.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ItemListGroupBinding
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration
import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.size.ItemSizeResolver
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.search.ui.multi.MultiSearchListModel
@@ -35,7 +35,7 @@ fun searchResultsAD(
binding.recyclerView.setRecycledViewPool(sharedPool)
val adapter = ListDelegationAdapter(
mangaGridItemAD(coil, lifecycleOwner, listener, sizeResolver),
mangaGridItemAD(coil, lifecycleOwner, sizeResolver, listener),
)
binding.recyclerView.addItemDecoration(selectionDecoration)
binding.recyclerView.adapter = adapter

View File

@@ -28,8 +28,8 @@ import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver
import org.koitharu.kotatsu.favourites.ui.FavouritesActivity
import org.koitharu.kotatsu.history.ui.HistoryActivity
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.size.DynamicItemSizeResolver
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.search.ui.MangaListActivity
@@ -67,7 +67,7 @@ class ShelfFragment :
override fun onViewBindingCreated(binding: FragmentShelfBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState)
nestedScrollStateHandle = NestedScrollStateHandle(savedInstanceState, KEY_NESTED_SCROLL)
val sizeResolver = ItemSizeResolver(resources, settings)
val sizeResolver = DynamicItemSizeResolver(resources, settings)
selectionController = SectionedSelectionController(
activity = requireActivity(),
owner = this,

View File

@@ -2,14 +2,12 @@ package org.koitharu.kotatsu.shelf.ui.adapter
import android.content.Context
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import coil.ImageLoader
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import org.koitharu.kotatsu.core.ui.list.NestedScrollStateHandle
import org.koitharu.kotatsu.core.ui.list.SectionedSelectionController
import org.koitharu.kotatsu.core.ui.list.fastscroll.FastScroller
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
import org.koitharu.kotatsu.list.ui.adapter.emptyHintAD
import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD
@@ -17,9 +15,8 @@ import org.koitharu.kotatsu.list.ui.adapter.errorStateListAD
import org.koitharu.kotatsu.list.ui.adapter.loadingFooterAD
import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
import org.koitharu.kotatsu.list.ui.size.ItemSizeResolver
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
import kotlin.jvm.internal.Intrinsics
class ShelfAdapter(
lifecycleOwner: LifecycleOwner,
@@ -33,16 +30,16 @@ class ShelfAdapter(
init {
val pool = RecyclerView.RecycledViewPool()
delegatesManager.addDelegate(
shelfGroupAD(
sharedPool = pool,
lifecycleOwner = lifecycleOwner,
coil = coil,
sizeResolver = sizeResolver,
selectionController = selectionController,
listener = listener,
nestedScrollStateHandle = nestedScrollStateHandle,
),
).addDelegate(loadingStateAD()).addDelegate(loadingFooterAD())
shelfGroupAD(
sharedPool = pool,
lifecycleOwner = lifecycleOwner,
coil = coil,
sizeResolver = sizeResolver,
selectionController = selectionController,
listener = listener,
nestedScrollStateHandle = nestedScrollStateHandle,
),
).addDelegate(loadingStateAD()).addDelegate(loadingFooterAD())
.addDelegate(emptyHintAD(coil, lifecycleOwner, listener))
.addDelegate(emptyStateListAD(coil, lifecycleOwner, listener)).addDelegate(errorStateListAD(listener))
}

View File

@@ -15,10 +15,10 @@ import org.koitharu.kotatsu.core.ui.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.core.util.ext.removeItemDecoration
import org.koitharu.kotatsu.core.util.ext.setTextAndVisible
import org.koitharu.kotatsu.databinding.ItemListGroupBinding
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.size.ItemSizeResolver
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
@@ -49,7 +49,7 @@ fun shelfGroupAD(
val adapter = AsyncListDifferDelegationAdapter(
ListModelDiffCallback,
mangaGridItemAD(coil, lifecycleOwner, listenerAdapter, sizeResolver),
mangaGridItemAD(coil, lifecycleOwner, sizeResolver, listenerAdapter),
)
adapter.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
adapter.registerAdapterDataObserver(ScrollKeepObserver(binding.recyclerView))

View File

@@ -301,6 +301,56 @@
app:constraint_referenced_ids="recyclerView_scrobbling,textView_scrobbling_title,button_scrobbling_more"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_related_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:padding="@dimen/grid_spacing"
android:singleLine="true"
android:text="@string/related_manga"
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
app:layout_constraintEnd_toStartOf="@id/button_related_more"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/recyclerView_scrobbling" />
<Button
android:id="@+id/button_related_more"
style="@style/Widget.Kotatsu.Button.More"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/show_all"
app:layout_constraintBaseline_toBaselineOf="@id/textView_related_title"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView_related"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
android:orientation="horizontal"
android:paddingStart="12dp"
android:paddingEnd="12dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView_related_title"
tools:listitem="@layout/item_manga_grid" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_related"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="recyclerView_related,textView_related_title,button_related_more"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -19,6 +19,7 @@
<dimen name="chapter_list_item_height">48dp</dimen>
<dimen name="preferred_grid_width">120dp</dimen>
<dimen name="small_grid_width">92dp</dimen>
<dimen name="smaller_grid_width">102dp</dimen>
<dimen name="header_height">48dp</dimen>
<dimen name="list_footer_height_inner">36dp</dimen>
<dimen name="list_footer_height_outer">48dp</dimen>
@@ -45,6 +46,7 @@
<dimen name="dialog_radius">8dp</dimen>
<dimen name="card_indicator_size">32dp</dimen>
<dimen name="card_indicator_size_small">24dp</dimen>
<dimen name="card_indicator_offset">8dp</dimen>
<dimen name="appwidget_corner_radius_inner">8dp</dimen>

View File

@@ -452,4 +452,5 @@
<string name="description">Description</string>
<string name="this_month">This month</string>
<string name="voice_search">Voice search</string>
<string name="related_manga">Related manga</string>
</resources>