Skip to content

Commit

Permalink
Making post card lists use PagingLazyColumn (#389)
Browse files Browse the repository at this point in the history
- Deleting `PostCardList` composables
- Moving post card lists over to `PagingLazyColumn` and
`PullRefreshLazyColumn`

---------

Co-authored-by: John Oberhauser <j.git-global@obez.io>
  • Loading branch information
JohnOberhauser and John Oberhauser committed Jan 22, 2024
1 parent 7501e6a commit 20b6972
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 353 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,23 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import org.mozilla.social.core.designsystem.theme.MoSoTheme
import org.mozilla.social.core.ui.common.R
import org.mozilla.social.core.ui.common.animation.DelayedVisibility
import org.mozilla.social.core.ui.common.error.GenericError

@Composable
fun <A : Any> PagingLazyColumn(
lazyPagingItems: LazyPagingItems<A>,
modifier: Modifier = Modifier,
noResultText: String,
noResultText: String = stringResource(id = R.string.theres_nothing_here),
listState: LazyListState = rememberLazyListState(),
emptyListState: LazyListState = rememberLazyListState(),
headerContent: LazyListScope.() -> Unit = {},
content: LazyListScope.() -> Unit,
) {
Box(
Expand All @@ -44,6 +47,7 @@ fun <A : Any> PagingLazyColumn(
listState
},
) {
headerContent()
when (lazyPagingItems.loadState.refresh) {
is LoadState.Error -> {} // handle the error outside the lazy column
is LoadState.Loading -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import org.mozilla.social.core.ui.common.R
import org.mozilla.social.core.ui.common.paging.PagingLazyColumn

@Composable
Expand All @@ -34,6 +32,7 @@ fun <A : Any> PullRefreshLazyColumn(
},
listState: LazyListState = rememberLazyListState(),
emptyListState: LazyListState = rememberLazyListState(),
headerContent: LazyListScope.() -> Unit = {},
content: LazyListScope.() -> Unit,
) {
Box(
Expand All @@ -43,9 +42,9 @@ fun <A : Any> PullRefreshLazyColumn(
) {
PagingLazyColumn(
lazyPagingItems = lazyPagingItems,
noResultText = stringResource(id = R.string.theres_nothing_here),
listState = listState,
emptyListState = emptyListState,
headerContent = headerContent,
content = content,
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,203 +1,10 @@
package org.mozilla.social.core.ui.postcard

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import kotlinx.coroutines.flow.Flow
import org.mozilla.social.core.ui.common.divider.MoSoDivider
import org.mozilla.social.core.ui.common.error.GenericError
import org.mozilla.social.core.ui.common.loading.MaxSizeLoading
import org.mozilla.social.core.ui.common.loading.MoSoCircularProgressIndicator
import org.mozilla.social.core.ui.common.pullrefresh.PullRefreshIndicator
import org.mozilla.social.core.ui.common.pullrefresh.pullRefresh
import org.mozilla.social.core.ui.common.pullrefresh.rememberPullRefreshState

/**
* Shows a list of post cards and various loading and error states.
*
* @param errorToastMessage a flow of toast messages to show when an error happens
* @param refreshSignalFlow a flow that causes the list to refresh when it's emitted to
* @param pullToRefreshEnabled if true, the user will be able to pull to refresh, and
* the pull to refresh loading indicator will be used when doing an initial load or a refresh.
* @param isFullScreenLoading if false, loading and error states will appear below the header
* If true, loading and error states will be centered and full screen.
* Note that if [pullToRefreshEnabled] is true, the pull to refresh loading indicator will be used instead.
* @param headerContent content that will show above the list
*/
@Composable
fun PostCardList(
feed: Flow<PagingData<PostCardUiState>>,
refreshSignalFlow: Flow<Any>? = null,
postCardInteractions: PostCardInteractions,
pullToRefreshEnabled: Boolean = false,
isFullScreenLoading: Boolean = false,
scrollState: LazyListState = rememberLazyListState(),
headerContent: @Composable () -> Unit = {},
) {
val lazyingPagingItems: LazyPagingItems<PostCardUiState> = feed.collectAsLazyPagingItems()

LaunchedEffect(Unit) {
refreshSignalFlow?.collect {
lazyingPagingItems.refresh()
}
}

val pullRefreshState =
rememberPullRefreshState(
refreshing = lazyingPagingItems.loadState.refresh == LoadState.Loading,
onRefresh = { lazyingPagingItems.refresh() },
)

Box(
modifier =
Modifier
.pullRefresh(
pullRefreshState,
enabled = pullToRefreshEnabled,
),
) {
PostCardLazyColumn(
lazyingPagingItems = lazyingPagingItems,
postCardInteractions = postCardInteractions,
pullToRefreshEnabled = pullToRefreshEnabled,
isFullScreenLoading = isFullScreenLoading,
scrollState = scrollState,
emptyListState = rememberLazyListState(),
headerContent = headerContent,
)

if (lazyingPagingItems.loadState.refresh is LoadState.Error && isFullScreenLoading) {
GenericError(
modifier =
Modifier
.fillMaxSize(),
onRetryClicked = { lazyingPagingItems.refresh() },
)
}

if (lazyingPagingItems.loadState.refresh is LoadState.Loading && !pullToRefreshEnabled && isFullScreenLoading) {
MaxSizeLoading()
}

if (pullToRefreshEnabled) {
PullRefreshIndicator(
modifier = Modifier.align(Alignment.TopCenter),
refreshing = lazyingPagingItems.loadState.refresh == LoadState.Loading,
state = pullRefreshState,
)
}
}
}

@Composable
private fun PostCardLazyColumn(
lazyingPagingItems: LazyPagingItems<PostCardUiState>,
postCardInteractions: PostCardInteractions,
pullToRefreshEnabled: Boolean,
isFullScreenLoading: Boolean,
scrollState: LazyListState,
emptyListState: LazyListState,
headerContent: @Composable () -> Unit,
) {
// When navigating back to a list, the lazyPagingItems seem to have a list size of 0
// for a split second before going back to where it was. This causes the list scroll state
// to reset at 0, losing the scroll position. The emptyListState variable fixes this by
// only being in use when the item count is 0.
// There is an issue in google issue tracker
// https://issuetracker.google.com/issues/179397301
// There is also a fixed issue that is related to this
// https://issuetracker.google.com/issues/177245496

LazyColumn(
Modifier
.fillMaxSize(),
state =
if (lazyingPagingItems.itemCount == 0) {
emptyListState
} else {
scrollState
},
) {
item { headerContent() }

when (lazyingPagingItems.loadState.refresh) {
is LoadState.Error -> {
if (!isFullScreenLoading) {
item {
GenericError(
modifier =
Modifier
.fillMaxSize(),
onRetryClicked = { lazyingPagingItems.refresh() },
)
}
}
}
is LoadState.Loading -> {
if (!isFullScreenLoading && !pullToRefreshEnabled) {
item {
Box(
modifier =
Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally)
.padding(top = 40.dp),
) {
MoSoCircularProgressIndicator()
}
}
}
if (pullToRefreshEnabled) {
postListContent(lazyingPagingItems, postCardInteractions)
}
}
is LoadState.NotLoading -> {
postListContent(lazyingPagingItems, postCardInteractions)
}
}

when (lazyingPagingItems.loadState.append) {
is LoadState.Loading -> {
item {
CircularProgressIndicator(
modifier =
Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally)
.padding(16.dp),
)
}
}

is LoadState.Error -> {
item {
GenericError(
onRetryClicked = { lazyingPagingItems.retry() },
)
}
}

is LoadState.NotLoading -> {}
}
}
}

fun LazyListScope.postListContent(
lazyPagingItems: LazyPagingItems<PostCardUiState>,
Expand All @@ -208,45 +15,30 @@ fun LazyListScope.postListContent(
key = lazyPagingItems.itemKey { it.statusId },
) { index ->
lazyPagingItems[index]?.let { item ->
PostCard(
post = item,
PostCardListItem(
uiState = item,
postCardInteractions = postCardInteractions,
index = index,
itemCount = lazyPagingItems.itemCount,
)
if (index < lazyPagingItems.itemCount) {
MoSoDivider()
}
}
}
}

/**
* @param threadId if viewing a thread, pass the threadId in to prevent
* the user from being able to click on the same status as the thread's root status
*/
@Composable
fun PostCardList(
items: List<PostCardUiState>,
fun PostCardListItem(
uiState: PostCardUiState,
postCardInteractions: PostCardInteractions,
index: Int,
itemCount: Int,
threadId: String? = null,
) {
LazyColumn(
Modifier
.fillMaxSize()
.padding(horizontal = 4.dp),
) {
items(
count = items.count(),
key = { items[it].statusId },
) { index ->
val item = items[index]
PostCard(
post = item,
postCardInteractions = postCardInteractions,
threadId = threadId,
)
if (index < items.count()) {
MoSoDivider()
}
}
PostCard(
post = uiState,
postCardInteractions = postCardInteractions,
threadId = threadId,
)
if (index < itemCount) {
MoSoDivider()
}
}
1 change: 1 addition & 0 deletions feature/account/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
implementation(libs.androidx.navigation.compose)
implementation(libs.koin.core)
implementation(libs.koin.androidx.compose)
implementation(libs.androidx.paging.compose)

implementation(libs.androidx.datastore)
implementation(libs.protobuf.kotlin.lite)
Expand Down

0 comments on commit 20b6972

Please sign in to comment.