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 0f1cc335b..980954edb 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -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), + 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), + 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), + 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, + getMangaState: @Composable ((Manga) -> State), + onClickManga: (Manga) -> Unit, + onLongClickManga: (Manga) -> Unit, +) { + GlobalSearchCardRow( + titles = mangas, + getManga = getMangaState, + onClick = onClickManga, + onLongClick = onLongClickManga, + selection = emptyList(), + ) +} +// KMK <-- + @Composable private fun SharedMangaBottomActionMenu( selected: List, 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 f4671eef4..148db03cf 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreenConstants.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreenConstants.kt @@ -32,4 +32,8 @@ enum class MangaScreenItem { // SY <-- CHAPTER_HEADER, CHAPTER, + + // KMK --> + RELATED_TITLES, + // KMK <-- } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index b4bfba757..c365c5c36 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -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) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index 33895fb5a..d1d8d2167 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -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 { + 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)) diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt index 4f41b18d6..e2b53b1b9 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt @@ -65,6 +65,11 @@ data class Manga( get() = customMangaInfo?.status ?: ogStatus // SY <-- + // KMK --> + val relatedMangas: List? + 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() }