Improve scrobbling selection
This commit is contained in:
@@ -15,7 +15,9 @@ import androidx.core.view.descendants
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.progressindicator.BaseProgressIndicator
|
||||
import com.google.android.material.slider.Slider
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
@@ -161,3 +163,12 @@ val Toolbar.menuView: ActionMenuView?
|
||||
menu // to call ensureMenu()
|
||||
return children.firstNotNullOfOrNull { it as? ActionMenuView }
|
||||
}
|
||||
|
||||
fun MaterialButton.setProgressIcon() {
|
||||
val progressDrawable = CircularProgressDrawable(context)
|
||||
progressDrawable.strokeWidth = resources.resolveDp(2f)
|
||||
progressDrawable.setColorSchemeColors(currentTextColor)
|
||||
progressDrawable.setTintList(textColors)
|
||||
icon = progressDrawable
|
||||
progressDrawable.start()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package org.koitharu.kotatsu.explore.ui.adapter
|
||||
|
||||
import android.graphics.Color
|
||||
import android.view.View
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -15,10 +13,9 @@ import org.koitharu.kotatsu.core.ui.image.TrimTransformation
|
||||
import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.util.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||
import org.koitharu.kotatsu.core.util.ext.newImageRequest
|
||||
import org.koitharu.kotatsu.core.util.ext.resolveDp
|
||||
import org.koitharu.kotatsu.core.util.ext.setOnContextClickListenerCompat
|
||||
import org.koitharu.kotatsu.core.util.ext.setProgressIcon
|
||||
import org.koitharu.kotatsu.core.util.ext.source
|
||||
import org.koitharu.kotatsu.core.util.ext.textAndVisible
|
||||
import org.koitharu.kotatsu.databinding.ItemExploreButtonsBinding
|
||||
@@ -30,7 +27,6 @@ import org.koitharu.kotatsu.explore.ui.model.MangaSourceItem
|
||||
import org.koitharu.kotatsu.explore.ui.model.RecommendationsItem
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
fun exploreButtonsAD(
|
||||
clickListener: View.OnClickListener,
|
||||
@@ -45,16 +41,7 @@ fun exploreButtonsAD(
|
||||
|
||||
bind {
|
||||
if (item.isRandomLoading) {
|
||||
val icon = CircularProgressDrawable(context)
|
||||
icon.strokeWidth = context.resources.resolveDp(2f)
|
||||
icon.setColorSchemeColors(
|
||||
context.getThemeColor(
|
||||
materialR.attr.colorPrimary,
|
||||
Color.DKGRAY,
|
||||
),
|
||||
)
|
||||
binding.buttonRandom.icon = icon
|
||||
icon.start()
|
||||
binding.buttonRandom.setProgressIcon()
|
||||
} else {
|
||||
binding.buttonRandom.setIconResource(R.drawable.ic_dice)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.scrobbling.common.domain
|
||||
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.collection.LongSparseArray
|
||||
import androidx.collection.getOrElse
|
||||
import androidx.core.text.parseAsHtml
|
||||
@@ -11,9 +12,11 @@ import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.util.ext.findKeyByValue
|
||||
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.core.util.ext.sanitize
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository
|
||||
import org.koitharu.kotatsu.scrobbling.common.data.ScrobblingEntity
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerMangaInfo
|
||||
@@ -21,13 +24,12 @@ import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus
|
||||
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||
import java.util.EnumMap
|
||||
|
||||
abstract class Scrobbler(
|
||||
protected val db: MangaDatabase,
|
||||
val scrobblerService: ScrobblerService,
|
||||
private val repository: org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository,
|
||||
private val repository: ScrobblerRepository,
|
||||
) {
|
||||
|
||||
private val infoCache = LongSparseArray<ScrobblerMangaInfo>()
|
||||
@@ -76,7 +78,12 @@ abstract class Scrobbler(
|
||||
return entity.toScrobblingInfo()
|
||||
}
|
||||
|
||||
abstract suspend fun updateScrobblingInfo(mangaId: Long, rating: Float, status: ScrobblingStatus?, comment: String?)
|
||||
abstract suspend fun updateScrobblingInfo(
|
||||
mangaId: Long,
|
||||
@FloatRange(from = 0.0, to = 1.0) rating: Float,
|
||||
status: ScrobblingStatus?,
|
||||
comment: String?,
|
||||
)
|
||||
|
||||
fun observeScrobblingInfo(mangaId: Long): Flow<ScrobblingInfo?> {
|
||||
return db.getScrobblingDao().observe(scrobblerService.id, mangaId)
|
||||
|
||||
@@ -23,6 +23,8 @@ import org.koitharu.kotatsu.core.util.ext.firstVisibleItemPosition
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.core.util.ext.setProgressIcon
|
||||
import org.koitharu.kotatsu.core.util.ext.setTabsEnabled
|
||||
import org.koitharu.kotatsu.core.util.ext.withArgs
|
||||
import org.koitharu.kotatsu.databinding.SheetScrobblingSelectorBinding
|
||||
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
|
||||
@@ -80,6 +82,15 @@ class ScrobblingSelectorSheet :
|
||||
viewModel.onClose.observeEvent(viewLifecycleOwner) {
|
||||
dismiss()
|
||||
}
|
||||
viewModel.isLoading.observe(viewLifecycleOwner) { isLoading ->
|
||||
binding.buttonDone.isEnabled = !isLoading
|
||||
if (isLoading) {
|
||||
binding.buttonDone.setProgressIcon()
|
||||
} else {
|
||||
binding.buttonDone.icon = null
|
||||
}
|
||||
binding.tabs.setTabsEnabled(!isLoading)
|
||||
}
|
||||
viewModel.selectedScrobblerIndex.observe(viewLifecycleOwner) { index ->
|
||||
val tab = binding.tabs.getTabAt(index)
|
||||
if (tab != null && !tab.isSelected) {
|
||||
@@ -100,7 +111,7 @@ class ScrobblingSelectorSheet :
|
||||
}
|
||||
|
||||
override fun onItemClick(item: ScrobblerManga, view: View) {
|
||||
viewModel.selectedItemId.value = item.id
|
||||
viewModel.selectItem(item.id)
|
||||
}
|
||||
|
||||
override fun onRetryClick(error: Throwable) {
|
||||
|
||||
@@ -14,20 +14,24 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.findById
|
||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
||||
import org.koitharu.kotatsu.core.parser.MangaIntent
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
import org.koitharu.kotatsu.core.util.ext.call
|
||||
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.core.util.ext.require
|
||||
import org.koitharu.kotatsu.core.util.ext.requireValue
|
||||
import org.koitharu.kotatsu.history.data.HistoryRepository
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus
|
||||
import org.koitharu.kotatsu.scrobbling.common.ui.selector.model.ScrobblerHint
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -35,6 +39,8 @@ import javax.inject.Inject
|
||||
class ScrobblingSelectorViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
scrobblers: Set<@JvmSuppressWildcards Scrobbler>,
|
||||
private val historyRepository: HistoryRepository,
|
||||
private val mangaRepositoryFactory: MangaRepository.Factory,
|
||||
) : BaseViewModel() {
|
||||
|
||||
val manga = savedStateHandle.require<ParcelableManga>(MangaIntent.KEY_MANGA).manga
|
||||
@@ -92,6 +98,13 @@ class ScrobblingSelectorViewModel @Inject constructor(
|
||||
loadList(append = false)
|
||||
}
|
||||
|
||||
fun selectItem(id: Long) {
|
||||
if (doneJob?.isActive == true) {
|
||||
return
|
||||
}
|
||||
selectedItemId.value = id
|
||||
}
|
||||
|
||||
fun loadNextPage() {
|
||||
if (scrobblerMangaList.value.isNotEmpty() && hasNextPage.value) {
|
||||
loadList(append = true)
|
||||
@@ -109,7 +122,7 @@ class ScrobblingSelectorViewModel @Inject constructor(
|
||||
if (loadingJob?.isActive == true) {
|
||||
return
|
||||
}
|
||||
loadingJob = launchLoadingJob(Dispatchers.Default) {
|
||||
loadingJob = launchJob(Dispatchers.Default) {
|
||||
listError.value = null
|
||||
val offset = if (append) scrobblerMangaList.value.size else 0
|
||||
runCatchingCancellable {
|
||||
@@ -136,8 +149,31 @@ class ScrobblingSelectorViewModel @Inject constructor(
|
||||
if (targetId == NO_ID) {
|
||||
onClose.call(Unit)
|
||||
}
|
||||
doneJob = launchJob(Dispatchers.Default) {
|
||||
doneJob = launchLoadingJob(Dispatchers.Default) {
|
||||
val prevInfo = currentScrobbler.getScrobblingInfoOrNull(manga.id)
|
||||
currentScrobbler.linkManga(manga.id, targetId)
|
||||
val history = historyRepository.getOne(manga)
|
||||
currentScrobbler.updateScrobblingInfo(
|
||||
mangaId = manga.id,
|
||||
rating = prevInfo?.rating ?: manga.rating,
|
||||
status = prevInfo?.status ?: if (history == null) {
|
||||
ScrobblingStatus.PLANNED
|
||||
} else {
|
||||
ScrobblingStatus.READING
|
||||
},
|
||||
comment = prevInfo?.comment,
|
||||
)
|
||||
if (history != null) {
|
||||
val chapter = mangaRepositoryFactory.create(manga.source)
|
||||
.getDetails(manga)
|
||||
.chapters?.findById(history.chapterId)
|
||||
if (chapter != null) {
|
||||
currentScrobbler.scrobble(
|
||||
mangaId = manga.id,
|
||||
chapter = chapter,
|
||||
)
|
||||
}
|
||||
}
|
||||
onClose.call(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
app:tabMode="scrollable"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_done"
|
||||
style="@style/Widget.Material3.Button.UnelevatedButton"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
Reference in New Issue
Block a user