compose-lobsters/android/src/main/kotlin/dev/msfjarvis/claw/android/paging/LobstersPagingSource.kt

86 lines
3.0 KiB
Kotlin

/*
* Copyright © 2021-2024 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.android.paging
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.slack.eithernet.ApiResult.Failure
import com.slack.eithernet.ApiResult.Success
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dev.msfjarvis.claw.android.viewmodel.ReadPostsRepository
import dev.msfjarvis.claw.android.viewmodel.SavedPostsRepository
import dev.msfjarvis.claw.core.injection.IODispatcher
import dev.msfjarvis.claw.database.local.SavedPost
import dev.msfjarvis.claw.model.LobstersPost
import dev.msfjarvis.claw.model.UIPost
import dev.msfjarvis.claw.model.toUIPost
import java.io.IOException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
class LobstersPagingSource
@AssistedInject
constructor(
@Assisted private val remoteFetcher: RemoteFetcher<LobstersPost>,
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
private val savedPostsRepository: SavedPostsRepository,
private val readPostsRepository: ReadPostsRepository,
) : PagingSource<Int, UIPost>() {
private lateinit var savedPosts: List<String>
private lateinit var readPosts: List<String>
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, UIPost> {
if (!::savedPosts.isInitialized) {
savedPosts = savedPostsRepository.savedPosts.first().map(SavedPost::shortId)
}
if (!::readPosts.isInitialized) {
readPosts = readPostsRepository.readPosts.first()
}
val page = params.key ?: STARTING_PAGE_INDEX
return when (val result = withContext(ioDispatcher) { remoteFetcher.getItemsAtPage(page) }) {
is Success ->
LoadResult.Page(
itemsBefore = (page - 1) * PAGE_SIZE,
data =
result.value.map {
it
.toUIPost()
.copy(
isSaved = savedPosts.contains(it.shortId),
isRead = readPosts.contains(it.shortId),
)
},
prevKey = if (page == STARTING_PAGE_INDEX) null else page - 1,
nextKey = page + 1,
)
is Failure.NetworkFailure -> LoadResult.Error(result.error)
is Failure.UnknownFailure -> LoadResult.Error(result.error)
is Failure.HttpFailure,
is Failure.ApiFailure -> LoadResult.Error(IOException("API returned an invalid response"))
}
}
override fun getRefreshKey(state: PagingState<Int, UIPost>): Int? {
return state.anchorPosition?.let { anchorPosition ->
(anchorPosition / PAGE_SIZE).coerceAtLeast(STARTING_PAGE_INDEX)
}
}
@AssistedFactory
interface Factory {
fun create(remoteFetcher: RemoteFetcher<LobstersPost>): LobstersPagingSource
}
companion object {
const val PAGE_SIZE = 25
const val STARTING_PAGE_INDEX = 1
}
}