diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewScreen.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewScreen.kt index eda541b6a..d508690b7 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewScreen.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewScreen.kt @@ -2,7 +2,6 @@ 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.Box @@ -70,107 +69,105 @@ import moe.tarsin.coroutines.runSuspendCatching import my.nanihadesuka.compose.LazyGridScrollbar class GalleryPreviewScreen : Fragment() { - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - return ComposeView(inflater.context).apply { - setMD3Content { - val galleryDetail = remember { requireArguments().getParcelableCompat(KEY_GALLERY_DETAIL)!! } - val context = LocalContext.current - fun onPreviewCLick(index: Int) = context.navToReader(galleryDetail, index) - var toNextPage by rememberSaveable { mutableStateOf(requireArguments().getBoolean(KEY_NEXT_PAGE)) } - val scrollBehaviour = TopAppBarDefaults.pinnedScrollBehavior() - val columnCount = calculateSuitableSpanCount() - val state = rememberLazyGridState() - val dialogState = rememberDialogState() - dialogState.Handler() - val coroutineScope = rememberCoroutineScope { Dispatchers.IO } - val pages = galleryDetail.pages - val pgSize = galleryDetail.previewList.size + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = ComposeView(inflater.context).apply { + setMD3Content { + val galleryDetail = remember { requireArguments().getParcelableCompat(KEY_GALLERY_DETAIL)!! } + val context = LocalContext.current + fun onPreviewCLick(index: Int) = context.navToReader(galleryDetail, index) + var toNextPage by rememberSaveable { mutableStateOf(requireArguments().getBoolean(KEY_NEXT_PAGE)) } + val scrollBehaviour = TopAppBarDefaults.pinnedScrollBehavior() + val columnCount = calculateSuitableSpanCount() + val state = rememberLazyGridState() + val dialogState = rememberDialogState() + dialogState.Handler() + val coroutineScope = rememberCoroutineScope { Dispatchers.IO } + val pages = galleryDetail.pages + val pgSize = galleryDetail.previewList.size - LaunchedEffect(Unit) { - if (toNextPage) state.scrollToItem(pgSize) - toNextPage = false - } + 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() }) } } - } + 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 } + result.first.first + } - val previewPagesMap = rememberMemorized { galleryDetail.previewList.associateBy { it.position } as MutableMap } - val data = remember { - Pager(PagingConfig(pageSize = pgSize, initialLoadSize = pgSize, jumpThreshold = 2 * pgSize)) { - object : PagingSource() { - override fun getRefreshKey(state: PagingState) = state.getClippedRefreshKey() - override suspend fun load(params: LoadParams): LoadResult { - 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) + val previewPagesMap = rememberMemorized { galleryDetail.previewList.associateBy { it.position } as MutableMap } + val data = remember { + Pager(PagingConfig(pageSize = pgSize, initialLoadSize = pgSize, jumpThreshold = 2 * pgSize)) { + object : PagingSource() { + override fun getRefreshKey(state: PagingState) = state.getClippedRefreshKey() + override suspend fun load(params: LoadParams): LoadResult { + 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) } - override val jumpingSupported = true + 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) } - }.flow.cachedIn(lifecycleScope) - }.collectAsLazyPagingItems() + override val jumpingSupported = true + } + }.flow.cachedIn(lifecycleScope) + }.collectAsLazyPagingItems() - Scaffold( - topBar = { - TopAppBar( - title = { Text(stringResource(R.string.gallery_previews)) }, - navigationIcon = { - IconButton(onClick = { findNavController().popBackStack() }) { - Icon(imageVector = Icons.Default.ArrowBack, contentDescription = null) + Scaffold( + topBar = { + TopAppBar( + title = { Text(stringResource(R.string.gallery_previews)) }, + navigationIcon = { + IconButton(onClick = { findNavController().popBackStack() }) { + Icon(imageVector = Icons.Default.ArrowBack, contentDescription = null) + } + }, + scrollBehavior = scrollBehaviour, + ) + }, + ) { paddingValues -> + Box { + LazyVerticalGrid( + columns = GridCells.Fixed(columnCount), + modifier = Modifier.nestedScroll(scrollBehaviour.nestedScrollConnection).padding(horizontal = dimensionResource(id = R.dimen.gallery_list_margin_h), vertical = dimensionResource(id = R.dimen.gallery_list_margin_v)), + 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] + if (item != null) { + EhPreviewItem(item) { + onPreviewCLick(item.position) } - }, - scrollBehavior = scrollBehaviour, - ) - }, - ) { paddingValues -> - Box { - LazyVerticalGrid( - columns = GridCells.Fixed(columnCount), - modifier = Modifier.nestedScroll(scrollBehaviour.nestedScrollConnection).padding(horizontal = dimensionResource(id = R.dimen.gallery_list_margin_h), vertical = dimensionResource(id = R.dimen.gallery_list_margin_v)), - 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] - if (item != null) { - EhPreviewItem(item) { - onPreviewCLick(item.position) - } - } else { - EhPreviewItemPlaceholder(index) { - onPreviewCLick(index) - } + } else { + EhPreviewItemPlaceholder(index) { + onPreviewCLick(index) } } } } - Box(modifier = Modifier.padding(paddingValues = paddingValues)) { - LazyGridScrollbar(listState = state) - } + } + Box(modifier = Modifier.padding(paddingValues = paddingValues)) { + LazyGridScrollbar(listState = state) } } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/HistoryScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/HistoryScene.kt index 5786864ba..4cb5f1209 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/HistoryScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/HistoryScene.kt @@ -17,7 +17,6 @@ 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.Image import androidx.compose.foundation.layout.Arrangement @@ -75,122 +74,120 @@ import kotlinx.coroutines.delay import my.nanihadesuka.compose.LazyColumnScrollbar class HistoryScene : BaseScene() { - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - return ComposeView(inflater.context).apply { - setMD3Content { - val dialogState = rememberDialogState() - dialogState.Handler() - val coroutineScope = rememberCoroutineScope() - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - val historyData = remember { Pager(PagingConfig(20, jumpThreshold = 40)) { EhDB.historyLazyList }.flow.cachedIn(lifecycleScope) }.collectAsLazyPagingItems() - Scaffold( - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), - topBar = { - TopAppBar( - title = { Text(text = stringResource(id = R.string.history)) }, - navigationIcon = { - IconButton(onClick = { toggleDrawer(GravityCompat.START) }) { - Icon(imageVector = Icons.Default.Menu, contentDescription = null) + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = ComposeView(inflater.context).apply { + setMD3Content { + val dialogState = rememberDialogState() + dialogState.Handler() + val coroutineScope = rememberCoroutineScope() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() + val historyData = remember { Pager(PagingConfig(20, jumpThreshold = 40)) { EhDB.historyLazyList }.flow.cachedIn(lifecycleScope) }.collectAsLazyPagingItems() + Scaffold( + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + TopAppBar( + title = { Text(text = stringResource(id = R.string.history)) }, + navigationIcon = { + IconButton(onClick = { toggleDrawer(GravityCompat.START) }) { + Icon(imageVector = Icons.Default.Menu, contentDescription = null) + } + }, + actions = { + IconButton(onClick = { + coroutineScope.launchIO { + val clear = dialogState.show( + confirmText = R.string.clear_all, + dismissText = android.R.string.cancel, + text = { Text(text = stringResource(id = R.string.clear_all_history)) }, + ) + if (clear) EhDB.clearHistoryInfo() } - }, - actions = { - IconButton(onClick = { - coroutineScope.launchIO { - val clear = dialogState.show( - confirmText = R.string.clear_all, - dismissText = android.R.string.cancel, - text = { Text(text = stringResource(id = R.string.clear_all_history)) }, - ) - if (clear) EhDB.clearHistoryInfo() - } - }) { - Icon(imageVector = Icons.Default.ClearAll, contentDescription = null) - } - }, - scrollBehavior = scrollBehavior, - ) - }, - ) { paddingValues -> - val state = rememberLazyListState() - Box { - val cardHeight = remember { (3 * listThumbSize * 3).pxToDp.dp } - LazyColumn( - modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.gallery_list_margin_h), vertical = dimensionResource(id = R.dimen.gallery_list_margin_v)), - state = state, - contentPadding = paddingValues, - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.gallery_list_interval)), - ) { - items( - count = historyData.itemCount, - key = historyData.itemKey(key = { item -> item.gid }), - contentType = historyData.itemContentType(), - ) { index -> - val info = historyData[index] - // TODO: item delete & add animation - // Bug tracker: https://issuetracker.google.com/issues/150812265 - info?.let { - val dismissState = rememberDismissState( - confirmValueChange = { - if (it == DismissValue.DismissedToStart) { + }) { + Icon(imageVector = Icons.Default.ClearAll, contentDescription = null) + } + }, + scrollBehavior = scrollBehavior, + ) + }, + ) { paddingValues -> + val state = rememberLazyListState() + Box { + val cardHeight = remember { (3 * listThumbSize * 3).pxToDp.dp } + LazyColumn( + modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.gallery_list_margin_h), vertical = dimensionResource(id = R.dimen.gallery_list_margin_v)), + state = state, + contentPadding = paddingValues, + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.gallery_list_interval)), + ) { + items( + count = historyData.itemCount, + key = historyData.itemKey(key = { item -> item.gid }), + contentType = historyData.itemContentType(), + ) { index -> + val info = historyData[index] + // TODO: item delete & add animation + // Bug tracker: https://issuetracker.google.com/issues/150812265 + info?.let { + val dismissState = rememberDismissState( + confirmValueChange = { + if (it == DismissValue.DismissedToStart) { + coroutineScope.launchIO { + EhDB.deleteHistoryInfo(info) + } + } + true + }, + ) + + SwipeToDismiss( + state = dismissState, + background = {}, + dismissContent = { + // TODO: item delete & add animation + // Bug tracker: https://issuetracker.google.com/issues/150812265 + GalleryInfoListItem( + onClick = { + navAnimated( + R.id.galleryDetailScene, + bundleOf( + GalleryDetailScene.KEY_ACTION to GalleryDetailScene.ACTION_GALLERY_INFO, + GalleryDetailScene.KEY_GALLERY_INFO to it, + ), + ) + }, + onLongClick = { coroutineScope.launchIO { - EhDB.deleteHistoryInfo(info) + dialogState.doGalleryInfoAction(info, context) } - } - true - }, - ) - - SwipeToDismiss( - state = dismissState, - background = {}, - dismissContent = { - // TODO: item delete & add animation - // Bug tracker: https://issuetracker.google.com/issues/150812265 - GalleryInfoListItem( - onClick = { - navAnimated( - R.id.galleryDetailScene, - bundleOf( - GalleryDetailScene.KEY_ACTION to GalleryDetailScene.ACTION_GALLERY_INFO, - GalleryDetailScene.KEY_GALLERY_INFO to it, - ), - ) - }, - onLongClick = { - coroutineScope.launchIO { - dialogState.doGalleryInfoAction(info, context) - } - }, - info = it, - modifier = Modifier.height(cardHeight), - ) - }, - directions = setOf(DismissDirection.EndToStart), - ) - } + }, + info = it, + modifier = Modifier.height(cardHeight), + ) + }, + directions = setOf(DismissDirection.EndToStart), + ) } - } - Box(modifier = Modifier.padding(paddingValues = paddingValues)) { - LazyColumnScrollbar(listState = state) } } - Deferred({ delay(200) }) { - if (historyData.itemCount == 0) { - Column( - modifier = Modifier.padding(paddingValues).fillMaxSize(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Image( - painter = painterResource(id = R.drawable.big_history), - contentDescription = null, - Modifier.padding(16.dp), - ) - Text( - text = stringResource(id = R.string.no_history), - style = MaterialTheme.typography.headlineMedium, - ) - } + Box(modifier = Modifier.padding(paddingValues = paddingValues)) { + LazyColumnScrollbar(listState = state) + } + } + Deferred({ delay(200) }) { + if (historyData.itemCount == 0) { + Column( + modifier = Modifier.padding(paddingValues).fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Image( + painter = painterResource(id = R.drawable.big_history), + contentDescription = null, + Modifier.padding(16.dp), + ) + Text( + text = stringResource(id = R.string.no_history), + style = MaterialTheme.typography.headlineMedium, + ) } } }