Last active
January 9, 2024 10:28
-
-
Save tadfisher/da98590a9f8dfba0df47780011d8b5e0 to your computer and use it in GitHub Desktop.
Basic paging
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
data class Page<T>( | |
val items: List<T>, | |
val pageSize: Int | |
} | |
abstract class PagedDataState<T> { | |
val loadStates by mutableStateOf(PageLoadStates()) | |
val items = mutableStateListOf<T>() | |
protected abstract suspend fun refreshItems(): Page<T> | |
protected abstract suspend fun prependItems(before: T): Page<T> | |
protected abstract suspend fun appendItems(after: T): Page<T> | |
suspend fun refresh() { | |
if (loadStates.refresh == PageLoadState.Loading) return | |
loadStates = PageLoadStates(refresh = PageLoadState.Loading) | |
runCatching { | |
refreshItems() | |
}.fold( | |
onSuccess = { page -> | |
Snapshot.withMutableSnapshot { | |
items.clear() | |
items.addAll(page.items) | |
val loadState = if (page.items.size < page.pageSize) Complete else Incomplete | |
loadStates = loadStates.copy( | |
refresh = loadState, | |
prepend = Complete, | |
append = loadState | |
) | |
} | |
}, | |
onFailure = { exception -> | |
loadStates = loadStates.copy(refresh = PageLoadState.Error(exception)) | |
emptyList() | |
} | |
) | |
} | |
suspend fun prepend() { | |
if (loadStates.refresh == PageLoadState.Loading) return emptyList() | |
if (loadStates.prepend == PageLoadState.Loading) return emptyList() | |
if (items.isEmpty()) { | |
refresh() | |
return | |
} | |
loadStates = loadStates.copy(prepend = PageLoadState.Loading) | |
runCatching { | |
prependItems(before = items.first()) | |
}.fold( | |
onSuccess = { page -> | |
Snapshot.withMutableSnapshot { | |
items.addAll(0, page.items) | |
loadStates = loadStates.copy( | |
prepend = if (page.items.size < page.pageSize) Complete else Incomplete, | |
) | |
} | |
}, | |
onFailure = { exception -> | |
loadStates = loadStates.copy(prepend = PageLoadState.Error(exception)) | |
emptyList() | |
} | |
) | |
} | |
suspend fun append() { | |
if (loadStates.refresh == PageLoadState.Loading) return emptyList() | |
if (loadStates.append == PageLoadState.Loading) return emptyList() | |
if (items.isEmpty()) { | |
refresh() | |
return | |
} | |
loadStates = loadStates.copy(append = PageLoadState.Loading) | |
runCatching { | |
appendItems(after = items.last()) | |
}.fold( | |
onSuccess = { page -> | |
Snapshot.withMutableSnapshot { | |
items.addAll(page.items) | |
loadStates = loadStates.copy( | |
append = if (page.items.size < page.pageSize) Complete else Incomplete, | |
) | |
} | |
}, | |
onFailure = { exception -> | |
loadStates = loadStates.copy(append = PageLoadState.Error(exception)) | |
emptyList() | |
} | |
) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sealed class PageLoadState(val endOfDataReached: Boolean) { | |
data class Error(val error: Throwable) : PageLoadState(false) { | |
override fun toString(): String = "Error(error=$error" | |
} | |
sealed class NotLoading(endOfDataReached: Boolean) : PageLoadState(endOfDataReached) { | |
object Complete : NotLoading(true) { | |
override fun toString(): String = "Complete" | |
} | |
object Incomplete : NotLoading(false) { | |
override fun toString(): String = "Incomplete" | |
} | |
companion object { | |
operator fun invoke(endOfDataReached: Boolean): NotLoading = | |
if (endOfDataReached) Complete else Incomplete | |
} | |
} | |
object Loading : PageLoadState(false) { | |
override fun toString(): String = "Loading" | |
} | |
} | |
@Immutable | |
data class PageLoadStates( | |
val refresh: PageLoadState = PageLoadState.NotLoading.Incomplete, | |
val prepend: PageLoadState = PageLoadState.NotLoading.Incomplete, | |
val append: PageLoadState = PageLoadState.NotLoading.Incomplete | |
) { | |
val isLoading: Boolean get() = | |
refresh == PageLoadState.Loading || | |
prepend == PageLoadState.Loading || | |
append == PageLoadState.Loading | |
val error: Throwable? get() = | |
(refresh as? PageLoadState.Error)?.error | |
?: (append as? PageLoadState.Error)?.error | |
?: (prepend as? PageLoadState.Error)?.error | |
companion object { | |
val Complete = PageLoadStates( | |
refresh = PageLoadState.NotLoading.Complete, | |
prepend = PageLoadState.NotLoading.Complete, | |
append = PageLoadState.NotLoading.Complete | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment