Skip to content

Instantly share code, notes, and snippets.

@tadfisher
Last active January 9, 2024 10:28
Show Gist options
  • Save tadfisher/da98590a9f8dfba0df47780011d8b5e0 to your computer and use it in GitHub Desktop.
Save tadfisher/da98590a9f8dfba0df47780011d8b5e0 to your computer and use it in GitHub Desktop.
Basic paging
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()
           }
       )
   }
}
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