Extract a fragmentless GalleryPreviewScreen
This commit is contained in:
1 parent
19e5636dd2
commit
5f02769012
3 files changed
+181
-170
No files matched your search
@@ -0,0 +1,180 @@
|
||||
package com.hippo.ehviewer.ui.scene
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import androidx.paging.cachedIn
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.itemContentType
|
||||
import androidx.paging.compose.itemKey
|
||||
import androidx.room.paging.util.getClippedRefreshKey
|
||||
import androidx.room.paging.util.getLimit
|
||||
import androidx.room.paging.util.getOffset
|
||||
import arrow.fx.coroutines.parMap
|
||||
import coil.imageLoader
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
import com.hippo.ehviewer.client.data.GalleryPreview
|
||||
import com.hippo.ehviewer.coil.justDownload
|
||||
import com.hippo.ehviewer.ktbuilder.imageRequest
|
||||
import com.hippo.ehviewer.ui.MainActivity
|
||||
import com.hippo.ehviewer.ui.legacy.calculateSuitableSpanCount
|
||||
import com.hippo.ehviewer.ui.main.EhPreviewItem
|
||||
import com.hippo.ehviewer.ui.navToReader
|
||||
import com.hippo.ehviewer.ui.setMD3Content
|
||||
import com.hippo.ehviewer.ui.tools.FastScrollLazyVerticalGrid
|
||||
import com.hippo.ehviewer.ui.tools.rememberInVM
|
||||
import com.hippo.ehviewer.util.findActivity
|
||||
import com.hippo.ehviewer.util.getParcelableCompat
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
|
||||
@Destination
|
||||
@Composable
|
||||
fun GalleryPreviewScreen(galleryDetail: GalleryDetail, toNextPageArg: Boolean, navigator: NavController) {
|
||||
val context = LocalContext.current
|
||||
fun onPreviewClick(index: Int) = context.navToReader(galleryDetail.galleryInfo, index)
|
||||
val scrollBehaviour = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
val columnCount = calculateSuitableSpanCount()
|
||||
val state = rememberLazyGridState()
|
||||
val coroutineScope = rememberCoroutineScope { Dispatchers.IO }
|
||||
val pages = galleryDetail.pages
|
||||
val pgSize = galleryDetail.previewList.size
|
||||
var toNextPage by rememberSaveable { mutableStateOf(toNextPageArg) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (toNextPage) state.scrollToItem(pgSize)
|
||||
toNextPage = false
|
||||
}
|
||||
|
||||
suspend fun getPreviewListByPage(page: Int) = galleryDetail.run {
|
||||
val url = EhUrl.getGalleryDetailUrl(gid, token, page, false)
|
||||
val result = EhEngine.getPreviewList(url)
|
||||
if (Settings.preloadThumbAggressively) {
|
||||
coroutineScope.launch {
|
||||
context.run { result.first.first.forEach { imageLoader.enqueue(imageRequest(it) { justDownload() }) } }
|
||||
}
|
||||
}
|
||||
result.first.first
|
||||
}
|
||||
|
||||
val data = rememberInVM {
|
||||
val previewPagesMap = galleryDetail.previewList.associateBy { it.position } as MutableMap
|
||||
Pager(PagingConfig(pageSize = pgSize, initialLoadSize = pgSize, jumpThreshold = 2 * pgSize)) {
|
||||
object : PagingSource<Int, GalleryPreview>() {
|
||||
override fun getRefreshKey(state: PagingState<Int, GalleryPreview>) = state.getClippedRefreshKey()
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, GalleryPreview> {
|
||||
val key = params.key ?: 0
|
||||
val up = getOffset(params, key, pages)
|
||||
val end = (up + getLimit(params, key) - 1).coerceAtMost(pages - 1)
|
||||
runSuspendCatching {
|
||||
(up..end).filterNot { it in previewPagesMap }.map { it / pgSize }.toSet()
|
||||
.parMap(Dispatchers.IO) { getPreviewListByPage(it) }
|
||||
.forEach { previews -> previews.forEach { previewPagesMap[it.position] = it } }
|
||||
}.onFailure {
|
||||
return LoadResult.Error(it)
|
||||
}
|
||||
val r = (up..end).map { requireNotNull(previewPagesMap[it]) }
|
||||
val prevK = if (up <= 0 || r.isEmpty()) null else up
|
||||
val nextK = if (end == pages - 1) null else end + 1
|
||||
return LoadResult.Page(r, prevK, nextK, up, pages - end - 1)
|
||||
}
|
||||
override val jumpingSupported = true
|
||||
}
|
||||
}.flow.cachedIn(viewModelScope)
|
||||
}.collectAsLazyPagingItems()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.gallery_previews)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { navigator.popBackStack() }) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null)
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehaviour,
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
FastScrollLazyVerticalGrid(
|
||||
columns = GridCells.Fixed(columnCount),
|
||||
modifier = Modifier.nestedScroll(scrollBehaviour.nestedScrollConnection).padding(horizontal = dimensionResource(id = R.dimen.gallery_list_margin_h)),
|
||||
state = state,
|
||||
contentPadding = paddingValues,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
items(
|
||||
count = data.itemCount,
|
||||
key = data.itemKey(key = { item -> item.position }),
|
||||
contentType = data.itemContentType(),
|
||||
) { index ->
|
||||
val item = data[index]
|
||||
EhPreviewItem(item, index) {
|
||||
onPreviewClick(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GalleryPreviewFragment : Fragment() {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = ComposeView(inflater.context).apply {
|
||||
setMD3Content {
|
||||
val galleryDetail = remember { requireArguments().getParcelableCompat<GalleryDetail>(KEY_GALLERY_DETAIL)!! }
|
||||
val toNextPage = remember { requireArguments().getBoolean(KEY_NEXT_PAGE) }
|
||||
val navController = remember { findNavController() }
|
||||
GalleryPreviewScreen(galleryDetail, toNextPage, navController)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
requireContext().findActivity<MainActivity>().recompose()
|
||||
}
|
||||
}
|
||||
|
||||
const val KEY_GALLERY_DETAIL = "gallery_detail"
|
||||
const val KEY_NEXT_PAGE = "next_page"
|
||||
@@ -1,169 +0,0 @@
|
||||
package com.hippo.ehviewer.ui.scene
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import androidx.paging.cachedIn
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.itemContentType
|
||||
import androidx.paging.compose.itemKey
|
||||
import androidx.room.paging.util.getClippedRefreshKey
|
||||
import androidx.room.paging.util.getLimit
|
||||
import androidx.room.paging.util.getOffset
|
||||
import arrow.fx.coroutines.parMap
|
||||
import coil.imageLoader
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
import com.hippo.ehviewer.client.data.GalleryPreview
|
||||
import com.hippo.ehviewer.coil.justDownload
|
||||
import com.hippo.ehviewer.ktbuilder.imageRequest
|
||||
import com.hippo.ehviewer.ui.MainActivity
|
||||
import com.hippo.ehviewer.ui.legacy.calculateSuitableSpanCount
|
||||
import com.hippo.ehviewer.ui.main.EhPreviewItem
|
||||
import com.hippo.ehviewer.ui.navToReader
|
||||
import com.hippo.ehviewer.ui.setMD3Content
|
||||
import com.hippo.ehviewer.ui.tools.FastScrollLazyVerticalGrid
|
||||
import com.hippo.ehviewer.ui.tools.rememberInVM
|
||||
import com.hippo.ehviewer.util.findActivity
|
||||
import com.hippo.ehviewer.util.getParcelableCompat
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
|
||||
class GalleryPreviewScreen : Fragment() {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = ComposeView(inflater.context).apply {
|
||||
setMD3Content {
|
||||
val galleryDetail = remember { requireArguments().getParcelableCompat<GalleryDetail>(KEY_GALLERY_DETAIL)!! }
|
||||
val context = LocalContext.current
|
||||
fun onPreviewCLick(index: Int) = context.navToReader(galleryDetail.galleryInfo, index)
|
||||
var toNextPage by rememberSaveable { mutableStateOf(requireArguments().getBoolean(KEY_NEXT_PAGE)) }
|
||||
val scrollBehaviour = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
val columnCount = calculateSuitableSpanCount()
|
||||
val state = rememberLazyGridState()
|
||||
val coroutineScope = rememberCoroutineScope { Dispatchers.IO }
|
||||
val pages = galleryDetail.pages
|
||||
val pgSize = galleryDetail.previewList.size
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (toNextPage) state.scrollToItem(pgSize)
|
||||
toNextPage = false
|
||||
}
|
||||
|
||||
suspend fun getPreviewListByPage(page: Int) = galleryDetail.run {
|
||||
val url = EhUrl.getGalleryDetailUrl(gid, token, page, false)
|
||||
val result = EhEngine.getPreviewList(url)
|
||||
if (Settings.preloadThumbAggressively) {
|
||||
coroutineScope.launch {
|
||||
context.run { result.first.first.forEach { imageLoader.enqueue(imageRequest(it) { justDownload() }) } }
|
||||
}
|
||||
}
|
||||
result.first.first
|
||||
}
|
||||
|
||||
val data = rememberInVM {
|
||||
val previewPagesMap = galleryDetail.previewList.associateBy { it.position } as MutableMap
|
||||
Pager(PagingConfig(pageSize = pgSize, initialLoadSize = pgSize, jumpThreshold = 2 * pgSize)) {
|
||||
object : PagingSource<Int, GalleryPreview>() {
|
||||
override fun getRefreshKey(state: PagingState<Int, GalleryPreview>) = state.getClippedRefreshKey()
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, GalleryPreview> {
|
||||
val key = params.key ?: 0
|
||||
val up = getOffset(params, key, pages)
|
||||
val end = (up + getLimit(params, key) - 1).coerceAtMost(pages - 1)
|
||||
runSuspendCatching {
|
||||
(up..end).filterNot { it in previewPagesMap }.map { it / pgSize }.toSet()
|
||||
.parMap(Dispatchers.IO) { getPreviewListByPage(it) }
|
||||
.forEach { previews -> previews.forEach { previewPagesMap[it.position] = it } }
|
||||
}.onFailure {
|
||||
return LoadResult.Error(it)
|
||||
}
|
||||
val r = (up..end).map { requireNotNull(previewPagesMap[it]) }
|
||||
val prevK = if (up <= 0 || r.isEmpty()) null else up
|
||||
val nextK = if (end == pages - 1) null else end + 1
|
||||
return LoadResult.Page(r, prevK, nextK, up, pages - end - 1)
|
||||
}
|
||||
override val jumpingSupported = true
|
||||
}
|
||||
}.flow.cachedIn(viewModelScope)
|
||||
}.collectAsLazyPagingItems()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.gallery_previews)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { findNavController().popBackStack() }) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null)
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehaviour,
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
FastScrollLazyVerticalGrid(
|
||||
columns = GridCells.Fixed(columnCount),
|
||||
modifier = Modifier.nestedScroll(scrollBehaviour.nestedScrollConnection).padding(horizontal = dimensionResource(id = R.dimen.gallery_list_margin_h)),
|
||||
state = state,
|
||||
contentPadding = paddingValues,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
items(
|
||||
count = data.itemCount,
|
||||
key = data.itemKey(key = { item -> item.position }),
|
||||
contentType = data.itemContentType(),
|
||||
) { index ->
|
||||
val item = data[index]
|
||||
EhPreviewItem(item, index) {
|
||||
onPreviewCLick(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
requireContext().findActivity<MainActivity>().recompose()
|
||||
}
|
||||
}
|
||||
|
||||
const val KEY_GALLERY_DETAIL = "gallery_detail"
|
||||
const val KEY_NEXT_PAGE = "next_page"
|
||||
@@ -53,7 +53,7 @@
|
||||
android:label="GalleryCommentsScene" />
|
||||
<fragment
|
||||
android:id="@+id/galleryPreviewsScene"
|
||||
android:name="com.hippo.ehviewer.ui.scene.GalleryPreviewScreen"
|
||||
android:name="com.hippo.ehviewer.ui.scene.GalleryPreviewFragment"
|
||||
android:label="GalleryPreviewsScene" />
|
||||
<fragment
|
||||
android:id="@+id/nav_favourite"
|
||||
|
||||
Reference in new issue
Block a user