From 627cf73d7226bdd1391519dc241d1884a9e52ce9 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 11 Jan 2024 18:53:45 +0200 Subject: [PATCH] Action to mark manga as completed --- app/build.gradle | 2 +- .../ui/list/FavouritesListFragment.kt | 13 +++++ .../ui/list/FavouritesListViewModel.kt | 18 ++++++- .../history/domain/MarkAsReadUseCase.kt | 49 +++++++++++++++++++ app/src/main/res/menu/mode_favourites.xml | 6 +++ app/src/main/res/values/strings.xml | 2 + 6 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/history/domain/MarkAsReadUseCase.kt diff --git a/app/build.gradle b/app/build.gradle index b83b9b333..dc912313e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -141,7 +141,7 @@ dependencies { compileOnly 'com.google.auto.service:auto-service-annotations:1.1.1' ksp 'dev.zacsweers.autoservice:auto-service-ksp:1.1.0' - debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12' + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.13' testImplementation 'junit:junit:4.13.2' testImplementation 'org.json:json:20231013' diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt index 86f0a83e6..3575f51f6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt @@ -7,6 +7,7 @@ import android.view.View import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.PopupMenu import androidx.fragment.app.viewModels +import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ui.list.ListSelectionController @@ -73,6 +74,18 @@ class FavouritesListFragment : MangaListFragment(), PopupMenu.OnMenuItemClickLis true } + R.id.action_mark_current -> { + MaterialAlertDialogBuilder(context ?: return false) + .setTitle(item.title) + .setMessage(R.string.mark_as_completed_prompt) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(android.R.string.ok) { _, _ -> + viewModel.markAsRead(selectedItems) + mode.finish() + }.show() + true + } + else -> super.onActionItemClicked(controller, mode, item) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt index e4e305060..84ceb10e1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt @@ -21,6 +21,7 @@ import org.koitharu.kotatsu.download.ui.worker.DownloadWorker import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.ARG_CATEGORY_ID import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID +import org.koitharu.kotatsu.history.domain.MarkAsReadUseCase import org.koitharu.kotatsu.list.domain.ListExtraProvider import org.koitharu.kotatsu.list.domain.ListSortOrder import org.koitharu.kotatsu.list.ui.MangaListViewModel @@ -28,6 +29,7 @@ import org.koitharu.kotatsu.list.ui.model.EmptyState import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.list.ui.model.toErrorState import org.koitharu.kotatsu.list.ui.model.toUi +import org.koitharu.kotatsu.parsers.model.Manga import javax.inject.Inject @HiltViewModel @@ -35,11 +37,13 @@ class FavouritesListViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val repository: FavouritesRepository, private val listExtraProvider: ListExtraProvider, + private val markAsReadUseCase: MarkAsReadUseCase, settings: AppSettings, downloadScheduler: DownloadWorker.Scheduler, ) : MangaListViewModel(settings, downloadScheduler) { val categoryId: Long = savedStateHandle[ARG_CATEGORY_ID] ?: NO_ID + private val refreshTrigger = MutableStateFlow(Any()) override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_FAVORITES) { favoritesListMode } .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.favoritesListMode) @@ -59,7 +63,8 @@ class FavouritesListViewModel @Inject constructor( repository.observeAll(categoryId) }, listMode, - ) { list, mode -> + refreshTrigger, + ) { list, mode, _ -> when { list.isEmpty() -> listOf( EmptyState( @@ -80,10 +85,19 @@ class FavouritesListViewModel @Inject constructor( emit(listOf(it.toErrorState(canRetry = false))) }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState)) - override fun onRefresh() = Unit + override fun onRefresh() { + refreshTrigger.value = Any() + } override fun onRetry() = Unit + fun markAsRead(items: Set) { + launchLoadingJob(Dispatchers.Default) { + markAsReadUseCase(items) + onRefresh() + } + } + fun removeFromFavourites(ids: Set) { if (ids.isEmpty()) { return diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/domain/MarkAsReadUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/domain/MarkAsReadUseCase.kt new file mode 100644 index 000000000..cd24dbfc0 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/domain/MarkAsReadUseCase.kt @@ -0,0 +1,49 @@ +package org.koitharu.kotatsu.history.domain + +import dagger.Reusable +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope +import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.history.data.HistoryRepository +import org.koitharu.kotatsu.parsers.model.Manga +import javax.inject.Inject + +@Reusable +class MarkAsReadUseCase @Inject constructor( + private val historyRepository: HistoryRepository, + private val mangaRepositoryFactory: MangaRepository.Factory, +) { + + suspend operator fun invoke(manga: Manga) { + val repo = mangaRepositoryFactory.create(manga.source) + val details = if (manga.chapters.isNullOrEmpty()) { + repo.getDetails(manga) + } else { + manga + } + val lastChapter = checkNotNull(details.chapters).last() + val pages = repo.getPages(lastChapter) + historyRepository.addOrUpdate( + manga = details, + chapterId = lastChapter.id, + page = pages.lastIndex, + scroll = 0, + percent = 1f, + ) + } + + suspend operator fun invoke(manga: Collection) { + when (manga.size) { + 0 -> Unit + 1 -> invoke(manga.first()) + else -> supervisorScope { + manga.map { + launch { + invoke(it) + } + }.joinAll() + } + } + } +} diff --git a/app/src/main/res/menu/mode_favourites.xml b/app/src/main/res/menu/mode_favourites.xml index 954c24faf..4c5d3897e 100644 --- a/app/src/main/res/menu/mode_favourites.xml +++ b/app/src/main/res/menu/mode_favourites.xml @@ -27,6 +27,12 @@ android:title="@string/categories" app:showAsAction="ifRoom|withText" /> + + Suggestive Adult Default tab + Mark as completed + Mark selected manga as completely read?\n\nWarning: current reading progress will be lost.