Improve page previews

This commit is contained in:
Jobobby04 2023-12-24 15:18:15 -05:00
parent c36d2794bb
commit cbb743f995
4 changed files with 134 additions and 61 deletions

View File

@ -34,6 +34,7 @@ import androidx.compose.runtime.Composable
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
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -44,6 +45,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
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
@ -57,6 +59,7 @@ import eu.kanade.presentation.manga.components.MangaInfoBox
import eu.kanade.presentation.manga.components.MangaInfoButtons import eu.kanade.presentation.manga.components.MangaInfoButtons
import eu.kanade.presentation.manga.components.MangaToolbar import eu.kanade.presentation.manga.components.MangaToolbar
import eu.kanade.presentation.manga.components.MissingChapterCountListItem import eu.kanade.presentation.manga.components.MissingChapterCountListItem
import eu.kanade.presentation.manga.components.PagePreviewItems
import eu.kanade.presentation.manga.components.PagePreviews import eu.kanade.presentation.manga.components.PagePreviews
import eu.kanade.presentation.manga.components.SearchMetadataChips import eu.kanade.presentation.manga.components.SearchMetadataChips
import eu.kanade.presentation.util.formatChapterNumber import eu.kanade.presentation.util.formatChapterNumber
@ -339,6 +342,9 @@ private fun MangaScreenSmallImpl(
} }
// SY --> // SY -->
val metadataDescription = metadataDescription(state.source) val metadataDescription = metadataDescription(state.source)
var maxWidth by remember {
mutableStateOf(Dp.Hairline)
}
// SY <-- // SY <--
val internalOnBackPressed = { val internalOnBackPressed = {
@ -546,17 +552,14 @@ private fun MangaScreenSmallImpl(
} }
if (state.pagePreviewsState !is PagePreviewState.Unused) { if (state.pagePreviewsState !is PagePreviewState.Unused) {
item( PagePreviewItems(
key = MangaScreenItem.CHAPTER_PREVIEW,
contentType = MangaScreenItem.CHAPTER_PREVIEW,
) {
PagePreviews(
pagePreviewState = state.pagePreviewsState, pagePreviewState = state.pagePreviewsState,
onOpenPage = onOpenPagePreview, onOpenPage = onOpenPagePreview,
onMorePreviewsClicked = onMorePreviewsClicked, onMorePreviewsClicked = onMorePreviewsClicked,
maxWidth = maxWidth,
setMaxWidth = { maxWidth = it }
) )
} }
}
// SY <-- // SY <--
item( item(

View File

@ -25,7 +25,9 @@ enum class MangaScreenItem {
// SY --> // SY -->
INFO_BUTTONS, INFO_BUTTONS,
CHAPTER_PREVIEW, CHAPTER_PREVIEW_LOADING,
CHAPTER_PREVIEW_ROW,
CHAPTER_PREVIEW_MORE,
// SY <-- // SY <--
CHAPTER_HEADER, CHAPTER_HEADER,

View File

@ -10,6 +10,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -31,49 +33,38 @@ import androidx.compose.ui.unit.dp
import coil.compose.SubcomposeAsyncImage import coil.compose.SubcomposeAsyncImage
import coil.compose.SubcomposeAsyncImageContent import coil.compose.SubcomposeAsyncImageContent
import eu.kanade.domain.manga.model.PagePreview import eu.kanade.domain.manga.model.PagePreview
import eu.kanade.presentation.manga.MangaScreenItem
import eu.kanade.tachiyomi.ui.manga.PagePreviewState import eu.kanade.tachiyomi.ui.manga.PagePreviewState
import exh.util.floor import exh.util.floor
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import tachiyomi.i18n.sy.SYMR import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@Composable @Composable
fun PagePreviews( private fun PagePreviewLoading(
pagePreviewState: PagePreviewState, setMaxWidth: (Dp) -> Unit
onOpenPage: (Int) -> Unit,
onMorePreviewsClicked: () -> Unit,
) { ) {
when (pagePreviewState) { val density = LocalDensity.current
PagePreviewState.Loading -> {
Box( Box(
modifier = Modifier modifier = Modifier
.height(60.dp) .height(60.dp)
.fillMaxWidth(), .fillMaxWidth()
.onGloballyPositioned {
setMaxWidth(with(density) { it.size.width.toDp() })
},
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
CircularProgressIndicator() CircularProgressIndicator()
} }
} }
is PagePreviewState.Success -> {
var maxWidth by remember { @Composable
mutableStateOf(Dp.Hairline) private fun PagePreviewRow(
} onOpenPage: (Int) -> Unit,
val density = LocalDensity.current items: ImmutableList<PagePreview>
Box( ) {
Modifier
.fillMaxWidth()
.onGloballyPositioned {
maxWidth = with(density) { it.size.width.toDp() }
},
) {
if (maxWidth == Dp.Hairline) return@Box
val itemPerRowCount = (maxWidth / 120.dp).floor()
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) {
pagePreviewState.pagePreviews.take(4 * itemPerRowCount).chunked(itemPerRowCount).forEach {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -81,7 +72,7 @@ fun PagePreviews(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
) { ) {
it.forEach { page -> items.forEach { page ->
PagePreview( PagePreview(
modifier = Modifier.weight(1F), modifier = Modifier.weight(1F),
page = page, page = page,
@ -89,11 +80,85 @@ fun PagePreviews(
) )
} }
} }
} }
@Composable
private fun PagePreviewMore(
onMorePreviewsClicked: () -> Unit,
) {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
TextButton(onClick = onMorePreviewsClicked) { TextButton(onClick = onMorePreviewsClicked) {
Text(stringResource(SYMR.strings.more_previews)) Text(stringResource(SYMR.strings.more_previews))
} }
} }
}
@Composable
fun PagePreviews(
pagePreviewState: PagePreviewState,
onOpenPage: (Int) -> Unit,
onMorePreviewsClicked: () -> Unit,
) {
Column(Modifier.fillMaxWidth()) {
var maxWidth by remember {
mutableStateOf(Dp.Hairline)
}
when {
pagePreviewState is PagePreviewState.Loading || maxWidth == Dp.Hairline -> {
PagePreviewLoading(setMaxWidth = { maxWidth = it })
}
pagePreviewState is PagePreviewState.Success -> {
val itemPerRowCount = (maxWidth / 120.dp).floor()
pagePreviewState.pagePreviews.take(4 * itemPerRowCount).chunked(itemPerRowCount).forEach {
PagePreviewRow(
onOpenPage = onOpenPage,
items = remember(it) { it.toImmutableList() }
)
}
PagePreviewMore(onMorePreviewsClicked)
}
else -> {}
}
}
}
fun LazyListScope.PagePreviewItems(
pagePreviewState: PagePreviewState,
onOpenPage: (Int) -> Unit,
onMorePreviewsClicked: () -> Unit,
maxWidth: Dp,
setMaxWidth: (Dp) -> Unit
) {
when {
pagePreviewState is PagePreviewState.Loading || maxWidth == Dp.Hairline -> {
item(
key = MangaScreenItem.CHAPTER_PREVIEW_LOADING,
contentType = MangaScreenItem.CHAPTER_PREVIEW_LOADING,
) {
PagePreviewLoading(setMaxWidth = setMaxWidth)
}
}
pagePreviewState is PagePreviewState.Success -> {
val itemPerRowCount = (maxWidth / 120.dp).floor()
items(
key = { "${MangaScreenItem.CHAPTER_PREVIEW_ROW}-$it" },
contentType = { MangaScreenItem.CHAPTER_PREVIEW_ROW },
items = pagePreviewState.pagePreviews.take(4 * itemPerRowCount).chunked(itemPerRowCount),
) {
PagePreviewRow(
onOpenPage = onOpenPage,
items = remember(it) { it.toImmutableList() }
)
}
item(
key = MangaScreenItem.CHAPTER_PREVIEW_MORE,
contentType = MangaScreenItem.CHAPTER_PREVIEW_MORE,
) {
PagePreviewMore(onMorePreviewsClicked)
} }
} }
else -> {} else -> {}

View File

@ -406,7 +406,6 @@ class EHentai(
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
override fun fetchChapterList(manga: SManga) = fetchChapterList(manga) {} override fun fetchChapterList(manga: SManga) = fetchChapterList(manga) {}
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Use the 1.x API instead", replaceWith = ReplaceWith("getChapterList")) @Deprecated("Use the 1.x API instead", replaceWith = ReplaceWith("getChapterList"))
fun fetchChapterList(manga: SManga, throttleFunc: suspend () -> Unit) = runAsObservable { fun fetchChapterList(manga: SManga, throttleFunc: suspend () -> Unit) = runAsObservable {
getChapterList(manga, throttleFunc) getChapterList(manga, throttleFunc)
@ -476,6 +475,7 @@ class EHentai(
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates")) @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates"))
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> { override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
@Suppress("DEPRECATION")
return super<HttpSource>.fetchLatestUpdates(page).checkValid() return super<HttpSource>.fetchLatestUpdates(page).checkValid()
} }
@ -485,6 +485,7 @@ class EHentai(
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularManga")) @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularManga"))
override fun fetchPopularManga(page: Int): Observable<MangasPage> { override fun fetchPopularManga(page: Int): Observable<MangasPage> {
@Suppress("DEPRECATION")
return super<HttpSource>.fetchPopularManga(page).checkValid() return super<HttpSource>.fetchPopularManga(page).checkValid()
} }
@ -496,6 +497,7 @@ class EHentai(
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getSearchManga")) @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getSearchManga"))
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> = override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
urlImportFetchSearchManga(context, query) { urlImportFetchSearchManga(context, query) {
@Suppress("DEPRECATION")
super<HttpSource>.fetchSearchManga(page, query, filters).checkValid() super<HttpSource>.fetchSearchManga(page, query, filters).checkValid()
} }
@ -506,7 +508,7 @@ class EHentai(
} }
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val toplist = ToplistOption.values()[filters.firstNotNullOfOrNull { (it as? ToplistOptions)?.state } ?: 0] val toplist = ToplistOption.entries[filters.firstNotNullOfOrNull { (it as? ToplistOptions)?.state } ?: 0]
if (toplist != ToplistOption.NONE) { if (toplist != ToplistOption.NONE) {
val uri = "https://e-hentai.org".toUri().buildUpon() val uri = "https://e-hentai.org".toUri().buildUpon()
uri.appendPath("toplist.php") uri.appendPath("toplist.php")
@ -794,6 +796,7 @@ class EHentai(
override fun pageListParse(response: Response) = override fun pageListParse(response: Response) =
throw UnsupportedOperationException("Unused method was called somehow!") throw UnsupportedOperationException("Unused method was called somehow!")
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getImageUrl"))
override fun fetchImageUrl(page: Page): Observable<String> { override fun fetchImageUrl(page: Page): Observable<String> {
return client.newCall(imageUrlRequest(page)) return client.newCall(imageUrlRequest(page))
.asObservableSuccess() .asObservableSuccess()
@ -957,7 +960,7 @@ class EHentai(
class ToplistOptions : Filter.Select<ToplistOption>( class ToplistOptions : Filter.Select<ToplistOption>(
"Toplists", "Toplists",
ToplistOption.values(), ToplistOption.entries.toTypedArray(),
) )
class GenreOption(name: String, val genreId: Int) : Filter.CheckBox(name, false) class GenreOption(name: String, val genreId: Int) : Filter.CheckBox(name, false)