diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index ef4c62657..edcb463e3 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -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, - state: MangaScreenState.Success, selected: SnapshotStateList, selectedPositions: Array, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List, 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, diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreenConstants.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreenConstants.kt index 5fcac70a7..bacef07bf 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreenConstants.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreenConstants.kt @@ -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, +} diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt index 583a0e59b..80a42dc31 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt @@ -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 }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt index e5cc0a775..54526721d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt @@ -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(), + 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.toChapterItems(manga: DomainManga, mergedData: MergedMangaData?): List { + private fun List.toChapterItems( + context: Context, + manga: DomainManga, + dateRelativeTime: Int, + dateFormat: DateFormat, + mergedData: MergedMangaData?, + alwaysShowReadingProgress: Boolean, + ): List { 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, 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 = '.' }, +)