Improve scrobbling selection

This commit is contained in:
Koitharu
2024-01-17 11:41:03 +02:00
parent 18e573b6b8
commit 48808f8a7d
6 changed files with 74 additions and 22 deletions

View File

@@ -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()
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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)
}
}

View File

@@ -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"