Skip to content

Bug: Floating '+' button disappears after failed receipt scan on iOS #10

@alex-pythonista

Description

@alex-pythonista

Description

The floating "+" button disappears from the main screen after dismissing the /new modal (for any form type). The button only reappears when switching to a different tab and back.

Affected Flows

This bug affects ALL four form flows, not just the receipt flow:

Form Error Handling Severity
Receipt console.error only - no user feedback High
Statement console.error only - no user feedback High
Transaction (Form) Shows Alert.alert but button still disappears Medium
Text Shows Alert.alert but button still disappears Medium

Steps to Reproduce

Receipt Flow (High Severity)

  1. Tap the floating "+" button
  2. Select "Receipt" option
  3. Choose an image from library
  4. Tap "Extract"
  5. Wait for extraction to fail
  6. Dismiss the modal (swipe down)
  7. Observe: "+" button is missing, no error feedback shown

Statement Flow (High Severity)

  1. Tap the floating "+" button
  2. Select "Statement" option
  3. Choose a CSV file
  4. Tap "Extract"
  5. Wait for extraction to fail
  6. Dismiss the modal (swipe down)
  7. Observe: "+" button is missing, no error feedback shown

Form Flow (Medium Severity)

  1. Tap the floating "+" button
  2. Select "Form" option
  3. Fill in form fields
  4. Tap "Save"
  5. If API error occurs, alert is shown
  6. Dismiss alert and then dismiss modal
  7. Observe: "+" button is missing

Text Flow (Medium Severity)

  1. Tap the floating "+" button
  2. Select "Text" option
  3. Enter some text
  4. Tap "Extract"
  5. If error occurs, alert is shown
  6. Dismiss alert and then dismiss modal
  7. Observe: "+" button is missing

Alternative Reproduction (Any Flow)

  1. Tap the floating "+" button
  2. Select any option
  3. Immediately dismiss the modal (swipe down without doing anything)
  4. Observe: "+" button is missing

Environment

  • Device: iPhone 16e Simulator
  • Platform: iOS

Root Cause Analysis

The issue is caused by the overlay state not being reset when the modal is dismissed.

Code Flow

  1. User taps "+" buttonuseOverlayStore.show() is called → isVisible: true
  2. User taps any optionrouter.push("/new?type=...") AND hide() is called (add-button.tsx:27-29)
  3. Modal opens → pathname changes to /new
  4. User encounters error or dismisses modal
  5. User manually dismisses modal (swipe down gesture on iOS)
  6. User returns to "/" but the AddButton component has stale animation state

Why Tab Switching Fixes It

Switching tabs causes a full navigation state change, forcing React to properly re-render the entire protected layout and remount components with fresh state.

Relevant Files

File Issue
app/(protected)/_layout.tsx:28 Determines button visibility but doesn't reset overlay state on route change
app/(protected)/_layout.tsx:82 Renders {showAddButton && <AddButton />}
components/add-button.tsx:43-54 Animation tied to useOverlayStore.isVisible
components/receipt-form.tsx:81-83 Error handler only logs, no alert
components/statement-form.tsx:62-64 Error handler only logs, no alert
components/transaction-form.tsx:101-104 Has alert but button still disappears
components/text-transaction-input.tsx:37-43 Has alert but button still disappears
store/overlayStore.ts Zustand store controlling overlay visibility

Suggested Fix

Primary Fix (Resolves button disappearing for all flows)

Add a useEffect in app/(protected)/_layout.tsx to reset the overlay state when the pathname changes back to a main route:

// Reset overlay state when returning to main routes (e.g., after modal dismiss)
useEffect(() => {
  if (ADD_BUTTON_ROUTES.some((route) => pathname === route)) {
    hide();
  }
}, [pathname, hide]);

Secondary Fix (Improve error UX for Receipt and Statement forms)

Add proper error alerts in receipt-form.tsx and statement-form.tsx:

// receipt-form.tsx lines 81-83
onError: (error) => {
  Alert.alert("Extraction Failed", error.message || "Could not extract transactions from the receipt. Please try again.");
}

// statement-form.tsx lines 62-64
onError: (error) => {
  Alert.alert("Upload Failed", error.message || "Could not process the statement. Please try again.");
}

Labels

bug, iOS, navigation, UI

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions