Related titles screen

This commit is contained in:
Cuong M. Tran 2024-04-23 12:41:08 +07:00
parent cdd3a7f6f8
commit d764ac3b63
No known key found for this signature in database
GPG key ID: 733AA7624B9315C2
5 changed files with 126 additions and 0 deletions

View file

@ -31,6 +31,7 @@ import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
@ -49,6 +50,8 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastMap
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
import eu.kanade.presentation.browse.components.GlobalSearchResultItem
import eu.kanade.presentation.components.relativeDateText
import eu.kanade.presentation.manga.components.ChapterDownloadAction
import eu.kanade.presentation.manga.components.ChapterHeader
@ -167,6 +170,12 @@ fun MangaScreen(
onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit,
// KMK -->
getMangaState: @Composable ((Manga) -> State<Manga>),
onRelatedMangaClick: (Manga) -> Unit,
onRelatedMangaLongClick: (Manga) -> Unit,
// KMK <--
) {
val context = LocalContext.current
val onCopyTagToClipboard: (tag: String) -> Unit = {
@ -220,6 +229,11 @@ fun MangaScreen(
onChapterSelected = onChapterSelected,
onAllChapterSelected = onAllChapterSelected,
onInvertSelection = onInvertSelection,
// KMK -->
getMangaState = getMangaState,
onRelatedMangaClick = onRelatedMangaClick,
onRelatedMangaLongClick = onRelatedMangaLongClick,
// KMK <--
)
} else {
MangaScreenLargeImpl(
@ -266,6 +280,11 @@ fun MangaScreen(
onChapterSelected = onChapterSelected,
onAllChapterSelected = onAllChapterSelected,
onInvertSelection = onInvertSelection,
// KMK -->
getMangaState = getMangaState,
onRelatedMangaClick = onRelatedMangaClick,
onRelatedMangaLongClick = onRelatedMangaLongClick,
// KMK <--
)
}
}
@ -328,6 +347,12 @@ private fun MangaScreenSmallImpl(
onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit,
// KMK -->
getMangaState: @Composable ((Manga) -> State<Manga>),
onRelatedMangaClick: (Manga) -> Unit,
onRelatedMangaLongClick: (Manga) -> Unit,
// KMK <--
) {
val chapterListState = rememberLazyListState()
@ -534,6 +559,29 @@ private fun MangaScreenSmallImpl(
)
}
// KMK -->
if (state.manga.relatedMangas!!.isNotEmpty()) {
item(
key = MangaScreenItem.RELATED_TITLES,
contentType = MangaScreenItem.RELATED_TITLES,
) {
GlobalSearchResultItem(
title = "Related titles",
subtitle = null,
onLongClick = null,
onClick = { /* Should show a page with grid/list of all the related manga */ },
) {
RelatedMangas(
mangas = state.manga.relatedMangas!!,
getMangaState = { getMangaState(it) },
onClickManga = onRelatedMangaClick,
onLongClickManga = onRelatedMangaLongClick,
)
}
}
}
// KMK <--
// SY -->
if (!state.showRecommendationsInOverflow || state.showMergeWithAnother) {
item(
@ -654,6 +702,12 @@ fun MangaScreenLargeImpl(
onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit,
// KMK -->
getMangaState: @Composable ((Manga) -> State<Manga>),
onRelatedMangaClick: (Manga) -> Unit,
onRelatedMangaLongClick: (Manga) -> Unit,
// KMK <--
) {
val layoutDirection = LocalLayoutDirection.current
val density = LocalDensity.current
@ -863,6 +917,29 @@ fun MangaScreenLargeImpl(
bottom = contentPadding.calculateBottomPadding(),
),
) {
// KMK -->
if (state.manga.relatedMangas!!.isNotEmpty()) {
item(
key = MangaScreenItem.RELATED_TITLES,
contentType = MangaScreenItem.RELATED_TITLES,
) {
GlobalSearchResultItem(
title = "Related titles",
subtitle = null,
onLongClick = null,
onClick = { /* Should show a page with grid/list of all the related manga */ },
) {
RelatedMangas(
mangas = state.manga.relatedMangas!!,
getMangaState = { getMangaState(it) },
onClickManga = onRelatedMangaClick,
onLongClickManga = onRelatedMangaLongClick,
)
}
}
}
// KMK <--
item(
key = MangaScreenItem.CHAPTER_HEADER,
contentType = MangaScreenItem.CHAPTER_HEADER,
@ -900,6 +977,24 @@ fun MangaScreenLargeImpl(
}
}
// KMK -->
@Composable
fun RelatedMangas(
mangas: List<Manga>,
getMangaState: @Composable ((Manga) -> State<Manga>),
onClickManga: (Manga) -> Unit,
onLongClickManga: (Manga) -> Unit,
) {
GlobalSearchCardRow(
titles = mangas,
getManga = getMangaState,
onClick = onClickManga,
onLongClick = onLongClickManga,
selection = emptyList(),
)
}
// KMK <--
@Composable
private fun SharedMangaBottomActionMenu(
selected: List<ChapterList.Item>,

View file

@ -32,4 +32,8 @@ enum class MangaScreenItem {
// SY <--
CHAPTER_HEADER,
CHAPTER,
// KMK -->
RELATED_TITLES,
// KMK <--
}

View file

@ -225,6 +225,11 @@ class MangaScreen(
onChapterSelected = screenModel::toggleSelection,
onAllChapterSelected = screenModel::toggleAllSelection,
onInvertSelection = screenModel::invertSelection,
// KMK -->
getMangaState = { screenModel.getManga(initialManga = it) },
onRelatedMangaClick = { navigator.push(MangaScreen(it.id, true)) },
onRelatedMangaLongClick = { },
// KMK <--
)
var showScanlatorsDialog by remember { mutableStateOf(false) }

View file

@ -3,8 +3,10 @@ package eu.kanade.tachiyomi.ui.manga
import android.content.Context
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.ui.util.fastAny
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
@ -72,6 +74,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@ -135,6 +138,7 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import kotlin.math.floor
import androidx.compose.runtime.State as RuntimeState
class MangaScreenModel(
val context: Context,
@ -553,6 +557,19 @@ class MangaScreenModel(
}
}
// KMK -->
@Composable
fun getManga(initialManga: Manga): RuntimeState<Manga> {
return produceState(initialValue = initialManga) {
getManga.subscribe(initialManga.url, initialManga.source)
.filterNotNull()
.collectLatest { manga ->
value = manga
}
}
}
// KMK <--
suspend fun smartSearchMerge(manga: Manga, originalMangaId: Long): Manga {
val originalManga = getManga.await(originalMangaId)
?: throw IllegalArgumentException(context.stringResource(SYMR.strings.merge_unknown_entry, originalMangaId))

View file

@ -65,6 +65,11 @@ data class Manga(
get() = customMangaInfo?.status ?: ogStatus
// SY <--
// KMK -->
val relatedMangas: List<Manga>?
get() = listOf(this, this, this, this, this, this, this, this, this, this, this, this, this)
// KMK <--
val expectedNextUpdate: Instant?
get() = nextUpdate
.takeIf { status != SManga.COMPLETED.toLong() }