package eu.kanade.data import androidx.paging.PagingSource import androidx.paging.PagingState import com.squareup.sqldelight.Query import eu.kanade.tachiyomi.Database import kotlin.properties.Delegates class QueryPagingSource( val handler: DatabaseHandler, val countQuery: Database.() -> Query, val queryProvider: Database.(Long, Long) -> Query, ) : PagingSource(), Query.Listener { override val jumpingSupported: Boolean = true private var currentQuery: Query? by Delegates.observable(null) { _, old, new -> old?.removeListener(this) new?.addListener(this) } init { registerInvalidatedCallback { currentQuery?.removeListener(this) currentQuery = null } } override suspend fun load(params: LoadParams): LoadResult { try { val key = params.key ?: 0L val loadSize = params.loadSize val count = handler.awaitOne { countQuery() } val (offset, limit) = when (params) { is LoadParams.Prepend -> key - loadSize to loadSize.toLong() else -> key to loadSize.toLong() } val data = handler.awaitList { queryProvider(limit, offset) .also { currentQuery = it } } val (prevKey, nextKey) = when (params) { is LoadParams.Append -> { offset - loadSize to offset + loadSize } else -> { offset to offset + loadSize } } return LoadResult.Page( data = data, prevKey = if (offset <= 0L || prevKey < 0L) null else prevKey, nextKey = if (offset + loadSize >= count) null else nextKey, itemsBefore = maxOf(0L, offset).toInt(), itemsAfter = maxOf(0L, count - (offset + loadSize)).toInt(), ) } catch (e: Exception) { return LoadResult.Error(throwable = e) } } override fun getRefreshKey(state: PagingState): Long? { return state.anchorPosition?.let { anchorPosition -> val anchorPage = state.closestPageToPosition(anchorPosition) anchorPage?.prevKey ?: anchorPage?.nextKey } } override fun queryResultsChanged() { invalidate() } }