a4ab61925a3e
fix settings card collapse gesture and vault list peach mark - C2's drag-to-collapse never fired because the focused card's own verticalScroll consumed all drag touches before the outer raw detectDragGestures on the card container ever saw them; replaced with a NestedScrollConnection on the scrollable body that watches for leftover downward drag once scrollState is already at 0, accumulates past a 60dp threshold, then hands off to the deck's collapse callback - same physical metaphor as a bottom sheet; also added trailing bottom spacer inside each card's scrollable body so bottom-of-card content (Pro card's CTA button) doesn't sit flush against the fanned tab stacked beneath it; and added the missing peach brand mark icon (PeachMark) next to the vault list's top bar wordmark, matching the browser header - live-verified card focus/collapse cycle and the peach icon on-device
- time
- 2026-07-24 12:19 UTC · 3 months ago
- machine
- koh2
- agent
- claude-sonnet-5
- session
- critical-corrections-c1-c7
- parent
- 560cbfdb55f0
↓ peach-android-a4ab6192.tar.gzatlas at this save
files changed2 files
app/src/main/java/com/peachpasswords/ui/settings/SettingsScreen.kt+39 −1app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+4 −0app/src/main/java/com/peachpasswords/ui/settings/SettingsScreen.kt+39 −1
@@ -30,6 +30,9 @@3030 import androidx.compose.foundation.shape.RoundedCornerShape3131 import androidx.compose.foundation.rememberScrollState3232 import androidx.compose.foundation.verticalScroll33+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection34+import androidx.compose.ui.input.nestedscroll.NestedScrollSource35+import androidx.compose.ui.input.nestedscroll.nestedScroll3336 import androidx.compose.runtime.Composable3437 import androidx.compose.runtime.LaunchedEffect3538 import androidx.compose.runtime.getValue@@ -323,6 +326,7 @@323326 deckOpen = false324327 }325328 },329+ onCollapseRequest = { deckOpen = true },326330 enabled = !concealed,327331 )328332 }@@ -347,6 +351,7 @@347351 body: @Composable () -> Unit,348352 modifier: Modifier,349353 onClick: () -> Unit,354+ onCollapseRequest: () -> Unit,350355 enabled: Boolean,351356 ) {352357 val interaction = remember { MutableInteractionSource() }@@ -406,12 +411,45 @@406411 )407412 // Body scrolls independently; only revealed at full height in408413 // focus mode, but kept mounted so deck-mode shows a preview.414+ // Collapse-by-scroll: once the body is already at its scroll415+ // top, a further downward drag has nowhere to go inside the416+ // scrollable — hand that leftover delta to the deck instead417+ // of letting it disappear as a no-op overscroll.418+ val scrollState = rememberScrollState()419+ val density = LocalDensity.current420+ val collapseThresholdPx = with(density) { DRAG_THRESHOLD.toPx() }421+ val nestedScrollConnection = remember(scrollState, focused) {422+ object : NestedScrollConnection {423+ var collapseAccum = 0f424+ override fun onPostScroll(425+ consumed: Offset,426+ available: Offset,427+ source: NestedScrollSource,428+ ): Offset {429+ if (!focused || source != NestedScrollSource.Drag || scrollState.value != 0 || available.y <= 0f) {430+ collapseAccum = 0f431+ return Offset.Zero432+ }433+ collapseAccum += available.y434+ if (collapseAccum >= collapseThresholdPx) {435+ collapseAccum = 0f436+ onCollapseRequest()437+ }438+ return Offset(0f, available.y)439+ }440+ }441+ }409442 Column(410443 Modifier.fillMaxWidth().weight(1f)411444 .padding(top = 14.dp)412- .verticalScroll(rememberScrollState()),445+ .nestedScroll(nestedScrollConnection)446+ .verticalScroll(scrollState),413447 ) {414448 body()449+ // Trailing breathing room so bottom-of-card content (e.g. the450+ // Pro card's CTA button) never sits flush against the fanned451+ // tab stacked directly beneath it.452+ Spacer(Modifier.height(PeachSpacing.xl))415453 }416454 }417455 } else {app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+4 −0
@@ -97,6 +97,7 @@9797 import com.peachpasswords.ui.PeachNav9898 import com.peachpasswords.ui.components.PeachEmptyOrEndState9999 import com.peachpasswords.ui.components.PeachFilterPills100+import com.peachpasswords.ui.components.PeachMark100101 import com.peachpasswords.ui.components.PeachSegment101102 import com.peachpasswords.ui.components.PeachTopBar102103 import com.peachpasswords.ui.components.FaviconInitials@@ -384,6 +385,9 @@384385 modifier = Modifier385386 .statusBarsPadding()386387 .padding(horizontal = PeachSpacing.md, vertical = PeachSpacing.xs),388+ leading = {389+ PeachMark(modifier = Modifier.padding(end = PeachSpacing.xs))390+ },387391 actions = {388392 Box(Modifier.size(6.dp).clip(CircleShape).background(syncDotColor))389393 TopBarIcon(