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.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf 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.fastAll
import androidx.compose.ui.util.fastAny import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastMap 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.components.relativeDateText
import eu.kanade.presentation.manga.components.ChapterDownloadAction import eu.kanade.presentation.manga.components.ChapterDownloadAction
import eu.kanade.presentation.manga.components.ChapterHeader import eu.kanade.presentation.manga.components.ChapterHeader
@ -167,6 +170,12 @@ fun MangaScreen(
onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
// KMK -->
getMangaState: @Composable ((Manga) -> State<Manga>),
onRelatedMangaClick: (Manga) -> Unit,
onRelatedMangaLongClick: (Manga) -> Unit,
// KMK <--
) { ) {
val context = LocalContext.current val context = LocalContext.current
val onCopyTagToClipboard: (tag: String) -> Unit = { val onCopyTagToClipboard: (tag: String) -> Unit = {
@ -220,6 +229,11 @@ fun MangaScreen(
onChapterSelected = onChapterSelected, onChapterSelected = onChapterSelected,
onAllChapterSelected = onAllChapterSelected, onAllChapterSelected = onAllChapterSelected,
onInvertSelection = onInvertSelection, onInvertSelection = onInvertSelection,
// KMK -->
getMangaState = getMangaState,
onRelatedMangaClick = onRelatedMangaClick,
onRelatedMangaLongClick = onRelatedMangaLongClick,
// KMK <--
) )
} else { } else {
MangaScreenLargeImpl( MangaScreenLargeImpl(
@ -266,6 +280,11 @@ fun MangaScreen(
onChapterSelected = onChapterSelected, onChapterSelected = onChapterSelected,
onAllChapterSelected = onAllChapterSelected, onAllChapterSelected = onAllChapterSelected,
onInvertSelection = onInvertSelection, onInvertSelection = onInvertSelection,
// KMK -->
getMangaState = getMangaState,
onRelatedMangaClick = onRelatedMangaClick,
onRelatedMangaLongClick = onRelatedMangaLongClick,
// KMK <--
) )
} }
} }
@ -328,6 +347,12 @@ private fun MangaScreenSmallImpl(
onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
// KMK -->
getMangaState: @Composable ((Manga) -> State<Manga>),
onRelatedMangaClick: (Manga) -> Unit,
onRelatedMangaLongClick: (Manga) -> Unit,
// KMK <--
) { ) {
val chapterListState = rememberLazyListState() 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 --> // SY -->
if (!state.showRecommendationsInOverflow || state.showMergeWithAnother) { if (!state.showRecommendationsInOverflow || state.showMergeWithAnother) {
item( item(
@ -654,6 +702,12 @@ fun MangaScreenLargeImpl(
onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
// KMK -->
getMangaState: @Composable ((Manga) -> State<Manga>),
onRelatedMangaClick: (Manga) -> Unit,
onRelatedMangaLongClick: (Manga) -> Unit,
// KMK <--
) { ) {
val layoutDirection = LocalLayoutDirection.current val layoutDirection = LocalLayoutDirection.current
val density = LocalDensity.current val density = LocalDensity.current
@ -863,6 +917,29 @@ fun MangaScreenLargeImpl(
bottom = contentPadding.calculateBottomPadding(), 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( item(
key = MangaScreenItem.CHAPTER_HEADER, key = MangaScreenItem.CHAPTER_HEADER,
contentType = 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 @Composable
private fun SharedMangaBottomActionMenu( private fun SharedMangaBottomActionMenu(
selected: List<ChapterList.Item>, selected: List<ChapterList.Item>,

View file

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

View file

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

View file

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

View file

@ -65,6 +65,11 @@ data class Manga(
get() = customMangaInfo?.status ?: ogStatus get() = customMangaInfo?.status ?: ogStatus
// SY <-- // 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? val expectedNextUpdate: Instant?
get() = nextUpdate get() = nextUpdate
.takeIf { status != SManga.COMPLETED.toLong() } .takeIf { status != SManga.COMPLETED.toLong() }