Add implement for infinite scroll

This commit is contained in:
Ivan Agosto
2025-02-04 21:48:36 -06:00
parent b3d8347200
commit ac9004fe36
5 changed files with 137 additions and 23 deletions

View File

@@ -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

View File

@@ -35,9 +35,7 @@ class MainActivity : ComponentActivity() {
) { innerPadding ->
VideosView(
modifier = Modifier.padding(innerPadding)
) {
}
)
}
}
}

View File

@@ -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<VideoModel>, header: @Composable (() -> Unit)?, onLoadMore: (() -> Unit)? = null) {
fun VideoList (videos: List<VideoModel>, 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()
}
}
}

View File

@@ -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<String> {
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<ArrayList<VideoModel>>(arrayListOf<VideoModel>()) }
val task = TaskManager<ArrayList<VideoModel>>()
task.runTask({ client.getLastVideos() }) {
videos = it
var videoFilter by rememberSaveable { mutableStateOf("trending") }
val videos by rememberSaveable { mutableStateOf(mutableListOf<VideoModel>()) }
var isLoading by rememberSaveable { mutableStateOf(true) }
LaunchedEffect(isLoading) {
if (isLoading) {
val task = TaskManager<List<VideoModel>>()
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
}
}
)

View File

@@ -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()
}
}
}
}