ref: catalog screen to remember scroll state

This commit is contained in:
tapframe 2026-06-02 20:33:51 +05:30
parent bfed3b4cd8
commit 9d2fe291e8
5 changed files with 92 additions and 9 deletions

View file

@ -11,3 +11,8 @@ data class CatalogUiState(
val canLoadMore: Boolean
get() = nextSkip != null
}
data class CatalogScrollPosition(
val firstVisibleItemIndex: Int = 0,
val firstVisibleItemScrollOffset: Int = 0,
)

View file

@ -25,6 +25,7 @@ object CatalogRepository {
private var activeJob: Job? = null
private var activeRequest: CatalogRequest? = null
private val scrollPositions = linkedMapOf<CatalogRequest, CatalogScrollPosition>()
fun load(
manifestUrl: String,
@ -34,7 +35,7 @@ object CatalogRepository {
supportsPagination: Boolean = false,
force: Boolean = false,
) {
val request = CatalogRequest(
val request = catalogRequest(
manifestUrl = manifestUrl,
type = type,
catalogId = catalogId,
@ -62,9 +63,36 @@ object CatalogRepository {
fun clear() {
activeJob?.cancel()
activeRequest = null
scrollPositions.clear()
_uiState.value = CatalogUiState()
}
fun scrollPosition(
manifestUrl: String,
type: String,
catalogId: String,
genre: String? = null,
supportsPagination: Boolean = false,
): CatalogScrollPosition =
scrollPositions[catalogRequest(manifestUrl, type, catalogId, genre, supportsPagination)]
?: CatalogScrollPosition()
fun saveScrollPosition(
manifestUrl: String,
type: String,
catalogId: String,
genre: String? = null,
supportsPagination: Boolean = false,
firstVisibleItemIndex: Int,
firstVisibleItemScrollOffset: Int,
) {
val request = catalogRequest(manifestUrl, type, catalogId, genre, supportsPagination)
scrollPositions[request] = CatalogScrollPosition(
firstVisibleItemIndex = firstVisibleItemIndex,
firstVisibleItemScrollOffset = firstVisibleItemScrollOffset,
)
}
private fun fetchInternalLibrary(request: CatalogRequest) {
activeJob?.cancel()
_uiState.value = _uiState.value.copy(
@ -127,7 +155,7 @@ object CatalogRepository {
catalogId = request.catalogId,
genre = request.genre,
skip = requestedSkip.takeIf { it > 0 },
).withUnreleasedFilter()
).withUnreleasedFilter(request.hideUnreleasedContent)
}.fold(
onSuccess = { page ->
if (activeRequest != request) return@fold
@ -159,10 +187,26 @@ object CatalogRepository {
)
}
}
private fun catalogRequest(
manifestUrl: String,
type: String,
catalogId: String,
genre: String? = null,
supportsPagination: Boolean = false,
): CatalogRequest =
CatalogRequest(
manifestUrl = manifestUrl,
type = type,
catalogId = catalogId,
genre = genre,
supportsPagination = supportsPagination,
hideUnreleasedContent = HomeCatalogSettingsRepository.snapshot().hideUnreleasedContent,
)
}
private fun CatalogPage.withUnreleasedFilter(): CatalogPage {
if (!HomeCatalogSettingsRepository.snapshot().hideUnreleasedContent) return this
private fun CatalogPage.withUnreleasedFilter(hideUnreleasedContent: Boolean): CatalogPage {
if (!hideUnreleasedContent) return this
val filteredItems = items.filterReleasedItems(CurrentDateProvider.todayIsoDate())
return if (filteredItems.size == items.size) this else copy(items = filteredItems)
}
@ -173,4 +217,5 @@ private data class CatalogRequest(
val catalogId: String,
val genre: String?,
val supportsPagination: Boolean,
val hideUnreleasedContent: Boolean,
)

View file

@ -86,7 +86,26 @@ fun CatalogScreen(
WatchedRepository.ensureLoaded()
WatchedRepository.uiState
}.collectAsStateWithLifecycle()
val gridState = rememberLazyGridState()
val initialScrollPosition = remember(
manifestUrl,
type,
catalogId,
genre,
supportsPagination,
homeCatalogSettingsUiState.hideUnreleasedContent,
) {
CatalogRepository.scrollPosition(
manifestUrl = manifestUrl,
type = type,
catalogId = catalogId,
genre = genre,
supportsPagination = supportsPagination,
)
}
val gridState = rememberLazyGridState(
initialFirstVisibleItemIndex = initialScrollPosition.firstVisibleItemIndex,
initialFirstVisibleItemScrollOffset = initialScrollPosition.firstVisibleItemScrollOffset,
)
var headerHeightPx by remember { mutableIntStateOf(0) }
var observedOfflineState by remember { mutableStateOf(false) }
@ -97,10 +116,25 @@ fun CatalogScreen(
catalogId = catalogId,
genre = genre,
supportsPagination = supportsPagination,
force = true,
)
}
LaunchedEffect(gridState, manifestUrl, type, catalogId, genre, supportsPagination, homeCatalogSettingsUiState.hideUnreleasedContent) {
snapshotFlow { gridState.firstVisibleItemIndex to gridState.firstVisibleItemScrollOffset }
.distinctUntilChanged()
.collect { (index, offset) ->
CatalogRepository.saveScrollPosition(
manifestUrl = manifestUrl,
type = type,
catalogId = catalogId,
genre = genre,
supportsPagination = supportsPagination,
firstVisibleItemIndex = index,
firstVisibleItemScrollOffset = offset,
)
}
}
LaunchedEffect(gridState, uiState.canLoadMore, uiState.isLoading) {
snapshotFlow { gridState.layoutInfo }
.map { layoutInfo ->

View file

@ -96,7 +96,6 @@ internal actual fun CollectionCardRemoteImage(
UIKitView(
modifier = modifier,
interactive = false,
factory = {
UIImageView().apply {
contentMode = UIViewContentMode.UIViewContentModeScaleAspectFill

View file

@ -9,10 +9,10 @@ androidx-appcompat = "1.7.1"
androidx-core = "1.17.0"
androidx-core-splashscreen = "1.0.1"
androidx-espresso = "3.7.0"
androidx-lifecycle = "2.11.0-alpha03"
androidx-lifecycle = "2.11.0-beta01"
androidx-work = "2.10.3"
androidx-testExt = "1.3.0"
composeMultiplatform = "1.11.0-beta03"
composeMultiplatform = "1.11.1"
coil = "3.5.0-beta01"
kermit = "2.0.5"
junit = "4.13.2"