Skip to content

Add mobile history pull to refresh - #758

Merged
wheval merged 1 commit into
ancore-org:mainfrom
Emelie-Dev:codex/mobile-history-pull-refresh
Jun 6, 2026
Merged

Add mobile history pull to refresh#758
wheval merged 1 commit into
ancore-org:mainfrom
Emelie-Dev:codex/mobile-history-pull-refresh

Conversation

@Emelie-Dev

@Emelie-Dev Emelie-Dev commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Description

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📝 Documentation update
  • 🔧 Configuration change
  • ♻️ Code refactoring
  • ⚡ Performance improvement
  • ✅ Test addition/improvement

Security Impact

  • This change involves cryptographic operations
  • This change affects account validation logic
  • This change modifies smart contracts
  • This change handles user private keys
  • This change affects authorization/authentication
  • No security impact

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • E2E tests added/updated (if applicable)

Test Coverage

  • Current coverage: __%
  • New/modified code coverage: __%

Manual Testing Steps

Breaking Changes

  • This PR introduces breaking changes

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings or errors
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published
  • Any flaky-test quarantine entry includes a linked follow-up issue, owner, expiry, and stable failure signature

For High-Security Changes

  • I have documented all security assumptions
  • I have considered attack vectors
  • I have added security-focused test cases
  • I have reviewed against the threat model

Related Issues

Closes #614
Related to #

Additional Context

Reviewer Notes


Summary by CodeRabbit

  • New Features
    • Pull-to-refresh gesture for transaction history with dynamic status feedback ("Refreshing transactions..." and "Release to refresh" messages)
    • Offline detection system that automatically disables refresh functionality during connectivity loss
    • Improved refresh control with intelligent handling of poor network conditions, preventing unnecessary data operations and displaying offline status to users

@drips-wave

drips-wave Bot commented Jun 2, 2026

Copy link
Copy Markdown

@Emelie-Dev Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds pull-to-refresh gesture support to the mobile transaction history screen with network-aware state management. A new PullToRefreshControl component tracks vertical touch distance and triggers refresh on threshold. The usePaginatedTransactionHistory hook gains offline detection, skipping network calls when unavailable. UI layers are wired to display offline status and disable refresh buttons accordingly.

Changes

Pull-to-refresh with offline awareness

Layer / File(s) Summary
PullToRefreshControl gesture component
apps/mobile-wallet/src/components/PullToRefreshControl.tsx
New reusable touch-tracking component with Props type and PULL_THRESHOLD_PX constant; touch handlers measure vertical pull distance, trigger refresh when crossing 48px threshold, and render pull status with aria-live="polite" accessibility label.
usePaginatedTransactionHistory offline support
apps/mobile-wallet/src/screens/history/usePaginatedTransactionHistory.ts
Options type gains optional isOnline; State type adds isOffline: boolean; new getDefaultOnlineStatus() helper derives status from navigator.onLine; fetchPage callback short-circuits when offline, clears loading/error state, and sets isOffline: true; success/error paths set isOffline: false.
TransactionHistoryList pull-to-refresh integration
apps/mobile-wallet/src/components/TransactionHistoryList.tsx
Imports PullToRefreshControl and adds optional isOffline prop; wraps empty and populated transaction states with pull-to-refresh control; displays offline banner and disables refresh button when offline or refreshing.
HistoryScreen prop threading
apps/mobile-wallet/src/screens/history/HistoryScreen.tsx
Props type adds optional isOnline; HistoryScreen forwards isOnline to usePaginatedTransactionHistory hook and passes returned history.isOffline to TransactionHistoryList.
TransactionHistoryList pull-to-refresh tests
apps/mobile-wallet/src/screens/history/__tests__/TransactionHistoryList.test.tsx
Adds fireEvent to imports; new test group validates "Refreshing transactions..." status during active refresh, confirms onRefresh is not triggered by insufficient pull, verifies threshold crossing invokes onRefresh, and tests offline state disables manual refresh button.
usePaginatedTransactionHistory offline behavior tests
apps/mobile-wallet/src/screens/history/usePaginatedTransactionHistory.test.tsx
Adds HistoryPage import; two new tests verify refresh() skips network calls when isOnline: false, and validate refresh/pagination interaction during in-flight page fetches with expected call order and final state.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ancore-org/ancore#326: This PR extends the transaction history pagination implementation from #326 by adding pull-to-refresh gestures and offline-aware refresh logic to the existing usePaginatedTransactionHistory and TransactionHistoryList components.

Poem

🐰 A gentle pull, the gesture flows,
Transactions refresh as the rabbit knows.
Offline or not, the state stays true,
Pull-to-refresh, now hopping through! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements core pull-to-refresh functionality with offline-aware behavior, but lacks query client integration and explicit deduplication logic mentioned in issue #614 acceptance criteria. Integrate query client invalidation, implement explicit transaction deduplication logic, and ensure full iOS/Android compatibility testing coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add mobile history pull to refresh' directly and concisely describes the main feature added in the changeset.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing pull-to-refresh on the transaction history screen, with no unrelated modifications detected.
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.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/mobile-wallet/src/components/PullToRefreshControl.tsx`:
- Around line 49-53: The indicator text is misleading because
shouldShowIndicator turns true for any pullDistance > 0; update the conditional
rendering inside PullToRefreshControl (where shouldShowIndicator and
isRefreshing are used) to branch the copy based on whether pullDistance has
reached the refresh threshold (e.g., replace the current 'Release to refresh'
with a conditional that shows 'Pull to refresh' when pullDistance <
refreshThreshold and 'Release to refresh' when pullDistance >=
refreshThreshold), keeping the isRefreshing case unchanged so it still shows
'Refreshing transactions...' when isRefreshing is true and preserving the
aria-live/role attributes.
- Around line 18-40: Guard pull-to-refresh by requiring the scroll container be
at the top and correct the indicator copy logic: in
handleTouchStart/handleTouchMove record/validate only when the target scroll
container's scrollTop === 0 (or a provided scrollContainerRef's
current.scrollTop === 0) so downward swipes mid-scroll don't start a pull; in
handleTouchEnd only call onRefresh when pullDistance >= PULL_THRESHOLD_PX and
the container was at top and !isRefreshing; also change the indicator text logic
to show "Pull to refresh" when pullDistance > 0 but < PULL_THRESHOLD_PX,
"Release to refresh" when pullDistance >= PULL_THRESHOLD_PX and not
isRefreshing, and the existing refreshing text when isRefreshing is true,
updating any component that reads pullDistance/isRefreshing to use these
thresholds.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: d6446ef9-d37d-4836-8aa4-8b7069b410f1

📥 Commits

Reviewing files that changed from the base of the PR and between 3d6e31f and 731ba46.

📒 Files selected for processing (6)
  • apps/mobile-wallet/src/components/PullToRefreshControl.tsx
  • apps/mobile-wallet/src/components/TransactionHistoryList.tsx
  • apps/mobile-wallet/src/screens/history/HistoryScreen.tsx
  • apps/mobile-wallet/src/screens/history/__tests__/TransactionHistoryList.test.tsx
  • apps/mobile-wallet/src/screens/history/usePaginatedTransactionHistory.test.tsx
  • apps/mobile-wallet/src/screens/history/usePaginatedTransactionHistory.ts

Comment on lines +18 to +40
const handleTouchStart = (event: TouchEvent<HTMLElement>) => {
startYRef.current = event.touches[0]?.clientY ?? null;
};

const handleTouchMove = (event: TouchEvent<HTMLElement>) => {
if (startYRef.current === null || isRefreshing) {
return;
}

const nextY = event.touches[0]?.clientY ?? startYRef.current;
setPullDistance(Math.max(0, Math.min(nextY - startYRef.current, PULL_THRESHOLD_PX)));
};

const handleTouchEnd = () => {
const shouldRefresh = pullDistance >= PULL_THRESHOLD_PX;

startYRef.current = null;
setPullDistance(0);

if (shouldRefresh && !isRefreshing) {
void onRefresh();
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how the list/section is laid out and whether an ancestor is the scroll container.
rg -nP -C4 'overflow(-y)?\s*[:=]|overflow-(y-)?(auto|scroll)|PullToRefreshControl' --type=ts --type=tsx
fd -e tsx -e ts | rg -i 'history|scroll|layout'

Repository: ancore-org/ancore

Length of output: 1168


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Read the full PullToRefreshControl component to verify touch/indicator logic.
FILE="apps/mobile-wallet/src/components/PullToRefreshControl.tsx"
if [ -f "$FILE" ]; then
  wc -l "$FILE"
  sed -n '1,220p' "$FILE" | cat -n
fi

# 2) Find all usages of PullToRefreshControl to identify the actual scroll container.
rg -n "PullToRefreshControl" --hidden -S .

# 3) Search history/transaction list components for scroll container refs/handlers.
rg -n "onScroll|scrollTop|ref=.*scroll|overflow(-y)?|Section|<section|TransactionHistory|HistoryScreen" apps/mobile-wallet/src/screens/history apps/mobile-wallet/src/components -S

Repository: ancore-org/ancore

Length of output: 10589


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect how PullToRefreshControl wraps the history list and what the scroll container is.
FILE1="apps/mobile-wallet/src/components/TransactionHistoryList.tsx"
FILE2="apps/mobile-wallet/src/screens/history/HistoryScreen.tsx"

for f in "$FILE1" "$FILE2"; do
  echo "==== $f ===="
  wc -l "$f"
  sed -n '1,260p' "$f" | cat -n
done

# 2) Search for scroll/overflow indicators specifically in those files/folders.
rg -n --hidden "scrollTop|onScroll|overflow(-y)?\s*:|overflow(-y)?\s*=" apps/mobile-wallet/src/components/TransactionHistoryList.tsx apps/mobile-wallet/src/screens/history/HistoryScreen.tsx
rg -n --hidden "overflow(-y)?\s*[:=]|onScroll|scrollTop" apps/mobile-wallet/src/screens/history apps/mobile-wallet/src/components -S

Repository: ancore-org/ancore

Length of output: 6181


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find where HistoryScreen is mounted.
rg -n --hidden "HistoryScreen" apps/mobile-wallet/src -S

# 2) Find scroll containers in mobile-wallet (tailwind overflow-y-* or CSS overflow-y).
rg -n --hidden "(overflow-y-auto|overflow-y-scroll|overflow-y)\-" apps/mobile-wallet/src -S
rg -n --hidden "overflow(-y)?\s*[:=]" apps/mobile-wallet/src -S
rg -n --hidden "onScroll=" apps/mobile-wallet/src -S

# 3) If HistoryScreen is used inside some layout/container, inspect that file(s).
# (We’ll print only the nearby lines around matches.)
rg -n --hidden "HistoryScreen" apps/mobile-wallet/src -S | head -n 50

Repository: ancore-org/ancore

Length of output: 316


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where HistoryScreen is instantiated in the app.
rg -n --hidden "<HistoryScreen|HistoryScreen\s*=" apps/mobile-wallet/src -S

# Search for the actual scrolling container/styling and touch gesture handling.
rg -n --hidden "overflow(-y)?(-auto|-scroll|-hidden|-visible)?|scrollTop|onScroll=|touch-action|pan-y|overscroll" apps/mobile-wallet/src -S

# If nothing turns up, expand to the whole repo (still limited to mobile-wallet).
rg -n --hidden "overflow(-y)?(-auto|-scroll|-hidden|-visible)?|onScroll=|touch-action|pan-y|overscroll" apps/mobile-wallet/src -S

Repository: ancore-org/ancore

Length of output: 217


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find where HistoryScreen/TransactionHistoryList are used/mounted anywhere in the repo.
rg -n --hidden "<\s*HistoryScreen\b|HistoryScreen\s*=" . -S
rg -n --hidden "<\s*TransactionHistoryList\b|TransactionHistoryList\s*=" . -S

# 2) Search for likely scroll-container / overflow handling and touch-gesture modifiers.
rg -n --hidden "overflow(-y)?-(auto|scroll|hidden|visible)|overscroll|scrollTop|onScroll=|touch-action|pan-y|ScrollView" . -S

# 3) Narrow to mobile-wallet layout/entrypoints if present.
rg -n --hidden "mobile-wallet" . -S | head -n 80

Repository: ancore-org/ancore

Length of output: 10262


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find how the history list is actually rendered/scrolls in the mobile-wallet app (RN primitives).
rg -n --hidden "ScrollView|FlatList|SectionList|VirtualizedList|onScroll|scrollEnabled|contentOffset" apps/mobile-wallet/src -S

# Find where TransactionHistoryList/HistoryScreen are used in mobile-wallet.
rg -n --hidden "TransactionHistoryList" apps/mobile-wallet/src -S
rg -n --hidden "HistoryScreen" apps/mobile-wallet/src -S

# Find any styling that indicates a scroll container in mobile-wallet (RN uses style objects; web uses className/overflow).
rg -n --hidden "overflow(-y)?|touch-action|pan-y|overscroll|scrollTop" apps/mobile-wallet/src -S

Repository: ancore-org/ancore

Length of output: 43


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for any CSS overflow/scroll class usage in mobile-wallet.
rg -n --hidden "overflow(-y)?-?(auto|scroll|hidden|visible)|overscroll|scrollbar|touch-action|pan-y" apps/mobile-wallet/src -S

# Find mobile-wallet entry/root layout to locate scroll container.
rg -n --hidden "createRoot|ReactDOM\.createRoot|router|Routes|route|BrowserRouter|MemoryRouter|Scroll" apps/mobile-wallet/src -S

# List top-level files to guide where to inspect.
ls -R apps/mobile-wallet/src | head -n 120

Repository: ancore-org/ancore

Length of output: 43


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "=== mobile-wallet src tree (partial) ==="
ls -R apps/mobile-wallet/src | head -n 160

echo "=== searches (non-failing) ==="
rg -n --hidden "overflow(-y)?-?(auto|scroll|hidden|visible)|overscroll|touch-action|pan-y|scrollTop|scrollbar" apps/mobile-wallet/src -S || true
rg -n --hidden "ScrollView|FlatList|SectionList|VirtualizedList|onScroll|scrollEnabled|contentOffset" apps/mobile-wallet/src -S || true
rg -n --hidden "createRoot|ReactDOM\.createRoot|router|Routes|route|BrowserRouter|MemoryRouter|Scroll" apps/mobile-wallet/src -S || true

Repository: ancore-org/ancore

Length of output: 5240


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in \
  apps/mobile-wallet/src/app/App.tsx \
  apps/mobile-wallet/src/navigation/MobileWalletShell.tsx \
  apps/mobile-wallet/src/navigation/index.ts \
  apps/mobile-wallet/src/app/bootstrap.ts \
  apps/mobile-wallet/src/index.ts
do
  echo "==== $f ===="
  wc -l "$f" || true
  sed -n '1,220p' "$f" | cat -n || true
done

Repository: ancore-org/ancore

Length of output: 4610


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --hidden "pull-to-refresh|Release to refresh|Refreshing transactions|onRefresh\\(" apps/mobile-wallet/src -S || true
rg -n --hidden "pullDistance|PULL_THRESHOLD_PX" . -S || true

# Inspect TransactionHistoryList tests (may include indicator / refresh expectations)
sed -n '1,260p' apps/mobile-wallet/src/screens/history/__tests__/TransactionHistoryList.test.tsx | cat -n

Repository: ancore-org/ancore

Length of output: 11208


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="apps/mobile-wallet/src/screens/history/__tests__/TransactionHistoryList.test.tsx"
sed -n '330,470p' "$FILE" | cat -n

Repository: ancore-org/ancore

Length of output: 4023


Add “scroll-at-top” gating to prevent unintended refresh, and fix the indicator copy

  • apps/mobile-wallet/src/components/PullToRefreshControl.tsx touch handlers derive pullDistance from finger delta only and call onRefresh once pullDistance >= 48—there’s no check that the relevant scroll container is at scrollTop === 0, so a downward swipe can initiate refresh while the list is mid-scroll.
  • The indicator text shows “Release to refresh” for any pullDistance > 0 (it only switches when isRefreshing changes), even when the pull is still below the threshold—this copy is misleading.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/mobile-wallet/src/components/PullToRefreshControl.tsx` around lines 18 -
40, Guard pull-to-refresh by requiring the scroll container be at the top and
correct the indicator copy logic: in handleTouchStart/handleTouchMove
record/validate only when the target scroll container's scrollTop === 0 (or a
provided scrollContainerRef's current.scrollTop === 0) so downward swipes
mid-scroll don't start a pull; in handleTouchEnd only call onRefresh when
pullDistance >= PULL_THRESHOLD_PX and the container was at top and
!isRefreshing; also change the indicator text logic to show "Pull to refresh"
when pullDistance > 0 but < PULL_THRESHOLD_PX, "Release to refresh" when
pullDistance >= PULL_THRESHOLD_PX and not isRefreshing, and the existing
refreshing text when isRefreshing is true, updating any component that reads
pullDistance/isRefreshing to use these thresholds.

Comment on lines +49 to +53
{shouldShowIndicator ? (
<p aria-live="polite" role="status">
{isRefreshing ? 'Refreshing transactions...' : 'Release to refresh'}
</p>
) : null}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

"Release to refresh" is misleading below the threshold.

shouldShowIndicator becomes true as soon as pullDistance > 0, so the message reads "Release to refresh" even when the user hasn't pulled far enough — releasing then does nothing. Branch the copy on whether the threshold is met.

💡 Proposed change
   const isPulling = pullDistance > 0 && !isRefreshing;
   const shouldShowIndicator = isPulling || isRefreshing;
+  const hasReachedThreshold = pullDistance >= PULL_THRESHOLD_PX;
       {shouldShowIndicator ? (
         <p aria-live="polite" role="status">
-          {isRefreshing ? 'Refreshing transactions...' : 'Release to refresh'}
+          {isRefreshing
+            ? 'Refreshing transactions...'
+            : hasReachedThreshold
+              ? 'Release to refresh'
+              : 'Pull to refresh'}
         </p>
       ) : null}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/mobile-wallet/src/components/PullToRefreshControl.tsx` around lines 49 -
53, The indicator text is misleading because shouldShowIndicator turns true for
any pullDistance > 0; update the conditional rendering inside
PullToRefreshControl (where shouldShowIndicator and isRefreshing are used) to
branch the copy based on whether pullDistance has reached the refresh threshold
(e.g., replace the current 'Release to refresh' with a conditional that shows
'Pull to refresh' when pullDistance < refreshThreshold and 'Release to refresh'
when pullDistance >= refreshThreshold), keeping the isRefreshing case unchanged
so it still shows 'Refreshing transactions...' when isRefreshing is true and
preserving the aria-live/role attributes.

@wheval
wheval merged commit 4c5e412 into ancore-org:main Jun 6, 2026
1 check passed
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.

[MOBILE] Add pull-to-refresh on transaction history screen

2 participants