Add implement for infinite scroll
This commit is contained in:
@@ -3,9 +3,9 @@ package org.libre.agosto.p2play
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import org.libre.agosto.p2play.activities.MainActivity
|
||||||
import org.libre.agosto.p2play.ajax.Auth
|
import org.libre.agosto.p2play.ajax.Auth
|
||||||
import org.libre.agosto.p2play.helpers.TaskManager
|
import org.libre.agosto.p2play.helpers.TaskManager
|
||||||
import org.libre.agosto.p2play.models.TokenModel
|
import org.libre.agosto.p2play.models.TokenModel
|
||||||
|
@@ -35,9 +35,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
VideosView(
|
VideosView(
|
||||||
modifier = Modifier.padding(innerPadding)
|
modifier = Modifier.padding(innerPadding)
|
||||||
) {
|
)
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
package org.libre.agosto.p2play.components.lists
|
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.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
@@ -8,29 +11,69 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import org.libre.agosto.p2play.components.organisms.VideoItem
|
import org.libre.agosto.p2play.components.organisms.VideoItem
|
||||||
import org.libre.agosto.p2play.models.VideoModel
|
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
|
@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()
|
val lazyState = rememberLazyListState()
|
||||||
|
|
||||||
LazyColumn {
|
PullToRefreshBox(
|
||||||
if (header !== null) {
|
isRefreshing,
|
||||||
item { header() }
|
{
|
||||||
|
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) {
|
if (onLoadMore !== null) {
|
||||||
LaunchedEffect(lazyState) {
|
LaunchedEffect (isLoading) {
|
||||||
snapshotFlow { lazyState.layoutInfo.visibleItemsInfo }
|
snapshotFlow { isLoading }
|
||||||
.collect { visibleItems ->
|
.collect {
|
||||||
if (visibleItems.isNotEmpty() &&
|
if (!it) {
|
||||||
visibleItems.last().index >= videos.size - 1) {
|
isRefreshing = false
|
||||||
onLoadMore()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InfiniteScrollHandler(lazyState, 2) {
|
||||||
|
onLoadMore()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,14 +5,17 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Star
|
import androidx.compose.material.icons.filled.Star
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
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.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import org.libre.agosto.p2play.R
|
import org.libre.agosto.p2play.R
|
||||||
import org.libre.agosto.p2play.ajax.Videos
|
import org.libre.agosto.p2play.ajax.Videos
|
||||||
import org.libre.agosto.p2play.components.lists.VideoList
|
import org.libre.agosto.p2play.components.lists.VideoList
|
||||||
@@ -23,9 +26,7 @@ import org.libre.agosto.p2play.models.VideoModel
|
|||||||
|
|
||||||
@SuppressLint("MutableCollectionMutableState")
|
@SuppressLint("MutableCollectionMutableState")
|
||||||
@Composable
|
@Composable
|
||||||
fun VideosView (modifier: Modifier, click: (VideoModel) -> Unit) {
|
fun VideosView (modifier: Modifier) {
|
||||||
val client: Videos = Videos()
|
|
||||||
var videoFilter by rememberSaveable { mutableStateOf("trending") }
|
|
||||||
val chips = arrayListOf(
|
val chips = arrayListOf(
|
||||||
object : ChipValues<String> {
|
object : ChipValues<String> {
|
||||||
override val text = stringResource(R.string.nav_trending)
|
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)
|
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() }) {
|
var videoFilter by rememberSaveable { mutableStateOf("trending") }
|
||||||
videos = it
|
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) {
|
Column (modifier) {
|
||||||
@@ -66,6 +102,20 @@ fun VideosView (modifier: Modifier, click: (VideoModel) -> Unit) {
|
|||||||
header = {
|
header = {
|
||||||
ChipSelector(chips, videoFilter) {
|
ChipSelector(chips, videoFilter) {
|
||||||
videoFilter = it
|
videoFilter = it
|
||||||
|
videos.clear()
|
||||||
|
isLoading = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isLoading = isLoading,
|
||||||
|
onRefresh = {
|
||||||
|
if (!isLoading) {
|
||||||
|
videos.clear()
|
||||||
|
isLoading = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoadMore = {
|
||||||
|
if (!isLoading) {
|
||||||
|
isLoading = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user