Option to undo removing from favourites
This commit is contained in:
@@ -102,7 +102,7 @@ dependencies {
|
|||||||
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2'
|
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2'
|
||||||
|
|
||||||
implementation 'io.insert-koin:koin-android:3.2.0'
|
implementation 'io.insert-koin:koin-android:3.2.0'
|
||||||
implementation 'io.coil-kt:coil-base:2.0.0-rc03'
|
implementation 'io.coil-kt:coil-base:2.0.0'
|
||||||
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
|
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
|
||||||
implementation 'com.github.solkin:disk-lru-cache:1.4'
|
implementation 'com.github.solkin:disk-lru-cache:1.4'
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.koitharu.kotatsu.base.domain
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
|
||||||
|
|
||||||
|
fun interface ReversibleHandle {
|
||||||
|
|
||||||
|
suspend fun reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ReversibleHandle.reverseAsync() = processLifecycleScope.launch(Dispatchers.Default) {
|
||||||
|
reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun ReversibleHandle.plus(other: ReversibleHandle) = ReversibleHandle {
|
||||||
|
this.reverse()
|
||||||
|
other.reverse()
|
||||||
|
}
|
||||||
@@ -15,6 +15,10 @@ abstract class HistoryDao {
|
|||||||
@Query("SELECT * FROM history ORDER BY updated_at DESC LIMIT :limit OFFSET :offset")
|
@Query("SELECT * FROM history ORDER BY updated_at DESC LIMIT :limit OFFSET :offset")
|
||||||
abstract suspend fun findAll(offset: Int, limit: Int): List<HistoryWithManga>
|
abstract suspend fun findAll(offset: Int, limit: Int): List<HistoryWithManga>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT * FROM history WHERE manga_id IN (:ids)")
|
||||||
|
abstract suspend fun findAll(ids: Collection<Long>): List<HistoryEntity?>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM history ORDER BY updated_at DESC")
|
@Query("SELECT * FROM history ORDER BY updated_at DESC")
|
||||||
abstract fun observeAll(): Flow<List<HistoryWithManga>>
|
abstract fun observeAll(): Flow<List<HistoryWithManga>>
|
||||||
@@ -69,4 +73,13 @@ abstract class HistoryDao {
|
|||||||
true
|
true
|
||||||
} else false
|
} else false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
open suspend fun upsert(entities: Iterable<HistoryEntity>) {
|
||||||
|
for (e in entities) {
|
||||||
|
if (update(e) == 0) {
|
||||||
|
insert(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import androidx.room.withTransaction
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import org.koitharu.kotatsu.base.domain.ReversibleHandle
|
||||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||||
import org.koitharu.kotatsu.core.db.entity.*
|
import org.koitharu.kotatsu.core.db.entity.*
|
||||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||||
@@ -100,6 +101,19 @@ class HistoryRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun deleteReversible(ids: Collection<Long>): ReversibleHandle {
|
||||||
|
val entities = db.withTransaction {
|
||||||
|
val entities = db.historyDao.findAll(ids.toList()).filterNotNull()
|
||||||
|
for (id in ids) {
|
||||||
|
db.historyDao.delete(id)
|
||||||
|
}
|
||||||
|
entities
|
||||||
|
}
|
||||||
|
return ReversibleHandle {
|
||||||
|
db.historyDao.upsert(entities)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to replace one manga with another one
|
* Try to replace one manga with another one
|
||||||
* Useful for replacing saved manga on deleting it with remove source
|
* Useful for replacing saved manga on deleting it with remove source
|
||||||
|
|||||||
@@ -7,8 +7,11 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.base.domain.ReversibleHandle
|
||||||
|
import org.koitharu.kotatsu.base.domain.reverseAsync
|
||||||
import org.koitharu.kotatsu.list.ui.MangaListFragment
|
import org.koitharu.kotatsu.list.ui.MangaListFragment
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
|
||||||
@@ -22,6 +25,7 @@ class HistoryListFragment : MangaListFragment() {
|
|||||||
viewModel.isGroupingEnabled.observe(viewLifecycleOwner) {
|
viewModel.isGroupingEnabled.observe(viewLifecycleOwner) {
|
||||||
activity?.invalidateOptionsMenu()
|
activity?.invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
|
viewModel.onItemsRemoved.observe(viewLifecycleOwner, ::onItemsRemoved)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onScrolledToEnd() = Unit
|
override fun onScrolledToEnd() = Unit
|
||||||
@@ -80,6 +84,12 @@ class HistoryListFragment : MangaListFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onItemsRemoved(reversibleHandle: ReversibleHandle) {
|
||||||
|
Snackbar.make(binding.recyclerView, R.string.removed_from_history, Snackbar.LENGTH_LONG)
|
||||||
|
.setAction(R.string.undo) { reversibleHandle.reverseAsync() }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun newInstance() = HistoryListFragment()
|
fun newInstance() = HistoryListFragment()
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import kotlinx.coroutines.flow.catch
|
|||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.base.domain.ReversibleHandle
|
||||||
|
import org.koitharu.kotatsu.base.domain.plus
|
||||||
import org.koitharu.kotatsu.core.os.ShortcutsRepository
|
import org.koitharu.kotatsu.core.os.ShortcutsRepository
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||||
@@ -17,6 +19,7 @@ import org.koitharu.kotatsu.history.domain.MangaWithHistory
|
|||||||
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||||
import org.koitharu.kotatsu.list.ui.model.*
|
import org.koitharu.kotatsu.list.ui.model.*
|
||||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||||
|
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||||
import org.koitharu.kotatsu.utils.ext.daysDiff
|
import org.koitharu.kotatsu.utils.ext.daysDiff
|
||||||
import org.koitharu.kotatsu.utils.ext.onFirst
|
import org.koitharu.kotatsu.utils.ext.onFirst
|
||||||
@@ -31,6 +34,7 @@ class HistoryListViewModel(
|
|||||||
) : MangaListViewModel(settings) {
|
) : MangaListViewModel(settings) {
|
||||||
|
|
||||||
val isGroupingEnabled = MutableLiveData<Boolean>()
|
val isGroupingEnabled = MutableLiveData<Boolean>()
|
||||||
|
val onItemsRemoved = SingleLiveEvent<ReversibleHandle>()
|
||||||
|
|
||||||
private val historyGrouping = settings.observeAsFlow(AppSettings.KEY_HISTORY_GROUPING) { historyGrouping }
|
private val historyGrouping = settings.observeAsFlow(AppSettings.KEY_HISTORY_GROUPING) { historyGrouping }
|
||||||
.onEach { isGroupingEnabled.postValue(it) }
|
.onEach { isGroupingEnabled.postValue(it) }
|
||||||
@@ -72,9 +76,12 @@ class HistoryListViewModel(
|
|||||||
if (ids.isEmpty()) {
|
if (ids.isEmpty()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
launchJob {
|
launchJob(Dispatchers.Default) {
|
||||||
repository.delete(ids)
|
val handle = repository.deleteReversible(ids) + ReversibleHandle {
|
||||||
|
shortcutsRepository.updateShortcuts()
|
||||||
|
}
|
||||||
shortcutsRepository.updateShortcuts()
|
shortcutsRepository.updateShortcuts()
|
||||||
|
onItemsRemoved.postCall(handle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -293,4 +293,6 @@
|
|||||||
<string name="bookmarks">Bookmarks</string>
|
<string name="bookmarks">Bookmarks</string>
|
||||||
<string name="bookmark_removed">Bookmark removed</string>
|
<string name="bookmark_removed">Bookmark removed</string>
|
||||||
<string name="bookmark_added">Bookmark added</string>
|
<string name="bookmark_added">Bookmark added</string>
|
||||||
|
<string name="undo">Undo</string>
|
||||||
|
<string name="removed_from_history">Removed from history</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user