From 7af87d63d94efe85af3d669301b04488aea28576 Mon Sep 17 00:00:00 2001 From: austinried <4966622+austinried@users.noreply.github.com> Date: Sat, 26 Sep 2026 06:59:12 +0000 Subject: [PATCH] extract the artist grid scroll estimate and test it The index to pixel arithmetic was inline and fiddly (it was missing the row gap), so pull it out as estimateScrollPx and pin the 2-column cases. ROW_GAP is shared with the grid spacing. --- .../ui/library/ArtistDetailScreen.kt | 33 ++++++++++++++----- .../ui/library/EstimateScrollPxTest.kt | 30 +++++++++++++++++ 2 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 app/src/test/kotlin/com/subtracks/ui/library/EstimateScrollPxTest.kt diff --git a/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailScreen.kt b/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailScreen.kt index f1aeaa03..abb067d2 100644 --- a/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailScreen.kt +++ b/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailScreen.kt @@ -82,6 +82,21 @@ import org.koin.core.parameter.parametersOf private val ART_HEIGHT = 420.dp private val TITLE_INSET = 16.dp +private val ROW_GAP = 16.dp + +internal fun estimateScrollPx( + index: Int, + offset: Float, + imageHeightPx: Float, + albumHeightPx: Float, + albumRowGapPx: Float, +): Float = + if (index <= 0) { + offset + } else { + imageHeightPx + albumRowGapPx + (albumHeightPx + albumRowGapPx) * ((index - 1) / 2) + offset + } + private val FADE_LEAD = 24.dp private const val THEME_TRANSITION_MS = 100 private const val SCRIM_FADE_MS = 180 @@ -164,16 +179,16 @@ fun ArtistDetailScreen( } var imageHeightPx by remember { mutableFloatStateOf(0f) } var albumHeightPx by remember { mutableFloatStateOf(0f) } - val albumRowGapPx = with(density) { 16.dp.toPx() } + val albumRowGapPx = with(density) { ROW_GAP.toPx() } val scrollPx by remember { derivedStateOf { - val index = listState.firstVisibleItemIndex - val offset = listState.firstVisibleItemScrollOffset.toFloat() - if (index <= 0) { - offset - } else { - imageHeightPx + albumRowGapPx + (albumHeightPx + albumRowGapPx) * ((index - 1) / 2) + offset - } + estimateScrollPx( + index = listState.firstVisibleItemIndex, + offset = listState.firstVisibleItemScrollOffset.toFloat(), + imageHeightPx = imageHeightPx, + albumHeightPx = albumHeightPx, + albumRowGapPx = albumRowGapPx, + ) } } val nameTextStyle = MaterialTheme.typography.headlineLarge @@ -207,7 +222,7 @@ fun ArtistDetailScreen( columns = GridCells.Fixed(2), contentPadding = PaddingValues(bottom = 16.dp + navBarBottom), horizontalArrangement = Arrangement.spacedBy(12.dp), - verticalArrangement = Arrangement.spacedBy(16.dp), + verticalArrangement = Arrangement.spacedBy(ROW_GAP), modifier = Modifier.fillMaxSize(), ) { item(span = { GridItemSpan(maxLineSpan) }) { diff --git a/app/src/test/kotlin/com/subtracks/ui/library/EstimateScrollPxTest.kt b/app/src/test/kotlin/com/subtracks/ui/library/EstimateScrollPxTest.kt new file mode 100644 index 00000000..d4175049 --- /dev/null +++ b/app/src/test/kotlin/com/subtracks/ui/library/EstimateScrollPxTest.kt @@ -0,0 +1,30 @@ +package com.subtracks.ui.library + +import org.junit.Assert.assertEquals +import org.junit.Test + +class EstimateScrollPxTest { + private val image = 432f + private val album = 200f + private val gap = 16f + + @Test + fun theHeroUsesTheRawOffset() { + assertEquals(50f, estimateScrollPx(0, 50f, image, album, gap), 0.01f) + } + + @Test + fun theFirstAlbumRowAddsTheHeroAndOneGap() { + assertEquals(image + gap + 10f, estimateScrollPx(1, 10f, image, album, gap), 0.01f) + } + + @Test + fun theSecondCellOfARowDoesNotAdvance() { + assertEquals(estimateScrollPx(1, 10f, image, album, gap), estimateScrollPx(2, 10f, image, album, gap), 0.01f) + } + + @Test + fun theNextRowAddsACellAndAGap() { + assertEquals(image + gap + (album + gap) + 10f, estimateScrollPx(3, 10f, image, album, gap), 0.01f) + } +}