Action to mark manga as completed

master
Koitharu 2 years ago
parent 514870f71c
commit 627cf73d72
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -141,7 +141,7 @@ dependencies {
compileOnly 'com.google.auto.service:auto-service-annotations:1.1.1' compileOnly 'com.google.auto.service:auto-service-annotations:1.1.1'
ksp 'dev.zacsweers.autoservice:auto-service-ksp:1.1.0' 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 'junit:junit:4.13.2'
testImplementation 'org.json:json:20231013' testImplementation 'org.json:json:20231013'

@ -7,6 +7,7 @@ import android.view.View
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.list.ListSelectionController
@ -73,6 +74,18 @@ class FavouritesListFragment : MangaListFragment(), PopupMenu.OnMenuItemClickLis
true 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) else -> super.onActionItemClicked(controller, mode, item)
} }
} }

@ -21,6 +21,7 @@ import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository 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.ARG_CATEGORY_ID
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_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.ListExtraProvider
import org.koitharu.kotatsu.list.domain.ListSortOrder import org.koitharu.kotatsu.list.domain.ListSortOrder
import org.koitharu.kotatsu.list.ui.MangaListViewModel 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.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorState import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.list.ui.model.toUi import org.koitharu.kotatsu.list.ui.model.toUi
import org.koitharu.kotatsu.parsers.model.Manga
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@ -35,11 +37,13 @@ class FavouritesListViewModel @Inject constructor(
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
private val repository: FavouritesRepository, private val repository: FavouritesRepository,
private val listExtraProvider: ListExtraProvider, private val listExtraProvider: ListExtraProvider,
private val markAsReadUseCase: MarkAsReadUseCase,
settings: AppSettings, settings: AppSettings,
downloadScheduler: DownloadWorker.Scheduler, downloadScheduler: DownloadWorker.Scheduler,
) : MangaListViewModel(settings, downloadScheduler) { ) : MangaListViewModel(settings, downloadScheduler) {
val categoryId: Long = savedStateHandle[ARG_CATEGORY_ID] ?: NO_ID 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 } override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_FAVORITES) { favoritesListMode }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.favoritesListMode) .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.favoritesListMode)
@ -59,7 +63,8 @@ class FavouritesListViewModel @Inject constructor(
repository.observeAll(categoryId) repository.observeAll(categoryId)
}, },
listMode, listMode,
) { list, mode -> refreshTrigger,
) { list, mode, _ ->
when { when {
list.isEmpty() -> listOf( list.isEmpty() -> listOf(
EmptyState( EmptyState(
@ -80,10 +85,19 @@ class FavouritesListViewModel @Inject constructor(
emit(listOf(it.toErrorState(canRetry = false))) emit(listOf(it.toErrorState(canRetry = false)))
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState)) }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState))
override fun onRefresh() = Unit override fun onRefresh() {
refreshTrigger.value = Any()
}
override fun onRetry() = Unit override fun onRetry() = Unit
fun markAsRead(items: Set<Manga>) {
launchLoadingJob(Dispatchers.Default) {
markAsReadUseCase(items)
onRefresh()
}
}
fun removeFromFavourites(ids: Set<Long>) { fun removeFromFavourites(ids: Set<Long>) {
if (ids.isEmpty()) { if (ids.isEmpty()) {
return return

@ -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<Manga>) {
when (manga.size) {
0 -> Unit
1 -> invoke(manga.first())
else -> supervisorScope {
manga.map {
launch {
invoke(it)
}
}.joinAll()
}
}
}
}

@ -27,6 +27,12 @@
android:title="@string/categories" android:title="@string/categories"
app:showAsAction="ifRoom|withText" /> app:showAsAction="ifRoom|withText" />
<item
android:id="@+id/action_mark_current"
android:icon="@drawable/ic_eye_check"
android:title="@string/mark_as_completed"
app:showAsAction="ifRoom|withText" />
<item <item
android:id="@+id/action_select_all" android:id="@+id/action_select_all"
android:icon="?actionModeSelectAllDrawable" android:icon="?actionModeSelectAllDrawable"

@ -556,4 +556,6 @@
<string name="rating_suggestive">Suggestive</string> <string name="rating_suggestive">Suggestive</string>
<string name="rating_adult">Adult</string> <string name="rating_adult">Adult</string>
<string name="default_tab">Default tab</string> <string name="default_tab">Default tab</string>
<string name="mark_as_completed">Mark as completed</string>
<string name="mark_as_completed_prompt">Mark selected manga as completely read?\n\nWarning: current reading progress will be lost.</string>
</resources> </resources>

Loading…
Cancel
Save