Option to reverse chapters order

This commit is contained in:
Koitharu
2021-01-25 19:09:01 +02:00
parent d1aa0f0407
commit 7f37c1f99e
11 changed files with 128 additions and 42 deletions

View File

@@ -8,5 +8,5 @@ class CloudFlareProtectedException(
val url: String
) : IOException("Protected by CloudFlare"), ResolvableException {
override val resolveTextId: Int = R.string.resolve
override val resolveTextId: Int = R.string.captcha_solve
}

View File

@@ -74,6 +74,8 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
var historyGrouping by BoolPreferenceDelegate(KEY_HISTORY_GROUPING, true)
var chaptersReverse by BoolPreferenceDelegate(KEY_REVERSE_CHAPTERS, false)
val zoomMode by EnumPreferenceDelegate(
ZoomMode::class.java,
KEY_ZOOM_MODE,
@@ -175,5 +177,6 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
const val KEY_RESTORE = "restore"
const val KEY_HISTORY_GROUPING = "history_grouping"
const val KEY_DOZE_WHITELIST = "doze_whitelist"
const val KEY_REVERSE_CHAPTERS = "reverse_chapters"
}
}

View File

@@ -9,6 +9,6 @@ val detailsModule
get() = module {
viewModel { (intent: MangaIntent) ->
DetailsViewModel(intent, get(), get(), get(), get(), get())
DetailsViewModel(intent, get(), get(), get(), get(), get(), get())
}
}

View File

@@ -33,6 +33,11 @@ class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
private var actionMode: ActionMode? = null
private var selectionDecoration: ChaptersSelectionDecoration? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?
@@ -51,6 +56,9 @@ class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
viewModel.isLoading.observe(viewLifecycleOwner, this::onLoadingStateChanged)
viewModel.chapters.observe(viewLifecycleOwner, this::onChaptersChanged)
viewModel.isChaptersReversed.observe(viewLifecycleOwner) {
activity?.invalidateOptionsMenu()
}
}
override fun onDestroyView() {
@@ -59,12 +67,22 @@ class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
super.onDestroyView()
}
private fun onChaptersChanged(list: List<ChapterListItem>) {
chaptersAdapter?.items = list
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.opt_chapters, menu)
}
private fun onLoadingStateChanged(isLoading: Boolean) {
binding.progressBar.isVisible = isLoading
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
menu.findItem(R.id.action_reversed).isChecked = viewModel.isChaptersReversed.value == true
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_reversed -> {
viewModel.setChaptersReversed(!item.isChecked)
true
}
else -> super.onOptionsItemSelected(item)
}
override fun onItemClick(item: MangaChapter, view: View) {
@@ -159,4 +177,12 @@ class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
bottom = insets.bottom
)
}
private fun onChaptersChanged(list: List<ChapterListItem>) {
chaptersAdapter?.items = list
}
private fun onLoadingStateChanged(isLoading: Boolean) {
binding.progressBar.isVisible = isLoading
}
}

View File

@@ -10,6 +10,7 @@ import org.koitharu.kotatsu.base.domain.MangaIntent
import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.core.exceptions.MangaNotFoundException
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.details.ui.model.toListItem
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.history.domain.ChapterExtra
@@ -25,7 +26,8 @@ class DetailsViewModel(
private val favouritesRepository: FavouritesRepository,
private val localMangaRepository: LocalMangaRepository,
private val trackingRepository: TrackingRepository,
private val mangaDataRepository: MangaDataRepository
private val mangaDataRepository: MangaDataRepository,
private val settings: AppSettings
) : BaseViewModel() {
private val mangaData = MutableStateFlow<Manga?>(intent.manga)
@@ -48,6 +50,12 @@ class DetailsViewModel(
trackingRepository.getNewChaptersCount(mangaId)
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, 0)
private val chaptersReversed = settings.observe()
.filter { it == AppSettings.KEY_REVERSE_CHAPTERS }
.map { settings.chaptersReverse }
.onStart { emit(settings.chaptersReverse) }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, false)
val manga = mangaData.filterNotNull()
.asLiveData(viewModelScope.coroutineContext)
val favouriteCategories = favourite
@@ -56,17 +64,20 @@ class DetailsViewModel(
.asLiveData(viewModelScope.coroutineContext)
val readingHistory = history
.asLiveData(viewModelScope.coroutineContext)
val isChaptersReversed = chaptersReversed
.asLiveData(viewModelScope.coroutineContext)
val onMangaRemoved = SingleLiveEvent<Manga>()
val chapters = combine(
mangaData.map { it?.chapters.orEmpty() },
history.map { it?.chapterId },
newChapters
) { chapters, currentId, newCount ->
newChapters,
chaptersReversed
) { chapters, currentId, newCount, reversed ->
val currentIndex = chapters.indexOfFirst { it.id == currentId }
val firstNewIndex = chapters.size - newCount
chapters.mapIndexed { index, chapter ->
val res = chapters.mapIndexed { index, chapter ->
chapter.toListItem(
when {
index >= firstNewIndex -> ChapterExtra.NEW
@@ -76,6 +87,7 @@ class DetailsViewModel(
}
)
}
if (reversed) res.asReversed() else res
}.asLiveData(viewModelScope.coroutineContext + Dispatchers.Default)
init {
@@ -98,4 +110,8 @@ class DetailsViewModel(
onMangaRemoved.postCall(manga)
}
}
fun setChaptersReversed(newValue: Boolean) {
settings.chaptersReverse = newValue
}
}

View File

@@ -1,6 +1,7 @@
package org.koitharu.kotatsu.list.ui.model
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.prefs.ListMode
@@ -46,7 +47,7 @@ fun <C : MutableCollection<ListModel>> List<Manga>.toUi(destination: C, mode: Li
fun Throwable.toErrorState(canRetry: Boolean = true) = ErrorState(
exception = this,
icon = R.drawable.ic_error_large,
icon = getErrorIcon(this),
canRetry = canRetry,
buttonText = (this as? ResolvableException)?.resolveTextId ?: R.string.try_again
)
@@ -54,4 +55,9 @@ fun Throwable.toErrorState(canRetry: Boolean = true) = ErrorState(
fun Throwable.toErrorFooter() = ErrorFooter(
exception = this,
icon = R.drawable.ic_alert_outline
)
)
private fun getErrorIcon(error: Throwable) = when (error) {
is CloudFlareProtectedException -> R.drawable.ic_denied_large
else -> R.drawable.ic_error_large
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:tint="@color/error"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M12 2C17.5 2 22 6.5 22 12S17.5 22 12 22 2 17.5 2 12 6.5 2 12 2M12 4C10.1 4 8.4 4.6 7.1 5.7L18.3 16.9C19.3 15.5 20 13.8 20 12C20 7.6 16.4 4 12 4M16.9 18.3L5.7 7.1C4.6 8.4 4 10.1 4 12C4 16.4 7.6 20 12 20C13.9 20 15.6 19.4 16.9 18.3Z" />
</vector>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_reversed"
android:checkable="true"
android:orderInCategory="20"
android:title="@string/reverse"
app:showAsAction="never" />
</menu>

View File

@@ -1,37 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_share"
android:icon="@drawable/ic_share"
android:title="@string/share"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_share"
android:icon="@drawable/ic_share"
android:orderInCategory="10"
android:title="@string/share"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_save"
android:title="@string/save"
android:visible="false"
app:showAsAction="never" />
<item
android:id="@+id/action_save"
android:orderInCategory="40"
android:title="@string/save"
android:visible="false"
app:showAsAction="never" />
<item
android:id="@+id/action_delete"
android:title="@string/delete"
android:visible="false"
app:showAsAction="never" />
<item
android:id="@+id/action_delete"
android:orderInCategory="40"
android:title="@string/delete"
android:visible="false"
app:showAsAction="never" />
<item android:id="@+id/action_related"
android:title="@string/related"
app:showAsAction="never" />
<item
android:id="@+id/action_related"
android:orderInCategory="50"
android:title="@string/related"
app:showAsAction="never" />
<item
android:id="@+id/action_browser"
android:title="@string/open_in_browser"
app:showAsAction="never" />
<item
android:id="@+id/action_browser"
android:orderInCategory="50"
android:title="@string/open_in_browser"
app:showAsAction="never" />
<item
android:id="@+id/action_shortcut"
android:title="@string/create_shortcut"
app:showAsAction="never" />
<item
android:id="@+id/action_shortcut"
android:orderInCategory="50"
android:title="@string/create_shortcut"
app:showAsAction="never" />
</menu>

View File

@@ -189,7 +189,7 @@
<string name="reader_mode_hint">Выбранный режим будет сохранён для текущей манги</string>
<string name="silent">Без звука</string>
<string name="captcha_required">Необходимо пройти CAPTCHA</string>
<string name="resolve">Resolve</string>
<string name="captcha_solve">Пройти</string>
<string name="clear_cookies">Очистить куки</string>
<string name="cookies_cleared">Все куки удалены</string>
<string name="chapers_checking_progress">Проверка новых глав: %1$d из %2$d</string>
@@ -199,4 +199,5 @@
<string name="disable_power_optimization">Отключить оптимизацию батареи</string>
<string name="power_optimization_already_disabled">Отпимизация батареи уже отключена</string>
<string name="new_chapters_checking">Проверка новых глав</string>
<string name="reverse">В обратном порядке</string>
</resources>

View File

@@ -191,7 +191,7 @@
<string name="reader_mode_hint">Chosen configuration will be remembered for this manga</string>
<string name="silent">Silent</string>
<string name="captcha_required">CAPTCHA is required</string>
<string name="resolve">Resolve</string>
<string name="captcha_solve">Solve</string>
<string name="clear_cookies">Clear cookies</string>
<string name="cookies_cleared">All cookies was removed</string>
<string name="chapers_checking_progress">Checking for new chapters: %1$d of %2$d</string>
@@ -201,4 +201,5 @@
<string name="power_optimization_simmary">Helps with background operations such as checking for new chapters. Use only if you have a troubles with it</string>
<string name="disable_power_optimization">Disable power optimization</string>
<string name="new_chapters_checking">New chapters checking</string>
<string name="reverse">Reverse</string>
</resources>