From ac9004fe36fbfcd15e2d109078ce97648af106a9 Mon Sep 17 00:00:00 2001 From: Ivan Agosto Date: Tue, 4 Feb 2025 21:48:36 -0600 Subject: [PATCH] Add implement for infinite scroll --- .../org/libre/agosto/p2play/SplashActivity.kt | 2 +- .../agosto/p2play/activities/MainActivity.kt | 4 +- .../p2play/components/lists/VideoList.kt | 67 +++++++++++++++---- .../p2play/components/views/VideosView.kt | 64 ++++++++++++++++-- .../p2play/helpers/InfiniteScrollHandler.kt | 23 +++++++ 5 files changed, 137 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/org/libre/agosto/p2play/helpers/InfiniteScrollHandler.kt diff --git a/app/src/main/java/org/libre/agosto/p2play/SplashActivity.kt b/app/src/main/java/org/libre/agosto/p2play/SplashActivity.kt index 248d96a..0a77396 100644 --- a/app/src/main/java/org/libre/agosto/p2play/SplashActivity.kt +++ b/app/src/main/java/org/libre/agosto/p2play/SplashActivity.kt @@ -3,9 +3,9 @@ package org.libre.agosto.p2play import android.content.Intent import android.content.SharedPreferences import android.os.Bundle -import android.util.Log import androidx.appcompat.app.AppCompatActivity import androidx.preference.PreferenceManager +import org.libre.agosto.p2play.activities.MainActivity import org.libre.agosto.p2play.ajax.Auth import org.libre.agosto.p2play.helpers.TaskManager import org.libre.agosto.p2play.models.TokenModel diff --git a/app/src/main/java/org/libre/agosto/p2play/activities/MainActivity.kt b/app/src/main/java/org/libre/agosto/p2play/activities/MainActivity.kt index 64b5927..eb365b5 100644 --- a/app/src/main/java/org/libre/agosto/p2play/activities/MainActivity.kt +++ b/app/src/main/java/org/libre/agosto/p2play/activities/MainActivity.kt @@ -35,9 +35,7 @@ class MainActivity : ComponentActivity() { ) { innerPadding -> VideosView( modifier = Modifier.padding(innerPadding) - ) { - - } + ) } } } diff --git a/app/src/main/java/org/libre/agosto/p2play/components/lists/VideoList.kt b/app/src/main/java/org/libre/agosto/p2play/components/lists/VideoList.kt index 3b5725a..91078ea 100644 --- a/app/src/main/java/org/libre/agosto/p2play/components/lists/VideoList.kt +++ b/app/src/main/java/org/libre/agosto/p2play/components/lists/VideoList.kt @@ -1,5 +1,8 @@ package org.libre.agosto.p2play.components.lists +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState @@ -8,29 +11,69 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.snapshotFlow import org.libre.agosto.p2play.components.organisms.VideoItem import org.libre.agosto.p2play.models.VideoModel +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.pulltorefresh.PullToRefreshBox +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import org.libre.agosto.p2play.helpers.InfiniteScrollHandler +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun VideoList (videos: ArrayList, header: @Composable (() -> Unit)?, onLoadMore: (() -> Unit)? = null) { +fun VideoList (videos: List, header: @Composable (() -> Unit)?, isLoading: Boolean = false, onRefresh: (() -> Unit)? = null, onLoadMore: (() -> Unit)? = null) { + val videoList by remember { derivedStateOf { videos } } + var isRefreshing by remember { mutableStateOf(false) } val lazyState = rememberLazyListState() - LazyColumn { - if (header !== null) { - item { header() } + PullToRefreshBox( + isRefreshing, + { + if (onRefresh !== null) { + onRefresh() + } } - items (videos) { - VideoItem(it) + ) { + LazyColumn(state = lazyState) { + if (header !== null) { + item(key = "header") { header() } + } + items(videoList, key = { it.id }) { + VideoItem(it) + } + if (isLoading) { + item(key = "loading") { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 20.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } + } } } if (onLoadMore !== null) { - LaunchedEffect(lazyState) { - snapshotFlow { lazyState.layoutInfo.visibleItemsInfo } - .collect { visibleItems -> - if (visibleItems.isNotEmpty() && - visibleItems.last().index >= videos.size - 1) { - onLoadMore() + LaunchedEffect (isLoading) { + snapshotFlow { isLoading } + .collect { + if (!it) { + isRefreshing = false } } } + + InfiniteScrollHandler(lazyState, 2) { + onLoadMore() + } + } } \ No newline at end of file diff --git a/app/src/main/java/org/libre/agosto/p2play/components/views/VideosView.kt b/app/src/main/java/org/libre/agosto/p2play/components/views/VideosView.kt index 996ab56..823b486 100644 --- a/app/src/main/java/org/libre/agosto/p2play/components/views/VideosView.kt +++ b/app/src/main/java/org/libre/agosto/p2play/components/views/VideosView.kt @@ -5,14 +5,17 @@ import androidx.compose.foundation.layout.Column import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Star import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource +import kotlinx.coroutines.flow.distinctUntilChanged import org.libre.agosto.p2play.R import org.libre.agosto.p2play.ajax.Videos import org.libre.agosto.p2play.components.lists.VideoList @@ -23,9 +26,7 @@ import org.libre.agosto.p2play.models.VideoModel @SuppressLint("MutableCollectionMutableState") @Composable -fun VideosView (modifier: Modifier, click: (VideoModel) -> Unit) { - val client: Videos = Videos() - var videoFilter by rememberSaveable { mutableStateOf("trending") } +fun VideosView (modifier: Modifier) { val chips = arrayListOf( object : ChipValues { override val text = stringResource(R.string.nav_trending) @@ -53,11 +54,46 @@ fun VideosView (modifier: Modifier, click: (VideoModel) -> Unit) { override val icon = ImageVector.vectorResource(R.drawable.ic_home_black_24dp) } ) - var videos by rememberSaveable { mutableStateOf>(arrayListOf()) } - val task = TaskManager>() - task.runTask({ client.getLastVideos() }) { - videos = it + var videoFilter by rememberSaveable { mutableStateOf("trending") } + val videos by rememberSaveable { mutableStateOf(mutableListOf()) } + var isLoading by rememberSaveable { mutableStateOf(true) } + + LaunchedEffect(isLoading) { + if (isLoading) { + val task = TaskManager>() + task.runTask( + { + val client = Videos() + when (videoFilter) { + "trending" -> { + client.getTrendingVideos(videos.size) + } + "popular" -> { + client.getPopularVideos(videos.size) + } + "likes" -> { + client.getMostLikedVideos(videos.size) + } + "recent" -> { + client.getLastVideos(videos.size) + } + "local" -> { + client.getLocalVideos(videos.size) + } + else -> { + videoFilter = "trending" + client.getTrendingVideos(videos.size) + } + } + + }, + { + videos.addAll(it) + isLoading = false + } + ) + } } Column (modifier) { @@ -66,6 +102,20 @@ fun VideosView (modifier: Modifier, click: (VideoModel) -> Unit) { header = { ChipSelector(chips, videoFilter) { videoFilter = it + videos.clear() + isLoading = true + } + }, + isLoading = isLoading, + onRefresh = { + if (!isLoading) { + videos.clear() + isLoading = true + } + }, + onLoadMore = { + if (!isLoading) { + isLoading = true } } ) diff --git a/app/src/main/java/org/libre/agosto/p2play/helpers/InfiniteScrollHandler.kt b/app/src/main/java/org/libre/agosto/p2play/helpers/InfiniteScrollHandler.kt new file mode 100644 index 0000000..eba24c4 --- /dev/null +++ b/app/src/main/java/org/libre/agosto/p2play/helpers/InfiniteScrollHandler.kt @@ -0,0 +1,23 @@ +package org.libre.agosto.p2play.helpers + +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.snapshotFlow + +@Composable +fun InfiniteScrollHandler( + lazyState: LazyListState, + buffer: Int = 1, + onLoadMore: () -> Unit +) { + LaunchedEffect(lazyState) { + snapshotFlow { lazyState.layoutInfo.visibleItemsInfo } + .collect { visibleItems -> + val items = lazyState.layoutInfo.totalItemsCount + if (visibleItems.isNotEmpty() && items > buffer && visibleItems.last().index >= items - 1) { + onLoadMore() + } + } + } +} \ No newline at end of file