c83ebe04e301
reorder the entry-row pointer chain so long-press wins over the swipe detector, and rewire it to enter multi-select
- time
- 2026-07-25 23:40 UTC · 3 months ago
- machine
- koh2
- agent
- claude-opus-5
- session
- long press multi select
- parent
- 2a3b3c774a14
↓ peach-android-c83ebe04.tar.gzatlas at this save
files changed1 file
app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+31 −22app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+31 −22
@@ -243,9 +243,19 @@243243 if (isMultiSelect) vm.toggleSelected(entry.id)244244 else onNavigate(entryTapDestination(entry))245245 },246+ // Long-press enters multi-select (Material 3 / Android247+ // list convention) and selects the held entry. The248+ // prior behaviour opened a context menu; the menu's249+ // actions (copy password/username/TOTP, edit, trash)250+ // are still reachable through the new entry-detail251+ // surface, so no functionality is dropped — the252+ // gesture is just no longer ambushed by the253+ // swipe-copy detector, which is the bug this254+ // re-ordering also fixes (see VaultListScreen255+ // modifier chain comment near EntryRow).246256 onLongPress = {247- if (isMultiSelect) vm.toggleSelected(entry.id)248- else menuEntry = entry257+ if (!isMultiSelect) vm.enterMultiSelect()258+ vm.toggleSelected(entry.id)249259 },250260 onSwipeCopy = { vm.copyPassword(entry) },251261 // Sort changes, deletes, and new entries used to snap@@ -305,17 +315,6 @@305315 } // Box306316 }307317 308- menuEntry?.let { e ->309- ContextMenuSheet(310- entry = e,311- onCopyPassword = { vm.copyPassword(e); PeachHaptics.onCopy(view) },312- onCopyUsername = { vm.copyUsername(e); PeachHaptics.onCopy(view) },313- onCopyTotp = { vm.copyTotp(e); PeachHaptics.onCopy(view) },314- onEdit = { onNavigate(PeachNav.EntryDetail(e.id)) },315- onMoveToTrash = { vm.softDelete(e) },316- onDismiss = { menuEntry = null },317- )318- }319318 }320319 321320 @Composable@@ -636,6 +635,25 @@636635 .fillMaxWidth()637636 .clip(RoundedCornerShape(7.dp))638637 .background(rowBg)638+ // Modifier order matters here, even though it looks incidental.639+ // detectTapGestures needs to be the OUTERMOST pointer modifier so640+ // its onLongPress gets first chance to fire (~500ms with no641+ // movement past its slop). The downstream detectHorizontalDragGestures642+ // has a smaller slop and will claim a touch with even a few pixels643+ // of motion during a long-press, swallowing the gesture — that is644+ // why long-press on a real device "did nothing" with the prior645+ // arrangement, where the swipe detector was outermost. Putting646+ // tap detection first lets it decide intent before movement647+ // arbitration kicks in.648+ .pointerInput(entry.id, multiSelect) {649+ detectTapGestures(650+ onLongPress = {651+ PeachHaptics.onLongPress(context)652+ onLongPress()653+ },654+ )655+ }656+ .clickable(interactionSource = interaction, indication = null, onClick = onTap)639657 .pointerInput(entry.id, canSwipeCopy) {640658 if (!canSwipeCopy) return@pointerInput641659 var dx = 0f@@ -653,15 +671,6 @@653671 onHorizontalDrag = { change, amount -> dx += amount; dy += change.positionChange().y },654672 )655673 }656- .pointerInput(entry.id, multiSelect) {657- detectTapGestures(658- onLongPress = {659- PeachHaptics.onLongPress(context)660- onLongPress()661- },662- )663- }664- .clickable(interactionSource = interaction, indication = null, onClick = onTap)665674 .padding(horizontal = 16.dp, vertical = 7.dp),666675 horizontalArrangement = Arrangement.spacedBy(10.dp),667676 verticalAlignment = Alignment.CenterVertically,