Skip to content

Improve Configure Fees Design - #913

Open
leofelix077 wants to merge 15 commits into
mainfrom
lf-improve-configure-fees-design
Open

Improve Configure Fees Design#913
leofelix077 wants to merge 15 commits into
mainfrom
lf-improve-configure-fees-design

Conversation

@leofelix077

@leofelix077 leofelix077 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Closes #413

📱 - 1.19.27 (1782817626)

Reworks the transaction‑settings fee UI from a single free‑text input into a Low / Med / High / Custom priority selector, and makes the network fee data consistent and stable for the duration of a single Send / Swap / Send‑Collectible flow (no mid‑flow flicker). Presets are derived from Horizon feeStats, and the default tier follows current network congestion.

Builds on the existing FeeBreakdownBottomSheet (inclusion / resource / total) shipped earlier for this ticket.

Currently uses the API response of p10/p50 and p90 for low/med/high, instead of the mode of the recent values for the derivation

It respects the operation count from the Swap to new token, so med show as 2x fee, for example.

The network congestion is also frozen during the duration for consistency Low <> Low / Med <> Med / High <> High

ScreenRecording_06-30-2026.10-10-22_1.MP4

Small fix post recording to make the Fees show the total fee for soroban transaction, not the inclusion fee only on the SendReview sheet

Simulator Screenshot - iPhone 17 Pro - 2026-06-30 at 10 21 31 Simulator Screenshot - iPhone 17 Pro - 2026-06-30 at 10 21 35

Android Smoke test:

Screen.Recording.2026-06-30.at.10.47.48.mov

Checklist

PR structure

  • This PR does not mix refactoring changes with feature changes (break it down into smaller PRs if not).
  • This PR has reasonably narrow scope (break it down into smaller PRs if not).
  • This PR includes relevant before and after screenshots/videos highlighting these changes.
  • I took the time to review my own PR.

Testing

  • These changes have been tested and confirmed to work as intended on Android.
  • These changes have been tested and confirmed to work as intended on iOS.
  • These changes have been tested and confirmed to work as intended on small iOS screens.
  • These changes have been tested and confirmed to work as intended on small Android screens.
  • I have tried to break these changes while extensively testing them.
  • This PR adds tests for the new functionality or fixes.

Release

  • This is not a breaking change.
  • This PR updates existing JSDocs when applicable.
  • This PR adds JSDocs to new functionalities.
  • I've checked with the product team if we should add metrics to these changes.
  • I've shared relevant before and after screenshots/videos highlighting these changes with the design team and they've approved the changes.

@leofelix077 leofelix077 self-assigned this Jun 23, 2026
@leofelix077 leofelix077 added wip work in progress don't review yet Work in Progress / Draft PR / Code Review adjustments being worked on labels Jun 23, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

iOS Simulator preview build is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-5f95785a50f272c3e130 (SDF collaborators only — install instructions in the release description)

Copilot AI review requested due to automatic review settings June 30, 2026 13:23
@leofelix077 leofelix077 changed the title Lf improve configure fees design Improve Configure Fees Design Jun 30, 2026

Copilot AI left a comment

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.

Pull request overview

This PR implements #413 by reworking transaction fee configuration from a free-text input into Low / Med / High / Custom tiers, and by freezing a single “network fee snapshot” across a Send / Swap / Send-Collectible flow to prevent mid-flow fee/congestion flicker.

Changes:

  • Add fee priority tiers (Low/Med/High/Custom) backed by Horizon feeStats().max_fee percentiles and persist the selected tier in settings ducks.
  • Freeze network fee data per-network per-flow via a cache in useNetworkFees, with explicit cache clearing on flow exit and navigator pre-warming on flow entry.
  • Introduce a shared fee-details bottom sheet hook to unify the fee info affordance (classic info vs Soroban breakdown) and support previewing unsaved inclusion fee changes.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/services/stellar.ts Derives fee presets from Horizon fee stats and returns { recommendedFee, networkCongestion, feePresets }.
src/navigators/SwapNavigator.tsx Pre-warms cached network fees at Swap flow entry.
src/navigators/SendPaymentNavigator.tsx Pre-warms cached network fees and clears flow state + fee cache on unmount.
src/hooks/useNetworkFees.ts Implements frozen per-network fee snapshot + in-flight de-duping + explicit cache clear.
src/hooks/useInitialRecommendedFee.ts Defaults both fee amount and tier from congestion mapping until user edits.
src/hooks/useFeeDetailsBottomSheet.tsx New shared hook to render/open fee info sheets (classic vs Soroban breakdown).
src/config/types.ts Adds FeePriority, FeePresets, and CONGESTION_TO_FEE_PRIORITY.
src/ducks/transactionSettings.ts Persists feePriority in transaction settings store.
src/ducks/swapSettings.ts Persists feePriority in swap settings store.
src/components/TransactionSettingsBottomSheet.tsx Replaces fee input UX with tiered selector, locks input for presets, and integrates shared fee details sheets.
src/components/sds/SegmentedControl.tsx Updates segmented control layout and adds radio-group accessibility + improved touch target.
src/components/screens/SwapScreen/screens/SwapAmountScreen.tsx Uses congestion when initializing defaults and clears fee cache on unmount.
src/components/screens/SwapScreen/components/SwapReviewBottomSheet.tsx Displays fee row with info icon and mounts shared fee details sheets.
src/components/screens/SendScreen/screens/TransactionAmountScreen.tsx Uses congestion when initializing defaults; removes direct fee breakdown sheet wiring.
src/components/screens/SendScreen/screens/SendCollectibleReview.tsx Uses congestion when initializing defaults; removes direct fee breakdown sheet wiring.
src/components/screens/SendScreen/components/SendReviewBottomSheet.tsx Uses shared fee details sheets and aligns fee display with total fee logic.
src/components/FeeBreakdownBottomSheet.tsx Adds inclusion-fee preview override and adjusts total fee computation accordingly.
src/i18n/locales/en/translations.json Adds new transaction settings strings for network + priority tiers.
src/i18n/locales/pt/translations.json Adds Portuguese strings for network + priority tiers.
src/helpers/testUtils.tsx Refactors test render helper to use RTL wrapper to preserve providers across rerenders.
tests/services/stellar.test.ts Adds coverage for fee presets + congestion→recommended mapping + fallback behavior.
tests/hooks/useNetworkFees.test.ts Updates tests for cache freeze/clear behavior (needs mock shape alignment).
tests/hooks/useInitialRecommendedFee.test.tsx Adds assertions for congestion→tier defaulting and manual override behavior.
tests/ducks/transactionSettings.test.ts Adds tests for storing/resetting feePriority.
tests/components/TransactionSettingsBottomSheet.test.tsx Adds integration tests for tier selection, scaling, locking, and preview behavior.
tests/components/screens/SwapScreen/SwapAmountScreen.test.tsx Updates mocks for new swap settings fields and fee cache clearing.
tests/components/FeeBreakdownBottomSheet.test.tsx Adds tests for inclusion fee override preview behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/services/stellar.ts
Comment on lines +194 to +198
feePresets = {
[FeePriority.LOW]: stroopToXlm(maxFee.p10).toFixed(),
[FeePriority.MEDIUM]: stroopToXlm(maxFee.p50).toFixed(),
[FeePriority.HIGH]: stroopToXlm(maxFee.p90).toFixed(),
};
Comment on lines 36 to 40
expect(first.result.current.networkCongestion).toBe(NetworkCongestion.LOW);
expect(first.result.current.recommendedFee).toBe("");

// Flush the fetch promise + its setState.
await act(async () => {
await Promise.resolve();
});
await flushPromises();
expect(first.result.current.networkCongestion).toBe(NetworkCongestion.HIGH);
Comment on lines +420 to +424
const handleFeePriorityChange = useCallback(
(value: string | number) => {
feeInteractedRef.current = true;
const priority = value as FeePriority;
// Preset tiers derive their shown value automatically; switching to
Comment on lines +72 to +75
mockGetNetworkFees.mockResolvedValue({
recommendedFee: "0.005",
networkCongestion: NetworkCongestion.HIGH,
});
@leofelix077 leofelix077 added enhancement New feature or request and removed wip work in progress don't review yet Work in Progress / Draft PR / Code Review adjustments being worked on labels Jun 30, 2026
@CassioMG

Copy link
Copy Markdown
Contributor

Code review

Found 1 issue:

  1. handleFeePriorityChange sets feeInteractedRef.current = true before the guard that ignores the tap, so a no-op press permanently disables the tier sync (bug due to the early return in src/components/TransactionSettingsBottomSheet.tsx: if (priority !== FeePriority.CUSTOM && !areFeePresetsLoaded) { return; })

If the user taps Low/Med/High before the presets have loaded, the tap is discarded and selectedFeePriority is unchanged — but the ref is already flipped. That ref is a one-way switch gating the sync effect, so when useInitialRecommendedFee later writes the congestion-derived tier into the store, the SegmentedControl stays stuck on the stale tier for the life of the mounted sheet. Setting the ref after the guard fixes it.

const handleFeePriorityChange = useCallback(
(value: string | number) => {
feeInteractedRef.current = true;
const priority = value as FeePriority;
// Keep preset tiers unavailable until we have a fetched preset snapshot.
// Custom remains selectable so users can still set a manual fee.
if (priority !== FeePriority.CUSTOM && !areFeePresetsLoaded) {
return;
}
// Preset tiers derive their shown value automatically; switching to

The effect it disables:

// user picks a tier or types, their choice sticks (no further syncing).
const feeInteractedRef = useRef(false);
const [selectedFeePriority, setSelectedFeePriority] =
useState<FeePriority>(storeFeePriority);
useEffect(() => {
if (!feeInteractedRef.current) {
setSelectedFeePriority(storeFeePriority);
}
}, [storeFeePriority]);
// The editable value, used while on the "Custom" tier.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

# Conflicts:
#	src/components/sds/SegmentedControl.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Mobile] Improve Configure Fees Design

3 participants