Skip to content

[jules] ux: Swipe-to-delete for expenses with undo option#314

Open
Devasy wants to merge 2 commits intomainfrom
jules/ux-mobile-swipe-to-delete-4411574858630447702
Open

[jules] ux: Swipe-to-delete for expenses with undo option#314
Devasy wants to merge 2 commits intomainfrom
jules/ux-mobile-swipe-to-delete-4411574858630447702

Conversation

@Devasy
Copy link
Copy Markdown
Owner

@Devasy Devasy commented Apr 5, 2026

Implemented a complete swipe-to-delete system for expenses on the mobile Group Details screen, addressing the requested task in todo.md.

Changes Made:

  1. API Client: Added deleteExpense(groupId, expenseId) to mobile/api/groups.js based on the backend Fast API route (/groups/{group_id}/expenses/{expense_id}).
  2. UI Component: Modified mobile/screens/GroupDetailsScreen.js to wrap the existing expense cards with Swipeable from react-native-gesture-handler, allowing a right-to-left swipe to reveal a "Delete" action.
  3. Optimistic Undo System: When a user deletes an item, it is immediately hidden from the UI (added to hiddenExpenses state), and a Snackbar appears offering an "Undo" button.
  4. Robust Timeout Management: Utilized a useRef dictionary (deleteTimeouts) to track active 5-second countdowns for each deleted item. This ensures that rapid deletions don't cause state collisions, and tapping "Undo" cleanly clears the specific pending API calls.
  5. Babel Configuration: Added mobile/babel.config.js to explicitly register the react-native-reanimated/plugin, which is required for gesture handler animations to work smoothly.
  6. Tracking: Updated .Jules/todo.md and .Jules/changelog.md.

PR created automatically by Jules for task 4411574858630447702 started by @Devasy23

Summary by CodeRabbit

  • New Features
    • Added swipe-to-delete for expenses you paid in Group Details: swipe to reveal Delete, expense removed immediately with a 5-second Undo via a Snackbar.
    • Undo restores the expense; deletions finalize after the timeout. Interaction handling improved to keep undo/repeat actions reliable.

Implemented swipe-to-delete functionality for expenses on the Group Details screen in the mobile app.
- Created an optimistic deletion flow with a 5-second undo Snackbar using `react-native-paper`.
- Wrapped expense cards with `Swipeable` from `react-native-gesture-handler`.
- Added the `deleteExpense` function to the API client (`mobile/api/groups.js`).
- Created `mobile/babel.config.js` to support the required `react-native-reanimated` plugin.
- Added necessary ref-based timeout management to prevent state mismatch during rapid interactions.
- Updated `.Jules/todo.md` and `.Jules/changelog.md` to reflect the completed task.

Co-authored-by: Devasy23 <110348311+Devasy23@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@Devasy Devasy requested a review from vrajpatelll as a code owner April 5, 2026 20:11
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 5, 2026

Deploy Preview for split-but-wiser canceled.

Name Link
🔨 Latest commit 9198ac0
🔍 Latest deploy log https://app.netlify.com/projects/split-but-wiser/deploys/69d2c36d9daf7d00086ab468

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 5, 2026

Walkthrough

Adds swipe-to-delete for user-paid expenses on the Group Details mobile screen with optimistic removal, a 5-second undo Snackbar, ref-based timeout handling, and deferred DELETE API calls. Adds deleteExpense API wrapper, enables Reanimated Babel plugin, updates package deps, and documents the change.

Changes

Cohort / File(s) Summary
Swipe-to-Delete Implementation
mobile/screens/GroupDetailsScreen.js
Wraps expense items in Swipeable with a right-side Delete action for expenses paid by the current user. Performs optimistic hide via hiddenExpenses, shows a 5s Snackbar with Undo, schedules actual deleteExpense call via a ref-based timeout map, restores on undo or API failure, and adds Snackbar UI + styles.
API & Build Configuration
mobile/api/groups.js, mobile/babel.config.js, mobile/package.json
Adds deleteExpense(groupId, expenseId) API wrapper (HTTP DELETE). Adds react-native-reanimated/plugin to Babel config and adds react-native-gesture-handler and react-native-reanimated to mobile dependencies.
Docs / Task Tracking
.Jules/changelog.md, .Jules/todo.md
Documented the swipe-to-delete feature and marked the todo entry completed with updated description and metadata.

Suggested reviewers

  • vrajpatelll
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the main feature: swipe-to-delete for expenses with an undo option. It matches the core implementation described in the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@9404621). Learn more about missing BASE report.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #314   +/-   ##
=======================================
  Coverage        ?   63.55%           
=======================================
  Files           ?       21           
  Lines           ?     2456           
  Branches        ?      254           
=======================================
  Hits            ?     1561           
  Misses          ?      831           
  Partials        ?       64           
Components Coverage Δ
Authentication System 71.35% <ø> (?)
Expense Management 70.15% <ø> (?)
Group Management 73.78% <ø> (?)
User Management 97.16% <ø> (?)
Backend Core 70.78% <ø> (?)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mobile/babel.config.js`:
- Around line 1-7: Babel references 'react-native-reanimated/plugin' and
GroupDetailsScreen.js imports 'react-native-gesture-handler' but those packages
are missing from mobile/package.json; add "react-native-reanimated" and
"react-native-gesture-handler" to the mobile app dependencies (or run npx expo
install react-native-reanimated react-native-gesture-handler) and then
reinstall/lock dependencies so the Babel plugin and GroupDetailsScreen imports
resolve at build/runtime.

In `@mobile/screens/GroupDetailsScreen.js`:
- Around line 116-136: The handleDeleteSwipe flow registers timeouts in
deleteTimeouts.current but never clears them on unmount; add a useEffect cleanup
that iterates deleteTimeouts.current, calls clearTimeout for each stored
timeoutId and deletes the entries to prevent state updates after unmount. Locate
deleteTimeouts, handleDeleteSwipe, and the optimistic state setters
(setHiddenExpenses, setExpenses) and ensure the cleanup only clears the pending
timers (clearTimeout(timeoutId)) and removes them from deleteTimeouts.current to
avoid memory leaks and React warnings.
- Around line 277-284: The current global undo (handleUndoGlobal) restores all
pending deletions while the snackbar only reflects the most recent deletion;
update the UX by computing pendingCount =
Object.keys(deleteTimeouts.current).length wherever you set the snackbar and
call setSnackbarMessage(pendingCount > 1 ? `Deleted ${pendingCount} expenses` :
`Deleted "${item.description}"`), so the snackbar accurately reflects multiple
pending deletes; keep handleUndoGlobal logic that iterates
deleteTimeouts.current to clear timeouts and call setHiddenExpenses, then clear
deleteTimeouts.current and setSnackbarVisible(false) so the message and undo
behavior are consistent (use the symbols handleUndoGlobal,
deleteTimeouts.current, setHiddenExpenses, setSnackbarMessage,
setSnackbarVisible to locate code).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c582df4d-25fa-41f9-85fa-e0bd22b5a1ee

📥 Commits

Reviewing files that changed from the base of the PR and between 9404621 and ddbf900.

📒 Files selected for processing (5)
  • .Jules/changelog.md
  • .Jules/todo.md
  • mobile/api/groups.js
  • mobile/babel.config.js
  • mobile/screens/GroupDetailsScreen.js

Comment on lines +116 to +136
const handleDeleteSwipe = () => {
// Optimistic delete
setHiddenExpenses((prev) => [...prev, item._id]);
setSnackbarMessage(`Deleted "${item.description}"`);
setSnackbarVisible(true);

const timeoutId = setTimeout(async () => {
try {
await deleteExpense(groupId, item._id);
setExpenses((prev) => prev.filter((exp) => exp._id !== item._id));
} catch (error) {
console.error("Failed to delete expense:", error);
setHiddenExpenses((prev) => prev.filter((id) => id !== item._id));
Alert.alert("Error", "Failed to delete expense.");
}
setHiddenExpenses((prev) => prev.filter((id) => id !== item._id));
delete deleteTimeouts.current[item._id];
}, 5000); // 5 seconds to undo

deleteTimeouts.current[item._id] = { timeoutId, item };
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Memory leak: Pending timeouts not cleared on component unmount.

If the user navigates away from this screen before the 5-second timeout completes, the timeout will still fire and attempt to update state on an unmounted component, causing a React warning and potential memory leak.

🔧 Proposed fix: Add cleanup effect

Add a cleanup effect after the state declarations (around line 36):

  const deleteTimeouts = useRef({});

+ useEffect(() => {
+   return () => {
+     // Clear all pending deletion timeouts on unmount
+     Object.values(deleteTimeouts.current).forEach(({ timeoutId }) => {
+       clearTimeout(timeoutId);
+     });
+   };
+ }, []);
+
  // Currency configuration - can be made configurable later
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/screens/GroupDetailsScreen.js` around lines 116 - 136, The
handleDeleteSwipe flow registers timeouts in deleteTimeouts.current but never
clears them on unmount; add a useEffect cleanup that iterates
deleteTimeouts.current, calls clearTimeout for each stored timeoutId and deletes
the entries to prevent state updates after unmount. Locate deleteTimeouts,
handleDeleteSwipe, and the optimistic state setters (setHiddenExpenses,
setExpenses) and ensure the cleanup only clears the pending timers
(clearTimeout(timeoutId)) and removes them from deleteTimeouts.current to avoid
memory leaks and React warnings.

Comment on lines +277 to +284
const handleUndoGlobal = () => {
Object.keys(deleteTimeouts.current).forEach(id => {
clearTimeout(deleteTimeouts.current[id].timeoutId);
setHiddenExpenses((prev) => prev.filter((expId) => expId !== id));
});
deleteTimeouts.current = {};
setSnackbarVisible(false);
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider UX for multiple pending deletions.

The global undo restores all pending deletions, but the snackbar only shows the most recent deletion message. If a user rapidly deletes multiple expenses, pressing "Undo" will restore all of them, which may be unexpected.

💡 Optional enhancement: Show count when multiple items pending
+ const pendingCount = Object.keys(deleteTimeouts.current).length;
+ 
  const handleUndoGlobal = () => {
    Object.keys(deleteTimeouts.current).forEach(id => {
      clearTimeout(deleteTimeouts.current[id].timeoutId);
      setHiddenExpenses((prev) => prev.filter((expId) => expId !== id));
    });
    deleteTimeouts.current = {};
    setSnackbarVisible(false);
  };

And update snackbar message when multiple items:

setSnackbarMessage(pendingCount > 1 
  ? `Deleted ${pendingCount} expenses` 
  : `Deleted "${item.description}"`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/screens/GroupDetailsScreen.js` around lines 277 - 284, The current
global undo (handleUndoGlobal) restores all pending deletions while the snackbar
only reflects the most recent deletion; update the UX by computing pendingCount
= Object.keys(deleteTimeouts.current).length wherever you set the snackbar and
call setSnackbarMessage(pendingCount > 1 ? `Deleted ${pendingCount} expenses` :
`Deleted "${item.description}"`), so the snackbar accurately reflects multiple
pending deletes; keep handleUndoGlobal logic that iterates
deleteTimeouts.current to clear timeouts and call setHiddenExpenses, then clear
deleteTimeouts.current and setSnackbarVisible(false) so the message and undo
behavior are consistent (use the symbols handleUndoGlobal,
deleteTimeouts.current, setHiddenExpenses, setSnackbarMessage,
setSnackbarVisible to locate code).

Implemented swipe-to-delete functionality for expenses on the Group Details screen in the mobile app.
- Created an optimistic deletion flow with a 5-second undo Snackbar using `react-native-paper`.
- Wrapped expense cards with `Swipeable` from `react-native-gesture-handler`.
- Added the `deleteExpense` function to the API client (`mobile/api/groups.js`).
- Created `mobile/babel.config.js` to support the required `react-native-reanimated` plugin.
- Added necessary ref-based timeout management to prevent state mismatch during rapid interactions.
- Updated `.Jules/todo.md` and `.Jules/changelog.md` to reflect the completed task.

Co-authored-by: Devasy23 <110348311+Devasy23@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mobile/package.json`:
- Around line 25-27: The package.json currently pins
react-native-gesture-handler and react-native-reanimated to versions newer than
Expo SDK 54 supports; update the dependencies for react-native-gesture-handler
and react-native-reanimated to the SDK-54-compatible versions (e.g.,
react-native-gesture-handler ~2.28.0 and react-native-reanimated ~4.1.1) or run
`npx expo install react-native-gesture-handler react-native-reanimated` to let
Expo resolve compatible versions, then update package.json and reinstall to
ensure lockfile/modules match; leave react-native-paper unchanged unless `npx
expo install` suggests a different compatible version.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e72302d4-9939-4e8f-a0e6-5f639806bb9a

📥 Commits

Reviewing files that changed from the base of the PR and between ddbf900 and 9198ac0.

⛔ Files ignored due to path filters (1)
  • mobile/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • mobile/package.json

Comment on lines +25 to +27
"react-native-gesture-handler": "^2.31.0",
"react-native-paper": "^5.14.5",
"react-native-reanimated": "^4.3.0",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verifies dependency compatibility against current Expo SDK without mutating app code.
npx expo install --check react-native-gesture-handler react-native-reanimated

Repository: Devasy/splitwiser

Length of output: 1390


🏁 Script executed:

# First, let's find and read the mobile/package.json file
find . -name "package.json" -path "*/mobile/*" | head -5

Repository: Devasy/splitwiser

Length of output: 82


🏁 Script executed:

# Check the actual content of mobile/package.json around the relevant lines
cd mobile && cat -n package.json | sed -n '20,35p'

Repository: Devasy/splitwiser

Length of output: 647


🏁 Script executed:

# Get the full dependency section to understand the context
cd mobile && cat package.json | jq '.dependencies' 2>/dev/null || cat package.json | grep -A 50 '"dependencies"'

Repository: Devasy/splitwiser

Length of output: 735


🌐 Web query:

Expo SDK 54 react-native-gesture-handler compatible version

💡 Result:

The compatible version of react-native-gesture-handler for Expo SDK 54 is ~2.28.0. Install it using npx expo install react-native-gesture-handler which will resolve to the correct compatible version for your Expo SDK.

Citations:


🌐 Web query:

Expo SDK 54 react-native-reanimated compatible version

💡 Result:

The compatible version of react-native-reanimated for Expo SDK 54 is ~4.1.1, along with react-native-worklets 0.5.1. Use npx expo install react-native-reanimated to install the correct bundled version. Reanimated v4 requires the New Architecture (enabled by default in SDK 54), though v3 can still be used with Legacy Architecture if needed.

Citations:


🌐 Web query:

react-native-gesture-handler 2.31.0 react-native 0.81 compatibility

💡 Result:

Yes, react-native-gesture-handler version 2.31.0 is compatible with React Native 0.81. Official documentation states that react-native-gesture-handler supports the three latest minor releases of React Native, which includes 0.81 given the current versions up to 0.84. The library added explicit support for React Native 0.81 via pull request #3607 (merged August 2025), and subsequent releases like 2.31.0 (April 2026) build on this. The GitHub repo compatibility table shows versions from 2.28.0+ support React Native 0.79.0+, placing 0.81 well within the supported range. Issues reporting problems with 0.81 were resolved as user configuration errors, not library incompatibilities.

Citations:


🌐 Web query:

react-native-reanimated 4.3.0 compatibility with react-native 0.81

💡 Result:

Yes, react-native-reanimated 4.3.0 is compatible with react-native 0.81. It requires the React Native New Architecture (Fabric/Bridgeless). The official compatibility table confirms support for RN 0.81 with Reanimated 4.3.x (marked "yes"). The 4.3.0 release bumped the minimum RN version to 0.81. Reanimated 4.x supports only the three latest RN versions at release time and the New Architecture exclusively.

Citations:


Use Expo SDK 54-compatible versions for native modules.

Lines 25 and 27 specify versions newer than those tested with Expo SDK 54. The official Expo documentation recommends react-native-gesture-handler ~2.28.0 and react-native-reanimated ~4.1.1 for SDK 54 compatibility. Using npx expo install will resolve to the correct compatible versions.

Suggested change
-    "react-native-gesture-handler": "^2.31.0",
+    "react-native-gesture-handler": "~2.28.0",
-    "react-native-reanimated": "^4.3.0",
+    "react-native-reanimated": "~4.1.1",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"react-native-gesture-handler": "^2.31.0",
"react-native-paper": "^5.14.5",
"react-native-reanimated": "^4.3.0",
"react-native-gesture-handler": "~2.28.0",
"react-native-paper": "^5.14.5",
"react-native-reanimated": "~4.1.1",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/package.json` around lines 25 - 27, The package.json currently pins
react-native-gesture-handler and react-native-reanimated to versions newer than
Expo SDK 54 supports; update the dependencies for react-native-gesture-handler
and react-native-reanimated to the SDK-54-compatible versions (e.g.,
react-native-gesture-handler ~2.28.0 and react-native-reanimated ~4.1.1) or run
`npx expo install react-native-gesture-handler react-native-reanimated` to let
Expo resolve compatible versions, then update package.json and reinstall to
ensure lockfile/modules match; leave react-native-paper unchanged unless `npx
expo install` suggests a different compatible version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant