Add base of pagination in TimelineTab

This commit is contained in:
Shinokuni 2024-01-11 22:16:30 +01:00
parent 413dba4db5
commit 304f3c02e0
5 changed files with 100 additions and 65 deletions

View File

@ -68,7 +68,7 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
def composeBom = platform('androidx.compose:compose-bom:2023.08.00')
def composeBom = platform('androidx.compose:compose-bom:2023.10.01')
implementation composeBom
androidTestImplementation composeBom
@ -107,7 +107,9 @@ dependencies {
implementation "io.coil-kt:coil:2.4.0"
implementation "io.coil-kt:coil-compose:2.4.0"
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1"

View File

@ -1,15 +1,12 @@
package com.readrops.app.compose.timelime
import android.util.Log
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.CircularProgressIndicator
@ -27,11 +24,12 @@ import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
@ -42,6 +40,7 @@ import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.readrops.app.compose.R
import com.readrops.app.compose.item.ItemScreen
import com.readrops.app.compose.timelime.drawer.TimelineDrawer
import com.readrops.app.compose.util.components.CenteredColumn
import com.readrops.app.compose.util.theme.spacing
import org.koin.androidx.compose.getViewModel
@ -60,11 +59,14 @@ object TimelineTab : Tab {
override fun Content() {
val viewModel = getViewModel<TimelineViewModel>()
val state by viewModel.timelineState.collectAsStateWithLifecycle()
val items = state.itemState.collectAsLazyPagingItems()
val navigator = LocalNavigator.currentOrThrow
val context = LocalContext.current
val scrollState = rememberLazyListState()
// Use the depreciated refresh swipe as the material 3 one isn't available yet
val swipeState = rememberSwipeRefreshState(state.isRefreshing)
val drawerState = rememberDrawerState(
initialValue = DrawerValue.Closed,
@ -131,7 +133,9 @@ object TimelineTab : Tab {
)
}
IconButton(onClick = { }) {
IconButton(
onClick = { viewModel.refreshTimeline() }
) {
Icon(
painter = painterResource(id = R.drawable.ic_sync),
contentDescription = null
@ -154,21 +158,16 @@ object TimelineTab : Tab {
onRefresh = { viewModel.refreshTimeline() },
modifier = Modifier.padding(paddingValues)
) {
when (val itemState = state.items) {
is ItemState.Loading -> {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxSize()
) {
when {
items.isLoading() -> {
Log.d("TAG", "loading")
CenteredColumn {
CircularProgressIndicator()
}
}
is ItemState.Error -> TODO()
is ItemState.Loaded -> {
val items = itemState.items.collectAsLazyPagingItems()
items.isError() -> Text(text = "error")
else -> {
LazyColumn(
state = scrollState,
contentPadding = PaddingValues(vertical = MaterialTheme.spacing.shortSpacing),
@ -176,7 +175,7 @@ object TimelineTab : Tab {
) {
items(
count = items.itemCount,
key = { items[it]!!.item.id },
//key = { items[it]!! },
contentType = { "item_with_feed" }
) { itemCount ->
val itemWithFeed = items[itemCount]!!
@ -192,6 +191,7 @@ object TimelineTab : Tab {
onShare = {
viewModel.shareItem(itemWithFeed.item, context)
},
compactLayout = true
)
}
}
@ -203,20 +203,11 @@ object TimelineTab : Tab {
}
}
@Composable
fun NoItemPlaceholder() {
val scrollState = rememberScrollState()
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)
) {
Text(
text = "No item",
style = MaterialTheme.typography.displayMedium
)
}
fun <T : Any> LazyPagingItems<T>.isLoading(): Boolean {
return loadState.append is LoadState.Loading //|| loadState.refresh is LoadState.Loading
}
fun <T : Any> LazyPagingItems<T>.isError(): Boolean {
return loadState.append is LoadState.Error //|| loadState.refresh is LoadState.Error
}

View File

@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@ -46,24 +47,23 @@ class TimelineViewModel(
accountEvent.consumeAsFlow(),
filters
) { account, filters ->
filters.accountId = account.id
Pair(account, filters)
}.collectLatest { (account, filters) ->
val query = ItemsQueryBuilder.buildItemsQuery(filters.copy(accountId = account.id))
val query = ItemsQueryBuilder.buildItemsQuery(filters)
_timelineState.update {
it.copy(
items = ItemState.Loaded(
items = Pager(
config = PagingConfig(
pageSize = 100,
prefetchDistance = 150
),
pagingSourceFactory = {
database.newItemDao().selectAll(query)
},
).flow
.cachedIn(viewModelScope)
)
itemState = Pager(
config = PagingConfig(
pageSize = 10,
prefetchDistance = 10
),
pagingSourceFactory = {
database.newItemDao().selectAll(query)
},
).flow
.cachedIn(viewModelScope)
)
}
@ -86,7 +86,12 @@ class TimelineViewModel(
}
_timelineState.update { it.copy(isRefreshing = false) }
_timelineState.update {
it.copy(
isRefreshing = false,
endSynchronizing = true
)
}
}
}
@ -183,18 +188,8 @@ class TimelineViewModel(
data class TimelineState(
val isRefreshing: Boolean = false,
val isDrawerOpen: Boolean = false,
val endSynchronizing: Boolean = false,
val filters: QueryFilters = QueryFilters(),
val foldersAndFeeds: Map<Folder?, List<Feed>> = emptyMap(),
val items: ItemState = ItemState.Loading
val itemState: Flow<PagingData<ItemWithFeed>> = emptyFlow()
)
sealed class ItemState {
@Immutable
object Loading : ItemState()
@Immutable
data class Error(val exception: Exception) : ItemState()
@Immutable
data class Loaded(val items: Flow<PagingData<ItemWithFeed>>) : ItemState()
}

View File

@ -0,0 +1,47 @@
package com.readrops.app.compose.util.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import com.readrops.app.compose.util.toDp
@Composable
fun CenteredColumn(
content: @Composable () -> Unit
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
content()
}
}
@Composable
fun Placeholder(
text: String,
painter: Painter,
) {
CenteredColumn {
Icon(
painter = painter,
contentDescription = null,
modifier = Modifier.size(MaterialTheme.typography.displayMedium.toDp() * 1.5f)
)
Text(
text = text,
style = MaterialTheme.typography.displayMedium
)
}
}

View File

@ -97,9 +97,9 @@ dependencies {
api "io.insert-koin:koin-androidx-compose:3.4.2"
api "io.insert-koin:koin-android-compat:$rootProject.ext.koin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
api "androidx.paging:paging-runtime-ktx:3.2.0"
api "androidx.paging:paging-compose:3.2.0"
api "androidx.paging:paging-runtime-ktx:3.2.1"
api "androidx.paging:paging-compose:3.2.1"
}