Potentially fix library IndexOutOfBound crash (#2341)
(cherry picked from commit c4407eda0eed5a7faed47d4470d79e6b1512b5c2)
This commit is contained in:
@@ -31,7 +31,7 @@ fun LibraryContent(
|
||||
searchQuery: String?,
|
||||
selection: Set<Long>,
|
||||
contentPadding: PaddingValues,
|
||||
currentPage: () -> Int,
|
||||
currentPage: Int,
|
||||
hasActiveFilters: Boolean,
|
||||
showPageTabs: Boolean,
|
||||
onChangeCurrentPage: (Int) -> Unit,
|
||||
@@ -53,8 +53,7 @@ fun LibraryContent(
|
||||
end = contentPadding.calculateEndPadding(LocalLayoutDirection.current),
|
||||
),
|
||||
) {
|
||||
// SY -->
|
||||
val coercedCurrentPage = remember(categories) { currentPage().coerceIn(0, categories.lastIndex) }
|
||||
val coercedCurrentPage = remember(categories, currentPage) { currentPage.coerceIn(0, categories.lastIndex) }
|
||||
// SY <--
|
||||
val pagerState = rememberPagerState(coercedCurrentPage) { categories.size }
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.updateAndGet
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mihon.core.common.utils.mutate
|
||||
import tachiyomi.core.common.i18n.stringResource
|
||||
@@ -115,6 +116,7 @@ import tachiyomi.source.local.LocalSource
|
||||
import tachiyomi.source.local.isLocal
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import kotlin.collections.map
|
||||
import kotlin.random.Random
|
||||
|
||||
class LibraryScreenModel(
|
||||
@@ -149,9 +151,6 @@ class LibraryScreenModel(
|
||||
// SY <--
|
||||
) : StateScreenModel<LibraryScreenModel.State>(State()) {
|
||||
|
||||
var activeCategoryIndex: Int by libraryPreferences.lastUsedCategory().asState(screenModelScope)
|
||||
val activeCategory: Category get() = state.value.displayedCategories[activeCategoryIndex]
|
||||
|
||||
// SY -->
|
||||
val favoritesSync = FavoritesSyncHelper(preferences.context)
|
||||
val recommendationSearch = RecommendationSearchHelper(preferences.context)
|
||||
@@ -160,6 +159,9 @@ class LibraryScreenModel(
|
||||
// SY <--
|
||||
|
||||
init {
|
||||
mutableState.update { state ->
|
||||
state.copy(activeCategoryIndex = libraryPreferences.lastUsedCategory().get())
|
||||
}
|
||||
screenModelScope.launchIO {
|
||||
combine(
|
||||
combine(
|
||||
@@ -182,6 +184,7 @@ class LibraryScreenModel(
|
||||
// SY <--
|
||||
getLibraryItemPreferencesFlow(),
|
||||
) { (searchQuery, categories, favorites), (tracksMap, trackingFilters), /* SY --> */ (groupType, sortingMode) /* <-- SY */, itemPreferences ->
|
||||
val showSystemCategory = favorites.any { it.libraryManga.categories.contains(0) }
|
||||
val filteredFavorites = favorites
|
||||
.applyFilters(tracksMap, trackingFilters, itemPreferences)
|
||||
.let {
|
||||
@@ -197,6 +200,7 @@ class LibraryScreenModel(
|
||||
|
||||
LibraryData(
|
||||
isInitialized = true,
|
||||
showSystemCategory = showSystemCategory,
|
||||
categories = categories,
|
||||
favorites = filteredFavorites,
|
||||
tracksMap = tracksMap,
|
||||
@@ -227,6 +231,7 @@ class LibraryScreenModel(
|
||||
data.favorites
|
||||
.applyGrouping(
|
||||
data.categories,
|
||||
data.showSystemCategory,
|
||||
// SY -->
|
||||
groupType,
|
||||
// SY <--
|
||||
@@ -428,6 +433,7 @@ class LibraryScreenModel(
|
||||
|
||||
private fun List<LibraryItem>.applyGrouping(
|
||||
categories: List<Category>,
|
||||
showSystemCategory: Boolean,
|
||||
// SY -->
|
||||
groupType: Int,
|
||||
// <-- SY
|
||||
@@ -442,7 +448,7 @@ class LibraryScreenModel(
|
||||
groupCache.getOrPut(categoryId) { mutableListOf() }.add(item.id)
|
||||
}
|
||||
}
|
||||
val showSystemCategory = groupCache.containsKey(0L)
|
||||
|
||||
return categories.filter { showSystemCategory || !it.isSystemCategory }
|
||||
.associateWith { groupCache[it.id]?.toList().orEmpty() }
|
||||
}
|
||||
@@ -850,7 +856,7 @@ class LibraryScreenModel(
|
||||
}
|
||||
clearSelection()
|
||||
}
|
||||
// SY <--
|
||||
// SY <--
|
||||
|
||||
/**
|
||||
* Marks mangas' chapters read status.
|
||||
@@ -941,7 +947,8 @@ class LibraryScreenModel(
|
||||
}
|
||||
|
||||
fun getRandomLibraryItemForCurrentCategory(): LibraryItem? {
|
||||
return state.value.getItemsForCategoryId(activeCategory.id).randomOrNull()
|
||||
val state = state.value
|
||||
return state.getItemsForCategoryId(state.activeCategory.id).randomOrNull()
|
||||
}
|
||||
|
||||
fun showSettingsDialog() {
|
||||
@@ -1129,7 +1136,7 @@ class LibraryScreenModel(
|
||||
}
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
// SY <--
|
||||
|
||||
private var lastSelectionCategory: Long? = null
|
||||
|
||||
@@ -1182,7 +1189,7 @@ class LibraryScreenModel(
|
||||
lastSelectionCategory = null
|
||||
mutableState.update { state ->
|
||||
val newSelection = state.selection.mutate { list ->
|
||||
state.getItemsForCategoryId(activeCategory.id).map { it.id }.let(list::addAll)
|
||||
state.getItemsForCategoryId(state.activeCategory.id).map { it.id }.let(list::addAll)
|
||||
}
|
||||
state.copy(selection = newSelection)
|
||||
}
|
||||
@@ -1192,7 +1199,7 @@ class LibraryScreenModel(
|
||||
lastSelectionCategory = null
|
||||
mutableState.update { state ->
|
||||
val newSelection = state.selection.mutate { list ->
|
||||
val itemIds = state.getItemsForCategoryId(activeCategory.id).fastMap { it.id }
|
||||
val itemIds = state.getItemsForCategoryId(state.activeCategory.id).fastMap { it.id }
|
||||
val (toRemove, toAdd) = itemIds.partition { it in list }
|
||||
list.removeAll(toRemove.toSet())
|
||||
list.addAll(toAdd)
|
||||
@@ -1205,6 +1212,15 @@ class LibraryScreenModel(
|
||||
mutableState.update { it.copy(searchQuery = query) }
|
||||
}
|
||||
|
||||
fun updateActiveCategoryIndex(index: Int) {
|
||||
val newIndex = mutableState.updateAndGet { state ->
|
||||
state.copy(activeCategoryIndex = index)
|
||||
}
|
||||
.coercedActiveCategoryIndex
|
||||
|
||||
libraryPreferences.lastUsedCategory().set(newIndex)
|
||||
}
|
||||
|
||||
fun openChangeCategoryDialog() {
|
||||
screenModelScope.launchIO {
|
||||
// Create a copy of selected manga
|
||||
@@ -1256,7 +1272,7 @@ class LibraryScreenModel(
|
||||
// SY <--
|
||||
}
|
||||
|
||||
// SY -->
|
||||
// SY -->
|
||||
|
||||
/** Returns first unread chapter of a manga */
|
||||
suspend fun getFirstUnread(manga: Manga): Chapter? {
|
||||
@@ -1380,7 +1396,7 @@ class LibraryScreenModel(
|
||||
)
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
// SY <--
|
||||
|
||||
@Immutable
|
||||
private data class ItemPreferences(
|
||||
@@ -1405,6 +1421,7 @@ class LibraryScreenModel(
|
||||
@Immutable
|
||||
data class LibraryData(
|
||||
val isInitialized: Boolean = false,
|
||||
val showSystemCategory: Boolean = false,
|
||||
val categories: List<Category> = emptyList(),
|
||||
val favorites: List<LibraryItem> = emptyList(),
|
||||
val tracksMap: Map</* Manga */ Long, List<Track>> = emptyMap(),
|
||||
@@ -1425,6 +1442,7 @@ class LibraryScreenModel(
|
||||
val showMangaContinueButton: Boolean = false,
|
||||
val dialog: Dialog? = null,
|
||||
val libraryData: LibraryData = LibraryData(),
|
||||
private val activeCategoryIndex: Int = 0,
|
||||
private val groupedFavorites: Map<Category, List</* LibraryItem */ Long>> = emptyMap(),
|
||||
// SY -->
|
||||
val showSyncExh: Boolean = false,
|
||||
@@ -1432,18 +1450,21 @@ class LibraryScreenModel(
|
||||
val groupType: Int = LibraryGroup.BY_DEFAULT,
|
||||
// SY <--
|
||||
) {
|
||||
val displayedCategories: List<Category> = groupedFavorites.keys.toList()
|
||||
|
||||
val coercedActiveCategoryIndex = activeCategoryIndex.coerceIn(
|
||||
minimumValue = 0,
|
||||
maximumValue = displayedCategories.lastIndex.coerceAtLeast(0),
|
||||
)
|
||||
|
||||
val activeCategory: Category by lazy { displayedCategories[coercedActiveCategoryIndex] }
|
||||
|
||||
val isLibraryEmpty = libraryData.favorites.isEmpty()
|
||||
|
||||
val selectionMode = selection.isNotEmpty()
|
||||
|
||||
val selectedManga by lazy { selection.mapNotNull { libraryData.favoritesById[it]?.libraryManga?.manga } }
|
||||
|
||||
/**
|
||||
* The grouped tabs which is displayed above the library screen.
|
||||
* They can be actual [Category] or [Source], [Track]...
|
||||
*/
|
||||
val displayedCategories = groupedFavorites.keys.toList()
|
||||
|
||||
// SY -->
|
||||
val showCleanTitles: Boolean by lazy {
|
||||
selectedManga.fastAny {
|
||||
|
||||
@@ -137,7 +137,7 @@ data object LibraryTab : Tab {
|
||||
val title = state.getToolbarTitle(
|
||||
defaultTitle = stringResource(MR.strings.label_library),
|
||||
defaultCategoryTitle = stringResource(MR.strings.label_default),
|
||||
page = screenModel.activeCategoryIndex,
|
||||
page = state.coercedActiveCategoryIndex,
|
||||
)
|
||||
LibraryToolbar(
|
||||
hasActiveFilters = state.hasActiveFilters,
|
||||
@@ -147,7 +147,7 @@ data object LibraryTab : Tab {
|
||||
onClickSelectAll = screenModel::selectAll,
|
||||
onClickInvertSelection = screenModel::invertSelection,
|
||||
onClickFilter = screenModel::showSettingsDialog,
|
||||
onClickRefresh = { onClickRefresh(screenModel.activeCategory) },
|
||||
onClickRefresh = { onClickRefresh(state.activeCategory) },
|
||||
onClickGlobalUpdate = { onClickRefresh(null) },
|
||||
onClickOpenRandomManga = {
|
||||
scope.launch {
|
||||
@@ -238,10 +238,10 @@ data object LibraryTab : Tab {
|
||||
searchQuery = state.searchQuery,
|
||||
selection = state.selection,
|
||||
contentPadding = contentPadding,
|
||||
currentPage = { screenModel.activeCategoryIndex },
|
||||
currentPage = state.coercedActiveCategoryIndex,
|
||||
hasActiveFilters = state.hasActiveFilters,
|
||||
showPageTabs = state.showCategoryTabs || !state.searchQuery.isNullOrEmpty(),
|
||||
onChangeCurrentPage = { screenModel.activeCategoryIndex = it },
|
||||
onChangeCurrentPage = screenModel::updateActiveCategoryIndex,
|
||||
onClickManga = { navigator.push(MangaScreen(it)) },
|
||||
onContinueReadingClicked = { it: LibraryManga ->
|
||||
scope.launchIO {
|
||||
@@ -261,7 +261,7 @@ data object LibraryTab : Tab {
|
||||
screenModel.toggleRangeSelection(category, manga)
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
},
|
||||
onRefresh = { onClickRefresh(screenModel.activeCategory) },
|
||||
onRefresh = { onClickRefresh(state.activeCategory) },
|
||||
onGlobalSearchClicked = {
|
||||
navigator.push(GlobalSearchScreen(screenModel.state.value.searchQuery ?: ""))
|
||||
},
|
||||
@@ -280,7 +280,7 @@ data object LibraryTab : Tab {
|
||||
LibrarySettingsDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
screenModel = settingsScreenModel,
|
||||
category = screenModel.activeCategory,
|
||||
category = state.activeCategory,
|
||||
// SY -->
|
||||
hasCategories = state.libraryData.categories.fastAny { !it.isSystemCategory },
|
||||
// SY <--
|
||||
|
||||
Reference in New Issue
Block a user