Hey guys do you use paging library if you have lon...
# compose
j
Hey guys do you use paging library if you have long list with load more or jetpack compose own implementation? I hit a little bit wall as i have quite simple task that starts to be hard with paging library. I create new post in app and need to add it on top of list. Very simple. And with paging lib it seems it is not easy at all
i
Paging is based on having a single source of truth - your PagingSource. E.g., if you are using Room or SqlDelight's PagingSource, you'd add a new element to the database and the whole rest of the system (up to and including your UI) automatically invalidates and refreshes
j
Problem is that i do not need room or something. I just want to get list of posts from network and when user create new post , add it on top of it .
i
So what is the source of truth that combines the list of posts from the network and 'when user create new post' data? Even if you don't involve paging at all, you still need to combine those two at some layer, right?
j
RepositoryImpl
Copy code
internal class CommunityFeedRepositoryImpl @Inject constructor(
    private val communityFeedApi: CommunityFeedApi
) : CommunityFeedRepository {
    override fun getData(path: String): PagingSource<String, Tile> {
        return CommunityPagingSource(
            communityFeedApi = communityFeedApi,
            path = path
        )
    }
}
Paging Source
Copy code
internal class CommunityPagingSource(
    private val communityFeedApi: CommunityFeedApi,
    private val path: String
) : PagingSource<String, Tile>() {

    override fun getRefreshKey(state: PagingState<String, Tile>): String? = null

    override suspend fun load(params: LoadParams<String>): LoadResult<String, Tile> {
        return runCatching {
            val response = communityFeedApi.getData(path, params.key)
            if (response.isSuccessful) {
                val data = response.body() ?: throw java.lang.Exception()

                LoadResult.Page(data = data.tiles, nextKey = data.token, prevKey = null)
            } else {
                throw java.lang.Exception()
            }
        }.getOrElse {
            LoadResult.Error(it)
        }
    }
}
ViewModel
Copy code
internal class CommunityPageViewModel(
    private val injectionsProvider: InjectionsProvider,
    path: String
) : ViewModel() {

    init {
        viewModelScope.launch {
            injectionsProvider.getFeed(
                GetFeed.Parameters(
                    path = path,
                    pagingConfig = PAGING_CONFIG
                )
            )
        }
    }

    init {
        injectionsProvider.savePostRepository.newPostStatus
            .onEach {
                if (it == NewPostStatus.UPLOADED_SUCCESS) {
                    val tile = injectionsProvider.savePostRepository.newPost.value
                    if (tile != null) {
                        // injectionsProvider.communityPagingSource.addNewTile(tile)
                    }
                }
            }
    }

    private val reaction = MutableStateFlow<List<Pair<String, String>>?>(null)

    val pagedList: Flow<PagingData<Tile>> =
        injectionsProvider.getFeed.flow.cachedIn(viewModelScope)
            .combine(reaction) { pagingData, myReaction ->
                if (myReaction != null) {
                    updatePagingData(
                        pagingData = pagingData,
                        myReaction = myReaction
                    )
                } else pagingData
            }

    private fun updatePagingData(
        pagingData: PagingData<Tile>,
        myReaction: List<Pair<String, String>>
    ): PagingData<Tile> {
        return pagingData.map { tile ->
            updateTile(tile, myReaction)
        }
    }

    private fun updateTile(tile: Tile, reactionsList: List<Pair<String, String>>): Tile {
        if (tile is PostTile) {
            for (pair in reactionsList) {
                if (tile.id == pair.first) {
                    val reactions = tile.reactions?.copy(
                        myReaction = pair.second
                    )

                    return when (tile) {
                        is QuestionTile -> tile.copy(reactions = reactions)
                        is ConversationStarterContainerTile -> tile.copy(reactions = reactions)
                        is MediaTile -> tile.copy(reactions = reactions)
                        else -> tile
                    }
                }
            }
        }

        return tile
    }

    fun addReaction(
        reactionType: String,
        id: String,
        onError: () -> Unit
    ) {
        viewModelScope.launch {
            injectionsProvider.addReaction(AddReaction.Params(reactionType.toReactionType(), id))
                .collectStatus(
                    counter = ObservableLoadingCounter(),
                    onSuccess = {
                        val mutableList = reaction.value?.toMutableList() ?: mutableListOf()
                        mutableList.add(Pair(id, reactionType))
                        reaction.value = mutableList
                    },
                    onError = onError
                )
        }
    }

    private fun String.toReactionType(): ReactionType = ReactionType(replace("_", "-"))

    companion object {
        private val PAGING_CONFIG = PagingConfig(
            pageSize = 50,
            initialLoadSize = 50
        )
    }
}
Interactor
Copy code
internal class GetFeed @Inject constructor(private val communityFeedRepository: CommunityFeedRepository) :
    PagingInteractor<GetFeed.Parameters, Tile>() {
    override fun createObservable(params: Parameters): Flow<PagingData<Tile>> =
        Pager(config = params.pagingConfig) { communityFeedRepository.getData(params.path) }.flow

    data class Parameters(
        val path: String,
        override val pagingConfig: PagingConfig
    ) : PagingInteractor.Parameters<Tile>
}
So this is my setup. So should I do it in paging source?
@Ian Lake any idea how to make it ? I was thinking to restructure code a bit?
i
I think it probably would be helpful to take a step back and look at the • Paging from network guide: https://developer.android.com/topic/libraries/architecture/paging/v3-network-db • PagingWithNetwork sample: https://github.com/android/architecture-components-samples/tree/main/PagingWithNetworkSample
While they aren't based on Compose, all of the layers under Compose would be exactly the same
160 Views