diff --git a/app/build.gradle b/app/build.gradle index 1c67c45f5..57228a164 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -102,7 +102,7 @@ dependencies { implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2' 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.github.solkin:disk-lru-cache:1.4' diff --git a/app/src/main/java/org/koitharu/kotatsu/base/domain/ReversibleHandle.kt b/app/src/main/java/org/koitharu/kotatsu/base/domain/ReversibleHandle.kt new file mode 100644 index 000000000..43c9bf7e4 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/base/domain/ReversibleHandle.kt @@ -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() +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/history/data/HistoryDao.kt b/app/src/main/java/org/koitharu/kotatsu/history/data/HistoryDao.kt index 062e8d898..f52c5db6d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/history/data/HistoryDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/history/data/HistoryDao.kt @@ -15,6 +15,10 @@ abstract class HistoryDao { @Query("SELECT * FROM history ORDER BY updated_at DESC LIMIT :limit OFFSET :offset") abstract suspend fun findAll(offset: Int, limit: Int): List + @Transaction + @Query("SELECT * FROM history WHERE manga_id IN (:ids)") + abstract suspend fun findAll(ids: Collection): List + @Transaction @Query("SELECT * FROM history ORDER BY updated_at DESC") abstract fun observeAll(): Flow> @@ -69,4 +73,13 @@ abstract class HistoryDao { true } else false } + + @Transaction + open suspend fun upsert(entities: Iterable) { + for (e in entities) { + if (update(e) == 0) { + insert(e) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/history/domain/HistoryRepository.kt b/app/src/main/java/org/koitharu/kotatsu/history/domain/HistoryRepository.kt index a88c8a82d..a4b2ab772 100644 --- a/app/src/main/java/org/koitharu/kotatsu/history/domain/HistoryRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/history/domain/HistoryRepository.kt @@ -4,6 +4,7 @@ import androidx.room.withTransaction import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged 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.entity.* import org.koitharu.kotatsu.core.model.MangaHistory @@ -100,6 +101,19 @@ class HistoryRepository( } } + suspend fun deleteReversible(ids: Collection): 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 * Useful for replacing saved manga on deleting it with remove source diff --git a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt index 6980b80ee..27f4a86ca 100644 --- a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt @@ -7,8 +7,11 @@ import android.view.MenuItem import android.view.View import androidx.appcompat.view.ActionMode import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.snackbar.Snackbar import org.koin.androidx.viewmodel.ext.android.viewModel 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.parsers.model.MangaSource @@ -22,6 +25,7 @@ class HistoryListFragment : MangaListFragment() { viewModel.isGroupingEnabled.observe(viewLifecycleOwner) { activity?.invalidateOptionsMenu() } + viewModel.onItemsRemoved.observe(viewLifecycleOwner, ::onItemsRemoved) } 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 { fun newInstance() = HistoryListFragment() diff --git a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt index 1e14aeeca..60190535b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt @@ -7,6 +7,8 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.onEach 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.prefs.AppSettings 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.model.* 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.daysDiff import org.koitharu.kotatsu.utils.ext.onFirst @@ -31,6 +34,7 @@ class HistoryListViewModel( ) : MangaListViewModel(settings) { val isGroupingEnabled = MutableLiveData() + val onItemsRemoved = SingleLiveEvent() private val historyGrouping = settings.observeAsFlow(AppSettings.KEY_HISTORY_GROUPING) { historyGrouping } .onEach { isGroupingEnabled.postValue(it) } @@ -72,9 +76,12 @@ class HistoryListViewModel( if (ids.isEmpty()) { return } - launchJob { - repository.delete(ids) + launchJob(Dispatchers.Default) { + val handle = repository.deleteReversible(ids) + ReversibleHandle { + shortcutsRepository.updateShortcuts() + } shortcutsRepository.updateShortcuts() + onItemsRemoved.postCall(handle) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 13dde4b4e..bb74b8b83 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -293,4 +293,6 @@ Bookmarks Bookmark removed Bookmark added + Undo + Removed from history \ No newline at end of file