MangaScreen: Improve chapter list scrolling performance (#7491)
* MangaScreen: Improve chapter list scrolling performance Process chapter title, date and read progress string ahead of time * Use enum for contentType and add key (cherry picked from commit 1551891c15eb5d323cb1d425794876a753316091) # Conflicts: # app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt
This commit is contained in:
parent
d3e8ae54d9
commit
d3b59768d4
@ -44,7 +44,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
@ -52,7 +51,6 @@ import androidx.compose.ui.res.stringResource
|
||||
import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||
import eu.kanade.domain.chapter.model.Chapter
|
||||
import eu.kanade.domain.manga.model.Manga.Companion.CHAPTER_DISPLAY_NUMBER
|
||||
import eu.kanade.presentation.components.ExtendedFloatingActionButton
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.components.SwipeRefreshIndicator
|
||||
@ -76,18 +74,8 @@ import eu.kanade.tachiyomi.source.getNameForMangaInfo
|
||||
import eu.kanade.tachiyomi.source.online.MetadataSource
|
||||
import eu.kanade.tachiyomi.ui.manga.ChapterItem
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
|
||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
||||
import exh.source.MERGED_SOURCE_ID
|
||||
import exh.source.getMainSource
|
||||
import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
import java.util.Date
|
||||
|
||||
private val chapterDecimalFormat = DecimalFormat(
|
||||
"#.###",
|
||||
DecimalFormatSymbols()
|
||||
.apply { decimalSeparator = '.' },
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun MangaScreen(
|
||||
@ -361,7 +349,10 @@ private fun MangaScreenSmallImpl(
|
||||
state = chapterListState,
|
||||
contentPadding = noTopContentPadding,
|
||||
) {
|
||||
item(contentType = "info_box") {
|
||||
item(
|
||||
key = MangaScreenItem.INFO_BOX,
|
||||
contentType = MangaScreenItem.INFO_BOX,
|
||||
) {
|
||||
MangaInfoBox(
|
||||
windowWidthSizeClass = WindowWidthSizeClass.Compact,
|
||||
appBarPadding = topPadding,
|
||||
@ -377,7 +368,10 @@ private fun MangaScreenSmallImpl(
|
||||
)
|
||||
}
|
||||
|
||||
item(contentType = "action_row") {
|
||||
item(
|
||||
key = MangaScreenItem.ACTION_ROW,
|
||||
contentType = MangaScreenItem.ACTION_ROW,
|
||||
) {
|
||||
MangaActionRow(
|
||||
favorite = state.manga.favorite,
|
||||
trackingCount = state.trackingCount,
|
||||
@ -393,7 +387,10 @@ private fun MangaScreenSmallImpl(
|
||||
|
||||
// SY -->
|
||||
if (metadataSource != null) {
|
||||
item(contentType = "metadata_info") {
|
||||
item(
|
||||
key = MangaScreenItem.METADATA_INFO,
|
||||
contentType = MangaScreenItem.METADATA_INFO,
|
||||
) {
|
||||
metadataSource.DescriptionComposable(
|
||||
state = state,
|
||||
openMetadataViewer = onMetadataViewerClicked,
|
||||
@ -403,7 +400,10 @@ private fun MangaScreenSmallImpl(
|
||||
}
|
||||
// SY <--
|
||||
|
||||
item(contentType = "desc") {
|
||||
item(
|
||||
key = MangaScreenItem.DESCRIPTION_WITH_TAG,
|
||||
contentType = MangaScreenItem.DESCRIPTION_WITH_TAG,
|
||||
) {
|
||||
ExpandableMangaDescription(
|
||||
defaultExpandState = state.isFromSource,
|
||||
description = state.manga.description,
|
||||
@ -420,7 +420,10 @@ private fun MangaScreenSmallImpl(
|
||||
|
||||
// SY -->
|
||||
if (!state.showRecommendationsInOverflow || state.showMergeWithAnother) {
|
||||
item(contentType = "info_buttons") {
|
||||
item(
|
||||
key = MangaScreenItem.INFO_BUTTONS,
|
||||
contentType = MangaScreenItem.INFO_BUTTONS,
|
||||
) {
|
||||
MangaInfoButtons(
|
||||
showRecommendsButton = !state.showRecommendationsInOverflow,
|
||||
showMergeWithAnotherButton = state.showMergeWithAnother,
|
||||
@ -431,7 +434,10 @@ private fun MangaScreenSmallImpl(
|
||||
}
|
||||
// SY <--
|
||||
|
||||
item(contentType = "header") {
|
||||
item(
|
||||
key = MangaScreenItem.CHAPTER_HEADER,
|
||||
contentType = MangaScreenItem.CHAPTER_HEADER,
|
||||
) {
|
||||
ChapterHeader(
|
||||
chapterCount = chapters.size,
|
||||
isChapterFiltered = state.manga.chaptersFiltered(),
|
||||
@ -441,7 +447,6 @@ private fun MangaScreenSmallImpl(
|
||||
|
||||
sharedChapterItems(
|
||||
chapters = chapters,
|
||||
state = state,
|
||||
selected = selected,
|
||||
selectedPositions = selectedPositions,
|
||||
onChapterClicked = onChapterClicked,
|
||||
@ -680,7 +685,10 @@ fun MangaScreenLargeImpl(
|
||||
state = chapterListState,
|
||||
contentPadding = withNavBarContentPadding,
|
||||
) {
|
||||
item(contentType = "header") {
|
||||
item(
|
||||
key = MangaScreenItem.CHAPTER_HEADER,
|
||||
contentType = MangaScreenItem.CHAPTER_HEADER,
|
||||
) {
|
||||
ChapterHeader(
|
||||
chapterCount = chapters.size,
|
||||
isChapterFiltered = state.manga.chaptersFiltered(),
|
||||
@ -690,7 +698,6 @@ fun MangaScreenLargeImpl(
|
||||
|
||||
sharedChapterItems(
|
||||
chapters = chapters,
|
||||
state = state,
|
||||
selected = selected,
|
||||
selectedPositions = selectedPositions,
|
||||
onChapterClicked = onChapterClicked,
|
||||
@ -753,56 +760,27 @@ private fun SharedMangaBottomActionMenu(
|
||||
|
||||
private fun LazyListScope.sharedChapterItems(
|
||||
chapters: List<ChapterItem>,
|
||||
state: MangaScreenState.Success,
|
||||
selected: SnapshotStateList<ChapterItem>,
|
||||
selectedPositions: Array<Int>,
|
||||
onChapterClicked: (Chapter) -> Unit,
|
||||
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
||||
) {
|
||||
items(items = chapters) { chapterItem ->
|
||||
val context = LocalContext.current
|
||||
items(
|
||||
items = chapters,
|
||||
key = { it.chapter.id },
|
||||
contentType = { MangaScreenItem.CHAPTER },
|
||||
) { chapterItem ->
|
||||
val haptic = LocalHapticFeedback.current
|
||||
|
||||
val (chapter, downloadState, downloadProgress) = chapterItem
|
||||
val chapterTitle = if (state.manga.displayMode == CHAPTER_DISPLAY_NUMBER) {
|
||||
stringResource(
|
||||
id = R.string.display_mode_chapter,
|
||||
chapterDecimalFormat.format(chapter.chapterNumber.toDouble()),
|
||||
)
|
||||
} else {
|
||||
chapter.name
|
||||
}
|
||||
val date = remember(chapter.dateUpload) {
|
||||
chapter.dateUpload
|
||||
.takeIf { it > 0 }
|
||||
?.let {
|
||||
Date(it).toRelativeString(
|
||||
context,
|
||||
state.dateRelativeTime,
|
||||
state.dateFormat,
|
||||
)
|
||||
}
|
||||
}
|
||||
val lastPageRead = remember(chapter.lastPageRead) {
|
||||
chapter.lastPageRead.takeIf { /* SY --> */(!chapter.read || state.alwaysShowPageProgress)/* SY <-- */ && it > 0 }
|
||||
}
|
||||
val scanlator = remember(chapter.scanlator) { chapter.scanlator.takeIf { !it.isNullOrBlank() } }
|
||||
|
||||
MangaChapterListItem(
|
||||
title = chapterTitle,
|
||||
date = date,
|
||||
readProgress = lastPageRead?.let {
|
||||
stringResource(
|
||||
id = R.string.chapter_progress,
|
||||
it + 1,
|
||||
)
|
||||
},
|
||||
scanlator = scanlator,
|
||||
read = chapter.read,
|
||||
bookmark = chapter.bookmark,
|
||||
title = chapterItem.chapterTitleString,
|
||||
date = chapterItem.dateUploadString,
|
||||
readProgress = chapterItem.readProgressString,
|
||||
scanlator = chapterItem.chapter.scanlator.takeIf { !it.isNullOrBlank() },
|
||||
read = chapterItem.chapter.read,
|
||||
bookmark = chapterItem.chapter.bookmark,
|
||||
selected = selected.contains(chapterItem),
|
||||
downloadStateProvider = { downloadState },
|
||||
downloadProgressProvider = { downloadProgress },
|
||||
downloadStateProvider = { chapterItem.downloadState },
|
||||
downloadProgressProvider = { chapterItem.downloadProgress },
|
||||
onLongClick = {
|
||||
val dispatched = onChapterItemLongClick(
|
||||
chapterItem = chapterItem,
|
||||
|
@ -20,3 +20,21 @@ enum class EditCoverAction {
|
||||
EDIT,
|
||||
DELETE,
|
||||
}
|
||||
|
||||
enum class MangaScreenItem {
|
||||
INFO_BOX,
|
||||
ACTION_ROW,
|
||||
|
||||
// SY -->
|
||||
METADATA_INFO,
|
||||
|
||||
// SY <--
|
||||
DESCRIPTION_WITH_TAG,
|
||||
|
||||
// SY -->
|
||||
INFO_BUTTONS,
|
||||
|
||||
// SY <--
|
||||
CHAPTER_HEADER,
|
||||
CHAPTER,
|
||||
}
|
||||
|
@ -81,8 +81,8 @@ fun MangaChapterListItem(
|
||||
}
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
.copy(color = textColor),
|
||||
color = textColor,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
onTextLayout = { textHeight = it.size.height },
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.ui.manga
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.Immutable
|
||||
@ -60,6 +61,7 @@ import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper
|
||||
import eu.kanade.tachiyomi.util.chapter.getChapterSort
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.preference.asImmediateFlow
|
||||
import eu.kanade.tachiyomi.util.removeCovers
|
||||
@ -80,7 +82,6 @@ import exh.metadata.metadata.base.RaisedSearchMetadata
|
||||
import exh.source.MERGED_SOURCE_ID
|
||||
import exh.source.getMainSource
|
||||
import exh.source.isEhBasedManga
|
||||
import exh.source.isEhBasedSource
|
||||
import exh.source.mangaDexSourceIds
|
||||
import exh.util.nullIfEmpty
|
||||
import exh.util.trimOrNull
|
||||
@ -110,6 +111,9 @@ import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.DateFormat
|
||||
import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
import java.util.Date
|
||||
import eu.kanade.domain.chapter.model.Chapter as DomainChapter
|
||||
import eu.kanade.domain.manga.model.Manga as DomainManga
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga.Companion as DbManga
|
||||
@ -284,7 +288,20 @@ class MangaPresenter(
|
||||
}
|
||||
// SY <--
|
||||
.collectLatest { (manga, chapters, flatMetadata, mergedData) ->
|
||||
val chapterItems = chapters.toChapterItems(manga, mergedData)
|
||||
val chapterItems = chapters.toChapterItems(
|
||||
context = view?.activity ?: Injekt.get<Application>(),
|
||||
manga = manga,
|
||||
// SY -->
|
||||
dateRelativeTime = if (manga.isEhBasedManga()) 0 else preferences.relativeTime().get(),
|
||||
dateFormat = if (manga.isEhBasedManga()) {
|
||||
MetadataUtil.EX_DATE_FORMAT
|
||||
} else {
|
||||
preferences.dateFormat()
|
||||
},
|
||||
mergedData = mergedData,
|
||||
alwaysShowReadingProgress = preferences.preserveReadingPosition().get() && manga.isEhBasedManga(),
|
||||
// SY <--
|
||||
)
|
||||
_state.update { currentState ->
|
||||
when (currentState) {
|
||||
// Initialize success state
|
||||
@ -293,12 +310,6 @@ class MangaPresenter(
|
||||
MangaScreenState.Success(
|
||||
manga = manga,
|
||||
source = source,
|
||||
dateRelativeTime = if (source.isEhBasedSource()) 0 else preferences.relativeTime().get(),
|
||||
dateFormat = if (source.isEhBasedSource()) {
|
||||
MetadataUtil.EX_DATE_FORMAT
|
||||
} else {
|
||||
preferences.dateFormat()
|
||||
},
|
||||
isFromSource = isFromSource,
|
||||
trackingAvailable = trackManager.hasLoggedServices(),
|
||||
chapters = chapterItems,
|
||||
@ -306,7 +317,6 @@ class MangaPresenter(
|
||||
mergedData = mergedData,
|
||||
showRecommendationsInOverflow = preferences.recommendsInOverflow().get(),
|
||||
showMergeWithAnother = smartSearched,
|
||||
alwaysShowPageProgress = preferences.preserveReadingPosition().get() && manga.isEhBasedManga(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -818,7 +828,14 @@ class MangaPresenter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<DomainChapter>.toChapterItems(manga: DomainManga, mergedData: MergedMangaData?): List<ChapterItem> {
|
||||
private fun List<DomainChapter>.toChapterItems(
|
||||
context: Context,
|
||||
manga: DomainManga,
|
||||
dateRelativeTime: Int,
|
||||
dateFormat: DateFormat,
|
||||
mergedData: MergedMangaData?,
|
||||
alwaysShowReadingProgress: Boolean,
|
||||
): List<ChapterItem> {
|
||||
return map { chapter ->
|
||||
val activeDownload = downloadManager.queue.find { chapter.id == it.chapter.id }
|
||||
val chapter = chapter.let { if (mergedData != null) it.toMergedDownloadedChapter() else it }
|
||||
@ -840,6 +857,29 @@ class MangaPresenter(
|
||||
chapter = chapter,
|
||||
downloadState = downloadState,
|
||||
downloadProgress = activeDownload?.progress ?: 0,
|
||||
chapterTitleString = if (manga.displayMode == DomainManga.CHAPTER_DISPLAY_NUMBER) {
|
||||
context.getString(
|
||||
R.string.display_mode_chapter,
|
||||
chapterDecimalFormat.format(chapter.chapterNumber.toDouble()),
|
||||
)
|
||||
} else {
|
||||
chapter.name
|
||||
},
|
||||
dateUploadString = chapter.dateUpload
|
||||
.takeIf { it > 0 }
|
||||
?.let {
|
||||
Date(it).toRelativeString(
|
||||
context,
|
||||
dateRelativeTime,
|
||||
dateFormat,
|
||||
)
|
||||
},
|
||||
readProgressString = chapter.lastPageRead.takeIf { /* SY --> */(!chapter.read || alwaysShowReadingProgress)/* SY <-- */ && it > 0 }?.let {
|
||||
context.getString(
|
||||
R.string.chapter_progress,
|
||||
it + 1,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1319,8 +1359,6 @@ sealed class MangaScreenState {
|
||||
data class Success(
|
||||
val manga: DomainManga,
|
||||
val source: Source,
|
||||
val dateRelativeTime: Int,
|
||||
val dateFormat: DateFormat,
|
||||
val isFromSource: Boolean,
|
||||
val chapters: List<ChapterItem>,
|
||||
val trackingAvailable: Boolean = false,
|
||||
@ -1334,7 +1372,6 @@ sealed class MangaScreenState {
|
||||
val mergedData: MergedMangaData?,
|
||||
val showRecommendationsInOverflow: Boolean,
|
||||
val showMergeWithAnother: Boolean,
|
||||
val alwaysShowPageProgress: Boolean,
|
||||
// SY <--
|
||||
) : MangaScreenState() {
|
||||
|
||||
@ -1386,6 +1423,16 @@ data class ChapterItem(
|
||||
val chapter: DomainChapter,
|
||||
val downloadState: Download.State,
|
||||
val downloadProgress: Int,
|
||||
|
||||
val chapterTitleString: String,
|
||||
val dateUploadString: String?,
|
||||
val readProgressString: String?,
|
||||
) {
|
||||
val isDownloaded = downloadState == Download.State.DOWNLOADED
|
||||
}
|
||||
|
||||
private val chapterDecimalFormat = DecimalFormat(
|
||||
"#.###",
|
||||
DecimalFormatSymbols()
|
||||
.apply { decimalSeparator = '.' },
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user