Skip to main content

Mobile Swipe Actions

This note documents a recurring React Native list issue: a swipe action is visible, but pressing the action does nothing.

Symptom

  • The list row can be swiped and the action button is visible.
  • Screen-level logs inside renderLeftActions or renderRightActions appear.
  • Logs inside the action button onPress do not appear.
  • The issue can look like a permission or business-rule problem, but the press event never reaches the action button.

Root cause

SwipeableFlashList renders action buttons behind the card and moves the card with translateX.

If the translated card content still has a higher touch layer than the action area, the action can be visible while the card layer still receives the tap. In this repo, itemContent already has zIndex: 1, so action layers must be explicitly raised above it.

Fix pattern

In apps/mobile/src/components/SwipeableFlashList/index.tsx:

  • set pointerEvents="box-none" on the action wrapper and inner action-content wrapper
  • keep the action wrapper at pointerEvents="none" until the row is actually open; otherwise invisible actions can be pressed before swiping
  • give both leftActions and rightActions a higher zIndex and elevation than itemContent
  • keep the action Pressable enabled when debug logs are needed, even if no business action should run

Expected action-layer shape:

<Animated.View
pointerEvents={actionsTouchable ? 'box-none' : 'none'}
style={[styles.leftActions, leftActionStyle]}
>
<View pointerEvents="box-none" style={styles.actionContent}>
{leftActions}
</View>
</Animated.View>

Set actionsTouchable to true only when the pan gesture decides to open the row, and set it back to false on close/reset. This prevents invisible action buttons from receiving taps while still allowing visible buttons to be pressed after a swipe.

Expected styles:

leftActions: {
...StyleSheet.absoluteFill,
zIndex: 2,
elevation: 2,
},
rightActions: {
...StyleSheet.absoluteFill,
zIndex: 2,
elevation: 2,
},
itemContent: {
width: '100%',
zIndex: 1,
},

Debug checklist

  1. Log inside renderLeftActions and renderRightActions.
  2. Log inside the action Pressable onPress.
  3. If render logs appear but press logs do not, inspect the swipe action layer, not the permission logic.
  4. Confirm swipe direction:
    • leftActions opens when the row is dragged to the right.
    • rightActions opens when the row is dragged to the left.
  5. If a disabled-looking action still needs debug logging, keep the Pressable enabled and call config.onPress?.() conditionally.

Invoice example

invoiceListScreen uses this pattern to debug cancel actions. The useful logs are:

  • [InvoiceListScreen] cancel swipe render
  • [InvoiceListScreen] swipe action press
  • [InvoiceListScreen] cancel swipe press

When checking owner permissions, log both:

  • loginUserCode
  • invoiceUserCode
  • rawInvoiceUserCode

Normalize numeric user codes before comparing because API data can omit leading zeroes. For example, 12265 and 012265 must compare as the same user.