Skip to content

Conversation

@Devasy
Copy link
Owner

@Devasy Devasy commented Jan 28, 2026

Implemented high-contrast focus-visible styles across interactive elements to improve keyboard accessibility.

  • Components: Button, Modal (close button), Toast (dismiss button).
  • Pages: Auth (Google button, toggle links).
  • Theming:
    • Neobrutalism: ring-black ring-2 ring-offset-2
    • Glassmorphism: ring-blue-500 ring-2 ring-offset-2 (with dark mode support)
  • Verification:
    • Validated via temporary Playwright tests checking document.activeElement.
    • Visually confirmed focus rings in generated screenshots.
  • Documentation: Updated project tracking files.

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

Summary by CodeRabbit

  • New Features

    • Native pull-to-refresh on mobile with haptic feedback, themed spinners, and silent refresh (separates refresh state from full-screen loading).
  • Style

    • Consistent visible focus indicators for buttons, modals, toasts, and auth actions with dual-theme handling.
  • Accessibility

    • Widespread accessibility metadata added across mobile screens and new Accessibility Patterns docs.
  • Tests

    • Backend tests updated to cover optimized settlement and recalculation flows.
  • Chores

    • Changelog, task list, and mobile dependency updates recorded.

✏️ Tip: You can customize this high-level summary in your review settings.

- Implemented `focus-visible` styles for `Button`, `Modal`, `Toast`, and `Auth` page components.
- Added dual-theme support: Black rings for Neobrutalism, Blue rings for Glassmorphism.
- Verified changes programmatically and visually.
- Updated `.Jules/todo.md` and `.Jules/changelog.md`.

Co-authored-by: Devasy <[email protected]>
@google-labs-jules
Copy link
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 January 28, 2026 20:14
@netlify
Copy link

netlify bot commented Jan 28, 2026

Deploy Preview for split-but-wiser canceled.

Name Link
🔨 Latest commit 89e6031
🔍 Latest deploy log https://app.netlify.com/projects/split-but-wiser/deploys/697dc4af286d8d000818a414

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 28, 2026

Walkthrough

Adds theme-aware focus-visible outlines to web UI controls; implements native pull-to-refresh with haptic feedback on multiple mobile screens and refactors fetch flows for silent refreshes; augments mobile accessibility attributes; adds expo-haptics dependency; updates changelog/todo/knowledge docs; and rewrites backend tests to exercise an optimized settlement/recalculation path.

Changes

Cohort / File(s) Summary
Changelog & Task Records
​.Jules/changelog.md, ​.Jules/todo.md
Added changelog entries and updated todo items to mark web style and mobile UX tasks completed; updated task metadata and file lists.
Web UI components & Auth page
web/components/ui/Button.tsx, web/components/ui/Modal.tsx, web/components/ui/Toast.tsx, web/pages/Auth.tsx
Presentation-only edits: added outline-none and focus-visible ring utilities with theme-aware ring colors and ring-offsets to buttons and close controls; no API/signature changes.
Mobile pull-to-refresh & haptics
mobile/package.json, mobile/screens/.../HomeScreen.js, mobile/screens/.../FriendsScreen.js, mobile/screens/.../GroupDetailsScreen.js
Added expo-haptics dependency; integrated RefreshControl pull-to-refresh with separate isRefreshing state, light haptic feedback, themed tinting, and refactored fetch functions to support silent refreshes.
Mobile accessibility (many screens)
mobile/screens/AccountScreen.js, mobile/screens/AddExpenseScreen.js, mobile/screens/EditProfileScreen.js, mobile/screens/GroupSettingsScreen.js, mobile/screens/JoinGroupScreen.js, mobile/screens/LoginScreen.js, mobile/screens/SignupScreen.js, mobile/screens/SplitwiseImportScreen.js, mobile/screens/*/*.js
Added accessibilityLabel, accessibilityRole, accessibilityHint, and accessibilityState to numerous interactive elements; no behavioral or exported-signature changes.
Backend tests — expenses
backend/tests/expenses/test_expense_service.py
Reworked tests to import/use OptimizedSettlement, mock and assert _recalculate_group_balances calls, and adjust assertions/error expectations to match the optimized settlement / recalculation flow.
Docs / Knowledge
​.Jules/knowledge.md
Added "Accessibility Patterns" guidance for React Native Paper components and explicit accessibility recommendations for mobile.

Possibly related PRs

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 PR title 'Consistent hover/focus states' accurately describes the main changes: implementing focus-visible styles across web components (Button, Modal, Toast, Auth) with theme-specific styling. However, the changeset also includes significant mobile accessibility enhancements (accessibilityLabel, accessibilityRole, accessibilityHint on 10+ screens) and pull-to-refresh functionality, which comprise substantial portions of the PR but are not reflected in the title.
Docstring Coverage ✅ Passed Docstring coverage is 85.29% which is sufficient. The required threshold is 80.00%.

✏️ 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.

Copy link
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/components/ui/Button.tsx (1)

33-43: Consider adding explicit ring-offset-color for Neobrutalism variant.

The Glassmorphism branch specifies focus-visible:ring-offset-gray-900 for dark mode and focus-visible:ring-offset-white for light mode, but the Neobrutalism branch (line 34) does not specify an offset color. This relies on Tailwind's default (transparent or inherited), which may cause inconsistent ring appearance if the button background color differs from the expected offset.

If Neobrutalism only operates in light mode, this is acceptable. Otherwise, consider adding an explicit offset color for consistency.

💡 Optional: Add explicit ring-offset for Neobrutalism
-    themeStyles = "border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] active:translate-x-[4px] active:translate-y-[4px] active:shadow-none rounded-none uppercase tracking-wider font-mono focus-visible:ring-black";
+    themeStyles = "border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] active:translate-x-[4px] active:translate-y-[4px] active:shadow-none rounded-none uppercase tracking-wider font-mono focus-visible:ring-black focus-visible:ring-offset-white";
🤖 Fix all issues with AI agents
In `@web/pages/Auth.tsx`:
- Around line 213-216: The focus ring color for the non-Neo Google button is
inconsistent: change the Tailwind class used in the className expression for the
non-Neo branch (the string containing 'focus-visible:ring-blue-500') to use
'focus-visible:ring-blue-400' so it matches the other components (see
Button.tsx, Modal.tsx and the toggle link) and keep the conditional logic around
isNeo intact.

Comment on lines +213 to 216
className={`w-full flex items-center justify-center gap-3 p-3 font-bold transition-all outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ${isNeo
? 'focus-visible:ring-black bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] rounded-none'
: 'focus-visible:ring-blue-500 bg-white text-black hover:bg-gray-50 border border-gray-200 shadow-sm rounded-xl'
}`}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Minor inconsistency: Use ring-blue-400 for Glassmorphism focus ring.

The Google button uses focus-visible:ring-blue-500 for the non-Neo theme, but other components in this PR (Button.tsx, Modal.tsx, and the toggle link below at line 333) use ring-blue-400. For visual consistency across the application, consider aligning with ring-blue-400.

🔧 Suggested fix
               className={`w-full flex items-center justify-center gap-3 p-3 font-bold transition-all outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ${isNeo
                 ? 'focus-visible:ring-black bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] rounded-none'
-                : 'focus-visible:ring-blue-500 bg-white text-black hover:bg-gray-50 border border-gray-200 shadow-sm rounded-xl'
+                : 'focus-visible:ring-blue-400 bg-white text-black hover:bg-gray-50 border border-gray-200 shadow-sm rounded-xl'
                 }`}
📝 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
className={`w-full flex items-center justify-center gap-3 p-3 font-bold transition-all outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ${isNeo
? 'focus-visible:ring-black bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] rounded-none'
: 'focus-visible:ring-blue-500 bg-white text-black hover:bg-gray-50 border border-gray-200 shadow-sm rounded-xl'
}`}
className={`w-full flex items-center justify-center gap-3 p-3 font-bold transition-all outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ${isNeo
? 'focus-visible:ring-black bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] rounded-none'
: 'focus-visible:ring-blue-400 bg-white text-black hover:bg-gray-50 border border-gray-200 shadow-sm rounded-xl'
}`}
🤖 Prompt for AI Agents
In `@web/pages/Auth.tsx` around lines 213 - 216, The focus ring color for the
non-Neo Google button is inconsistent: change the Tailwind class used in the
className expression for the non-Neo branch (the string containing
'focus-visible:ring-blue-500') to use 'focus-visible:ring-blue-400' so it
matches the other components (see Button.tsx, Modal.tsx and the toggle link) and
keep the conditional logic around isNeo intact.

vrajpatelll
vrajpatelll previously approved these changes Jan 30, 2026
@Devasy
Copy link
Owner Author

Devasy commented Jan 30, 2026

@jules there are some merge conflicts!

@google-labs-jules
Copy link
Contributor

@jules there are some merge conflicts!

I have merged the latest changes from main and resolved the conflicts in .Jules/changelog.md. The conflicting updates to the changelog (Mobile Pull-to-Refresh vs Consistent Focus States) have been preserved.

…tates

Resolved conflicts in:
- .Jules/changelog.md

Co-authored-by: Devasy <[email protected]>
Copy link
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/tests/expenses/test_expense_service.py (1)

1962-1987: ⚠️ Potential issue | 🟡 Minor

Narrow the failure assertion to the expected exception message.

Line 1986 uses pytest.raises(Exception), which is too broad and can hide unexpected failures. Since the mock raises Exception("Calculation failed"), assert the message to make the test more specific.

🛠️ Proposed fix
-        with pytest.raises(Exception):  # The service doesn't catch all exceptions
+        with pytest.raises(Exception, match="Calculation failed"):
             await expense_service.get_friends_balance_summary(user_id_str)
🤖 Fix all issues with AI agents
In `@mobile/screens/FriendsScreen.js`:
- Around line 61-66: The onRefresh handler can fail if Haptics.impactAsync
rejects and leave the spinner stuck; update the onRefresh function to
setIsRefreshing(true) then run Haptics.impactAsync inside try/catch (or ignore
errors) and call fetchData, and move setIsRefreshing(false) into a finally block
so it always runs; specifically modify the onRefresh function (references:
onRefresh, setIsRefreshing, Haptics.impactAsync, fetchData) and apply the same
try/catch/finally pattern to the identical handlers in GroupDetailsScreen.js and
HomeScreen.js.

In `@mobile/screens/GroupDetailsScreen.js`:
- Around line 57-62: The onRefresh handler (function onRefresh) can abort
fetchData and leave the spinner on if Haptics.impactAsync rejects; wrap the
haptics call and fetchData invocation in a try/finally so that
setIsRefreshing(false) always runs: call setIsRefreshing(true), then try { await
Haptics.impactAsync(...); await fetchData(false); } finally {
setIsRefreshing(false); }—apply the same change to the identical handlers in
HomeScreen.js and FriendsScreen.js to ensure refresh completes even when haptics
fails.

In `@mobile/screens/HomeScreen.js`:
- Around line 102-107: The onRefresh handler can leave isRefreshing stuck true
if Haptics.impactAsync rejects; update the onRefresh function to guard the
haptics call and always clear the spinner by wrapping the async sequence in
try/finally (or append .catch() to Haptics.impactAsync and move
setIsRefreshing(false) into a finally block). Specifically, modify onRefresh to
call Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light) with a .catch() or
inside try/catch, await fetchGroups(false) as before, and ensure
setIsRefreshing(false) runs in the finally so failures in Haptics or fetchGroups
never leave isRefreshing true (referencing onRefresh, Haptics.impactAsync,
fetchGroups, and setIsRefreshing).

@Devasy
Copy link
Owner Author

Devasy commented Jan 30, 2026

@jules please look into the review comments

In @mobile/screens/FriendsScreen.js:

  • Around line 61-66: The onRefresh handler can fail if Haptics.impactAsync
    rejects and leave the spinner stuck; update the onRefresh function to
    setIsRefreshing(true) then run Haptics.impactAsync inside try/catch (or ignore
    errors) and call fetchData, and move setIsRefreshing(false) into a finally block
    so it always runs; specifically modify the onRefresh function (references:
    onRefresh, setIsRefreshing, Haptics.impactAsync, fetchData) and apply the same
    try/catch/finally pattern to the identical handlers in GroupDetailsScreen.js and
    HomeScreen.js.

In @mobile/screens/GroupDetailsScreen.js:

  • Around line 57-62: The onRefresh handler (function onRefresh) can abort
    fetchData and leave the spinner on if Haptics.impactAsync rejects; wrap the
    haptics call and fetchData invocation in a try/finally so that
    setIsRefreshing(false) always runs: call setIsRefreshing(true), then try { await
    Haptics.impactAsync(...); await fetchData(false); } finally {
    setIsRefreshing(false); }—apply the same change to the identical handlers in
    HomeScreen.js and FriendsScreen.js to ensure refresh completes even when haptics
    fails.

In @mobile/screens/HomeScreen.js:

  • Around line 102-107: The onRefresh handler can leave isRefreshing stuck true
    if Haptics.impactAsync rejects; update the onRefresh function to guard the
    haptics call and always clear the spinner by wrapping the async sequence in
    try/finally (or append .catch() to Haptics.impactAsync and move
    setIsRefreshing(false) into a finally block). Specifically, modify onRefresh to
    call Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light) with a .catch() or
    inside try/catch, await fetchGroups(false) as before, and ensure
    setIsRefreshing(false) runs in the finally so failures in Haptics or fetchGroups
    never leave isRefreshing true (referencing onRefresh, Haptics.impactAsync,
    fetchGroups, and setIsRefreshing).

@google-labs-jules
Copy link
Contributor

@jules please look into the review comments

In @mobile/screens/FriendsScreen.js:

  • Around line 61-66: The onRefresh handler can fail if Haptics.impactAsync
    rejects and leave the spinner stuck; update the onRefresh function to
    setIsRefreshing(true) then run Haptics.impactAsync inside try/catch (or ignore
    errors) and call fetchData, and move setIsRefreshing(false) into a finally block
    so it always runs; specifically modify the onRefresh function (references:
    onRefresh, setIsRefreshing, Haptics.impactAsync, fetchData) and apply the same
    try/catch/finally pattern to the identical handlers in GroupDetailsScreen.js and
    HomeScreen.js.

In @mobile/screens/GroupDetailsScreen.js:

  • Around line 57-62: The onRefresh handler (function onRefresh) can abort
    fetchData and leave the spinner on if Haptics.impactAsync rejects; wrap the
    haptics call and fetchData invocation in a try/finally so that
    setIsRefreshing(false) always runs: call setIsRefreshing(true), then try { await
    Haptics.impactAsync(...); await fetchData(false); } finally {
    setIsRefreshing(false); }—apply the same change to the identical handlers in
    HomeScreen.js and FriendsScreen.js to ensure refresh completes even when haptics
    fails.

In @mobile/screens/HomeScreen.js:

  • Around line 102-107: The onRefresh handler can leave isRefreshing stuck true
    if Haptics.impactAsync rejects; update the onRefresh function to guard the
    haptics call and always clear the spinner by wrapping the async sequence in
    try/finally (or append .catch() to Haptics.impactAsync and move
    setIsRefreshing(false) into a finally block). Specifically, modify onRefresh to
    call Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light) with a .catch() or
    inside try/catch, await fetchGroups(false) as before, and ensure
    setIsRefreshing(false) runs in the finally so failures in Haptics or fetchGroups
    never leave isRefreshing true (referencing onRefresh, Haptics.impactAsync,
    fetchGroups, and setIsRefreshing).

I have updated the onRefresh logic in FriendsScreen.js, GroupDetailsScreen.js, and HomeScreen.js. The handlers now use try/catch/finally to ensure setIsRefreshing(false) is always called, and Haptics errors are gracefully ignored.

- Updated `onRefresh` in `FriendsScreen`, `GroupDetailsScreen`, and `HomeScreen`.
- Added `try/catch/finally` to ensure spinner is always dismissed.
- Gracefully handle `Haptics` failures.

Co-authored-by: Devasy <[email protected]>
Copy link
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: 2

🤖 Fix all issues with AI agents
In `@mobile/screens/FriendsScreen.js`:
- Around line 27-59: The loading state setIsLoading(true) in fetchData is
currently executed before the try block; move that call inside the try block (at
the top) to match the pattern used in HomeScreen.js and GroupDetailsScreen.js so
success/error paths are symmetrical, keep the finally block that calls
setIsLoading(false) when showLoading is true, and ensure this change is made
within the fetchData function scope referencing setIsLoading, getFriendsBalance,
and getGroups.

In `@mobile/screens/GroupDetailsScreen.js`:
- Line 261: In GroupDetailsScreen.js there is a formatting inconsistency where
the style declaration containing "fontSize: 20,    fontWeight: 'bold'" is on a
single line; update the stylesheet entry (the style object that currently sets
fontSize and fontWeight) to place each property on its own line with normal
spacing to match the rest of the StyleSheet (e.g., split into separate lines for
fontSize and fontWeight and remove the extra whitespace).

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@Devasy
Copy link
Owner Author

Devasy commented Jan 31, 2026

@jules
In @mobile/screens/FriendsScreen.js around lines 27 - 59, The loading state
setIsLoading(true) in fetchData is currently executed before the try block; move
that call inside the try block (at the top) to match the pattern used in
HomeScreen.js and GroupDetailsScreen.js so success/error paths are symmetrical,
keep the finally block that calls setIsLoading(false) when showLoading is true,
and ensure this change is made within the fetchData function scope referencing
setIsLoading, getFriendsBalance, and getGroups.

Copy link
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

🤖 Fix all issues with AI agents
In `@mobile/screens/GroupDetailsScreen.js`:
- Around line 258-267: The expensesTitle style in the StyleSheet (in
GroupDetailsScreen.js) is malformed: it contains a nested expensesTitle object
so fontSize and fontWeight are ignored; fix by merging the inner properties into
the outer expensesTitle object (remove the nested expensesTitle key) so the
style contains marginTop, marginBottom, fontSize, and fontWeight together,
ensuring components using expensesTitle receive the intended styling.

@google-labs-jules
Copy link
Contributor

@jules
In @mobile/screens/FriendsScreen.js around lines 27 - 59, The loading state
setIsLoading(true) in fetchData is currently executed before the try block; move
that call inside the try block (at the top) to match the pattern used in
HomeScreen.js and GroupDetailsScreen.js so success/error paths are symmetrical,
keep the finally block that calls setIsLoading(false) when showLoading is true,
and ensure this change is made within the fetchData function scope referencing
setIsLoading, getFriendsBalance, and getGroups.

I have moved if (showLoading) setIsLoading(true); inside the try block in mobile/screens/FriendsScreen.js to match the pattern used in other screens.

- Moved `setIsLoading(true)` inside `try` block in `FriendsScreen.js`.
- Ensures consistent error handling pattern across all screens.

Co-authored-by: Devasy <[email protected]>
@Devasy
Copy link
Owner Author

Devasy commented Jan 31, 2026

@jules pls resolve the merge conflict

@google-labs-jules
Copy link
Contributor

@jules pls resolve the merge conflict

I have merged the latest changes from main and resolved the conflicts. The mobile/screens files were auto-merged to include both my pull-to-refresh enhancements and the new accessibility roles from main.

…tates

Resolved conflicts in:
- .Jules/changelog.md

Verified auto-merge of:
- mobile/screens/FriendsScreen.js (combined pull-to-refresh + accessibility)
- mobile/screens/GroupDetailsScreen.js (combined pull-to-refresh + accessibility)
- mobile/screens/HomeScreen.js (combined pull-to-refresh + accessibility)

Co-authored-by: Devasy <[email protected]>
Copy link
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: 4

🤖 Fix all issues with AI agents
In @.Jules/todo.md:
- Around line 67-68: Update the completion date for the checklist item "[x]
**[a11y]** Complete accessibility labels for all screens" in .Jules/todo.md so
it does not lie in the future; replace "Completed: 2026-01-29" with the actual
completion date (e.g., 2026-01-28 or the date the work was finished) or remove
the date entirely if you cannot confirm it, ensuring the entry accurately
reflects when the accessibility work was completed.
- Around line 162-165: The TODO contains a duplicated completed task
"Pull-to-refresh with haptic feedback on all list screens"; remove the duplicate
entry from the "Completed Tasks" section (the one at lines 162-165) so only the
original entry in the Mobile high-priority section remains, and ensure any
associated metadata (completion date and modified file list) is preserved only
in the original Mobile entry to avoid redundancy.
- Around line 53-58: The task was marked complete while PR review requested
defensive error handling in the pull-to-refresh handlers; update each refresh
handler (e.g., onRefresh / handleRefresh used with RefreshControl in the refresh
handlers of FriendsScreen, GroupDetailsScreen and HomeScreen) to wrap the async
work and Haptics.impactAsync call in try/catch/finally so any rejection doesn't
leave the spinner active: call setRefreshing(true) before work, await the
refresh logic and Haptics.impactAsync inside try, log/handle errors in catch,
and always call setRefreshing(false) in finally; after fixing handlers, update
the todo entry to either uncheck the task or change the completion date once the
changes are merged.

In `@mobile/screens/GroupSettingsScreen.js`:
- Around line 338-342: The accessibilityLabel prop on the Button is static while
the visible text toggles based on pickedImage; update the accessibilityLabel to
mirror the button text by making it conditional (use pickedImage to choose
between "Change Image" and "Upload Image") so the Button's accessibilityLabel
and displayed label match; locate the Button component that uses the pickedImage
variable and replace the hardcoded accessibilityLabel string with the same
conditional expression used for the button children.

Comment on lines +53 to +58
- [x] **[ux]** Pull-to-refresh with haptic feedback on all list screens
- Completed: 2026-01-21
- Files: `mobile/screens/HomeScreen.js`, `mobile/screens/GroupDetailsScreen.js`, `mobile/screens/FriendsScreen.js`
- Context: Add RefreshControl + Expo Haptics to main lists
- Impact: Native feel, users can easily refresh data
- Size: ~45 lines
- Added: 2026-01-01
- Size: ~150 lines
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Task marked complete prematurely—outstanding review comments request changes.

The pull-to-refresh task is marked as completed (2026-01-21), but the PR objectives clearly document unresolved review comments requesting defensive try/catch/finally patterns in the refresh handlers for FriendsScreen.js, GroupDetailsScreen.js, and HomeScreen.js. The review feedback specifically asks for error handling to prevent refresh spinners from remaining active when Haptics.impactAsync rejects.

A task should not be marked complete while there are outstanding change requests on the same files and functionality.

📋 Recommended next steps

Either:

  1. Address the review comments by implementing the defensive error handling patterns, then update the completion date, OR
  2. Revert the completion status to [ ] until the review feedback is resolved and the PR is merged.
🤖 Prompt for AI Agents
In @.Jules/todo.md around lines 53 - 58, The task was marked complete while PR
review requested defensive error handling in the pull-to-refresh handlers;
update each refresh handler (e.g., onRefresh / handleRefresh used with
RefreshControl in the refresh handlers of FriendsScreen, GroupDetailsScreen and
HomeScreen) to wrap the async work and Haptics.impactAsync call in
try/catch/finally so any rejection doesn't leave the spinner active: call
setRefreshing(true) before work, await the refresh logic and Haptics.impactAsync
inside try, log/handle errors in catch, and always call setRefreshing(false) in
finally; after fixing handlers, update the todo entry to either uncheck the task
or change the completion date once the changes are merged.

Comment on lines +67 to +68
- [x] **[a11y]** Complete accessibility labels for all screens
- Completed: 2026-01-29
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Completion date is in the future.

The accessibility task shows a completion date of 2026-01-29, but this PR was created on 2026-01-28. While this might reflect a planned completion or timezone difference, it's clearer to use dates that reflect when work was actually completed rather than future dates.

🤖 Prompt for AI Agents
In @.Jules/todo.md around lines 67 - 68, Update the completion date for the
checklist item "[x] **[a11y]** Complete accessibility labels for all screens" in
.Jules/todo.md so it does not lie in the future; replace "Completed: 2026-01-29"
with the actual completion date (e.g., 2026-01-28 or the date the work was
finished) or remove the date entirely if you cannot confirm it, ensuring the
entry accurately reflects when the accessibility work was completed.

Comment on lines +162 to +165
- [x] **[ux]** Pull-to-refresh with haptic feedback on all list screens
- Completed: 2026-01-21
- Files modified: `mobile/screens/HomeScreen.js`, `mobile/screens/GroupDetailsScreen.js`, `mobile/screens/FriendsScreen.js`
- Impact: Native feel, users can easily refresh data
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove duplicate task entry.

The pull-to-refresh task appears in both the Mobile high-priority section (lines 53-58) and here in the "Completed Tasks" section. This duplication creates redundancy and maintenance burden.

Consider removing this duplicate entry since the task is already marked as complete in its original location (lines 53-58).

♻️ Suggested cleanup

Since the task is already marked [x] with completion date in the Mobile section (lines 53-58), this duplicate entry in the "Completed Tasks" section can be safely removed.

🤖 Prompt for AI Agents
In @.Jules/todo.md around lines 162 - 165, The TODO contains a duplicated
completed task "Pull-to-refresh with haptic feedback on all list screens";
remove the duplicate entry from the "Completed Tasks" section (the one at lines
162-165) so only the original entry in the Mobile high-priority section remains,
and ensure any associated metadata (completion date and modified file list) is
preserved only in the original Mobile entry to avoid redundancy.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@Devasy
Copy link
Owner Author

Devasy commented Jan 31, 2026

@jules pls resolve the merge conflict

@google-labs-jules
Copy link
Contributor

@jules pls resolve the merge conflict

I have verified that the branch is up-to-date with origin/main and there are no outstanding merge conflicts.

- Moved `setIsLoading(true)` inside the `try` block in `FriendsScreen.js` for consistency with other screens and better error handling symmetry.
- Confirmed branch is up-to-date with `main` and conflict-free.

Co-authored-by: Devasy <[email protected]>
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.

3 participants