Fix list items reorder
This commit is contained in:
@@ -95,7 +95,6 @@ dependencies {
|
|||||||
implementation 'androidx.activity:activity-ktx:1.8.1'
|
implementation 'androidx.activity:activity-ktx:1.8.1'
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.6.2'
|
implementation 'androidx.fragment:fragment-ktx:1.6.2'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
|
||||||
// implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-service:2.6.2'
|
implementation 'androidx.lifecycle:lifecycle-service:2.6.2'
|
||||||
implementation 'androidx.lifecycle:lifecycle-process:2.6.2'
|
implementation 'androidx.lifecycle:lifecycle-process:2.6.2'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
@@ -126,8 +125,8 @@ dependencies {
|
|||||||
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.2'
|
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.2'
|
||||||
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2'
|
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2'
|
||||||
|
|
||||||
implementation 'com.google.dagger:hilt-android:2.48.1'
|
implementation 'com.google.dagger:hilt-android:2.49'
|
||||||
kapt 'com.google.dagger:hilt-compiler:2.48.1'
|
kapt 'com.google.dagger:hilt-compiler:2.49'
|
||||||
implementation 'androidx.hilt:hilt-work:1.1.0'
|
implementation 'androidx.hilt:hilt-work:1.1.0'
|
||||||
kapt 'androidx.hilt:hilt-compiler:1.1.0'
|
kapt 'androidx.hilt:hilt-compiler:1.1.0'
|
||||||
|
|
||||||
@@ -156,6 +155,6 @@ dependencies {
|
|||||||
androidTestImplementation 'androidx.room:room-testing:2.6.1'
|
androidTestImplementation 'androidx.room:room-testing:2.6.1'
|
||||||
androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.15.0'
|
androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.15.0'
|
||||||
|
|
||||||
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.48.1'
|
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.49'
|
||||||
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.48.1'
|
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.49'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package org.koitharu.kotatsu.core.ui
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.AsyncListDiffer.ListListener
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import com.hannesdorfmann.adapterdelegates4.AdapterDelegate
|
||||||
|
import com.hannesdorfmann.adapterdelegates4.ListDelegationAdapter
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.FlowCollector
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
||||||
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
|
import java.util.Collections
|
||||||
|
import java.util.LinkedList
|
||||||
|
|
||||||
|
open class ReorderableListAdapter<T : ListModel> : ListDelegationAdapter<List<T>>(), FlowCollector<List<T>?> {
|
||||||
|
|
||||||
|
private val listListeners = LinkedList<ListListener<T>>()
|
||||||
|
|
||||||
|
override suspend fun emit(value: List<T>?) {
|
||||||
|
val oldList = items.orEmpty()
|
||||||
|
val newList = value.orEmpty()
|
||||||
|
val diffResult = withContext(Dispatchers.Default) {
|
||||||
|
val diffCallback = DiffCallback(oldList, newList)
|
||||||
|
DiffUtil.calculateDiff(diffCallback)
|
||||||
|
}
|
||||||
|
super.setItems(newList)
|
||||||
|
diffResult.dispatchUpdatesTo(this)
|
||||||
|
listListeners.forEach { it.onCurrentListChanged(oldList, newList) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Use emit() to dispatch list updates", level = DeprecationLevel.ERROR)
|
||||||
|
override fun setItems(items: List<T>?) {
|
||||||
|
super.setItems(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reorderItems(oldPos: Int, newPos: Int) {
|
||||||
|
Collections.swap(items ?: return, oldPos, newPos)
|
||||||
|
notifyItemMoved(oldPos, newPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addDelegate(type: ListItemType, delegate: AdapterDelegate<List<T>>): ReorderableListAdapter<T> {
|
||||||
|
delegatesManager.addDelegate(type.ordinal, delegate)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addListListener(listListener: ListListener<T>) {
|
||||||
|
listListeners.add(listListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeListListener(listListener: ListListener<T>) {
|
||||||
|
listListeners.remove(listListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class DiffCallback<T : ListModel>(
|
||||||
|
val oldList: List<T>,
|
||||||
|
val newList: List<T>,
|
||||||
|
) : DiffUtil.Callback() {
|
||||||
|
|
||||||
|
override fun getOldListSize(): Int = oldList.size
|
||||||
|
|
||||||
|
override fun getNewListSize(): Int = newList.size
|
||||||
|
|
||||||
|
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||||
|
val oldItem = oldList[oldItemPosition]
|
||||||
|
val newItem = newList[newItemPosition]
|
||||||
|
return newItem.areItemsTheSame(oldItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||||
|
val oldItem = oldList[oldItemPosition]
|
||||||
|
val newItem = newList[newItemPosition]
|
||||||
|
return newItem == oldItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -142,7 +142,11 @@ class FavouriteCategoriesActivity :
|
|||||||
}
|
}
|
||||||
val fromPos = viewHolder.bindingAdapterPosition
|
val fromPos = viewHolder.bindingAdapterPosition
|
||||||
val toPos = target.bindingAdapterPosition
|
val toPos = target.bindingAdapterPosition
|
||||||
return fromPos != toPos && fromPos != RecyclerView.NO_POSITION && toPos != RecyclerView.NO_POSITION
|
if (fromPos == toPos || fromPos == RecyclerView.NO_POSITION || toPos == RecyclerView.NO_POSITION) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
adapter.reorderItems(fromPos, toPos)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun canDropOver(
|
override fun canDropOver(
|
||||||
@@ -151,25 +155,16 @@ class FavouriteCategoriesActivity :
|
|||||||
target: RecyclerView.ViewHolder,
|
target: RecyclerView.ViewHolder,
|
||||||
): Boolean = current.itemViewType == target.itemViewType
|
): Boolean = current.itemViewType == target.itemViewType
|
||||||
|
|
||||||
override fun onMoved(
|
|
||||||
recyclerView: RecyclerView,
|
|
||||||
viewHolder: RecyclerView.ViewHolder,
|
|
||||||
fromPos: Int,
|
|
||||||
target: RecyclerView.ViewHolder,
|
|
||||||
toPos: Int,
|
|
||||||
x: Int,
|
|
||||||
y: Int,
|
|
||||||
) {
|
|
||||||
super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y)
|
|
||||||
viewModel.reorderCategories(fromPos, toPos)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isLongPressDragEnabled(): Boolean = false
|
override fun isLongPressDragEnabled(): Boolean = false
|
||||||
|
|
||||||
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
||||||
super.onSelectedChanged(viewHolder, actionState)
|
super.onSelectedChanged(viewHolder, actionState)
|
||||||
viewBinding.recyclerView.isNestedScrollingEnabled =
|
viewBinding.recyclerView.isNestedScrollingEnabled = actionState == ItemTouchHelper.ACTION_STATE_IDLE
|
||||||
actionState == ItemTouchHelper.ACTION_STATE_IDLE
|
}
|
||||||
|
|
||||||
|
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||||
|
super.clearView(recyclerView, viewHolder)
|
||||||
|
viewModel.saveOrder(adapter.items ?: return)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package org.koitharu.kotatsu.favourites.ui.categories
|
package org.koitharu.kotatsu.favourites.ui.categories
|
||||||
|
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.cancelAndJoin
|
import kotlinx.coroutines.cancelAndJoin
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.plus
|
||||||
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.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
@@ -19,7 +20,6 @@ import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
|
|||||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||||
import org.koitharu.kotatsu.parsers.util.move
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@@ -30,17 +30,9 @@ class FavouritesCategoriesViewModel @Inject constructor(
|
|||||||
|
|
||||||
private var commitJob: Job? = null
|
private var commitJob: Job? = null
|
||||||
|
|
||||||
val content = MutableStateFlow<List<ListModel>>(listOf(LoadingState))
|
val content = repository.observeCategoriesWithCovers()
|
||||||
|
.map { it.toUiList() }
|
||||||
init {
|
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState))
|
||||||
launchJob(Dispatchers.Default) {
|
|
||||||
repository.observeCategoriesWithCovers()
|
|
||||||
.collectLatest {
|
|
||||||
commitJob?.join()
|
|
||||||
updateContent(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteCategories(ids: Set<Long>) {
|
fun deleteCategories(ids: Set<Long>) {
|
||||||
launchJob(Dispatchers.Default) {
|
launchJob(Dispatchers.Default) {
|
||||||
@@ -54,11 +46,17 @@ class FavouritesCategoriesViewModel @Inject constructor(
|
|||||||
|
|
||||||
fun isEmpty(): Boolean = content.value.none { it is CategoryListModel }
|
fun isEmpty(): Boolean = content.value.none { it is CategoryListModel }
|
||||||
|
|
||||||
fun reorderCategories(oldPos: Int, newPos: Int) {
|
fun saveOrder(snapshot: List<ListModel>) {
|
||||||
val snapshot = content.requireValue().toMutableList()
|
val prevJob = commitJob
|
||||||
snapshot.move(oldPos, newPos)
|
commitJob = launchJob {
|
||||||
content.value = snapshot
|
prevJob?.cancelAndJoin()
|
||||||
commit(snapshot)
|
val ids = snapshot.mapNotNullTo(ArrayList(snapshot.size)) {
|
||||||
|
(it as? CategoryListModel)?.category?.id
|
||||||
|
}
|
||||||
|
if (ids.isNotEmpty()) {
|
||||||
|
repository.reorderCategories(ids)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setIsVisible(ids: Set<Long>, isVisible: Boolean) {
|
fun setIsVisible(ids: Set<Long>, isVisible: Boolean) {
|
||||||
@@ -76,36 +74,21 @@ class FavouritesCategoriesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun commit(snapshot: List<ListModel>) {
|
private fun Map<FavouriteCategory, List<Cover>>.toUiList(): List<ListModel> = map { (category, covers) ->
|
||||||
val prevJob = commitJob
|
CategoryListModel(
|
||||||
commitJob = launchJob {
|
mangaCount = covers.size,
|
||||||
prevJob?.cancelAndJoin()
|
covers = covers.take(3),
|
||||||
delay(500)
|
category = category,
|
||||||
val ids = snapshot.mapNotNullTo(ArrayList(snapshot.size)) {
|
isTrackerEnabled = settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources,
|
||||||
(it as? CategoryListModel)?.category?.id
|
)
|
||||||
}
|
}.ifEmpty {
|
||||||
repository.reorderCategories(ids)
|
listOf(
|
||||||
yield()
|
EmptyState(
|
||||||
}
|
icon = R.drawable.ic_empty_favourites,
|
||||||
}
|
textPrimary = R.string.text_empty_holder_primary,
|
||||||
|
textSecondary = R.string.empty_favourite_categories,
|
||||||
private fun updateContent(categories: Map<FavouriteCategory, List<Cover>>) {
|
actionStringRes = 0,
|
||||||
content.value = categories.map { (category, covers) ->
|
),
|
||||||
CategoryListModel(
|
)
|
||||||
mangaCount = covers.size,
|
|
||||||
covers = covers.take(3),
|
|
||||||
category = category,
|
|
||||||
isTrackerEnabled = settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources,
|
|
||||||
)
|
|
||||||
}.ifEmpty {
|
|
||||||
listOf(
|
|
||||||
EmptyState(
|
|
||||||
icon = R.drawable.ic_empty_favourites,
|
|
||||||
textPrimary = R.string.text_empty_holder_primary,
|
|
||||||
textSecondary = R.string.empty_favourite_categories,
|
|
||||||
actionStringRes = 0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package org.koitharu.kotatsu.favourites.ui.categories.adapter
|
|||||||
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import org.koitharu.kotatsu.core.ui.BaseListAdapter
|
import org.koitharu.kotatsu.core.ui.ReorderableListAdapter
|
||||||
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesListListener
|
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesListListener
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
|
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
|
||||||
@@ -15,7 +15,7 @@ class CategoriesAdapter(
|
|||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
onItemClickListener: FavouriteCategoriesListListener,
|
onItemClickListener: FavouriteCategoriesListListener,
|
||||||
listListener: ListStateHolderListener,
|
listListener: ListStateHolderListener,
|
||||||
) : BaseListAdapter<ListModel>() {
|
) : ReorderableListAdapter<ListModel>() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
addDelegate(ListItemType.CATEGORY_LARGE, categoryAD(coil, lifecycleOwner, onItemClickListener))
|
addDelegate(ListItemType.CATEGORY_LARGE, categoryAD(coil, lifecycleOwner, onItemClickListener))
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ package org.koitharu.kotatsu.settings.sources.adapter
|
|||||||
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import org.koitharu.kotatsu.core.ui.BaseListAdapter
|
import org.koitharu.kotatsu.core.ui.ReorderableListAdapter
|
||||||
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
||||||
|
|
||||||
class SourceConfigAdapter(
|
class SourceConfigAdapter(
|
||||||
listener: SourceConfigListener,
|
listener: SourceConfigListener,
|
||||||
coil: ImageLoader,
|
coil: ImageLoader,
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
) : BaseListAdapter<SourceConfigItem>() {
|
) : ReorderableListAdapter<SourceConfigItem>() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
with(delegatesManager) {
|
with(delegatesManager) {
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ class SourcesManageFragment :
|
|||||||
lateinit var shortcutManager: AppShortcutManager
|
lateinit var shortcutManager: AppShortcutManager
|
||||||
|
|
||||||
private var reorderHelper: ItemTouchHelper? = null
|
private var reorderHelper: ItemTouchHelper? = null
|
||||||
|
private var sourcesAdapter: SourceConfigAdapter? = null
|
||||||
private val viewModel by viewModels<SourcesManageViewModel>()
|
private val viewModel by viewModels<SourcesManageViewModel>()
|
||||||
|
|
||||||
override val recyclerView: RecyclerView
|
override val recyclerView: RecyclerView
|
||||||
@@ -69,7 +70,7 @@ class SourcesManageFragment :
|
|||||||
savedInstanceState: Bundle?,
|
savedInstanceState: Bundle?,
|
||||||
) {
|
) {
|
||||||
super.onViewBindingCreated(binding, savedInstanceState)
|
super.onViewBindingCreated(binding, savedInstanceState)
|
||||||
val sourcesAdapter = SourceConfigAdapter(this, coil, viewLifecycleOwner)
|
sourcesAdapter = SourceConfigAdapter(this, coil, viewLifecycleOwner)
|
||||||
with(binding.recyclerView) {
|
with(binding.recyclerView) {
|
||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
adapter = sourcesAdapter
|
adapter = sourcesAdapter
|
||||||
@@ -77,7 +78,7 @@ class SourcesManageFragment :
|
|||||||
it.attachToRecyclerView(this)
|
it.attachToRecyclerView(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModel.content.observe(viewLifecycleOwner, sourcesAdapter)
|
viewModel.content.observe(viewLifecycleOwner, checkNotNull(sourcesAdapter))
|
||||||
viewModel.onActionDone.observeEvent(
|
viewModel.onActionDone.observeEvent(
|
||||||
viewLifecycleOwner,
|
viewLifecycleOwner,
|
||||||
ReversibleActionObserver(binding.recyclerView),
|
ReversibleActionObserver(binding.recyclerView),
|
||||||
@@ -91,6 +92,7 @@ class SourcesManageFragment :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
sourcesAdapter = null
|
||||||
reorderHelper = null
|
reorderHelper = null
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
@@ -204,7 +206,7 @@ class SourcesManageFragment :
|
|||||||
y: Int,
|
y: Int,
|
||||||
) {
|
) {
|
||||||
super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y)
|
super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y)
|
||||||
viewModel.reorderSources(fromPos, toPos)
|
sourcesAdapter?.reorderItems(fromPos, toPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun canDropOver(
|
override fun canDropOver(
|
||||||
@@ -248,5 +250,10 @@ class SourcesManageFragment :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun isLongPressDragEnabled() = true
|
override fun isLongPressDragEnabled() = true
|
||||||
|
|
||||||
|
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||||
|
super.clearView(recyclerView, viewHolder)
|
||||||
|
viewModel.saveSourcesOrder(sourcesAdapter?.items ?: return)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.cancelAndJoin
|
import kotlinx.coroutines.cancelAndJoin
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.yield
|
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||||
import org.koitharu.kotatsu.core.db.removeObserverAsync
|
import org.koitharu.kotatsu.core.db.removeObserverAsync
|
||||||
@@ -43,17 +41,19 @@ class SourcesManageViewModel @Inject constructor(
|
|||||||
database.invalidationTracker.removeObserverAsync(listProducer)
|
database.invalidationTracker.removeObserverAsync(listProducer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reorderSources(oldPos: Int, newPos: Int) {
|
fun saveSourcesOrder(snapshot: List<SourceConfigItem>) {
|
||||||
val snapshot = content.value.toMutableList()
|
val prevJob = commitJob
|
||||||
if ((snapshot[oldPos] as? SourceConfigItem.SourceItem)?.isDraggable != true) {
|
commitJob = launchJob(Dispatchers.Default) {
|
||||||
return
|
prevJob?.cancelAndJoin()
|
||||||
|
val newSourcesList = snapshot.mapNotNull { x ->
|
||||||
|
if (x is SourceConfigItem.SourceItem && x.isDraggable) {
|
||||||
|
x.source
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repository.setPositions(newSourcesList)
|
||||||
}
|
}
|
||||||
if ((snapshot[newPos] as? SourceConfigItem.SourceItem)?.isDraggable != true) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
snapshot.move(oldPos, newPos)
|
|
||||||
content.value = snapshot
|
|
||||||
commit(snapshot)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canReorder(oldPos: Int, newPos: Int): Boolean {
|
fun canReorder(oldPos: Int, newPos: Int): Boolean {
|
||||||
@@ -72,28 +72,31 @@ class SourcesManageViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun bringToTop(source: MangaSource) {
|
fun bringToTop(source: MangaSource) {
|
||||||
var oldPos = -1
|
|
||||||
var newPos = -1
|
|
||||||
val snapshot = content.value
|
val snapshot = content.value
|
||||||
for ((i, x) in snapshot.withIndex()) {
|
launchJob(Dispatchers.Default) {
|
||||||
if (x !is SourceConfigItem.SourceItem) {
|
var oldPos = -1
|
||||||
continue
|
var newPos = -1
|
||||||
|
for ((i, x) in snapshot.withIndex()) {
|
||||||
|
if (x !is SourceConfigItem.SourceItem) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (newPos == -1) {
|
||||||
|
newPos = i
|
||||||
|
}
|
||||||
|
if (x.source == source) {
|
||||||
|
oldPos = i
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (newPos == -1) {
|
@Suppress("KotlinConstantConditions")
|
||||||
newPos = i
|
if (oldPos != -1 && newPos != -1) {
|
||||||
|
reorderSources(oldPos, newPos)
|
||||||
|
val revert = ReversibleAction(R.string.moved_to_top) {
|
||||||
|
reorderSources(newPos, oldPos)
|
||||||
|
}
|
||||||
|
commitJob?.join()
|
||||||
|
onActionDone.call(revert)
|
||||||
}
|
}
|
||||||
if (x.source == source) {
|
|
||||||
oldPos = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Suppress("KotlinConstantConditions")
|
|
||||||
if (oldPos != -1 && newPos != -1) {
|
|
||||||
reorderSources(oldPos, newPos)
|
|
||||||
val revert = ReversibleAction(R.string.moved_to_top) {
|
|
||||||
reorderSources(newPos, oldPos)
|
|
||||||
}
|
|
||||||
onActionDone.call(revert)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,20 +116,15 @@ class SourcesManageViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun commit(snapshot: List<SourceConfigItem>) {
|
private fun reorderSources(oldPos: Int, newPos: Int) {
|
||||||
val prevJob = commitJob
|
val snapshot = content.value.toMutableList()
|
||||||
commitJob = launchJob {
|
if ((snapshot[oldPos] as? SourceConfigItem.SourceItem)?.isDraggable != true) {
|
||||||
prevJob?.cancelAndJoin()
|
return
|
||||||
delay(500)
|
|
||||||
val newSourcesList = snapshot.mapNotNull { x ->
|
|
||||||
if (x is SourceConfigItem.SourceItem && x.isDraggable) {
|
|
||||||
x.source
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
repository.setPositions(newSourcesList)
|
|
||||||
yield()
|
|
||||||
}
|
}
|
||||||
|
if ((snapshot[newPos] as? SourceConfigItem.SourceItem)?.isDraggable != true) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
snapshot.move(oldPos, newPos)
|
||||||
|
saveSourcesOrder(snapshot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.1.4'
|
classpath 'com.android.tools.build:gradle:8.1.4'
|
||||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21'
|
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21'
|
||||||
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.48.1'
|
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.49'
|
||||||
classpath 'com.google.devtools.ksp:symbol-processing-gradle-plugin:1.9.21-1.0.15'
|
classpath 'com.google.devtools.ksp:symbol-processing-gradle-plugin:1.9.21-1.0.15'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,5 +22,5 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('clean', Delete) {
|
tasks.register('clean', Delete) {
|
||||||
delete rootProject.layout.buildDirectory
|
delete rootProject.layout.buildDirectory
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user