Related titles screen
This commit is contained in:
parent
cdd3a7f6f8
commit
d764ac3b63
5 changed files with 126 additions and 0 deletions
|
|
@ -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>,
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,8 @@ enum class MangaScreenItem {
|
||||||
// SY <--
|
// SY <--
|
||||||
CHAPTER_HEADER,
|
CHAPTER_HEADER,
|
||||||
CHAPTER,
|
CHAPTER,
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
RELATED_TITLES,
|
||||||
|
// KMK <--
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) }
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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() }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue