Skip to content

Instantly share code, notes, and snippets.

@electrolobzik
Created February 27, 2024 17:23
Show Gist options
  • Save electrolobzik/a52637aae091acc23d88a1bdf6a4e885 to your computer and use it in GitHub Desktop.
Save electrolobzik/a52637aae091acc23d88a1bdf6a4e885 to your computer and use it in GitHub Desktop.
class ReceivedMediaRepository(
private val receivedMediaApiHelper: ReceivedMediaApiHelper,
private val receivedMediaDbHelper: ReceivedMediaDbHelper,
coroutineContext: CoroutineContext,
) : CoroutineScope by CoroutineScope(coroutineContext) {
private val coroutineScope = CoroutineScope(coroutineContext)
private val factory = ReceivedMediaStoreFactory(api = receivedMediaApiHelper, db = receivedMediaDbHelper)
private val mutableStore = factory.create()
private val paginator = Paginator<ReceivedMedia, PaginationPosition>(coroutineContext = coroutineContext)
private val pager = Pager.create(coroutineScope, mutableStore, ReceivedMediaJoiner(), ReceivedMediaKeyFactory())
val state = paginator.state.map { it.toExternalState() }
private val errorsFlow = MutableSharedFlow<LoadingError>()
val loadingErrors: Flow<LoadingError> = errorsFlow
init {
launch {
paginator.sideEffects.collect { sideEffect ->
when (sideEffect) {
is Paginator.SideEffect.NeedToPerformRefresh,
is Paginator.SideEffect.NeedToPerformInitialLoad -> getReceivedMediaInitial().collect { storeResponse ->
when (storeResponse) {
StoreReadResponse.Initial,
is StoreReadResponse.Loading -> {
// doing nothing
}
is StoreReadResponse.Data -> {
val data = storeResponse.value
when (data) {
is ReceivedMediaStoreData.Collection -> {
paginator.sendAction(
Paginator.Action.InitialDataLoaded(
lastPage = data.getPaginationPosition(),
items = data.items.asMediaList()
)
)
}
is ReceivedMediaStoreData.Single -> throw IllegalStateException("Initial data can't be single")
}
}
is StoreReadResponse.Error -> {
val loadingError = storeResponse.toLoadingError("Initial load for ReceivedMediaRepository")
errorsFlow.emit(loadingError)
paginator.sendAction(Paginator.Action.InitialDataLoadingError(loadingError))
}
is StoreReadResponse.NoNewData -> TODO()
}
}
is Paginator.SideEffect.NeedToLoadNewPage -> {
val newKey = ReceivedMediaStoreKey.Page(cursor = sideEffect.lastPage.nextPageKey, size = MIN_PAGE_SIZE)
val storeResponse = pager.load(newKey)
when (storeResponse) {
StoreReadResponse.Initial,
is StoreReadResponse.Loading -> {
// doing nothing
}
is StoreReadResponse.Data -> {
val data = storeResponse.value
when (data) {
is ReceivedMediaStoreData.Collection -> {
paginator.sendAction(
Paginator.Action.NewPageLoaded(
lastPage = data.getPaginationPosition(),
items = data.items.asMediaList()
)
)
}
is ReceivedMediaStoreData.Single -> throw IllegalStateException("Page data can't be single")
}
}
is StoreReadResponse.Error -> {
val loadingError = storeResponse.toLoadingError("New page load for ReceivedMediaRepository")
paginator.sendAction(Paginator.Action.NewPageLoadingError(loadingError))
}
is StoreReadResponse.NoNewData -> TODO()
}
}
is Paginator.SideEffect.NewPageLoadErrorEvent -> errorsFlow.emit(sideEffect.error)
}
}
}
}
fun loadInitially() {
paginator.sendAction(Paginator.Action.InitialLoad())
}
fun getNextPage() {
paginator.sendAction(Paginator.Action.LoadMore())
}
fun refresh() {
paginator.sendAction(Paginator.Action.Refresh())
}
private fun getReceivedMediaInitial(): Flow<StoreReadResponse<ReceivedMediaStoreData>> {
return mutableStore.stream<Any>(request = StoreReadRequest.cached(ReceivedMediaStoreKey.Heap(), refresh = true))
}
data class PaginationPosition(
val lastLoadedMediaId: MediaId?,
val nextPageKey: ReceivedMediaPageKey?
)
fun ReceivedMediaStoreData.Collection.getPaginationPosition() = PaginationPosition(
lastLoadedMediaId = singles.lastOrNull()?.data?.receivedMediaId,
nextPageKey = lastPageKey
)
fun StoreReadResponse<*>.toLoadingError(actionDescription: String): LoadingError{
return if (this is StoreReadResponse.Error.Custom<*> && this.error is DataResult.ConnectionIssue<*>) {
LoadingError.NoConnection(actionDescription)
} else {
LoadingError.UnknownError(e = (this as StoreReadResponse.Error.Exception).error, actionDescription = actionDescription)
}
}
companion object {
const val MIN_PAGE_SIZE = 50
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment