1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2024-12-24 09:00:31 +00:00

Cache paging data using the cachedIn() extension

This commit is contained in:
Isira Seneviratne 2024-06-30 21:34:42 +05:30
parent 369a46f8fe
commit 02c5f2607a
6 changed files with 43 additions and 48 deletions

View File

@ -293,7 +293,7 @@ dependencies {
implementation 'androidx.compose.material3:material3' implementation 'androidx.compose.material3:material3'
implementation 'androidx.activity:activity-compose' implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.ui:ui-text:1.7.0-beta03' // Needed for parsing HTML to AnnotatedString implementation 'androidx.compose.ui:ui-text:1.7.0-beta04' // Needed for parsing HTML to AnnotatedString
implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.1.0' implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.1.0'
// Paging // Paging

View File

@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -37,6 +38,7 @@ import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import coil.compose.AsyncImage import coil.compose.AsyncImage
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.compose.theme.AppTheme import org.schabi.newpipe.compose.theme.AppTheme
@ -141,13 +143,15 @@ fun Comment(comment: CommentsInfoItem) {
if (showReplies) { if (showReplies) {
ModalBottomSheet(onDismissRequest = { showReplies = false }) { ModalBottomSheet(onDismissRequest = { showReplies = false }) {
val flow = remember(comment) { val coroutineScope = rememberCoroutineScope()
val flow = remember(coroutineScope) {
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) { Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
CommentsSource(comment.serviceId, comment.url, comment.replies) CommentsSource(comment.serviceId, comment.url, comment.replies)
}.flow }.flow
.cachedIn(coroutineScope)
} }
CommentSection(parentComment = comment, flow = flow) CommentSection(parentComment = comment, commentsData = flow)
} }
} }
} }

View File

@ -36,13 +36,13 @@ import org.schabi.newpipe.paging.CommentsDisabledException
@Composable @Composable
fun CommentSection( fun CommentSection(
parentComment: CommentsInfoItem? = null, parentComment: CommentsInfoItem? = null,
flow: Flow<PagingData<CommentsInfoItem>> commentsData: Flow<PagingData<CommentsInfoItem>>
) { ) {
val replies = flow.collectAsLazyPagingItems() val comments = commentsData.collectAsLazyPagingItems()
val itemCount by remember { derivedStateOf { replies.itemCount } } val itemCount by remember { derivedStateOf { comments.itemCount } }
Surface(color = MaterialTheme.colorScheme.background) { Surface(color = MaterialTheme.colorScheme.background) {
val refresh = replies.loadState.refresh val refresh = comments.loadState.refresh
if (itemCount == 0 && refresh !is LoadState.Loading) { if (itemCount == 0 && refresh !is LoadState.Loading) {
NoCommentsMessage((refresh as? LoadState.Error)?.error) NoCommentsMessage((refresh as? LoadState.Error)?.error)
} else { } else {
@ -58,7 +58,7 @@ fun CommentSection(
} }
items(itemCount) { items(itemCount) {
Comment(comment = replies[it]!!) Comment(comment = comments[it]!!)
} }
} }
} }
@ -113,7 +113,7 @@ private fun CommentSectionPreview(
@PreviewParameter(CommentDataProvider::class) pagingData: PagingData<CommentsInfoItem> @PreviewParameter(CommentDataProvider::class) pagingData: PagingData<CommentsInfoItem>
) { ) {
AppTheme { AppTheme {
CommentSection(flow = flowOf(pagingData)) CommentSection(commentsData = flowOf(pagingData))
} }
} }
@ -137,6 +137,6 @@ private fun CommentRepliesPreview() {
val flow = flowOf(PagingData.from(replies)) val flow = flowOf(PagingData.from(replies))
AppTheme { AppTheme {
CommentSection(parentComment = comment, flow = flow) CommentSection(parentComment = comment, commentsData = flow)
} }
} }

View File

@ -5,12 +5,14 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import org.schabi.newpipe.compose.comment.CommentSection import org.schabi.newpipe.compose.comment.CommentSection
import org.schabi.newpipe.compose.theme.AppTheme import org.schabi.newpipe.compose.theme.AppTheme
import org.schabi.newpipe.paging.CommentsSource import org.schabi.newpipe.paging.CommentsSource
@ -29,14 +31,16 @@ class CommentsFragment : Fragment() {
return ComposeView(requireContext()).apply { return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent { setContent {
val flow = remember(serviceId, url) { val coroutineScope = rememberCoroutineScope()
val flow = remember(coroutineScope) {
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) { Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
CommentsSource(serviceId, url, null) CommentsSource(serviceId, url, null)
}.flow }.flow
.cachedIn(coroutineScope)
} }
AppTheme { AppTheme {
CommentSection(flow = flow) CommentSection(commentsData = flow)
} }
} }
} }

View File

@ -1,33 +1,40 @@
package org.schabi.newpipe.paging package org.schabi.newpipe.paging
import androidx.paging.PagingSource
import androidx.paging.PagingState import androidx.paging.PagingState
import androidx.paging.rxjava3.RxPagingSource import kotlinx.coroutines.Dispatchers
import io.reactivex.rxjava3.core.Single import kotlinx.coroutines.withContext
import io.reactivex.rxjava3.schedulers.Schedulers import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.extractor.Page import org.schabi.newpipe.extractor.Page
import org.schabi.newpipe.extractor.comments.CommentsInfo
import org.schabi.newpipe.extractor.comments.CommentsInfoItem import org.schabi.newpipe.extractor.comments.CommentsInfoItem
import org.schabi.newpipe.util.ExtractorHelper import org.schabi.newpipe.util.NO_SERVICE_ID
class CommentsSource( class CommentsSource(
private val serviceId: Int, serviceId: Int,
private val url: String?, private val url: String?,
private val repliesPage: Page? private val repliesPage: Page?
) : RxPagingSource<Page, CommentsInfoItem>() { ) : PagingSource<Page, CommentsInfoItem>() {
override fun loadSingle(params: LoadParams<Page>): Single<LoadResult<Page, CommentsInfoItem>> { init {
require(serviceId != NO_SERVICE_ID) { "serviceId is NO_SERVICE_ID" }
}
private val service = NewPipe.getService(serviceId)
override suspend fun load(params: LoadParams<Page>): LoadResult<Page, CommentsInfoItem> {
// repliesPage is non-null only when used to load the comment replies // repliesPage is non-null only when used to load the comment replies
val nextKey = params.key ?: repliesPage val nextKey = params.key ?: repliesPage
return nextKey?.let { return withContext(Dispatchers.IO) {
ExtractorHelper.getMoreCommentItems(serviceId, url, it) nextKey?.let {
.subscribeOn(Schedulers.io()) val info = CommentsInfo.getMoreItems(service, url, it)
.map { LoadResult.Page(it.items, null, it.nextPage) } LoadResult.Page(info.items, null, info.nextPage)
} ?: ExtractorHelper.getCommentsInfo(serviceId, url, false) } ?: run {
.subscribeOn(Schedulers.io()) val info = CommentsInfo.getInfo(service, url)
.map { if (info.isCommentsDisabled) {
if (it.isCommentsDisabled) {
LoadResult.Error(CommentsDisabledException()) LoadResult.Error(CommentsDisabledException())
} else { } else {
LoadResult.Page(it.relatedItems, null, it.nextPage) LoadResult.Page(info.relatedItems, null, info.nextPage)
}
} }
} }
} }

View File

@ -42,8 +42,6 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.channel.tabs.ChannelTabInfo; import org.schabi.newpipe.extractor.channel.tabs.ChannelTabInfo;
import org.schabi.newpipe.extractor.comments.CommentsInfo;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.kiosk.KioskInfo; import org.schabi.newpipe.extractor.kiosk.KioskInfo;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
@ -146,24 +144,6 @@ public final class ExtractorHelper {
listLinkHandler, nextPage)); listLinkHandler, nextPage));
} }
public static Single<CommentsInfo> getCommentsInfo(final int serviceId,
final String url,
final boolean forceLoad) {
checkServiceId(serviceId);
return checkCache(forceLoad, serviceId, url, InfoCache.Type.COMMENTS,
Single.fromCallable(() ->
CommentsInfo.getInfo(NewPipe.getService(serviceId), url)));
}
public static Single<InfoItemsPage<CommentsInfoItem>> getMoreCommentItems(
final int serviceId,
final String url,
final Page nextPage) {
checkServiceId(serviceId);
return Single.fromCallable(() ->
CommentsInfo.getMoreItems(NewPipe.getService(serviceId), url, nextPage));
}
public static Single<PlaylistInfo> getPlaylistInfo(final int serviceId, public static Single<PlaylistInfo> getPlaylistInfo(final int serviceId,
final String url, final String url,
final boolean forceLoad) { final boolean forceLoad) {