Skip to content

Conversation

@NeOMakinG
Copy link
Collaborator

@NeOMakinG NeOMakinG commented Aug 20, 2025

Description

  • Add feature flag to remove other pages and use the new one with different order and stake/unstake feature on rfox
  • Add unstake/stake modal and add buttons to consume it in the rfox section of the fox ecosystem page
  • change icon of the section
  • Add rewards distributions notifications and actions
  • Update action card styling to have rows with cooldown and tx links

Issue (if applicable)

closes #10264

Risk

Low

High Risk PRs Require 2 approvals

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

Testing

  • Enable the flag if it's not enabled (should be on develop)
  • Try to use stake/unstake from the fox ecossytem page
  • If you have current distribution rewards, you should get notifications/action in the action center
  • If you have pending rewards, check the notifications/actions cards
  • You can also monkey patch to see that notifications are resolved if the reward distribution has been completed (maybe only for engineers)

Engineering

diff --git a/src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx b/src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
index 7c33381b8a..65d6088bfc 100644
--- a/src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
+++ b/src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
@@ -98,7 +98,7 @@ export const useRfoxRewardDistributionActionSubscriber = () => {
     Object.entries(rewardDistributionsByTxId).forEach(([_, distribution]) => {
       if (!distribution) return
 
-      if (distribution.status === 'complete' && distribution.txId) {
+      if (distribution.status === 'pending') {
         const actionId = `reward-distribution-${distribution.epoch}-${distribution.stakingContract}-${distribution.rewardAddress}`
 
         if (!actions[actionId]) return

You can use that to resolve pending reward distribution notifications if you want to check out how it works, there will be an infinite loop because the reward isn't truely resolved so the app will crash but notifications will be completed to show that it works

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

Screenshots (if applicable)

image image image image image image

Summary by CodeRabbit

  • New Features

    • Feature-flagged Fox Ecosystem page with nav entry and icon; enabled in development, off by default in production.
    • Action Center: reward distribution notifications plus dedicated reward-distribution and RFOX-initiated action cards.
    • RFOX UI: Stake, Unstake, Claim modals and updated flows that route to the ecosystem page when the flag is enabled.
  • Documentation / Content

    • Expanded user-facing translations for staking, unstaking, claims, and reward messages.
  • Chores

    • Feature flag added and wired into config, preferences, and test mocks.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 20, 2025

📝 Walkthrough

Walkthrough

Adds a feature-flagged FOX Ecosystem page and icon; replaces the Manage flow with modal-driven Stake/Unstake/Claim flows; introduces RewardDistribution action type, subscriber, ActionCenter cards and toasts; changes cooldown hook shape and consumers; updates translations, config flag, mocks, routing, and dialog/modal handling.

Changes

Cohort / File(s) Summary
Env & Config / Feature flags
/.env, /.env.development, src/config.ts, src/state/slices/preferencesSlice/preferencesSlice.ts, src/test/mocks/store.ts
Add VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE (default false, dev true); validate flag in config; expose RfoxFoxEcosystemPage feature flag and initialize mock.
Routing & Navigation
src/Routes/RoutesCommon.tsx, src/pages/Explore/Explore.tsx
Add gated /fox-ecosystem/* route with FoxPageIcon; adjust gating for /rfox/* and /fox; Explore routes to ecosystem when flag enabled.
FOX Ecosystem page & icon
src/pages/Fox/FoxEcosystemPage.tsx, src/components/Icons/FoxPageIcon.tsx
New FoxEcosystemPage composing Fox subcomponents and lazy RFOXSection; add FoxPageIcon.
RFOX section & modal flows
src/pages/Fox/components/RFOXSection.tsx, src/pages/RFOX/components/StakeModal.tsx, src/pages/RFOX/components/UnstakeModal.tsx, src/pages/RFOX/components/ClaimModal.tsx
Replace Manage with Stake/Unstake/Claim buttons when flag enabled; add modal components; auto-open claim modal via route state; sync staking account state.
Action Center UI & components
src/components/Layout/Header/ActionCenter/ActionCenter.tsx, src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx, src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx, src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx, src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
Add RewardDistribution notification and action card, add RfoxInitiatedActionCard, render new cards; minor footer refactor.
Subscribers & state types
src/hooks/useActionCenterSubscribers/useActionCenterSubscribers.ts, src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx, src/state/slices/actionSlice/types.ts
Register reward-distribution subscriber; add ActionType.RewardDistribution, RewardDistributionAction, cooldownPeriodSeconds metadata, and isRewardDistributionAction guard.
Cooldown hook & consumers
src/pages/RFOX/hooks/useCooldownPeriodQuery.ts, src/pages/RFOX/components/Stake/StakeInput.tsx, src/pages/RFOX/components/Stake/components/StakeSummary.tsx, src/pages/RFOX/components/Unstake/UnstakeInput.tsx, src/pages/RFOX/components/Unstake/components/UnstakeSummary.tsx, src/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsx, src/pages/RFOX/components/Stake/types.ts, src/pages/RFOX/components/Claim/types.ts
Change cooldown mapper to return object { cooldownPeriod, cooldownPeriodSeconds }; consumers renamed to cooldownPeriodData with optional chaining; some setStepIndex props made optional.
Translations
src/assets/translations/en/main.json
Add actionCenter.rewardDistribution keys, RFOX strings (stakeInitiated, unstakeInitiated, claimWithdraw), claim/reward pending/complete keys, TCY.claim notice adjustment, and minor punctuation tweaks.
UI gating & navigation updates
src/pages/Fox/components/FoxTokenHeader.tsx, various RFOX claim components (ClaimSelect, ClaimConfirm, RfoxClaimActionCard, etc.)
Add useFeatureFlag('RfoxFoxEcosystemPage') checks to conditionally render buttons and route to /fox-ecosystem when enabled.
Dialog / event handling / small UX
src/components/Modal/components/Dialog.tsx, src/components/Modal/components/DialogCloseButton.tsx, src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx, src/components/AssetSearch/components/AssetRow.tsx, src/context/DialogContextProvider/DialogContextProvider.tsx
Overlay click uses internal handleClose to stop propagation; DialogCloseButton explicitly stops propagation; added useSafeDialog safe hook and removed snapPoint from context; responsive Drawer-close logic for connect button; AssetRow click stops propagation.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Explore as Explore Page
  participant Router
  participant FoxEco as FoxEcosystemPage
  participant RFOXSection

  User->>Explore: Click FOX
  Explore->>Explore: check featureFlag RfoxFoxEcosystemPage
  alt Flag enabled
    Explore->>Router: navigate('/fox-ecosystem')
    Router->>FoxEco: Render page
    FoxEco->>RFOXSection: lazy-load RFOXSection (Suspense)
  else Flag disabled
    Explore->>Router: navigate('/fox')
  end
Loading
sequenceDiagram
  autonumber
  participant Subscriber as useRfoxRewardDistributionActionSubscriber
  participant Selectors as Redux selectors
  participant Query as LifetimeRewardDistributionsQuery
  participant Store as ActionCenter (upsertAction)
  participant Toast as Notification system
  participant ACUI as RewardDistributionNotification

  Subscriber->>Selectors: select arbitrum accountIds -> addresses
  Subscriber->>Query: fetch lifetime distributions for addresses
  loop per distribution
    alt pending
      Subscriber->>Store: upsert Action(status=Initiated)
      Subscriber->>Toast: show info toast (RewardDistributionNotification)
    else complete (with txId)
      Subscriber->>Store: upsert Action(status=Complete)
      Subscriber->>Toast: show success toast (RewardDistributionNotification)
    end
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant RFOXSection
  participant StakeModal
  participant UnstakeModal
  User->>RFOXSection: Click "Stake"
  RFOXSection->>StakeModal: set isOpen=true
  User->>RFOXSection: Click "Unstake"
  RFOXSection->>UnstakeModal: set isOpen=true
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Assessment against linked issues

Objective Addressed Explanation
Create FOX Ecosystem route and gate legacy routes [#10264]
Replace “Manage” with Stake/Unstake modals on FOX Ecosystem page [#10264]
Action Center shows cooldowns and claimable rewards [#10264] The PR adds reward-distribution and RFOX action cards and subscribers; explicit gating logic that shows the Action Center only when user has active cooldowns or claimable FOX is not clearly present.
Toasts for reward received & cooldown finishes [#10264] Reward distribution toasts are implemented; explicit cooldown-finish detection and dedicated "Claim now" cooldown-finish toast CTA are not clearly implemented in the diff.

Out-of-scope changes

Code Change Explanation
TCY claim notice text change (src/assets/translations/en/main.json) Plain translation copy/punctuation adjustment unrelated to FOX Ecosystem objectives.
KeepKey backup prompt punctuation tweak (src/assets/translations/en/main.json) Minor copy punctuation change not tied to feature goals.

Possibly related PRs

Suggested reviewers

  • premiumjibles
  • gomesalexandre

Poem

I hop through branches, code in paw,
Brought a fox page, shiny and raw.
Modals bloom where Manage used to be,
Toasts sing rewards for all to see.
Cooldowns count down — carrots safe and free. 🥕🦊

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch rfox-fox-ecosystem

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@NeOMakinG NeOMakinG marked this pull request as ready for review August 21, 2025 13:01
@NeOMakinG NeOMakinG requested a review from a team as a code owner August 21, 2025 13:01
@NeOMakinG NeOMakinG marked this pull request as draft August 21, 2025 13:02
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: 32

Caution

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

⚠️ Outside diff range comments (8)
.env (1)

190-191: Fix dotenv key order to satisfy dotenv-linter.

The linter suggests ordering feature flags alphabetically/grouped. Move VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE before VITE_FEATURE_TX_HISTORY_BYE_BYE.

Apply this diff:

-VITE_FEATURE_TX_HISTORY_BYE_BYE=false
-VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=false
+VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=false
+VITE_FEATURE_TX_HISTORY_BYE_BYE=false
.env.development (1)

75-77: Fix dotenv key order to satisfy dotenv-linter.

Keep feature flags ordered consistently to avoid linter warnings.

Apply this diff:

-VITE_FEATURE_TX_HISTORY_BYE_BYE=true
-VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=true
+VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=true
+VITE_FEATURE_TX_HISTORY_BYE_BYE=true
src/components/Layout/Header/ActionCenter/ActionCenter.tsx (2)

188-190: Fix malformed CSS calc() on Mobile drawer height

There's a missing closing parenthesis that will break layout on mobile.

Apply this diff:

-            height='calc(100vh - 70px - (env(safe-area-inset-top) - var(--safe-area-inset-top))'
+            height='calc(100vh - 70px - (env(safe-area-inset-top) - var(--safe-area-inset-top)))'

114-118: Filter out null action cards to avoid blank content without EmptyState

When some mapped actions resolve to null (e.g., missing order in LimitOrder), maybeActionCards.length remains > 0, so EmptyState doesn’t render and the drawer shows nothing. Filter falsy cards before the emptiness check.

Apply this diff:

   const maybeActionCards = useMemo(() => {
     return actions.map(action => {
       const actionsCards = (() => {
         switch (action.type) {
           /* ...cases... */
           default:
             return null
         }
       })()

       return actionsCards
     })
   }, [actions, ordersByActionId, swapsById])

-  const actionCardsOrEmpty = useMemo(() => {
-    if (!maybeActionCards.length) return <EmptyState onClose={closeDrawer} />
-
-    return maybeActionCards
-  }, [maybeActionCards, closeDrawer])
+  const actionCards = useMemo(
+    () => maybeActionCards.filter(Boolean),
+    [maybeActionCards],
+  )
+
+  const actionCardsOrEmpty = useMemo(() => {
+    if (!actionCards.length) return <EmptyState onClose={closeDrawer} />
+
+    return actionCards
+  }, [actionCards, closeDrawer])

Also applies to: 56-112

src/pages/RFOX/components/Unstake/components/UnstakeSummary.tsx (1)

75-82: Guard divide-by-zero in share-of-pool calculation

newContractBalanceOfCryptoBaseUnit can be zero, yielding invalid output. Add a zero-denominator guard.

Apply this diff:

-  const newShareOfPoolPercentage = useMemo(
-    () =>
-      bnOrZero(userStakingBalanceOfCryptoBaseUnit)
-        .minus(amountCryptoBaseUnit ?? 0)
-        .div(newContractBalanceOfCryptoBaseUnit ?? 0)
-        .toFixed(4),
-    [amountCryptoBaseUnit, newContractBalanceOfCryptoBaseUnit, userStakingBalanceOfCryptoBaseUnit],
-  )
+  const newShareOfPoolPercentage = useMemo(() => {
+    const denom = bnOrZero(newContractBalanceOfCryptoBaseUnit)
+    if (denom.isZero()) return '0'
+    return bnOrZero(userStakingBalanceOfCryptoBaseUnit)
+      .minus(amountCryptoBaseUnit ?? 0)
+      .div(denom)
+      .toFixed(4)
+  }, [amountCryptoBaseUnit, newContractBalanceOfCryptoBaseUnit, userStakingBalanceOfCryptoBaseUnit])
src/state/slices/preferencesSlice/preferencesSlice.ts (1)

21-95: Normalize acronym casing for RFOX feature flag

For consistency with other flags (e.g. RFOX, RFOX_LP, FoxPageRFOX), please rename all instances of RfoxFoxEcosystemPage to RFOXFoxEcosystemPage. This touches type definitions, config keys, mocks, selectors, hook calls, and route logic:

• src/config.ts
VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE getConfig

• src/test/mocks/store.ts (line 162)
RfoxFoxEcosystemPage: false

• src/state/slices/preferencesSlice/preferencesSlice.ts
– Type and initial state at lines 91 and 215

• src/pages/Fox/components/RFOXSection.tsx
– Hook call and conditional render around line 100 and use in props at 252/271

• src/pages/Explore/Explore.tsx
– Hook call and navigation logic at lines 91–112

• src/Routes/RoutesCommon.tsx
– Route disable flags at lines 255, 265, and 274

Suggested diff (example for slice and hook usage):

--- a/src/state/slices/preferencesSlice/preferencesSlice.ts
+++ b/src/state/slices/preferencesSlice/preferencesSlice.ts
@@ -91,7 +91,7 @@ export type FeatureFlags = {
-  RfoxFoxEcosystemPage: boolean
+  RFOXFoxEcosystemPage: boolean
--- a/src/pages/Fox/components/RFOXSection.tsx
+++ b/src/pages/Fox/components/RFOXSection.tsx
@@ -100,7 +100,7 @@ export const RFOXSection: React.FC = () => {
-  const isRFOXFoxEcosystemPageEnabled = useFeatureFlag('RfoxFoxEcosystemPage')
+  const isRFOXFoxEcosystemPageEnabled = useFeatureFlag('RFOXFoxEcosystemPage')

Please update all matching references accordingly to maintain uniform casing.

src/pages/RFOX/components/Unstake/UnstakeInput.tsx (2)

431-437: Type mismatch: pass a string fallback, not a number

amountCryptoPrecision is typed as string. Using number fallback (0) widens the type and can break UnstakeSummary props.

Apply this diff:

-              <UnstakeSummary
-                amountCryptoPrecision={amountCryptoPrecision ?? 0}
+              <UnstakeSummary
+                amountCryptoPrecision={amountCryptoPrecision ?? '0'}

214-216: Remove dead stub or wire account selection callback

handleAccountIdChange is a no-op. Either remove it or wire it once multi-account is implemented to avoid confusion.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b4dc92a and 879d2ef.

📒 Files selected for processing (26)
  • .env (1 hunks)
  • .env.development (1 hunks)
  • src/Routes/RoutesCommon.tsx (3 hunks)
  • src/assets/translations/en/main.json (5 hunks)
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx (2 hunks)
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx (4 hunks)
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx (1 hunks)
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx (1 hunks)
  • src/config.ts (1 hunks)
  • src/hooks/useActionCenterSubscribers/useActionCenterSubscribers.ts (1 hunks)
  • src/pages/Explore/Explore.tsx (3 hunks)
  • src/pages/Fox/FoxEcosystemPage.tsx (1 hunks)
  • src/pages/Fox/components/RFOXSection.tsx (6 hunks)
  • src/pages/RFOX/components/Stake/StakeInput.tsx (4 hunks)
  • src/pages/RFOX/components/Stake/components/StakeSummary.tsx (2 hunks)
  • src/pages/RFOX/components/Stake/types.ts (1 hunks)
  • src/pages/RFOX/components/StakeModal.tsx (1 hunks)
  • src/pages/RFOX/components/Unstake/UnstakeInput.tsx (3 hunks)
  • src/pages/RFOX/components/Unstake/components/UnstakeSummary.tsx (2 hunks)
  • src/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsx (2 hunks)
  • src/pages/RFOX/components/UnstakeModal.tsx (1 hunks)
  • src/pages/RFOX/hooks/useCooldownPeriodQuery.ts (1 hunks)
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx (1 hunks)
  • src/state/slices/actionSlice/types.ts (5 hunks)
  • src/state/slices/preferencesSlice/preferencesSlice.ts (2 hunks)
  • src/test/mocks/store.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{ts,tsx}: ALWAYS use Result<T, E> pattern for error handling in swappers and APIs
ALWAYS use Ok() and Err() from @sniptt/monads for monadic error handling
ALWAYS use custom error classes from @shapeshiftoss/errors
ALWAYS provide meaningful error codes for internationalization
ALWAYS include relevant details in error objects
ALWAYS wrap async operations in try-catch blocks
ALWAYS use AsyncResultOf utility for converting promises to Results
ALWAYS provide fallback error handling
ALWAYS use timeoutMonadic for API calls
ALWAYS provide appropriate timeout values for API calls
ALWAYS handle timeout errors gracefully
ALWAYS validate inputs before processing
ALWAYS provide clear validation error messages
ALWAYS use early returns for validation failures
ALWAYS log errors for debugging
ALWAYS use structured logging for errors
ALWAYS include relevant context in error logs
Throwing errors instead of using monadic patterns is an anti-pattern
Missing try-catch blocks for async operations is an anti-pattern
Generic error messages without context are an anti-pattern
Not handling specific error types is an anti-pattern
Missing timeout handling is an anti-pattern
No input validation is an anti-pattern
Poor error logging is an anti-pattern
Using any for error types is an anti-pattern
Missing error codes for internationalization is an anti-pattern
No fallback error handling is an anti-pattern
Console.error without structured logging is an anti-pattern

**/*.{ts,tsx}: ALWAYS use camelCase for variables, functions, and methods
ALWAYS use descriptive names that explain the purpose for variables and functions
ALWAYS use verb prefixes for functions that perform actions
ALWAYS use PascalCase for types, interfaces, and enums
ALWAYS use descriptive names that indicate the structure for types, interfaces, and enums
ALWAYS use suffixes like Props, State, Config, Type when appropriate for types and interfaces
ALWAYS use UPPER_SNAKE_CASE for constants and configuration values
ALWAYS use d...

Files:

  • src/pages/RFOX/components/Stake/types.ts
  • src/pages/RFOX/components/StakeModal.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/config.ts
  • src/pages/RFOX/components/Stake/components/StakeSummary.tsx
  • src/hooks/useActionCenterSubscribers/useActionCenterSubscribers.ts
  • src/pages/Fox/FoxEcosystemPage.tsx
  • src/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsx
  • src/pages/RFOX/hooks/useCooldownPeriodQuery.ts
  • src/pages/RFOX/components/Unstake/components/UnstakeSummary.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/components/Stake/StakeInput.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
  • src/pages/Explore/Explore.tsx
  • src/pages/RFOX/components/Unstake/UnstakeInput.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/state/slices/actionSlice/types.ts
  • src/test/mocks/store.ts
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/Routes/RoutesCommon.tsx
  • src/state/slices/preferencesSlice/preferencesSlice.ts
**/*

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*: ALWAYS use appropriate file extensions
Flag files without kebab-case

Files:

  • src/pages/RFOX/components/Stake/types.ts
  • src/pages/RFOX/components/StakeModal.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/config.ts
  • src/pages/RFOX/components/Stake/components/StakeSummary.tsx
  • src/hooks/useActionCenterSubscribers/useActionCenterSubscribers.ts
  • src/pages/Fox/FoxEcosystemPage.tsx
  • src/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsx
  • src/pages/RFOX/hooks/useCooldownPeriodQuery.ts
  • src/pages/RFOX/components/Unstake/components/UnstakeSummary.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/components/Stake/StakeInput.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
  • src/pages/Explore/Explore.tsx
  • src/pages/RFOX/components/Unstake/UnstakeInput.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/state/slices/actionSlice/types.ts
  • src/test/mocks/store.ts
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/Routes/RoutesCommon.tsx
  • src/state/slices/preferencesSlice/preferencesSlice.ts
  • src/assets/translations/en/main.json
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

USE Redux only for global state shared across multiple places

Files:

  • src/pages/RFOX/components/Stake/types.ts
  • src/pages/RFOX/components/StakeModal.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/config.ts
  • src/pages/RFOX/components/Stake/components/StakeSummary.tsx
  • src/hooks/useActionCenterSubscribers/useActionCenterSubscribers.ts
  • src/pages/Fox/FoxEcosystemPage.tsx
  • src/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsx
  • src/pages/RFOX/hooks/useCooldownPeriodQuery.ts
  • src/pages/RFOX/components/Unstake/components/UnstakeSummary.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/components/Stake/StakeInput.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
  • src/pages/Explore/Explore.tsx
  • src/pages/RFOX/components/Unstake/UnstakeInput.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/state/slices/actionSlice/types.ts
  • src/test/mocks/store.ts
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/Routes/RoutesCommon.tsx
  • src/state/slices/preferencesSlice/preferencesSlice.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.tsx: ALWAYS wrap components in error boundaries
ALWAYS provide user-friendly fallback components in error boundaries
ALWAYS log errors for debugging in error boundaries
ALWAYS use useErrorToast hook for displaying errors
ALWAYS provide translated error messages in error toasts
ALWAYS handle different error types appropriately in error toasts
Missing error boundaries in React components is an anti-pattern

**/*.tsx: ALWAYS use PascalCase for React component names
ALWAYS use descriptive names that indicate the component's purpose
ALWAYS match the component name to the file name
Flag components without PascalCase
Flag default exports for components

Files:

  • src/pages/RFOX/components/StakeModal.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/pages/RFOX/components/Stake/components/StakeSummary.tsx
  • src/pages/Fox/FoxEcosystemPage.tsx
  • src/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsx
  • src/pages/RFOX/components/Unstake/components/UnstakeSummary.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/components/Stake/StakeInput.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
  • src/pages/Explore/Explore.tsx
  • src/pages/RFOX/components/Unstake/UnstakeInput.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/Routes/RoutesCommon.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

**/*.{tsx,jsx}: ALWAYS use useMemo for expensive computations, object/array creations, and filtered data
ALWAYS use useMemo for derived values and computed properties
ALWAYS use useMemo for conditional values and simple transformations
ALWAYS use useCallback for event handlers and functions passed as props
ALWAYS use useCallback for any function that could be passed as a prop or dependency
ALWAYS include all dependencies in useEffect, useMemo, useCallback dependency arrays
NEVER use // eslint-disable-next-line react-hooks/exhaustive-deps unless absolutely necessary
ALWAYS explain why dependencies are excluded if using eslint disable
ALWAYS use named exports for components
NEVER use default exports for components
KEEP component files under 200 lines when possible
BREAK DOWN large components into smaller, reusable pieces
EXTRACT complex logic into custom hooks
USE local state for component-level state
LIFT state up when needed across multiple components
USE Context for avoiding prop drilling
ALWAYS wrap components in error boundaries for production
ALWAYS handle async errors properly
ALWAYS provide user-friendly error messages
ALWAYS use virtualization for lists with 100+ items
ALWAYS implement proper key props for list items
ALWAYS lazy load heavy components
ALWAYS use React.lazy for code splitting
Components receiving props are wrapped with memo

Files:

  • src/pages/RFOX/components/StakeModal.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/pages/RFOX/components/Stake/components/StakeSummary.tsx
  • src/pages/Fox/FoxEcosystemPage.tsx
  • src/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsx
  • src/pages/RFOX/components/Unstake/components/UnstakeSummary.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/components/Stake/StakeInput.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
  • src/pages/Explore/Explore.tsx
  • src/pages/RFOX/components/Unstake/UnstakeInput.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/Routes/RoutesCommon.tsx
**/use*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/use*.{ts,tsx}: ALWAYS use use prefix for custom hooks
ALWAYS use descriptive names that indicate the hook's purpose
ALWAYS use camelCase after the use prefix for custom hooks

Files:

  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/hooks/useActionCenterSubscribers/useActionCenterSubscribers.ts
  • src/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsx
  • src/pages/RFOX/hooks/useCooldownPeriodQuery.ts
🧠 Learnings (6)
📚 Learning: 2025-07-29T15:04:28.083Z
Learnt from: NeOMakinG
PR: shapeshift/web#10139
File: src/components/MultiHopTrade/components/TradeConfirm/components/ExpandableStepperSteps.tsx:109-115
Timestamp: 2025-07-29T15:04:28.083Z
Learning: In src/components/MultiHopTrade/components/TradeConfirm/components/ExpandableStepperSteps.tsx, the component is used under an umbrella that 100% of the time contains the quote, making the type assertion `activeTradeQuote?.steps[currentHopIndex] as TradeQuoteStep` safe. Adding conditional returns before hooks would violate React's Rules of Hooks.

Applied to files:

  • src/pages/RFOX/components/Stake/types.ts
📚 Learning: 2025-08-08T14:59:40.422Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/pages/Explore/ExploreCategory.tsx:231-238
Timestamp: 2025-08-08T14:59:40.422Z
Learning: In src/pages/Explore/ExploreCategory.tsx, for the PageHeader filter trigger, NeOMakinG considers changing a clickable Chakra Icon to IconButton too nitpicky for this PR and prefers to keep the current Icon-based trigger; such minor a11y/UI nitpicks should be deferred to a follow-up if needed.

Applied to files:

  • src/pages/Explore/Explore.tsx
📚 Learning: 2025-08-06T09:47:29.865Z
Learnt from: NeOMakinG
PR: shapeshift/web#10191
File: src/pages/Explore/Explore.tsx:174-178
Timestamp: 2025-08-06T09:47:29.865Z
Learning: In src/pages/Explore/Explore.tsx, NeOMakinG prefers keeping complex height calculations inline rather than extracting them to useMemo, prioritizing perceived performance/execution speed over code readability, even when the calculation would only run when the dependency (isSearching) changes.

Applied to files:

  • src/pages/Explore/Explore.tsx
📚 Learning: 2025-08-06T11:02:09.501Z
Learnt from: NeOMakinG
PR: shapeshift/web#10191
File: src/pages/Explore/Explore.tsx:54-72
Timestamp: 2025-08-06T11:02:09.501Z
Learning: In src/pages/Explore/Explore.tsx, NeOMakinG indicated that memoizing the ExploreCard component would be unnecessary, questioning "why would we memoize that", suggesting that like CategoryCard, this component doesn't benefit from memoization despite receiving props, likely due to its specific usage patterns in the carousel with static content.

Applied to files:

  • src/pages/Explore/Explore.tsx
📚 Learning: 2025-08-06T11:44:30.697Z
Learnt from: NeOMakinG
PR: shapeshift/web#10191
File: src/pages/Explore/Explore.tsx:81-83
Timestamp: 2025-08-06T11:44:30.697Z
Learning: In src/pages/Explore/Explore.tsx, NeOMakinG questioned the suggestion to memoize a simple static carouselOptions object ({ loop: true }), indicating that static objects that never change should remain as module-level constants rather than being memoized inside components, as memoization would add unnecessary overhead for truly static values.

Applied to files:

  • src/pages/Explore/Explore.tsx
📚 Learning: 2025-08-14T17:54:32.563Z
Learnt from: gomesalexandre
PR: shapeshift/web#10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx:97-108
Timestamp: 2025-08-14T17:54:32.563Z
Learning: In ReusableLpStatus component (src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx), the txAssets dependency is stable from first render because poolAsset, baseAsset, actionSide, and action are all defined first render, making the current txAssetsStatuses initialization pattern safe without needing useEffect synchronization.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
🧬 Code graph analysis (17)
src/pages/RFOX/components/StakeModal.tsx (2)
src/components/Modal/components/Dialog.tsx (1)
  • Dialog (117-117)
src/components/Modal/components/DialogCloseButton.tsx (1)
  • DialogCloseButton (15-38)
src/components/Layout/Header/ActionCenter/ActionCenter.tsx (1)
src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx (1)
  • RewardDistributionActionCard (26-120)
src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx (8)
src/state/store.ts (2)
  • useAppSelector (143-143)
  • useAppDispatch (151-151)
src/state/slices/portfolioSlice/selectors.ts (1)
  • selectAccountIdsByChainIdFilter (671-683)
packages/caip/src/constants.ts (1)
  • arbitrumChainId (66-66)
src/components/Layout/Header/ActionCenter/ActionCenterContext.tsx (1)
  • useActionCenterContext (34-38)
src/hooks/useNotificationToast.tsx (1)
  • useNotificationToast (9-19)
src/state/slices/actionSlice/actionSlice.ts (1)
  • actionSlice (11-39)
src/pages/RFOX/hooks/useLifetimeRewardDistributionsQuery.ts (2)
  • useLifetimeRewardDistributionsQuery (25-67)
  • RewardDistributionWithMetadata (13-19)
src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx (1)
  • RewardDistributionNotification (27-88)
src/hooks/useActionCenterSubscribers/useActionCenterSubscribers.ts (1)
src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx (1)
  • useRfoxRewardDistributionActionSubscriber (15-146)
src/pages/Fox/FoxEcosystemPage.tsx (6)
src/pages/Fox/components/FoxHeader.tsx (1)
  • FoxHeader (21-80)
src/pages/Fox/components/RFOXSection.tsx (1)
  • RFOXSection (96-408)
src/utils/makeSuspenseful.tsx (1)
  • makeSuspenseful (34-44)
src/pages/Fox/hooks/useFoxPageContext.tsx (1)
  • FoxPageProvider (21-64)
src/components/Layout/Main.tsx (1)
  • Main (16-27)
src/pages/RFOX/hooks/useRfoxContext.tsx (1)
  • RFOXProvider (34-76)
src/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsx (1)
src/pages/RFOX/hooks/useCooldownPeriodQuery.ts (1)
  • useCooldownPeriodQuery (10-29)
src/pages/RFOX/hooks/useCooldownPeriodQuery.ts (1)
src/lib/utils/time.ts (1)
  • formatSecondsToDuration (12-18)
src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx (10)
src/pages/RFOX/hooks/useLifetimeRewardDistributionsQuery.ts (1)
  • RewardDistributionWithMetadata (13-19)
src/state/store.ts (1)
  • useAppSelector (143-143)
src/state/slices/assetsSlice/selectors.ts (1)
  • selectAssetById (16-20)
packages/caip/src/constants.ts (1)
  • thorchainAssetId (49-49)
src/state/slices/actionSlice/actionSlice.ts (1)
  • actionSlice (11-39)
src/components/Text/Text.tsx (2)
  • TextPropTypes (8-11)
  • Text (19-83)
src/lib/math.ts (1)
  • fromBaseUnit (10-24)
src/components/Layout/Header/ActionCenter/components/Notifications/NotificationWrapper.tsx (1)
  • NotificationWrapper (15-38)
src/components/AssetIconWithBadge.tsx (1)
  • AssetIconWithBadge (116-202)
src/components/Layout/Header/ActionCenter/components/ActionStatusIcon.tsx (1)
  • ActionStatusIcon (15-63)
src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx (11)
src/state/slices/actionSlice/types.ts (1)
  • RewardDistributionAction (156-162)
src/state/store.ts (1)
  • useAppSelector (143-143)
src/state/slices/assetsSlice/selectors.ts (1)
  • selectAssetById (16-20)
packages/caip/src/constants.ts (1)
  • thorchainAssetId (49-49)
src/components/Text/Text.tsx (2)
  • TextPropTypes (8-11)
  • Text (19-83)
src/lib/math.ts (1)
  • fromBaseUnit (10-24)
src/components/AssetIconWithBadge.tsx (1)
  • AssetIconWithBadge (116-202)
src/components/Layout/Header/ActionCenter/components/ActionStatusIcon.tsx (1)
  • ActionStatusIcon (15-63)
src/lib/getTxLink.ts (1)
  • getTxLink (84-152)
src/components/Layout/Header/ActionCenter/components/ActionStatusTag.tsx (1)
  • ActionStatusTag (20-88)
src/components/Layout/Header/ActionCenter/components/ActionCard.tsx (1)
  • ActionCard (28-111)
src/pages/RFOX/components/Stake/StakeInput.tsx (1)
src/pages/RFOX/hooks/useCooldownPeriodQuery.ts (1)
  • useCooldownPeriodQuery (10-29)
src/pages/RFOX/components/UnstakeModal.tsx (2)
src/components/Modal/components/Dialog.tsx (1)
  • Dialog (117-117)
src/components/Modal/components/DialogCloseButton.tsx (1)
  • DialogCloseButton (15-38)
src/pages/RFOX/components/Unstake/UnstakeInput.tsx (2)
src/pages/RFOX/hooks/useCooldownPeriodQuery.ts (1)
  • useCooldownPeriodQuery (10-29)
src/lib/math.ts (1)
  • toBaseUnit (26-34)
src/pages/Fox/components/RFOXSection.tsx (6)
src/pages/Fox/hooks/useFoxPageContext.tsx (1)
  • useFoxPageContext (66-72)
src/pages/RFOX/hooks/useRfoxContext.tsx (1)
  • useRFOXContext (78-84)
src/state/store.ts (1)
  • useAppDispatch (151-151)
packages/caip/src/constants.ts (1)
  • foxOnArbitrumOneAssetId (30-31)
src/pages/RFOX/components/StakeModal.tsx (1)
  • StakeModal (15-31)
src/pages/RFOX/components/UnstakeModal.tsx (1)
  • UnstakeModal (15-31)
src/state/slices/actionSlice/types.ts (2)
src/pages/RFOX/types.ts (1)
  • RewardDistribution (27-38)
src/pages/RFOX/hooks/useLifetimeRewardDistributionsQuery.ts (1)
  • RewardDistributionWithMetadata (13-19)
src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx (2)
src/components/Text/Text.tsx (1)
  • Text (19-83)
src/lib/utils/index.ts (1)
  • firstFourLastFour (22-23)
src/Routes/RoutesCommon.tsx (2)
src/config.ts (1)
  • getConfig (201-203)
src/pages/Fox/FoxEcosystemPage.tsx (1)
  • FoxEcosystemPage (28-44)
src/state/slices/preferencesSlice/preferencesSlice.ts (1)
src/config.ts (1)
  • getConfig (201-203)
🪛 dotenv-linter (3.3.0)
.env

[warning] 190-190: [UnorderedKey] The VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE key should go before the VITE_FEATURE_TX_HISTORY_BYE_BYE key

(UnorderedKey)

.env.development

[warning] 76-76: [UnorderedKey] The VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE key should go before the VITE_FEATURE_TX_HISTORY_BYE_BYE key

(UnorderedKey)

🪛 Biome (2.1.2)
src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx

[error] 98-98: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install and Cache
🔇 Additional comments (36)
src/pages/RFOX/components/Stake/types.ts (1)

14-15: No unguarded setStepIndex calls in RFOX Stake components

I’ve verified that within src/pages/RFOX/components/Stake/:

  • Neither StakeInput.tsx nor StakeConfirm.tsx invoke setStepIndex directly—these components merely pass it through to child props.
  • The Stake and StakeRoutes wrappers only forward setStepIndex (never call it internally).
  • All RFOX consumers of StakeRouteProps (e.g. in Widget.tsx) supply a defined handler.

No direct setStepIndex( calls without a guard were found, so making it optional is safe here.

src/pages/RFOX/components/Stake/components/StakeSummary.tsx (1)

39-41: LGTM: Updated selector alias matches new cooldown query shape.

The switch to cooldownPeriodData aligns with the new { cooldownPeriod, cooldownPeriodSeconds } object and keeps the query contract local and explicit.

src/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsx (2)

82-82: LGTM: Hook now consumes the new cooldownPeriodData shape.

Good alignment with useCooldownPeriodQuery’s return type and consistent naming across files.


184-186: Default cooldownPeriodSeconds to 0

To prevent undefined metadata for zero-cooldown contracts and simplify downstream logic (including i18n messages), default cooldownPeriodSeconds to 0. The GenericTransactionMetadata type already defines cooldownPeriodSeconds?: number, so using ?? 0 preserves its narrower type.

  • File: src/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsx (lines 184–186)
  • Update:
-            cooldownPeriod: cooldownPeriodData?.cooldownPeriod,
-            cooldownPeriodSeconds: cooldownPeriodData?.cooldownPeriodSeconds,
+            cooldownPeriod: cooldownPeriodData?.cooldownPeriod,
+            cooldownPeriodSeconds: cooldownPeriodData?.cooldownPeriodSeconds ?? 0,
src/pages/RFOX/components/Stake/StakeInput.tsx (1)

273-274: LGTM: Local variable rename and usage update are consistent with the new query shape.

src/pages/RFOX/components/StakeModal.tsx (2)

18-25: Verify close behavior wires through parent onClose.

DialogCloseButton toggles dialog state via context; confirm it also triggers the onClose handler passed to Dialog so the parent state remains in sync and any cleanup logic runs.

Manual check:

  • Open the Stake modal.
  • Click the close button; assert parent isOpen state flips to false and any parent onClose side effects run exactly once.
  • Confirm Escape key and overlay clicks also invoke the same onClose.

18-29: LGTM: modal structure, translation, and named export.

Dialog composition, translated title, and named export all look correct and consistent with existing patterns.

src/assets/translations/en/main.json (2)

3117-3125: LGTM: reward distribution notification copy.

Copy reads well, placeholder naming is clear (amountAndSymbol), and mirrors other sections.


1101-1113: Please verify usage of the new trade translation keys

I ran lightweight grep searches and did not find any references to trade.claim or trade.rewardDistribution in the source:

  • rg -n "trade.claim" src
  • rg -n "trade.rewardDistribution" src

However, absence of matches isn’t proof they’re unused. Please manually confirm that no components or translation calls (e.g. t('main:trade.claim.pending'), t("main:trade.rewardDistribution.complete")) reference these keys.

• If they’re truly unused, remove the trade.claim.* and trade.rewardDistribution.* entries from src/assets/translations/en/main.json to reduce maintenance overhead.
• If they are in use, refactor those components to reuse the existing actionCenter.claim.* and actionCenter.rewardDistribution.* keys instead of duplicating copy.

src/test/mocks/store.ts (1)

162-163: LGTM: mock feature flag added.

RfoxFoxEcosystemPage: false mirrors the new config flag and keeps tests deterministic.

src/components/Layout/Header/ActionCenter/ActionCenter.tsx (3)

23-23: rFOX Reward Distribution card integration looks correct

Import path and naming are consistent with the component location; no API surface changes. Good addition.


102-104: Switch case correctly wires RewardDistribution

The new case returns a keyed RewardDistributionActionCard. This matches ActionType.RewardDistribution and aligns with the ActionCard ecosystem.


120-150: Confirm ActionCenter tree is wrapped by an error boundary

Per guidelines, components should be wrapped in error boundaries for production. If not already handled at a higher level (e.g., in Layout/Header), wrap ActionCenter rendering with an error boundary to prevent header-wide crashes.

src/config.ts (1)

188-189: Approve feature flag integration

The VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE flag is properly defined, used throughout the codebase, and mocked in tests with a default of false, consistent with other feature flags.

• Defined in config

  • src/config.ts:188
    • Mapped in Redux slice
  • src/state/slices/preferencesSlice/preferencesSlice.ts:91, 215
    • Mocked for tests
  • src/test/mocks/store.ts:162
    • Consumed in components
  • src/pages/Explore/Explore.tsx:91, 111–112
  • src/pages/Fox/components/RFOXSection.tsx:100
    • Used in routing logic
  • src/Routes/RoutesCommon.tsx:255, 265, 274

All references and mocks are in place; no further changes required.

src/hooks/useActionCenterSubscribers/useActionCenterSubscribers.ts (1)

9-9: Subscriber import aligned with new Reward Distribution flow

Import path and naming are consistent.

src/pages/RFOX/components/Unstake/components/UnstakeSummary.tsx (1)

51-53: Alignment with new cooldownPeriod data shape is correct

Using data: cooldownPeriodData and isCooldownPeriodSuccess matches the updated hook API and keeps the UI logic clean.

src/state/slices/preferencesSlice/preferencesSlice.ts (1)

215-217: Flag wiring LGTM

The new RfoxFoxEcosystemPage flag is correctly sourced from getConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE and coexists with QuickBuy in initialState.

src/pages/RFOX/components/UnstakeModal.tsx (1)

18-29: Ensure close flows propagate to parent onClose

DialogCloseButton toggles provider state internally. Verify it also triggers the onClose prop passed to Dialog so parent state (isOpen) remains the source of truth, avoiding modal “re-open” flicker.

If needed, prefer wiring the close button to call onClose directly:

-            <DialogCloseButton />
+            <DialogCloseButton onClick={onClose} />

(Only if DialogCloseButton accepts onClick and the provider remains in sync.)

src/pages/RFOX/components/Unstake/UnstakeInput.tsx (1)

333-344: Validation and error copy LGTM

Fee-balance validation with a translated error is correct and follows the form pattern.

src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx (2)

68-86: Well-structured notification UI

Good composition: status-aware badge, precise crypto amount formatting with fromBaseUnit and a clean separation of title/description.


27-33: Add explicit component type

Annotate as React.FC for consistency with project guidelines.

Apply this diff:

-export const RewardDistributionNotification = ({
+export const RewardDistributionNotification: React.FC<RewardDistributionNotificationProps> = ({
   distribution,
   actionId,
   handleClick,
   onClose,
 }: RewardDistributionNotificationProps) => {

Likely an incorrect or invalid review comment.

src/Routes/RoutesCommon.tsx (2)

255-256: Gating /rfox route correctly when the Ecosystem page is enabled

The conditional disable: !VITE_FEATURE_RFOX || VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE looks correct and prevents duplicate entry points. LGTM.


265-266: Disable legacy /fox page when the new Ecosystem page is enabled

This ensures a clean rollout path without duplicate nav items. Looks good.

src/pages/Fox/components/RFOXSection.tsx (4)

106-107: LGTM: Local stake/unstake modal state is straightforward and scoped

The boolean state aligns with naming guidelines and keeps modal control local to the section (as requested in the PR objectives).


152-155: Confirm setter accepts undefined and won’t cause unnecessary updates

setStakingAssetAccountId(stakingAssetAccountId) will be called with undefined until the account mapping resolves. Ensure the context setter tolerates undefined and doesn’t trigger noisy effects downstream.

If needed, guard to avoid setting when unchanged:

 useEffect(() => {
-  setStakingAssetAccountId(stakingAssetAccountId)
+  setStakingAssetAccountId(stakingAssetAccountId)
 }, [setStakingAssetAccountId, stakingAssetAccountId])

Follow-up: if you want to prevent redundant sets, compare the previous value in the context/provider or early-return when equal.


251-276: Action buttons behavior matches the feature-flagged UX

Conditional rendering of Manage vs Stake/Unstake aligns with the objectives and keeps flows on-page when the flag is enabled. Handlers are memoized correctly.


283-406: Ensure this subtree is covered by an error boundary

Guidelines require components to be wrapped in an error boundary. If a higher-level boundary already wraps this page (e.g., at Main, FoxEcosystemPage, or a layout provider), ignore. If not, consider adding one here to protect the new modal subtree and section.

I can wire an ErrorBoundary with a user-friendly fallback and useErrorToast in the nearest parent if needed.

src/state/slices/actionSlice/types.ts (5)

27-28: Enum extension looks good

ActionType.RewardDistribution addition is consistent and scoped.


98-99: Good addition: normalized seconds field for cooldowns

Adding cooldownPeriodSeconds?: number helps consumers avoid parsing strings.


156-163: RewardDistributionAction shape is clear and future-proof

The metadata nest is consistent with other action types. LGTM.


171-171: Union wiring is correct

Including RewardDistributionAction in Action union enables exhaustiveness checks downstream.


211-214: Type guard is accurate

The predicate mirrors the metadata nesting pattern used elsewhere.

src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx (1)

68-90: Notification dedupe logic looks solid

Using toast.isActive(actionId) prevents duplicate notifications; actionSlice.upsertAction preserves createdAt while refreshing updatedAt. Good balance between idempotence and freshness.

src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx (3)

70-76: LGTM on icon/status/CTA wiring

Good use of AssetIconWithBadge + ActionStatusIcon, status tag in the footer, and a single “View Transaction” CTA that’s conditionally rendered. This matches the Action Center patterns.

Also applies to: 91-97, 111-117


22-26: Wrap in an error boundary per TSX guidelines (or confirm parent-level boundary)

Guidelines require error boundaries around components. If ActionCenter (parent) already wraps children in an error boundary with a user-friendly fallback and useErrorToast, we’re good; otherwise, add a boundary here.

I can add a wrapper/HOC once you confirm the preferred boundary utility in this repo (e.g., a withErrorBoundary or <ErrorBoundary> component).


4-5: fromNow() requires the dayjs relativeTime plugin—ensure it’s initialized before use

We verified that you do extend relativeTime in src/lib/utils/time.ts and in several ActionCard components, but RewardDistributionActionCard.tsx neither imports the plugin nor calls dayjs.extend(relativeTime). Unless your app’s root (e.g. index.tsx or App.tsx) imports src/lib/utils/time.ts on startup, dayjs(action.updatedAt).fromNow() will return undefined here.

Please confirm one of the following:

  • Your bootstrap file (e.g. src/index.tsx or src/App.tsx) has import '@/lib/utils/time' (which calls dayjs.extend(relativeTime)).
  • If not, either
    • Add import 'dayjs/plugin/relativeTime' and dayjs.extend(relativeTime) at the top of RewardDistributionActionCard.tsx, or
    • Import your global utils/time.ts in the app entrypoint so that the plugin is always initialized before any .fromNow() calls.

Affected locations:

  • src/lib/utils/time.ts (contains dayjs.extend(relativeTime))
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx (uses .fromNow() without ensuring plugin)

@NeOMakinG NeOMakinG marked this pull request as ready for review August 21, 2025 13:22
@premiumjibles premiumjibles self-requested a review August 21, 2025 23:35
Copy link
Collaborator

@premiumjibles premiumjibles left a comment

Choose a reason for hiding this comment

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

Pretty much looks good. Few nitpicky comments.

One blocking issue: When you click "stake" on the new fox eco page it tries to redirect to the old one. That button probably needs to get gone since we're already on the right page.

Image

https://jam.dev/c/c6c7d0e2-8c9e-47f4-b9b0-fa57cd3b83ab
https://jam.dev/c/bd5b007e-33ab-4224-91f6-4352868ff4fc

@NeOMakinG
Copy link
Collaborator Author

Pretty much looks good. Few nitpicky comments.

One blocking issue: When you click "stake" on the new fox eco page it tries to redirect to the old one. That button probably needs to get gone since we're already on the right page.

Image https://jam.dev/c/c6c7d0e2-8c9e-47f4-b9b0-fa57cd3b83ab https://jam.dev/c/bd5b007e-33ab-4224-91f6-4352868ff4fc

Removed the button if the flag is on! in 14c3cee

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: 8

Caution

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

⚠️ Outside diff range comments (4)
src/pages/Fox/components/RFOXSection.tsx (1)

110-113: Avoid lingering RTK Query subscriptions from initiate.

Dispatching marketApi.endpoints.findByAssetId.initiate(...) without cleanup can leave subscriptions active if this component re-mounts with different stakingAssetId. Prefer prefetch or store the result and unsubscribe in cleanup.

Option A (prefetch):

-  useEffect(() => {
-    appDispatch(marketApi.endpoints.findByAssetId.initiate(stakingAssetId))
-  }, [appDispatch, stakingAssetId])
+  useEffect(() => {
+    const prefetch = marketApi.util.prefetch(
+      marketApi.endpoints.findByAssetId.name as any,
+      stakingAssetId,
+      { force: false }
+    )
+    appDispatch(prefetch)
+  }, [appDispatch, stakingAssetId])

Option B (unsubscribe):

  useEffect(() => {
-   appDispatch(marketApi.endpoints.findByAssetId.initiate(stakingAssetId))
+   const promise = appDispatch(
+     marketApi.endpoints.findByAssetId.initiate(stakingAssetId, { forceRefetch: false })
+   )
+   return () => {
+     promise.unsubscribe?.()
+   }
  }, [appDispatch, stakingAssetId])
src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx (1)

98-103: Avoid rendering an inert external link when txLink is undefined

While isCollapsable already depends on txLink, the “View transaction” Button still renders a Chakra Link with an undefined href when txLink is falsy. Mirror RewardDistributionActionCard’s pattern and render the button group only when txLink exists.

Apply:

       <Stack gap={4}>
-        <ButtonGroup width='full' size='sm'>
-          <Button width='full' as={Link} isExternal href={txLink}>
-            {translate('actionCenter.viewTransaction')}
-          </Button>
-        </ButtonGroup>
+        {txLink && (
+          <ButtonGroup width='full' size='sm'>
+            <Button width='full' as={Link} isExternal href={txLink}>
+              {translate('actionCenter.viewTransaction')}
+            </Button>
+          </ButtonGroup>
+        )}
       </Stack>
src/Routes/RoutesCommon.tsx (1)

249-256: Add disable gating to the legacy rFOX route when the ecosystem page is enabled

The /rfox/* entry in src/Routes/RoutesCommon.tsx (around line 249) currently has no disable flag, so even when VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE is true the old rFOX routes remain visible alongside the new ecosystem flow. To fully consolidate under the new experience, hide the legacy route whenever the ecosystem feature is enabled.

• Location: src/Routes/RoutesCommon.tsx, lines ~249–256
• Proposed diff:

   {
     path: '/rfox/*',
     label: 'navBar.rFOX',
     icon: <RFOXIcon />,
     mobileNav: false,
     priority: 1,
     main: RFOX,
     category: RouteCategory.Fox,
+    disable: getConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE,
   },

This ensures the legacy /rfox/* routes are disabled whenever the ecosystem page flag is active.

src/components/Layout/Header/ActionCenter/ActionCenter.tsx (1)

191-196: Fix invalid CSS: missing closing parenthesis in Mobile height calc

The Mobile container height is missing a closing “)” which breaks the calc expression.

Apply:

-          <Box
-            overflow='auto'
-            height='calc(100vh - 70px - (env(safe-area-inset-top) - var(--safe-area-inset-top))'
-          >
+          <Box
+            overflow='auto'
+            height='calc(100vh - 70px - (env(safe-area-inset-top) - var(--safe-area-inset-top)))'
+          >
♻️ Duplicate comments (9)
src/pages/RFOX/components/StakeModal.tsx (1)

35-37: Use a valid background color value for Chakra Card.

bg='none' is not valid for background-color; use transparent (or a theme token) to ensure consistent rendering.

-        <Card bg='none'>
+        <Card bg='transparent'>
src/pages/RFOX/components/UnstakeModal.tsx (1)

21-41: On memoization (non-blocking, prior feedback acknowledged).

Past feedback suggested wrapping with React.memo, but maintainer preference is to avoid it here. No change requested.

src/pages/Fox/components/RFOXSection.tsx (1)

403-405: Wrap modals with an error boundary at the usage site.

Per TSX guidelines, components should be wrapped in error boundaries with a user-friendly fallback and error logging. Apply the boundary here (RFOXSection) or ensure a page-level boundary covers these modals.

Example (illustrative):

-      <StakeModal isOpen={isStakeModalOpen} onClose={handleCloseStakeModal} />
-      <UnstakeModal isOpen={isUnstakeModalOpen} onClose={handleCloseUnstakeModal} />
+      {/* ErrorBoundary should log via useErrorToast and show a simple fallback */}
+      <ErrorBoundary>
+        <StakeModal isOpen={isStakeModalOpen} onClose={handleCloseStakeModal} />
+      </ErrorBoundary>
+      <ErrorBoundary>
+        <UnstakeModal isOpen={isUnstakeModalOpen} onClose={handleCloseUnstakeModal} />
+      </ErrorBoundary>

Note: use the project’s ErrorBoundary implementation and a localized fallback.

src/state/slices/actionSlice/types.ts (1)

9-10: Avoid importing types from a hook module; colocate in a pure types file

Importing RewardDistributionWithMetadata from a React hook creates an unnecessary layering dependency in a slice types module. Move the type next to RewardDistribution (e.g., src/pages/RFOX/types.ts) and import it from there.

Follow-up:

- import type { RewardDistributionWithMetadata } from '@/pages/RFOX/hooks/useLifetimeRewardDistributionsQuery'
+ import type { RewardDistributionWithMetadata } from '@/pages/RFOX/types'
src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx (3)

3-3: Use the project’s canonical fromBaseUnit helper

This was already raised for this file; if you decide to keep using @shapeshiftoss/utils here, feel free to ignore.


37-53: Placeholder fallback for i18n components when asset is unavailable

Previously discussed; leaving as-is per confirmation that runeAsset is guaranteed to be defined when this renders.


78-90: Resilient tx link source and collapsibility boolean

Earlier suggestion to use action.rewardDistributionMetadata.txHash ?? distribution.txId and simplify isCollapsable was declined—acknowledging and not re-requesting changes.

src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx (2)

21-21: Use undefined instead of null for Chakra toast duration

Previously flagged; leaving as-is per your preference to keep consistency with existing patterns.


104-105: Skip-upsert on complete when pending was unseen is intentional

Acknowledging the product decision to only upgrade previously-seen actions; no further action.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4f6b1dd and 7f28ce0.

📒 Files selected for processing (12)
  • src/Routes/RoutesCommon.tsx (3 hunks)
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx (3 hunks)
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx (1 hunks)
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx (1 hunks)
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx (1 hunks)
  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx (1 hunks)
  • src/pages/Fox/components/FoxTokenHeader.tsx (3 hunks)
  • src/pages/Fox/components/RFOXSection.tsx (6 hunks)
  • src/pages/RFOX/components/StakeModal.tsx (1 hunks)
  • src/pages/RFOX/components/UnstakeModal.tsx (1 hunks)
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx (1 hunks)
  • src/state/slices/actionSlice/types.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{ts,tsx}: ALWAYS use Result<T, E> pattern for error handling in swappers and APIs
ALWAYS use Ok() and Err() from @sniptt/monads for monadic error handling
ALWAYS use custom error classes from @shapeshiftoss/errors
ALWAYS provide meaningful error codes for internationalization
ALWAYS include relevant details in error objects
ALWAYS wrap async operations in try-catch blocks
ALWAYS use AsyncResultOf utility for converting promises to Results
ALWAYS provide fallback error handling
ALWAYS use timeoutMonadic for API calls
ALWAYS provide appropriate timeout values for API calls
ALWAYS handle timeout errors gracefully
ALWAYS validate inputs before processing
ALWAYS provide clear validation error messages
ALWAYS use early returns for validation failures
ALWAYS log errors for debugging
ALWAYS use structured logging for errors
ALWAYS include relevant context in error logs
Throwing errors instead of using monadic patterns is an anti-pattern
Missing try-catch blocks for async operations is an anti-pattern
Generic error messages without context are an anti-pattern
Not handling specific error types is an anti-pattern
Missing timeout handling is an anti-pattern
No input validation is an anti-pattern
Poor error logging is an anti-pattern
Using any for error types is an anti-pattern
Missing error codes for internationalization is an anti-pattern
No fallback error handling is an anti-pattern
Console.error without structured logging is an anti-pattern

**/*.{ts,tsx}: ALWAYS use camelCase for variables, functions, and methods
ALWAYS use descriptive names that explain the purpose for variables and functions
ALWAYS use verb prefixes for functions that perform actions
ALWAYS use PascalCase for types, interfaces, and enums
ALWAYS use descriptive names that indicate the structure for types, interfaces, and enums
ALWAYS use suffixes like Props, State, Config, Type when appropriate for types and interfaces
ALWAYS use UPPER_SNAKE_CASE for constants and configuration values
ALWAYS use d...

Files:

  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/pages/Fox/components/FoxTokenHeader.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/pages/RFOX/components/StakeModal.tsx
  • src/Routes/RoutesCommon.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/state/slices/actionSlice/types.ts
  • src/pages/RFOX/components/UnstakeModal.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.tsx: ALWAYS wrap components in error boundaries
ALWAYS provide user-friendly fallback components in error boundaries
ALWAYS log errors for debugging in error boundaries
ALWAYS use useErrorToast hook for displaying errors
ALWAYS provide translated error messages in error toasts
ALWAYS handle different error types appropriately in error toasts
Missing error boundaries in React components is an anti-pattern

**/*.tsx: ALWAYS use PascalCase for React component names
ALWAYS use descriptive names that indicate the component's purpose
ALWAYS match the component name to the file name
Flag components without PascalCase
Flag default exports for components

Files:

  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/pages/Fox/components/FoxTokenHeader.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/pages/RFOX/components/StakeModal.tsx
  • src/Routes/RoutesCommon.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
**/*

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*: ALWAYS use appropriate file extensions
Flag files without kebab-case

Files:

  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/pages/Fox/components/FoxTokenHeader.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/pages/RFOX/components/StakeModal.tsx
  • src/Routes/RoutesCommon.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/state/slices/actionSlice/types.ts
  • src/pages/RFOX/components/UnstakeModal.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

**/*.{tsx,jsx}: ALWAYS use useMemo for expensive computations, object/array creations, and filtered data
ALWAYS use useMemo for derived values and computed properties
ALWAYS use useMemo for conditional values and simple transformations
ALWAYS use useCallback for event handlers and functions passed as props
ALWAYS use useCallback for any function that could be passed as a prop or dependency
ALWAYS include all dependencies in useEffect, useMemo, useCallback dependency arrays
NEVER use // eslint-disable-next-line react-hooks/exhaustive-deps unless absolutely necessary
ALWAYS explain why dependencies are excluded if using eslint disable
ALWAYS use named exports for components
NEVER use default exports for components
KEEP component files under 200 lines when possible
BREAK DOWN large components into smaller, reusable pieces
EXTRACT complex logic into custom hooks
USE local state for component-level state
LIFT state up when needed across multiple components
USE Context for avoiding prop drilling
ALWAYS wrap components in error boundaries for production
ALWAYS handle async errors properly
ALWAYS provide user-friendly error messages
ALWAYS use virtualization for lists with 100+ items
ALWAYS implement proper key props for list items
ALWAYS lazy load heavy components
ALWAYS use React.lazy for code splitting
Components receiving props are wrapped with memo

Files:

  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/pages/Fox/components/FoxTokenHeader.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/pages/RFOX/components/StakeModal.tsx
  • src/Routes/RoutesCommon.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

USE Redux only for global state shared across multiple places

Files:

  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/pages/Fox/components/FoxTokenHeader.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/pages/RFOX/components/StakeModal.tsx
  • src/Routes/RoutesCommon.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/state/slices/actionSlice/types.ts
  • src/pages/RFOX/components/UnstakeModal.tsx
**/use*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/use*.{ts,tsx}: ALWAYS use use prefix for custom hooks
ALWAYS use descriptive names that indicate the hook's purpose
ALWAYS use camelCase after the use prefix for custom hooks

Files:

  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
🧠 Learnings (34)
📓 Common learnings
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/Explore/Explore.tsx:110-113
Timestamp: 2025-08-22T13:03:36.881Z
Learning: In src/Routes/RoutesCommon.tsx, the /fox-ecosystem route is already properly gated using disable: !getConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE, which prevents direct navigation when the VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE flag is false.
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/Explore/Explore.tsx:110-113
Timestamp: 2025-08-22T13:03:36.881Z
Learning: In src/Routes/RoutesCommon.tsx, the /fox-ecosystem route is already properly gated using disable: !getConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE, which prevents direct navigation when the VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE flag is false.
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.813Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.
📚 Learning: 2025-08-22T12:59:01.664Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx:37-53
Timestamp: 2025-08-22T12:59:01.664Z
Learning: In RewardDistributionActionCard component (src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx), NeOMakinG confirmed that runeAsset is expected to always be defined when the component renders, so defensive guards against undefined runeAsset are not needed.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
📚 Learning: 2025-08-22T12:59:01.168Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx:26-29
Timestamp: 2025-08-22T12:59:01.168Z
Learning: In src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx, NeOMakinG declined wrapping the RewardDistributionActionCard component with React.memo, saying it was "too much", suggesting that like other action center components, memoization is not beneficial for this specific use case.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
📚 Learning: 2025-08-22T12:58:26.564Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx:108-111
Timestamp: 2025-08-22T12:58:26.564Z
Learning: In the RFOX GenericTransactionDisplayType flow in src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx, the txHash is always guaranteed to be present according to NeOMakinG, so defensive null checks for txLink are not needed in this context.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
📚 Learning: 2025-08-22T13:02:58.779Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx:33-41
Timestamp: 2025-08-22T13:02:58.779Z
Learning: In src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx, NeOMakinG declined optimizing useMemo dependencies to depend on lifetimeRewardDistributionsQuery.data instead of the entire query object, indicating indifference toward this type of performance optimization.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/state/slices/actionSlice/types.ts
📚 Learning: 2025-08-22T13:16:12.687Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx:104-105
Timestamp: 2025-08-22T13:16:12.687Z
Learning: In src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx, the guard `if (!actions[actionId]) return` before upserting completed reward distributions is intentional product behavior. NeOMakinG confirmed that the system should only show completion notifications for reward distributions that were previously seen in a pending state, not for distributions the user missed during the pending phase.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/state/slices/actionSlice/types.ts
📚 Learning: 2025-08-08T14:59:40.422Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/pages/Explore/ExploreCategory.tsx:231-238
Timestamp: 2025-08-08T14:59:40.422Z
Learning: In src/pages/Explore/ExploreCategory.tsx, for the PageHeader filter trigger, NeOMakinG considers changing a clickable Chakra Icon to IconButton too nitpicky for this PR and prefers to keep the current Icon-based trigger; such minor a11y/UI nitpicks should be deferred to a follow-up if needed.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
📚 Learning: 2025-08-08T15:00:49.887Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
📚 Learning: 2025-08-22T13:02:38.035Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/Explore/Explore.tsx:91-92
Timestamp: 2025-08-22T13:02:38.035Z
Learning: In the ShapeShift web codebase, feature flags are consistently used as string literals with the useFeatureFlag hook (e.g., useFeatureFlag('RfoxFoxEcosystemPage')), not as enums. The hook accepts keyof FeatureFlags which provides type safety through the TypeScript type system rather than requiring an enum. This is the established pattern used throughout the entire codebase.

Applied to files:

  • src/pages/Fox/components/FoxTokenHeader.tsx
📚 Learning: 2025-08-22T13:03:36.881Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/Explore/Explore.tsx:110-113
Timestamp: 2025-08-22T13:03:36.881Z
Learning: In src/Routes/RoutesCommon.tsx, the /fox-ecosystem route is already properly gated using disable: !getConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE, which prevents direct navigation when the VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE flag is false.

Applied to files:

  • src/pages/Fox/components/FoxTokenHeader.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/Routes/RoutesCommon.tsx
📚 Learning: 2025-08-08T15:00:22.321Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/state/slices/actionSlice/types.ts
📚 Learning: 2025-08-03T22:10:38.426Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/react-best-practices.mdc:0-0
Timestamp: 2025-08-03T22:10:38.426Z
Learning: Applies to **/*.{tsx,jsx} : EXTRACT complex logic into custom hooks

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-14T17:54:32.563Z
Learnt from: gomesalexandre
PR: shapeshift/web#10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx:97-108
Timestamp: 2025-08-14T17:54:32.563Z
Learning: In ReusableLpStatus component (src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx), the txAssets dependency is stable from first render because poolAsset, baseAsset, actionSide, and action are all defined first render, making the current txAssetsStatuses initialization pattern safe without needing useEffect synchronization.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
📚 Learning: 2025-08-22T12:58:36.059Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx:33-55
Timestamp: 2025-08-22T12:58:36.059Z
Learning: In RewardDistributionNotification component (src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx), NeOMakinG confirmed that runeAsset is expected to always be defined when the component renders, so defensive guards against undefined runeAsset are not needed.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
📚 Learning: 2025-08-14T17:56:23.721Z
Learnt from: gomesalexandre
PR: shapeshift/web#10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:545-566
Timestamp: 2025-08-14T17:56:23.721Z
Learning: gomesalexandre prefers not to extract helper functions for toast rendering patterns in TransactionRow.tsx (src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx), considering it over-abstraction even when there's code duplication between deposit and withdraw flows.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
📚 Learning: 2025-08-05T22:41:35.473Z
Learnt from: premiumjibles
PR: shapeshift/web#10187
File: src/pages/Assets/Asset.tsx:1-1
Timestamp: 2025-08-05T22:41:35.473Z
Learning: In the shapeshift/web codebase, component imports use direct file paths like '@/components/ComponentName/ComponentName' rather than barrel exports. The AssetAccountDetails component should be imported as '@/components/AssetAccountDetails/AssetAccountDetails', not from a directory index.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
📚 Learning: 2025-08-06T09:46:50.860Z
Learnt from: NeOMakinG
PR: shapeshift/web#10191
File: src/pages/Explore/components/CategoryCard.tsx:27-196
Timestamp: 2025-08-06T09:46:50.860Z
Learning: In src/pages/Explore/components/CategoryCard.tsx, NeOMakinG indicated that wrapping the CategoryCard component with React.memo would be "quite useless in this case", suggesting that memoization is not beneficial for this particular component despite it receiving props, likely due to its specific usage patterns or context.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
📚 Learning: 2025-08-03T22:10:38.426Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/react-best-practices.mdc:0-0
Timestamp: 2025-08-03T22:10:38.426Z
Learning: Applies to **/*.{tsx,jsx} : Components receiving props are wrapped with `memo`

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
📚 Learning: 2025-08-06T11:02:09.501Z
Learnt from: NeOMakinG
PR: shapeshift/web#10191
File: src/pages/Explore/Explore.tsx:54-72
Timestamp: 2025-08-06T11:02:09.501Z
Learning: In src/pages/Explore/Explore.tsx, NeOMakinG indicated that memoizing the ExploreCard component would be unnecessary, questioning "why would we memoize that", suggesting that like CategoryCard, this component doesn't benefit from memoization despite receiving props, likely due to its specific usage patterns in the carousel with static content.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
📚 Learning: 2025-08-03T22:10:38.426Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/react-best-practices.mdc:0-0
Timestamp: 2025-08-03T22:10:38.426Z
Learning: Applies to **/*.{tsx,jsx} : ALWAYS use `useMemo` for conditional values and simple transformations

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
📚 Learning: 2025-08-03T22:10:38.426Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/react-best-practices.mdc:0-0
Timestamp: 2025-08-03T22:10:38.426Z
Learning: Applies to **/*.{tsx,jsx} : ALWAYS use `useMemo` for derived values and computed properties

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
📚 Learning: 2025-08-03T22:10:38.426Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/react-best-practices.mdc:0-0
Timestamp: 2025-08-03T22:10:38.426Z
Learning: Applies to **/*.{tsx,jsx} : ALWAYS use `useMemo` for expensive computations, object/array creations, and filtered data

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
📚 Learning: 2025-08-08T11:41:22.794Z
Learnt from: NeOMakinG
PR: shapeshift/web#10234
File: src/components/MultiHopTrade/hooks/useGetTradeQuotes/hooks/useTrackTradeQuotes.ts:42-86
Timestamp: 2025-08-08T11:41:22.794Z
Learning: NeOMakinG prefers not to include refactors in move-only PRs; such suggestions should be deferred to follow-up issues instead of being applied within the same PR.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
📚 Learning: 2025-08-22T13:00:44.813Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.813Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
📚 Learning: 2025-07-29T10:22:27.037Z
Learnt from: NeOMakinG
PR: shapeshift/web#10136
File: src/lib/asset-service/service/encodedRelatedAssetIndex.json:1-1
Timestamp: 2025-07-29T10:22:27.037Z
Learning: PRs with titles starting with "feat: regenerate asset data" are routine daily asset updates that don't need detailed code analysis. Users prefer to skip automated reviews for these maintenance PRs using coderabbitai ignore.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
📚 Learning: 2025-07-29T10:27:23.424Z
Learnt from: NeOMakinG
PR: shapeshift/web#10128
File: .cursor/rules/react-best-practices.mdc:8-14
Timestamp: 2025-07-29T10:27:23.424Z
Learning: The ShapeShift team practices aggressive memoization in React components as documented in .cursor/rules/react-best-practices.mdc. They use useMemo for all transformations, derived values, and conditional values, and useCallback for all event handlers and functions that could be passed as props. This approach was adopted after experiencing performance issues ("had hard time") and is their current established practice, though they acknowledge it may evolve in the future.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
  • src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
📚 Learning: 2025-07-24T11:07:20.536Z
Learnt from: gomesalexandre
PR: shapeshift/web#10073
File: src/features/defi/providers/fox-farming/components/FoxFarmingManager/Claim/Claim.tsx:77-84
Timestamp: 2025-07-24T11:07:20.536Z
Learning: In fox farming components, the `opportunity?.rewardsCryptoBaseUnit?.amounts` property has a well-defined type signature that is always an array (never undefined), but can be empty: `readonly [] | readonly [string, string, string] | readonly [string, string] | readonly [string]`. Using optional chaining on the `amounts` property itself is unnecessary since it's always defined, though accessing `amounts[0]` on an empty array returns undefined which bnOrZero() handles safely.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : Missing error boundaries in React components is an anti-pattern

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : ALWAYS wrap components in error boundaries

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : ALWAYS provide user-friendly fallback components in error boundaries

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:10:38.426Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/react-best-practices.mdc:0-0
Timestamp: 2025-08-03T22:10:38.426Z
Learning: Applies to **/*.{tsx,jsx} : ALWAYS wrap components in error boundaries for production

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : ALWAYS handle different error types appropriately in error toasts

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : ALWAYS log errors for debugging in error boundaries

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : ALWAYS use useErrorToast hook for displaying errors

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
🧬 Code graph analysis (11)
src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx (11)
src/state/slices/actionSlice/types.ts (1)
  • GenericTransactionAction (122-132)
src/state/store.ts (1)
  • useAppSelector (143-143)
src/state/slices/assetsSlice/selectors.ts (2)
  • selectFeeAssetByChainId (58-62)
  • selectAssetById (16-20)
src/lib/getTxLink.ts (1)
  • getTxLink (84-152)
packages/caip/src/constants.ts (1)
  • uniV2EthFoxArbitrumAssetId (21-22)
src/state/slices/opportunitiesSlice/constants.ts (2)
  • foxEthLpAssetId (20-20)
  • foxEthPair (19-19)
src/components/AssetIconWithBadge.tsx (1)
  • AssetIconWithBadge (116-202)
src/components/Layout/Header/ActionCenter/components/ActionStatusIcon.tsx (1)
  • ActionStatusIcon (15-63)
src/components/Layout/Header/ActionCenter/components/ActionStatusTag.tsx (1)
  • ActionStatusTag (20-88)
src/components/Layout/Header/ActionCenter/components/ActionCard.tsx (1)
  • ActionCard (28-111)
src/lib/utils/index.ts (1)
  • firstFourLastFour (22-23)
src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx (1)
src/components/Layout/Header/ActionCenter/components/ActionStatusTag.tsx (1)
  • ActionStatusTag (20-88)
src/pages/Fox/components/RFOXSection.tsx (6)
src/pages/Fox/hooks/useFoxPageContext.tsx (1)
  • useFoxPageContext (66-72)
src/pages/RFOX/hooks/useRfoxContext.tsx (1)
  • useRFOXContext (78-84)
src/state/store.ts (1)
  • useAppDispatch (151-151)
packages/caip/src/constants.ts (1)
  • foxOnArbitrumOneAssetId (30-31)
src/pages/RFOX/components/StakeModal.tsx (1)
  • StakeModal (21-41)
src/pages/RFOX/components/UnstakeModal.tsx (1)
  • UnstakeModal (21-41)
src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx (10)
src/pages/RFOX/hooks/useLifetimeRewardDistributionsQuery.ts (1)
  • RewardDistributionWithMetadata (13-19)
src/state/store.ts (1)
  • useAppSelector (143-143)
src/state/slices/assetsSlice/selectors.ts (1)
  • selectAssetById (16-20)
packages/caip/src/constants.ts (1)
  • thorchainAssetId (49-49)
src/state/slices/actionSlice/actionSlice.ts (1)
  • actionSlice (11-39)
src/components/Text/Text.tsx (2)
  • TextPropTypes (8-11)
  • Text (19-83)
src/lib/math.ts (1)
  • fromBaseUnit (10-24)
src/components/Layout/Header/ActionCenter/components/Notifications/NotificationWrapper.tsx (1)
  • NotificationWrapper (15-38)
src/components/AssetIconWithBadge.tsx (1)
  • AssetIconWithBadge (116-202)
src/components/Layout/Header/ActionCenter/components/ActionStatusIcon.tsx (1)
  • ActionStatusIcon (15-63)
src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx (7)
src/state/slices/actionSlice/types.ts (1)
  • RewardDistributionAction (155-161)
src/state/store.ts (1)
  • useAppSelector (143-143)
src/state/slices/assetsSlice/selectors.ts (1)
  • selectAssetById (16-20)
packages/caip/src/constants.ts (1)
  • thorchainAssetId (49-49)
src/components/Text/Text.tsx (2)
  • TextPropTypes (8-11)
  • Text (19-83)
src/lib/math.ts (1)
  • fromBaseUnit (10-24)
src/lib/getTxLink.ts (1)
  • getTxLink (84-152)
src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx (8)
src/state/store.ts (2)
  • useAppSelector (143-143)
  • useAppDispatch (151-151)
src/state/slices/portfolioSlice/selectors.ts (1)
  • selectAccountIdsByChainIdFilter (671-683)
packages/caip/src/constants.ts (1)
  • arbitrumChainId (66-66)
src/components/Layout/Header/ActionCenter/ActionCenterContext.tsx (1)
  • useActionCenterContext (34-38)
src/hooks/useNotificationToast.tsx (1)
  • useNotificationToast (9-19)
src/state/slices/actionSlice/actionSlice.ts (1)
  • actionSlice (11-39)
src/pages/RFOX/hooks/useLifetimeRewardDistributionsQuery.ts (2)
  • useLifetimeRewardDistributionsQuery (25-67)
  • RewardDistributionWithMetadata (13-19)
src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx (1)
  • RewardDistributionNotification (27-86)
src/pages/RFOX/components/StakeModal.tsx (3)
src/components/Modal/components/Dialog.tsx (1)
  • Dialog (117-117)
src/components/Modal/components/DialogHeader.tsx (3)
  • DialogHeader (15-48)
  • DialogHeaderLeft (50-52)
  • DialogHeaderRight (60-62)
src/components/Modal/components/DialogCloseButton.tsx (1)
  • DialogCloseButton (15-38)
src/Routes/RoutesCommon.tsx (3)
src/config.ts (1)
  • getConfig (201-203)
src/components/Icons/FoxPageIcon.tsx (1)
  • FoxPageIcon (3-17)
src/pages/Fox/FoxEcosystemPage.tsx (1)
  • FoxEcosystemPage (28-44)
src/components/Layout/Header/ActionCenter/ActionCenter.tsx (2)
src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx (1)
  • RfoxInitiatedActionCard (28-146)
src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx (1)
  • RewardDistributionActionCard (26-116)
src/state/slices/actionSlice/types.ts (2)
src/pages/RFOX/types.ts (1)
  • RewardDistribution (27-38)
src/pages/RFOX/hooks/useLifetimeRewardDistributionsQuery.ts (1)
  • RewardDistributionWithMetadata (13-19)
src/pages/RFOX/components/UnstakeModal.tsx (3)
src/components/Modal/components/Dialog.tsx (1)
  • Dialog (117-117)
src/components/Modal/components/DialogHeader.tsx (3)
  • DialogHeader (15-48)
  • DialogHeaderLeft (50-52)
  • DialogHeaderRight (60-62)
src/components/Modal/components/DialogCloseButton.tsx (1)
  • DialogCloseButton (15-38)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Call / Static
🔇 Additional comments (20)
src/pages/Fox/components/FoxTokenHeader.tsx (3)

16-16: Feature-flag import matches established pattern (string-literal hook).

Importing useFeatureFlag and relying on string keys aligns with the codebase conventions noted in the learnings. No issues.


154-165: LGTM: Stake button hidden when the new rFOX Ecosystem experience is enabled.

This prevents navigation to the legacy /rfox route and addresses the previously noted blocking issue. No change requested.


55-55: Flag key, usage, and route gating verified — approving changes

All checks passed:

  • RfoxFoxEcosystemPage is declared in preferencesSlice (type and initial state via getConfig) and in test mocks.
  • useFeatureFlag('RfoxFoxEcosystemPage') is used consistently in Explore.tsx, FoxTokenHeader.tsx, and RFOXSection.tsx.
  • In src/Routes/RoutesCommon.tsx, the /fox-ecosystem route is correctly disabled when VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE is false, and the /fox route is disabled when either the base Fox page feature is off or the ecosystem feature is on—matching the intended navigation logic.

Approved.

src/pages/RFOX/components/StakeModal.tsx (3)

25-33: Verify close behavior is wired to parent state in controlled mode.

DialogCloseButton toggles internal dialog context; ensure it also triggers the onClose passed to Dialog so the parent’s isOpen state updates correctly (especially on mobile path where DialogCloseButton handles its own click). If not guaranteed by the provider, pass onClose to DialogCloseButton and ensure it’s called.

Minimal guard (only if needed):

-          <DialogCloseButton />
+          <DialogCloseButton onClick={onClose} />

21-41: Structure, i18n, and composition look solid.

Props typing, named export, header composition, and translated title match project patterns.


4-7: Unify import style: prefer alias over deep relative paths (consistency).

Other modal imports in the repo use the @/ alias. Align these to reduce brittleness when moving files.

-import { Dialog } from '../../../components/Modal/components/Dialog'
-import { DialogBody } from '../../../components/Modal/components/DialogBody'
-import { DialogCloseButton } from '../../../components/Modal/components/DialogCloseButton'
-import { DialogTitle } from '../../../components/Modal/components/DialogTitle'
+import { Dialog } from '@/components/Modal/components/Dialog'
+import { DialogBody } from '@/components/Modal/components/DialogBody'
+import { DialogCloseButton } from '@/components/Modal/components/DialogCloseButton'
+import { DialogTitle } from '@/components/Modal/components/DialogTitle'

Note: honoring team preference from prior PRs, treat this as non-blocking.

Also applies to: 10-14

⛔ Skipped due to learnings
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.
src/pages/RFOX/components/UnstakeModal.tsx (2)

25-33: Verify close behavior is wired to parent state in controlled mode.

As with StakeModal, confirm DialogCloseButton results in onClose being invoked so isOpen in the parent closes deterministically across breakpoints.

If needed:

-          <DialogCloseButton />
+          <DialogCloseButton onClick={onClose} />

21-41: Clear, symmetric implementation to StakeModal. LGTM.

Naming, i18n, and composition follow established patterns.

src/pages/Fox/components/RFOXSection.tsx (3)

99-100: Confirm feature-flag key mapping to environment configuration.

Ensure useFeatureFlag('RfoxFoxEcosystemPage') maps to the intended VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE config. Mismatched keys would silently disable the new flow.

Context: retrieved learning indicates route gating already uses VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE.


151-154: State sync into RFOX context looks correct.

Effect correctly updates stakingAssetAccountId in context when dependencies change.


250-275: Action buttons gating logic reads well and fixes the prior redirect behavior.

When the ecosystem flag is enabled, the section presents Stake/Unstake; otherwise, it shows Manage routing to /rfox. This aligns with the PR’s objective and resolves the earlier redirect issue.

src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx (1)

76-78: Footer simplification is correct and consistent with the ActionStatusTag API

Returning the tag directly removes an unnecessary fragment without changing behavior.

src/Routes/RoutesCommon.tsx (1)

265-266: Gating legacy /fox route behind the new feature flag is correct

Disabling /fox when VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE is true prevents duplicate entry points and matches the PR objective.

src/components/Layout/Header/ActionCenter/ActionCenter.tsx (2)

95-99: Good separation: use RfoxInitiatedActionCard for RFOX-specific initiated flows

Extracting the RFOX case out of the generic card reduces conditional complexity and matches the earlier concern about “generic” card doing too much.


107-109: RewardDistribution action integration looks correct

Rendering RewardDistributionActionCard for ActionType.RewardDistribution aligns UI with the new action type.

src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx (1)

38-55: Minor: rely on guaranteed runeAsset but keep components type explicit

Given product guarantees, returning undefined as components is fine and Text handles it. No change requested; just noting the assumption is encoded correctly.

src/state/slices/actionSlice/types.ts (1)

210-212: Type guard for RewardDistributionAction is correct

The predicate is straightforward and consistent with other guards.

src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx (2)

64-78: Icon selection logic LGTM

Correctly handles FOX/ETH LP special-casing and falls back to the action asset; status badge layering is clean.


48-58: Guard against empty txHash to avoid linking to the explorer homepage

If transactionMetadata.txHash is missing/empty, getTxLink will resolve to the explorer base URL. Bail early to keep the card non-collapsible and avoid a confusing link.

   const txLink = useMemo(() => {
     if (!feeAsset) return
+    if (!action.transactionMetadata.txHash) return

     return getTxLink({
       txId: action.transactionMetadata.txHash,
       chainId: action.transactionMetadata.chainId,
       defaultExplorerBaseUrl: feeAsset.explorerTxLink,
       address: undefined,
       maybeSafeTx: undefined,
     })
   }, [action.transactionMetadata.txHash, action.transactionMetadata.chainId, feeAsset])
⛔ Skipped due to learnings
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx:108-111
Timestamp: 2025-08-22T12:58:26.564Z
Learning: In the RFOX GenericTransactionDisplayType flow in src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx, the txHash is always guaranteed to be present according to NeOMakinG, so defensive null checks for txLink are not needed in this context.
Learnt from: gomesalexandre
PR: shapeshift/web#10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx:97-108
Timestamp: 2025-08-14T17:54:32.563Z
Learning: In ReusableLpStatus component (src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx), the txAssets dependency is stable from first render because poolAsset, baseAsset, actionSide, and action are all defined first render, making the current txAssetsStatuses initialization pattern safe without needing useEffect synchronization.
src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx (1)

33-35: Resolved: dayjs relativeTime plugin is already registered globally

A top-level call to dayjs.extend(relativeTime) exists in src/components/Layout/Header/ActionCenter/components/ActionCard.tsx (line 11), and because RewardDistributionActionCard imports ActionCard, the plugin is registered before any fromNow() calls. No changes are needed.

@premiumjibles
Copy link
Collaborator

premiumjibles commented Aug 23, 2025

@NeOMakinG only just spotted this because I just so happened to get some fox unstakes pop off whilst testing today.

Currently the claim button on claim action cards takes you to the old rFox page.
image

Which also made me realise it doesn't seem like this new page includes a claim section? Where should this go now?

image

I've got some weird behaviour when we double modal on mobile. Selecting a new asset boots you out of the drawer. Same with when you try to go back from asset selection. If it happens to be the same annoying pointer-events issue I ran into with the manage accounts page then check out this PRfor reference on some workarounds. Hopefully it's simpler than that though.

Asset row overflow you can see in this Jam as well but that's in prod already. I've posted in polish.

https://jam.dev/c/8bb27cfa-33ec-46e8-a6e2-4183e9d05403

Other more nitpicky stuff

Stake/Unstake drawers looking way better with drag handle and the layout changes you've made to minimise scroll.

Definitely non-blocking but the title on these drawers look very small. Wondering if we want no header or a bigger centered one?

image

Here's manage accounts for comparison 👇

image

Code rabbit is also making babies with your PR 🐇 🐇 🐇

@NeOMakinG
Copy link
Collaborator Author

@NeOMakinG only just spotted this because I just so happened to get some fox unstakes pop off whilst testing today.

Currently the claim button on claim action cards takes you to the old rFox page. image

Which also made me realise it doesn't seem like this new page includes a claim section? Where should this go now?

image I've got some weird behaviour when we double modal on mobile. Selecting a new asset boots you out of the drawer. Same with when you try to go back from asset selection. If it happens to be the same annoying pointer-events issue I ran into with the manage accounts page then check out [this PR](https://github.com//pull/10157/files)for reference on some workarounds. Hopefully it's simpler than that though.

Asset row overflow you can see in this Jam as well but that's in prod already. I've posted in polish.

https://jam.dev/c/8bb27cfa-33ec-46e8-a6e2-4183e9d05403

Other more nitpicky stuff

Stake/Unstake drawers looking way better with drag handle and the layout changes you've made to minimise scroll.

Definitely non-blocking but the title on these drawers look very small. Wondering if we want no header or a bigger centered one?

image Here's manage accounts for comparison 👇 image Code rabbit is also making babies with your PR 🐇 🐇 🐇

That's some nice catch, brang back the notion of disabling propagation on dialog side, as this is one of the use cases I told you when we removed it, I'm quite sure we are missing a few other parts in the app with such things but hey, as long as we don't see them...

Added a claim button as well as changing all the claim behaviors to use the new page if the flag is on, I left the titles thing for @shapeshift/product for any followup/other tickets

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

Caution

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

⚠️ Outside diff range comments (1)
src/components/Modal/components/Dialog.tsx (1)

82-93: Event listeners are never removed (new handler instances on cleanup).

removeEventListener uses fresh lambdas, so the listeners persist, causing leaks and globally blocking focus propagation after unmount. Use stable handler refs and pass the same capture flag on remove.

-import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react'
+import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
@@
-  useLayoutEffect(() => {
-    if (!isMobile || isLargerThanMd) return
-
-    document.addEventListener('focusin', e => e.stopImmediatePropagation())
-    document.addEventListener('focusout', e => e.stopImmediatePropagation())
-
-    return () => {
-      document.removeEventListener('focusin', e => e.stopImmediatePropagation())
-      document.removeEventListener('focusout', e => e.stopImmediatePropagation())
-    }
-  }, [isLargerThanMd])
+  const focusInHandlerRef = useRef<(e: Event) => void>()
+  const focusOutHandlerRef = useRef<(e: Event) => void>()
+
+  useLayoutEffect(() => {
+    if (!isMobile || isLargerThanMd) return
+
+    focusInHandlerRef.current = e => e.stopImmediatePropagation()
+    focusOutHandlerRef.current = e => e.stopImmediatePropagation()
+
+    const focusInHandler = focusInHandlerRef.current
+    const focusOutHandler = focusOutHandlerRef.current
+
+    // Use capture to intercept early and match on removal
+    document.addEventListener('focusin', focusInHandler!, true)
+    document.addEventListener('focusout', focusOutHandler!, true)
+
+    return () => {
+      if (focusInHandler) document.removeEventListener('focusin', focusInHandler, true)
+      if (focusOutHandler) document.removeEventListener('focusout', focusOutHandler, true)
+    }
+  }, [isLargerThanMd])
♻️ Duplicate comments (2)
src/pages/Fox/components/RFOXSection.tsx (1)

256-279: Modal state handlers are fine as-is

Explicit boolean state with small callbacks is perfectly readable here. A prior suggestion to use useDisclosure was acknowledged as nitpicky for this PR; keeping the current approach is acceptable.

src/pages/RFOX/components/ClaimModal.tsx (1)

49-51: Use a valid background token (transparent), not none.

This was called out earlier on a sibling modal and fixed there; the same applies here.

-        <Card bg='none'>
+        <Card bg='transparent'>
🧹 Nitpick comments (18)
src/pages/Fox/components/RFOXSection.tsx (2)

107-111: Avoid unsafe cast for location.state by typing useLocation

Type the router state to remove the as cast and get compiler-checked access to selectedUnstakingRequest.

-  const location = useLocation()
-  const selectedUnstakingRequest = location.state?.selectedUnstakingRequest as
-    | UnstakingRequest
-    | undefined
+  const location = useLocation<{ selectedUnstakingRequest?: UnstakingRequest }>()
+  const selectedUnstakingRequest = location.state?.selectedUnstakingRequest

248-254: Defensive default for distributionRate to avoid NaN/throws

If distributionRateByStakingContract[getStakingContract(stakingAssetId)] is ever undefined (e.g., missing key or unexpected stakingAssetId), bn(...).times(distributionRate) can yield an invalid value. Defaulting distributionRate to 0 prevents downstream issues and yields a safe $0 emission value.

-    const distributionRate =
-      currentEpochMetadataQuery.data.distributionRateByStakingContract[
-        getStakingContract(stakingAssetId)
-      ]
+    const distributionRate =
+      currentEpochMetadataQuery.data.distributionRateByStakingContract[
+        getStakingContract(stakingAssetId)
+      ] ?? 0
src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx (2)

35-48: DRY up the conditional navigation to avoid duplication and future drift

Both branches build the same state and differ only by the base path. Compute the base path and call navigate once to reduce duplication and the risk of diverging behavior over time.

Apply this minimal diff:

-    if (!isRFOXFoxEcosystemPageEnabled) {
-      navigate(`${RfoxRoute.Claim}/${index}/confirm`, {
-        state: {
-          selectedUnstakingRequest: action.rfoxClaimActionMetadata.request,
-        },
-      })
-    } else {
-      navigate(`/fox-ecosystem/${index}/confirm`, {
-        state: {
-          selectedUnstakingRequest: action.rfoxClaimActionMetadata.request,
-        },
-      })
-    }
+    const baseClaimPath = isRFOXFoxEcosystemPageEnabled ? '/fox-ecosystem' : RfoxRoute.Claim
+    navigate(`${baseClaimPath}/${index}/confirm`, {
+      state: { selectedUnstakingRequest: action.rfoxClaimActionMetadata.request },
+    })

49-49: Nit: Narrow useCallback deps by hoisting request/index

Current deps are fine, but you can avoid re-creating the callback when unrelated action fields change by hoisting request and index, then depending on them directly.

Example refactor (keeps logic identical):

-  const handleClaimClick = useCallback(() => {
-    const index = action.rfoxClaimActionMetadata.request.index
+  const request = action.rfoxClaimActionMetadata.request
+  const index = request.index
+  const handleClaimClick = useCallback(() => {
     const index = action.rfoxClaimActionMetadata.request.index
-    const baseClaimPath = isRFOXFoxEcosystemPageEnabled ? '/fox-ecosystem' : RfoxRoute.Claim
-    navigate(`${baseClaimPath}/${index}/confirm`, {
-      state: { selectedUnstakingRequest: action.rfoxClaimActionMetadata.request },
-    })
-  }, [action, navigate, isRFOXFoxEcosystemPageEnabled])
+    const baseClaimPath = isRFOXFoxEcosystemPageEnabled ? '/fox-ecosystem' : RfoxRoute.Claim
+    navigate(`${baseClaimPath}/${index}/confirm`, {
+      state: { selectedUnstakingRequest: request },
+    })
+  }, [index, request, navigate, isRFOXFoxEcosystemPageEnabled])
src/pages/RFOX/components/Claim/types.ts (1)

10-13: Consider broadening header type to ReactNode.

headerComponent?: JSX.Element excludes strings/fragments. If intentional, ignore; else ReactNode provides flexibility with no downside.

-export type ClaimRouteProps = {
-  headerComponent?: JSX.Element
+export type ClaimRouteProps = {
+  headerComponent?: React.ReactNode
   setStepIndex?: (index: number) => void
 }
src/components/Modal/components/Dialog.tsx (2)

57-66: Provide a safe initial/fallback viewport height.

window.visualViewport?.height may be undefined initially, yielding height: undefined when isFullScreen is true. Fall back to window.innerHeight.

-  const [viewportHeight, setViewportHeight] = useState(window.visualViewport?.height)
+  const [viewportHeight, setViewportHeight] = useState<number>(
+    window.visualViewport?.height ?? window.innerHeight,
+  )
@@
-    function updateViewportHeight() {
-      setViewportHeight(window.visualViewport?.height || 0)
-    }
+    function updateViewportHeight() {
+      setViewportHeight(window.visualViewport?.height ?? window.innerHeight)
+    }

105-115: Avoid double overlay click handlers when propagation is disabled.

When isDisablingPropagation is true, both the Chakra Box and CustomDrawerOverlay close on click. This can double-invoke onClose. Make the overlay’s handler conditional.

-          <CustomDrawerOverlay onClick={onClose} />
+          <CustomDrawerOverlay onClick={isDisablingPropagation ? undefined : onClose} />
src/pages/RFOX/components/Claim/ClaimConfirm.tsx (2)

159-204: Surface errors to users and action center on claim failures.

mutationFn returns undefined on missing wallet/accountNumber and relies on implicit React Query error handling for thrown errors. Consider adding an onError to show a translated error toast and mark the action as failed for visibility.

   } = useMutation({
     mutationFn: async () => {
       if (!wallet || stakingAssetAccountNumber === undefined) return
@@
       return txId
     },
+    onError: (e: unknown) => {
+      // Optional: toast error here via useErrorToast()
+      dispatch(
+        actionSlice.actions.upsertAction({
+          id: selectedUnstakingRequest.id,
+          status: ActionStatus.Failed,
+          type: ActionType.RfoxClaim,
+          createdAt: maybeClaimAction?.createdAt ?? Date.now(),
+          updatedAt: Date.now(),
+          rfoxClaimActionMetadata: {
+            request: selectedUnstakingRequest,
+            error: (e as Error)?.message ?? 'unknown_error',
+          },
+        }),
+      )
+    },
   })

336-346: Disable submit when prerequisites are missing.

If wallet is null or stakingAssetAccountNumber is undefined, the mutation returns undefined immediately with no UX feedback. Disable the CTA in that state to avoid a confusing no-op.

-          disabled={!isClaimFeesSuccess || isClaimTxPending}
+          disabled={
+            !isClaimFeesSuccess || isClaimTxPending || !wallet || stakingAssetAccountNumber === undefined
+          }
src/pages/RFOX/components/ClaimModal.tsx (5)

39-53: Align modal behavior with Stake/Unstake by disabling propagation.

To prevent the double-modal/pointer-events issue on mobile, pass the new isDisablingPropagation flag to Dialog as you did in StakeModal.

-    <Dialog isOpen={isOpen} onClose={handleClose} height='auto'>
+    <Dialog isOpen={isOpen} onClose={handleClose} height='auto' isDisablingPropagation>

4-5: Import useNavigate from react-router-dom for web apps.

Other files in this PR use react-router-dom. Align for consistency and to avoid bundling surprises.

-import { useNavigate } from 'react-router'
+import { useNavigate } from 'react-router-dom'

19-23: Remove unused prop or plumb it through.

selectedUnstakingRequest isn’t used. Either remove it from the API or pass it to <Claim /> if needed.

 type ClaimModalProps = {
   isOpen: boolean
   onClose: () => void
-  selectedUnstakingRequest?: UnstakingRequest
 }
@@
-export const ClaimModal: React.FC<ClaimModalProps> = ({ isOpen, onClose }) => {
+export const ClaimModal: React.FC<ClaimModalProps> = ({ isOpen, onClose }) => {

29-36: Guard navigation behind the feature flag (optional).

handleClose unconditionally navigates to /fox-ecosystem. If this modal ever renders with the flag off, that route may be disabled. Gate the redirect accordingly.

+  const isRFOXFoxEcosystemPageEnabled = useFeatureFlag('RfoxFoxEcosystemPage')
@@
-    setTimeout(() => {
-      navigate('/fox-ecosystem')
-    }, 100)
-  }, [onClose, navigate])
+    if (isRFOXFoxEcosystemPageEnabled) {
+      setTimeout(() => {
+        navigate('/fox-ecosystem')
+      }, 100)
+    }
+  }, [onClose, navigate, isRFOXFoxEcosystemPageEnabled])

Note: requires import { useFeatureFlag } from '@/hooks/useFeatureFlag/useFeatureFlag'.


6-9: Prefer absolute alias imports for modal components (consistency).

Other files import modal components via @/components/.... Consider aligning to a single convention in this module too.

-import { Dialog } from '../../../components/Modal/components/Dialog'
-import { DialogBody } from '../../../components/Modal/components/DialogBody'
-import { DialogCloseButton } from '../../../components/Modal/components/DialogCloseButton'
-import { DialogTitle } from '../../../components/Modal/components/DialogTitle'
+import { Dialog } from '@/components/Modal/components/Dialog'
+import { DialogBody } from '@/components/Modal/components/DialogBody'
+import { DialogCloseButton } from '@/components/Modal/components/DialogCloseButton'
+import { DialogTitle } from '@/components/Modal/components/DialogTitle'

Also applies to: 11-17

src/pages/RFOX/components/Claim/ClaimSelect.tsx (4)

99-110: Simplify navigation path construction to reduce duplication

Build the confirm path once, then pass it to navigate. This improves readability and makes future route changes less error-prone.

Apply this diff:

-      const handleClaimClick = (claimId: number) => {
-        navigate(
-          !isRFOXFoxEcosystemPageEnabled
-            ? `${RfoxRoute.Claim}/${claimId}/confirm`
-            : `/fox-ecosystem/${claimId}/confirm`,
-          {
-            state: {
-              selectedUnstakingRequest: unstakingRequest,
-            },
-          },
-        )
-      }
+      const handleClaimClick = (claimId: number) => {
+        const confirmPath = isRFOXFoxEcosystemPageEnabled
+          ? `/fox-ecosystem/${claimId}/confirm`
+          : `${RfoxRoute.Claim}/${claimId}/confirm`
+        navigate(confirmPath, {
+          state: { selectedUnstakingRequest: unstakingRequest },
+        })
+      }

70-73: Remove redundant/unreachable guard

Line 72 repeats the stakingAssetAccountId guard right after returning a component, so it can never run. Dropping it reduces noise.

Apply this diff:

   if (!stakingAssetAccountId) return <ChainNotSupported chainId={arbitrumChainId} />
-  if (!stakingAssetAccountId) return

113-121: Use a stable, unique key for list items

Using cooldownExpiry as the key risks collisions if multiple requests share the same expiry. Prefer index (if unique) or a composite key.

Apply this diff:

-          key={unstakingRequest.cooldownExpiry.toString()}
+          key={`${unstakingRequest.index}-${unstakingRequest.cooldownExpiry}`}

96-98: Minor: compute the humanized timestamp directly from the target time

dayjs(Date.now() + cooldownDeltaMs) is equivalent to dayjs(unstakingTimestampMs). The latter reads clearer and avoids double Date.now() calls.

Apply this diff:

-      const cooldownDeltaMs = unstakingTimestampMs - currentTimestampMs
-      const cooldownPeriodHuman = dayjs(Date.now() + cooldownDeltaMs).fromNow()
+      const cooldownDeltaMs = unstakingTimestampMs - currentTimestampMs
+      const cooldownPeriodHuman = dayjs(unstakingTimestampMs).fromNow()
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7f28ce0 and 456062a.

📒 Files selected for processing (11)
  • src/Routes/RoutesCommon.tsx (4 hunks)
  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx (3 hunks)
  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx (1 hunks)
  • src/components/Modal/components/Dialog.tsx (4 hunks)
  • src/pages/Fox/components/RFOXSection.tsx (8 hunks)
  • src/pages/RFOX/components/Claim/ClaimConfirm.tsx (4 hunks)
  • src/pages/RFOX/components/Claim/ClaimSelect.tsx (5 hunks)
  • src/pages/RFOX/components/Claim/types.ts (1 hunks)
  • src/pages/RFOX/components/ClaimModal.tsx (1 hunks)
  • src/pages/RFOX/components/StakeModal.tsx (1 hunks)
  • src/pages/RFOX/components/UnstakeModal.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
  • src/pages/RFOX/components/UnstakeModal.tsx
  • src/Routes/RoutesCommon.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{ts,tsx}: ALWAYS use Result<T, E> pattern for error handling in swappers and APIs
ALWAYS use Ok() and Err() from @sniptt/monads for monadic error handling
ALWAYS use custom error classes from @shapeshiftoss/errors
ALWAYS provide meaningful error codes for internationalization
ALWAYS include relevant details in error objects
ALWAYS wrap async operations in try-catch blocks
ALWAYS use AsyncResultOf utility for converting promises to Results
ALWAYS provide fallback error handling
ALWAYS use timeoutMonadic for API calls
ALWAYS provide appropriate timeout values for API calls
ALWAYS handle timeout errors gracefully
ALWAYS validate inputs before processing
ALWAYS provide clear validation error messages
ALWAYS use early returns for validation failures
ALWAYS log errors for debugging
ALWAYS use structured logging for errors
ALWAYS include relevant context in error logs
Throwing errors instead of using monadic patterns is an anti-pattern
Missing try-catch blocks for async operations is an anti-pattern
Generic error messages without context are an anti-pattern
Not handling specific error types is an anti-pattern
Missing timeout handling is an anti-pattern
No input validation is an anti-pattern
Poor error logging is an anti-pattern
Using any for error types is an anti-pattern
Missing error codes for internationalization is an anti-pattern
No fallback error handling is an anti-pattern
Console.error without structured logging is an anti-pattern

**/*.{ts,tsx}: ALWAYS use camelCase for variables, functions, and methods
ALWAYS use descriptive names that explain the purpose for variables and functions
ALWAYS use verb prefixes for functions that perform actions
ALWAYS use PascalCase for types, interfaces, and enums
ALWAYS use descriptive names that indicate the structure for types, interfaces, and enums
ALWAYS use suffixes like Props, State, Config, Type when appropriate for types and interfaces
ALWAYS use UPPER_SNAKE_CASE for constants and configuration values
ALWAYS use d...

Files:

  • src/pages/RFOX/components/Claim/types.ts
  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
  • src/pages/RFOX/components/ClaimModal.tsx
  • src/pages/RFOX/components/Claim/ClaimConfirm.tsx
  • src/pages/RFOX/components/Claim/ClaimSelect.tsx
  • src/components/Modal/components/Dialog.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/pages/RFOX/components/StakeModal.tsx
**/*

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*: ALWAYS use appropriate file extensions
Flag files without kebab-case

Files:

  • src/pages/RFOX/components/Claim/types.ts
  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
  • src/pages/RFOX/components/ClaimModal.tsx
  • src/pages/RFOX/components/Claim/ClaimConfirm.tsx
  • src/pages/RFOX/components/Claim/ClaimSelect.tsx
  • src/components/Modal/components/Dialog.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/pages/RFOX/components/StakeModal.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

USE Redux only for global state shared across multiple places

Files:

  • src/pages/RFOX/components/Claim/types.ts
  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
  • src/pages/RFOX/components/ClaimModal.tsx
  • src/pages/RFOX/components/Claim/ClaimConfirm.tsx
  • src/pages/RFOX/components/Claim/ClaimSelect.tsx
  • src/components/Modal/components/Dialog.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/pages/RFOX/components/StakeModal.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.tsx: ALWAYS wrap components in error boundaries
ALWAYS provide user-friendly fallback components in error boundaries
ALWAYS log errors for debugging in error boundaries
ALWAYS use useErrorToast hook for displaying errors
ALWAYS provide translated error messages in error toasts
ALWAYS handle different error types appropriately in error toasts
Missing error boundaries in React components is an anti-pattern

**/*.tsx: ALWAYS use PascalCase for React component names
ALWAYS use descriptive names that indicate the component's purpose
ALWAYS match the component name to the file name
Flag components without PascalCase
Flag default exports for components

Files:

  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
  • src/pages/RFOX/components/ClaimModal.tsx
  • src/pages/RFOX/components/Claim/ClaimConfirm.tsx
  • src/pages/RFOX/components/Claim/ClaimSelect.tsx
  • src/components/Modal/components/Dialog.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/pages/RFOX/components/StakeModal.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

**/*.{tsx,jsx}: ALWAYS use useMemo for expensive computations, object/array creations, and filtered data
ALWAYS use useMemo for derived values and computed properties
ALWAYS use useMemo for conditional values and simple transformations
ALWAYS use useCallback for event handlers and functions passed as props
ALWAYS use useCallback for any function that could be passed as a prop or dependency
ALWAYS include all dependencies in useEffect, useMemo, useCallback dependency arrays
NEVER use // eslint-disable-next-line react-hooks/exhaustive-deps unless absolutely necessary
ALWAYS explain why dependencies are excluded if using eslint disable
ALWAYS use named exports for components
NEVER use default exports for components
KEEP component files under 200 lines when possible
BREAK DOWN large components into smaller, reusable pieces
EXTRACT complex logic into custom hooks
USE local state for component-level state
LIFT state up when needed across multiple components
USE Context for avoiding prop drilling
ALWAYS wrap components in error boundaries for production
ALWAYS handle async errors properly
ALWAYS provide user-friendly error messages
ALWAYS use virtualization for lists with 100+ items
ALWAYS implement proper key props for list items
ALWAYS lazy load heavy components
ALWAYS use React.lazy for code splitting
Components receiving props are wrapped with memo

Files:

  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
  • src/pages/RFOX/components/ClaimModal.tsx
  • src/pages/RFOX/components/Claim/ClaimConfirm.tsx
  • src/pages/RFOX/components/Claim/ClaimSelect.tsx
  • src/components/Modal/components/Dialog.tsx
  • src/pages/Fox/components/RFOXSection.tsx
  • src/pages/RFOX/components/StakeModal.tsx
🧠 Learnings (23)
📓 Common learnings
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.813Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.
📚 Learning: 2025-07-29T15:04:28.083Z
Learnt from: NeOMakinG
PR: shapeshift/web#10139
File: src/components/MultiHopTrade/components/TradeConfirm/components/ExpandableStepperSteps.tsx:109-115
Timestamp: 2025-07-29T15:04:28.083Z
Learning: In src/components/MultiHopTrade/components/TradeConfirm/components/ExpandableStepperSteps.tsx, the component is used under an umbrella that 100% of the time contains the quote, making the type assertion `activeTradeQuote?.steps[currentHopIndex] as TradeQuoteStep` safe. Adding conditional returns before hooks would violate React's Rules of Hooks.

Applied to files:

  • src/pages/RFOX/components/Claim/types.ts
  • src/pages/RFOX/components/Claim/ClaimSelect.tsx
📚 Learning: 2025-08-22T12:59:01.168Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx:26-29
Timestamp: 2025-08-22T12:59:01.168Z
Learning: In src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx, NeOMakinG declined wrapping the RewardDistributionActionCard component with React.memo, saying it was "too much", suggesting that like other action center components, memoization is not beneficial for this specific use case.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
📚 Learning: 2025-08-22T13:02:58.779Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx:33-41
Timestamp: 2025-08-22T13:02:58.779Z
Learning: In src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx, NeOMakinG declined optimizing useMemo dependencies to depend on lifetimeRewardDistributionsQuery.data instead of the entire query object, indicating indifference toward this type of performance optimization.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
  • src/pages/RFOX/components/Claim/ClaimSelect.tsx
  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-22T13:03:36.881Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/Explore/Explore.tsx:110-113
Timestamp: 2025-08-22T13:03:36.881Z
Learning: In src/Routes/RoutesCommon.tsx, the /fox-ecosystem route is already properly gated using disable: !getConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE, which prevents direct navigation when the VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE flag is false.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
  • src/pages/RFOX/components/Claim/ClaimConfirm.tsx
  • src/pages/RFOX/components/Claim/ClaimSelect.tsx
  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-22T13:16:12.687Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx:104-105
Timestamp: 2025-08-22T13:16:12.687Z
Learning: In src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx, the guard `if (!actions[actionId]) return` before upserting completed reward distributions is intentional product behavior. NeOMakinG confirmed that the system should only show completion notifications for reward distributions that were previously seen in a pending state, not for distributions the user missed during the pending phase.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
📚 Learning: 2025-08-22T13:02:38.035Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/Explore/Explore.tsx:91-92
Timestamp: 2025-08-22T13:02:38.035Z
Learning: In the ShapeShift web codebase, feature flags are consistently used as string literals with the useFeatureFlag hook (e.g., useFeatureFlag('RfoxFoxEcosystemPage')), not as enums. The hook accepts keyof FeatureFlags which provides type safety through the TypeScript type system rather than requiring an enum. This is the established pattern used throughout the entire codebase.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
📚 Learning: 2025-08-22T12:59:01.664Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx:37-53
Timestamp: 2025-08-22T12:59:01.664Z
Learning: In RewardDistributionActionCard component (src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx), NeOMakinG confirmed that runeAsset is expected to always be defined when the component renders, so defensive guards against undefined runeAsset are not needed.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
📚 Learning: 2025-08-25T12:58:39.508Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx:69-70
Timestamp: 2025-08-25T12:58:39.508Z
Learning: In RewardDistributionNotification component (src/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx), NeOMakinG confirmed that action is expected to always be defined when the component renders, so defensive guards against undefined action are not needed.

Applied to files:

  • src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx
📚 Learning: 2025-08-08T15:00:22.321Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-08T15:00:49.887Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-25T12:59:43.823Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/Fox/components/RFOXSection.tsx:234-248
Timestamp: 2025-08-25T12:59:43.823Z
Learning: NeOMakinG considers suggestions to refactor working modal state management from useState to useDisclosure as too nitpicky, preferring to keep such cosmetic code improvements out of feature PRs unless they address actual issues.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-08T14:59:40.422Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/pages/Explore/ExploreCategory.tsx:231-238
Timestamp: 2025-08-08T14:59:40.422Z
Learning: In src/pages/Explore/ExploreCategory.tsx, for the PageHeader filter trigger, NeOMakinG considers changing a clickable Chakra Icon to IconButton too nitpicky for this PR and prefers to keep the current Icon-based trigger; such minor a11y/UI nitpicks should be deferred to a follow-up if needed.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-22T13:00:44.813Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.813Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-08T11:41:22.794Z
Learnt from: NeOMakinG
PR: shapeshift/web#10234
File: src/components/MultiHopTrade/hooks/useGetTradeQuotes/hooks/useTrackTradeQuotes.ts:42-86
Timestamp: 2025-08-08T11:41:22.794Z
Learning: NeOMakinG prefers not to include refactors in move-only PRs; such suggestions should be deferred to follow-up issues instead of being applied within the same PR.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-14T17:54:32.563Z
Learnt from: gomesalexandre
PR: shapeshift/web#10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx:97-108
Timestamp: 2025-08-14T17:54:32.563Z
Learning: In ReusableLpStatus component (src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx), the txAssets dependency is stable from first render because poolAsset, baseAsset, actionSide, and action are all defined first render, making the current txAssetsStatuses initialization pattern safe without needing useEffect synchronization.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : Missing error boundaries in React components is an anti-pattern

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : ALWAYS wrap components in error boundaries

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : ALWAYS provide user-friendly fallback components in error boundaries

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:10:38.426Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/react-best-practices.mdc:0-0
Timestamp: 2025-08-03T22:10:38.426Z
Learning: Applies to **/*.{tsx,jsx} : ALWAYS wrap components in error boundaries for production

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : ALWAYS handle different error types appropriately in error toasts

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : ALWAYS log errors for debugging in error boundaries

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
📚 Learning: 2025-08-03T22:09:37.542Z
Learnt from: CR
PR: shapeshift/web#0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-08-03T22:09:37.542Z
Learning: Applies to **/*.tsx : ALWAYS use useErrorToast hook for displaying errors

Applied to files:

  • src/pages/RFOX/components/StakeModal.tsx
🧬 Code graph analysis (4)
src/pages/RFOX/components/ClaimModal.tsx (4)
src/pages/RFOX/hooks/useGetUnstakingRequestsQuery/utils.ts (1)
  • UnstakingRequest (22-30)
src/components/Modal/components/Dialog.tsx (1)
  • Dialog (128-128)
src/components/Modal/components/DialogHeader.tsx (3)
  • DialogHeader (15-48)
  • DialogHeaderLeft (50-52)
  • DialogHeaderRight (60-62)
src/components/Modal/components/DialogCloseButton.tsx (1)
  • DialogCloseButton (15-38)
src/pages/RFOX/components/Claim/ClaimSelect.tsx (1)
src/pages/RFOX/Widget.tsx (1)
  • RfoxTabIndex (13-18)
src/pages/Fox/components/RFOXSection.tsx (7)
src/pages/Fox/hooks/useFoxPageContext.tsx (1)
  • useFoxPageContext (66-72)
src/pages/RFOX/hooks/useRfoxContext.tsx (1)
  • useRFOXContext (78-84)
src/state/store.ts (1)
  • useAppDispatch (151-151)
src/pages/RFOX/hooks/useGetUnstakingRequestsQuery/utils.ts (1)
  • UnstakingRequest (22-30)
src/pages/RFOX/components/StakeModal.tsx (1)
  • StakeModal (21-41)
src/pages/RFOX/components/UnstakeModal.tsx (1)
  • UnstakeModal (21-41)
src/pages/RFOX/components/ClaimModal.tsx (1)
  • ClaimModal (25-55)
src/pages/RFOX/components/StakeModal.tsx (2)
src/components/Modal/components/DialogHeader.tsx (3)
  • DialogHeader (15-48)
  • DialogHeaderLeft (50-52)
  • DialogHeaderRight (60-62)
src/components/Modal/components/DialogCloseButton.tsx (1)
  • DialogCloseButton (15-38)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Call / Static
🔇 Additional comments (17)
src/pages/Fox/components/RFOXSection.tsx (6)

36-42: Imports and alias usage look good

Using alias paths for FOX components and adding the modal imports improves consistency and keeps these modals local to the section (not the global provider), matching the PR objectives. The UnstakingRequest type import is correct.

Also applies to: 48-48


118-123: Good: deep-link account synchronization

Syncing stakingAssetAccountId from a deep-linked selectedUnstakingRequest ensures the context is correct when arriving from Action Center.


124-128: Good: claim modal auto-open is guarded to avoid loops

Initializing isClaimModalOpen and the follow-up effect using usePrevious ensures the dialog opens once on deep link and won’t immediately re-open after a manual close.

Also applies to: 113-117


280-309: CTA gating matches the flag intent

Rendering Manage when the ecosystem page is disabled and inline Stake/Unstake/Claim when enabled aligns with the PR’s flag-driven rollout.

Also applies to: 379-379


437-440: Check close behavior for ClaimModal navigation

Closing ClaimModal navigates to /fox-ecosystem (in the modal component) with a delayed push; that can add extra history entries and affect back-button UX. Validate whether using replace: true is preferable to avoid stacking duplicate entries, especially when a deep link opened the modal.

If needed, adjust in ClaimModal.tsx:

-      navigate('/fox-ecosystem')
+      navigate('/fox-ecosystem', { replace: true })

102-102: Feature flag key aligns correctly with config and routes — no change needed

The useFeatureFlag('RfoxFoxEcosystemPage') usage in RFOXSection.tsx exactly matches:

  • The preferences slice key RfoxFoxEcosystemPage in preferencesSlice.ts (mapped from getConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE)
  • The environment variable VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE in config.ts
  • The route gating in RoutesCommon.tsx (disable: !getConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE)
  • The test mock RfoxFoxEcosystemPage in src/test/mocks/store.ts

All references use the same key, so there is no misalignment between feature flag, config, and routing.

Likely an incorrect or invalid review comment.

src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx (3)

9-9: Feature-flag hook import matches established pattern — LGTM

Using useFeatureFlag with a string literal key is consistent with the codebase conventions and the new flag.


26-26: Good boolean naming and correct flag wiring

isRFOXFoxEcosystemPageEnabled clearly conveys intent and drives the navigation toggle as required by the PR objectives.


35-48: Routing and state usage validated

  • In src/Routes/RoutesCommon.tsx, the entry
    {
      path: '/fox-ecosystem/*',
      main: FoxEcosystemPage,}
    uses a wildcard (*) to catch all subpaths, including /:index/confirm.
  • In src/pages/Fox/FoxEcosystemPage.tsx, the page renders <RFOXSection />.
  • The <RFOXSection /> component (in src/pages/Fox/components/RFOXSection.tsx) reads
    location.state?.selectedUnstakingRequest and opens the Claim modal based on that state.

No changes needed—the gated route covers /fox-ecosystem/:index/confirm, and the target component consumes state.selectedUnstakingRequest as expected.

src/components/Modal/components/Dialog.tsx (1)

95-118: Confirm the necessity of both Box and Drawer.Overlay layers.

With modal={false} you add a full-screen Box plus the Drawer.Overlay. Ensure z-index stacking and pointer-events are as intended across devices; otherwise you might be able to drop one layer.

Manual test matrix:

  • Mobile narrow viewport: open two stacked drawers, verify background is non-interactive and a single click closes the topmost.
  • Toggle isDisablingPropagation true/false, check focus trap and backdrop behavior remain consistent.
src/pages/RFOX/components/StakeModal.tsx (1)

25-39: Good use of the new propagation-disabling behavior.

Using isDisablingPropagation here addresses the double-modal/pointer-events issue discussed in the PR. Header/body composition looks consistent, and background is correctly set to transparent.

src/pages/RFOX/components/Claim/ClaimConfirm.tsx (2)

100-107: Feature-flagged back navigation is correct.

The fallback to RfoxRoute.Claim keeps legacy behavior when the ecosystem page is disabled.


68-72: headerComponent prop is typed but unused.

If the header slot is not intended for this screen anymore, drop it from the prop type to avoid confusion; otherwise, render it or pass through to the header.

-export const ClaimConfirm: FC<Pick<ClaimRouteProps, 'headerComponent'> & ClaimConfirmProps> = ({
+export const ClaimConfirm: FC<ClaimConfirmProps> = ({
   selectedUnstakingRequest,
   claimTxid,
   setClaimTxid,
 }) => {
src/pages/RFOX/components/Claim/ClaimSelect.tsx (4)

25-28: Making setStepIndex optional + guarding the Unstake CTA is the right call

This allows reuse in contexts where a stepper isn’t present and prevents rendering a dead CTA when claims can’t be fetched. The optional chaining in the handler is also correct.

Also applies to: 33-35, 47-51


20-20: Good feature-flag integration

Importing useFeatureFlag and deriving isRFOXFoxEcosystemPageEnabled keeps routing decisions localized and easy to toggle. Nice.

Also applies to: 60-61


96-98: Optional: ensure dayjs relativeTime plugin is initialized once app-wide

fromNow() requires the relativeTime plugin. If not already initialized globally (e.g., app bootstrap), initialize it to avoid runtime surprises.

Wherever dayjs is configured (app entry), verify this exists:

import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(relativeTime)

20-20: All checks passed: /fox-ecosystem/:claimId/confirm is available and correctly gated

  • The route definition for /fox-ecosystem/* exists in src/Routes/RoutesCommon.tsx (lines 273–276) and will match /fox-ecosystem/:claimId/confirm.
  • In src/pages/RFOX/components/Claim/ClaimSelect.tsx, when isRFOXFoxEcosystemPageEnabled is true the code navigates to /fox-ecosystem/${claimId}/confirm and passes the selectedUnstakingRequest in navigation state.
  • src/pages/RFOX/components/Claim/ClaimConfirm.tsx correctly declares and consumes the selectedUnstakingRequest prop via React Router state.
  • The feature flag hook useFeatureFlag('RfoxFoxEcosystemPage') is used in both components, and the env var VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE is wired through config.ts, the preferences slice, and RoutesCommon.tsx.

No changes required here.

@premiumjibles
Copy link
Collaborator

@NeOMakinG feels bad 🙃 . The way these modals interact on mobile is a huge pain.

Currently now when I open up the stake drawer and need to connect my wallet. If I hit connect wallet my app get's into a state of blocking all clicks.

https://jam.dev/c/d2bbe7de-bde9-4354-bd16-6138a5c94ebd

If we bring disabling propagation then we're reintroducing the weird behaviour where you can scroll the background whilst a modal is open. Did you try introducing a similar hack to my manage accounts change to see if that works?
https://jam.dev/c/80245b5a-51f1-42b7-92c0-f64545611566

@NeOMakinG
Copy link
Collaborator Author

@NeOMakinG feels bad 🙃 . The way these modals interact on mobile is a huge pain.

Currently now when I open up the stake drawer and need to connect my wallet. If I hit connect wallet my app get's into a state of blocking all clicks.

https://jam.dev/c/d2bbe7de-bde9-4354-bd16-6138a5c94ebd

If we bring disabling propagation then we're reintroducing the weird behaviour where you can scroll the background whilst a modal is open. Did you try introducing a similar hack to my manage accounts change to see if that works? https://jam.dev/c/80245b5a-51f1-42b7-92c0-f64545611566

Well, I think I found a great solution for that, I disabled propagation on asset row click, avoiding the click to be outside of the underlying modal and so the close event, seems fine, wdyt about that?

Pushed the fix, let me know if it's more reliable now!

I spotted an issue that isn't probably related to this PR but the whole app, we might want to fix that in another ticket for the sake of progression but if you have to connect using a dialog, then connect your wallet, the previous dialog will be closed (this is the same issue, clicks are propagated outside of the underlying dialog which is then closing it as a side effect)

I don't know if we can find a proper solution for that, maybe we should disable this clicking outside behavior and only keep the overlay click outside one somehow

@premiumjibles
Copy link
Collaborator

@NeOMakinG disabling propagation on the asset row looks like it works well for the standard case of selecting an asset. And definitely i'd say the modal is usable now. The only thing I noticed that's mildly annoying but not that bad is that when you hit the "x" button on select an asset it closes both modals, not just the select an asset one. https://jam.dev/c/88cc9ad3-d616-4fb6-8223-189c63ae302e

Personally i'm going to say non-blocking on that one as it's pretty edge case. But if we could fix it now or as a follow up I think that would be ideal.

Connect wallet modal is now functional when you open it up through the modal but I am getting some weird behaviour where I can only interact with it after I click once. Almost like in the background my first click is closing some background modal and my second click can now interact. Hard to see in this jam since you can't actually see when i'm clicking but hopefully you get the idea https://jam.dev/c/ebc9e6ff-a0a2-40be-a3e9-41c7f2b236b1. This one I'd say is blocking as it feels a bit broken even though I think users would probably just try again anyway and successfully connect.

@NeOMakinG
Copy link
Collaborator Author

NeOMakinG commented Aug 27, 2025

@NeOMakinG disabling propagation on the asset row looks like it works well for the standard case of selecting an asset. And definitely i'd say the modal is usable now. The only thing I noticed that's mildly annoying but not that bad is that when you hit the "x" button on select an asset it closes both modals, not just the select an asset one. https://jam.dev/c/88cc9ad3-d616-4fb6-8223-189c63ae302e

Personally i'm going to say non-blocking on that one as it's pretty edge case. But if we could fix it now or as a follow up I think that would be ideal.

Connect wallet modal is now functional when you open it up through the modal but I am getting some weird behaviour where I can only interact with it after I click once. Almost like in the background my first click is closing some background modal and my second click can now interact. Hard to see in this jam since you can't actually see when i'm clicking but hopefully you get the idea https://jam.dev/c/ebc9e6ff-a0a2-40be-a3e9-41c7f2b236b1. This one I'd say is blocking as it feels a bit broken even though I think users would probably just try again anyway and successfully connect.

I've been able to fix your feedbacks, even thought it's not sexy and not very convenient re: stake or unstake modal is closed on connect wallet button (which wouldn't happen on mobile app anyway as the user can't access this page without connecting a wallet)

We absolutely need to find a better drawer library that would accept drawer stacking without any bugs so we can simplify this and reduce the pain

Gave it a big smoke test, hopefully we are good now and we can progress!

Also captured this pain in https://linear.app/shapeshift-dao/issue/SS-4850/find-a-better-library-to-support-mobile-drawers

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

🧹 Nitpick comments (2)
src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx (2)

38-40: Don’t force-enable the Connect button; let callers opt to disable when needed

Hard-coding isDisabled={false} removes upstream control and can surprise consumers. If the intent is “always allow connect,” consider documenting it; otherwise, let restProps drive disabled state.

-          <Button {...restProps} onClick={handleConnect} isDisabled={false} colorScheme='blue'>
+          <Button {...restProps} onClick={handleConnect} colorScheme='blue'>
             {translate('common.connectWallet')}
           </Button>
-      <Button {...restProps} onClick={handleConnect} isDisabled={false} colorScheme='blue'>
+      <Button {...restProps} onClick={handleConnect} colorScheme='blue'>
         {translate('common.connectWallet')}
       </Button>

Also applies to: 46-48


55-58: Minor: coerce disabled to a boolean to avoid passing undefined

Chakra handles undefined, but being explicit improves readability.

-        isDisabled={!isValidWallet || restProps.isDisabled}
+        isDisabled={!isValidWallet || Boolean(restProps.isDisabled)}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1051d52 and 18e6746.

📒 Files selected for processing (2)
  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx (2 hunks)
  • src/components/Modal/components/DialogCloseButton.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{ts,tsx}: ALWAYS use Result<T, E> pattern for error handling in swappers and APIs
ALWAYS use Ok() and Err() from @sniptt/monads for monadic error handling
ALWAYS use custom error classes from @shapeshiftoss/errors
ALWAYS provide meaningful error codes for internationalization
ALWAYS include relevant details in error objects
ALWAYS wrap async operations in try-catch blocks
ALWAYS use AsyncResultOf utility for converting promises to Results
ALWAYS provide fallback error handling
ALWAYS use timeoutMonadic for API calls
ALWAYS provide appropriate timeout values for API calls
ALWAYS handle timeout errors gracefully
ALWAYS validate inputs before processing
ALWAYS provide clear validation error messages
ALWAYS use early returns for validation failures
ALWAYS log errors for debugging
ALWAYS use structured logging for errors
ALWAYS include relevant context in error logs
Throwing errors instead of using monadic patterns is an anti-pattern
Missing try-catch blocks for async operations is an anti-pattern
Generic error messages without context are an anti-pattern
Not handling specific error types is an anti-pattern
Missing timeout handling is an anti-pattern
No input validation is an anti-pattern
Poor error logging is an anti-pattern
Using any for error types is an anti-pattern
Missing error codes for internationalization is an anti-pattern
No fallback error handling is an anti-pattern
Console.error without structured logging is an anti-pattern

**/*.{ts,tsx}: ALWAYS use camelCase for variables, functions, and methods
ALWAYS use descriptive names that explain the purpose for variables and functions
ALWAYS use verb prefixes for functions that perform actions
ALWAYS use PascalCase for types, interfaces, and enums
ALWAYS use descriptive names that indicate the structure for types, interfaces, and enums
ALWAYS use suffixes like Props, State, Config, Type when appropriate for types and interfaces
ALWAYS use UPPER_SNAKE_CASE for constants and configuration values
ALWAYS use d...

Files:

  • src/components/Modal/components/DialogCloseButton.tsx
  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.tsx: ALWAYS wrap components in error boundaries
ALWAYS provide user-friendly fallback components in error boundaries
ALWAYS log errors for debugging in error boundaries
ALWAYS use useErrorToast hook for displaying errors
ALWAYS provide translated error messages in error toasts
ALWAYS handle different error types appropriately in error toasts
Missing error boundaries in React components is an anti-pattern

**/*.tsx: ALWAYS use PascalCase for React component names
ALWAYS use descriptive names that indicate the component's purpose
ALWAYS match the component name to the file name
Flag components without PascalCase
Flag default exports for components

Files:

  • src/components/Modal/components/DialogCloseButton.tsx
  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
**/*

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*: ALWAYS use appropriate file extensions
Flag files without kebab-case

Files:

  • src/components/Modal/components/DialogCloseButton.tsx
  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

**/*.{tsx,jsx}: ALWAYS use useMemo for expensive computations, object/array creations, and filtered data
ALWAYS use useMemo for derived values and computed properties
ALWAYS use useMemo for conditional values and simple transformations
ALWAYS use useCallback for event handlers and functions passed as props
ALWAYS use useCallback for any function that could be passed as a prop or dependency
ALWAYS include all dependencies in useEffect, useMemo, useCallback dependency arrays
NEVER use // eslint-disable-next-line react-hooks/exhaustive-deps unless absolutely necessary
ALWAYS explain why dependencies are excluded if using eslint disable
ALWAYS use named exports for components
NEVER use default exports for components
KEEP component files under 200 lines when possible
BREAK DOWN large components into smaller, reusable pieces
EXTRACT complex logic into custom hooks
USE local state for component-level state
LIFT state up when needed across multiple components
USE Context for avoiding prop drilling
ALWAYS wrap components in error boundaries for production
ALWAYS handle async errors properly
ALWAYS provide user-friendly error messages
ALWAYS use virtualization for lists with 100+ items
ALWAYS implement proper key props for list items
ALWAYS lazy load heavy components
ALWAYS use React.lazy for code splitting
Components receiving props are wrapped with memo

Files:

  • src/components/Modal/components/DialogCloseButton.tsx
  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

USE Redux only for global state shared across multiple places

Files:

  • src/components/Modal/components/DialogCloseButton.tsx
  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
🧠 Learnings (1)
📓 Common learnings
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx:104-105
Timestamp: 2025-08-22T13:16:12.721Z
Learning: In src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx, the guard `if (!actions[actionId]) return` before upserting completed reward distributions is intentional product behavior. NeOMakinG confirmed that the system should only show completion notifications for reward distributions that were previously seen in a pending state, not for distributions the user missed during the pending phase.
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.879Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.
🧬 Code graph analysis (2)
src/components/Modal/components/DialogCloseButton.tsx (1)
src/lib/globals.ts (1)
  • isMobile (16-16)
src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx (1)
src/theme/theme.tsx (1)
  • breakpoints (37-44)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install and Cache
🔇 Additional comments (2)
src/components/Modal/components/DialogCloseButton.tsx (1)

29-37: No additional Drawer.Close instances require asChild
I scanned all .tsx files for Drawer.Close wrapping IconButton/Button elements without the asChild prop and found no other occurrences. The change in DialogCloseButton.tsx is the only instance, so no further audits are needed.

src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx (1)

2-2: LGTM: imports align with new responsive/drawer logic and are all used

Additions of useMediaQuery, Drawer, useDialog, and breakpoints are purposeful and correctly referenced.

Also applies to: 5-5, 7-7, 10-10

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: 0

🧹 Nitpick comments (1)
src/pages/Fox/components/RFOXSection.tsx (1)

107-111: Harden the location.state cast to avoid runtime shape issues

Guard the cast so we don’t read fields from an unexpected state shape (e.g., external navigation or stale history entries).

-  const selectedUnstakingRequest = location.state?.selectedUnstakingRequest as
-    | UnstakingRequest
-    | undefined
+  const selectedUnstakingRequest =
+    (location.state as Record<string, unknown> | undefined)?.selectedUnstakingRequest &&
+    typeof (location.state as any).selectedUnstakingRequest?.stakingAssetAccountId === 'string'
+      ? ((location.state as any).selectedUnstakingRequest as UnstakingRequest)
+      : undefined
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d94b155 and f9ffaab.

📒 Files selected for processing (1)
  • src/pages/Fox/components/RFOXSection.tsx (8 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{ts,tsx}: ALWAYS use Result<T, E> pattern for error handling in swappers and APIs
ALWAYS use Ok() and Err() from @sniptt/monads for monadic error handling
ALWAYS use custom error classes from @shapeshiftoss/errors
ALWAYS provide meaningful error codes for internationalization
ALWAYS include relevant details in error objects
ALWAYS wrap async operations in try-catch blocks
ALWAYS use AsyncResultOf utility for converting promises to Results
ALWAYS provide fallback error handling
ALWAYS use timeoutMonadic for API calls
ALWAYS provide appropriate timeout values for API calls
ALWAYS handle timeout errors gracefully
ALWAYS validate inputs before processing
ALWAYS provide clear validation error messages
ALWAYS use early returns for validation failures
ALWAYS log errors for debugging
ALWAYS use structured logging for errors
ALWAYS include relevant context in error logs
Throwing errors instead of using monadic patterns is an anti-pattern
Missing try-catch blocks for async operations is an anti-pattern
Generic error messages without context are an anti-pattern
Not handling specific error types is an anti-pattern
Missing timeout handling is an anti-pattern
No input validation is an anti-pattern
Poor error logging is an anti-pattern
Using any for error types is an anti-pattern
Missing error codes for internationalization is an anti-pattern
No fallback error handling is an anti-pattern
Console.error without structured logging is an anti-pattern

**/*.{ts,tsx}: ALWAYS use camelCase for variables, functions, and methods
ALWAYS use descriptive names that explain the purpose for variables and functions
ALWAYS use verb prefixes for functions that perform actions
ALWAYS use PascalCase for types, interfaces, and enums
ALWAYS use descriptive names that indicate the structure for types, interfaces, and enums
ALWAYS use suffixes like Props, State, Config, Type when appropriate for types and interfaces
ALWAYS use UPPER_SNAKE_CASE for constants and configuration values
ALWAYS use d...

Files:

  • src/pages/Fox/components/RFOXSection.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.tsx: ALWAYS wrap components in error boundaries
ALWAYS provide user-friendly fallback components in error boundaries
ALWAYS log errors for debugging in error boundaries
ALWAYS use useErrorToast hook for displaying errors
ALWAYS provide translated error messages in error toasts
ALWAYS handle different error types appropriately in error toasts
Missing error boundaries in React components is an anti-pattern

**/*.tsx: ALWAYS use PascalCase for React component names
ALWAYS use descriptive names that indicate the component's purpose
ALWAYS match the component name to the file name
Flag components without PascalCase
Flag default exports for components

Files:

  • src/pages/Fox/components/RFOXSection.tsx
**/*

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*: ALWAYS use appropriate file extensions
Flag files without kebab-case

Files:

  • src/pages/Fox/components/RFOXSection.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

**/*.{tsx,jsx}: ALWAYS use useMemo for expensive computations, object/array creations, and filtered data
ALWAYS use useMemo for derived values and computed properties
ALWAYS use useMemo for conditional values and simple transformations
ALWAYS use useCallback for event handlers and functions passed as props
ALWAYS use useCallback for any function that could be passed as a prop or dependency
ALWAYS include all dependencies in useEffect, useMemo, useCallback dependency arrays
NEVER use // eslint-disable-next-line react-hooks/exhaustive-deps unless absolutely necessary
ALWAYS explain why dependencies are excluded if using eslint disable
ALWAYS use named exports for components
NEVER use default exports for components
KEEP component files under 200 lines when possible
BREAK DOWN large components into smaller, reusable pieces
EXTRACT complex logic into custom hooks
USE local state for component-level state
LIFT state up when needed across multiple components
USE Context for avoiding prop drilling
ALWAYS wrap components in error boundaries for production
ALWAYS handle async errors properly
ALWAYS provide user-friendly error messages
ALWAYS use virtualization for lists with 100+ items
ALWAYS implement proper key props for list items
ALWAYS lazy load heavy components
ALWAYS use React.lazy for code splitting
Components receiving props are wrapped with memo

Files:

  • src/pages/Fox/components/RFOXSection.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

USE Redux only for global state shared across multiple places

Files:

  • src/pages/Fox/components/RFOXSection.tsx
🧠 Learnings (12)
📓 Common learnings
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx:7-7
Timestamp: 2025-08-27T09:47:06.236Z
Learning: In shapeshift/web project, NeOMakinG consistently prefers to defer UI/UX improvements and refactoring work (like the Drawer.Close hack fix in ButtonWalletPredicate.tsx) to follow-up PRs rather than expanding the scope of feature PRs, even when the improvements would enhance robustness.
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx:104-105
Timestamp: 2025-08-22T13:16:12.721Z
Learning: In src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx, the guard `if (!actions[actionId]) return` before upserting completed reward distributions is intentional product behavior. NeOMakinG confirmed that the system should only show completion notifications for reward distributions that were previously seen in a pending state, not for distributions the user missed during the pending phase.
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.879Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.
📚 Learning: 2025-08-22T13:02:58.824Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx:33-41
Timestamp: 2025-08-22T13:02:58.824Z
Learning: In src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx, NeOMakinG declined optimizing useMemo dependencies to depend on lifetimeRewardDistributionsQuery.data instead of the entire query object, indicating indifference toward this type of performance optimization.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-22T13:03:36.919Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/Explore/Explore.tsx:110-113
Timestamp: 2025-08-22T13:03:36.919Z
Learning: In src/Routes/RoutesCommon.tsx, the /fox-ecosystem route is already properly gated using disable: !getConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE, which prevents direct navigation when the VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE flag is false.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-08T15:00:22.321Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-08T15:00:49.887Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-27T09:47:06.236Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx:7-7
Timestamp: 2025-08-27T09:47:06.236Z
Learning: In shapeshift/web project, NeOMakinG consistently prefers to defer UI/UX improvements and refactoring work (like the Drawer.Close hack fix in ButtonWalletPredicate.tsx) to follow-up PRs rather than expanding the scope of feature PRs, even when the improvements would enhance robustness.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-25T12:59:43.842Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/Fox/components/RFOXSection.tsx:234-248
Timestamp: 2025-08-25T12:59:43.842Z
Learning: NeOMakinG considers suggestions to refactor working modal state management from useState to useDisclosure as too nitpicky, preferring to keep such cosmetic code improvements out of feature PRs unless they address actual issues.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-08T14:59:40.422Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/pages/Explore/ExploreCategory.tsx:231-238
Timestamp: 2025-08-08T14:59:40.422Z
Learning: In src/pages/Explore/ExploreCategory.tsx, for the PageHeader filter trigger, NeOMakinG considers changing a clickable Chakra Icon to IconButton too nitpicky for this PR and prefers to keep the current Icon-based trigger; such minor a11y/UI nitpicks should be deferred to a follow-up if needed.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-22T13:00:44.879Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.879Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-27T09:51:36.872Z
Learnt from: NeOMakinG
PR: shapeshift/web#10376
File: packages/contracts/src/viemClient.ts:39-40
Timestamp: 2025-08-27T09:51:36.872Z
Learning: NeOMakinG confirmed that issues flagged by CodeRabbit that predate the PR changes and are unrelated to the PR's core objective should be addressed separately, not within the current PR scope.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-08T11:41:22.794Z
Learnt from: NeOMakinG
PR: shapeshift/web#10234
File: src/components/MultiHopTrade/hooks/useGetTradeQuotes/hooks/useTrackTradeQuotes.ts:42-86
Timestamp: 2025-08-08T11:41:22.794Z
Learning: NeOMakinG prefers not to include refactors in move-only PRs; such suggestions should be deferred to follow-up issues instead of being applied within the same PR.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
📚 Learning: 2025-08-14T17:54:32.563Z
Learnt from: gomesalexandre
PR: shapeshift/web#10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx:97-108
Timestamp: 2025-08-14T17:54:32.563Z
Learning: In ReusableLpStatus component (src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx), the txAssets dependency is stable from first render because poolAsset, baseAsset, actionSide, and action are all defined first render, making the current txAssetsStatuses initialization pattern safe without needing useEffect synchronization.

Applied to files:

  • src/pages/Fox/components/RFOXSection.tsx
🧬 Code graph analysis (1)
src/pages/Fox/components/RFOXSection.tsx (7)
src/pages/Fox/hooks/useFoxPageContext.tsx (1)
  • useFoxPageContext (66-72)
src/pages/RFOX/hooks/useRfoxContext.tsx (1)
  • useRFOXContext (78-84)
src/state/store.ts (1)
  • useAppDispatch (151-151)
src/pages/RFOX/hooks/useGetUnstakingRequestsQuery/utils.ts (1)
  • UnstakingRequest (22-30)
src/pages/RFOX/components/StakeModal.tsx (1)
  • StakeModal (21-41)
src/pages/RFOX/components/UnstakeModal.tsx (1)
  • UnstakeModal (21-41)
src/pages/RFOX/components/ClaimModal.tsx (1)
  • ClaimModal (25-55)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install and Cache
🔇 Additional comments (5)
src/pages/Fox/components/RFOXSection.tsx (5)

118-123: Correct: respect deep-linked claim by syncing accountId from selected request

This ensures context is aligned with the deep link. LGTM.


124-128: Correct: open Claim modal once when a deep link appears

The previous-value check prevents re-opens. LGTM.


173-178: Good fix: avoid overriding deep-linked accountId

The early return prevents context thrash and preserves claim flow. LGTM.


282-311: Feature-flag gating of actions is correct

Cleanly swaps Manage vs. in-page Stake/Unstake/Claim when the ecosystem flag is on. LGTM.


439-441: Verify nested modal edge cases with Connect Wallet and selector close

Given known drawer/modal interactions, please recheck:

  • Open Stake/Unstake, then open Connect Wallet from within; ensure focus trap and backdrop are correct and no ghost modal persists.
  • Open Stake -> Asset selector -> close via “x”; confirm only the selector closes, not the parent modal.

@premiumjibles
Copy link
Collaborator

premiumjibles commented Aug 27, 2025

@NeOMakinG if we add useDialog then it kills desktop context

image

@NeOMakinG
Copy link
Collaborator Author

@NeOMakinG if we add useDialog then it kills desktop context

image

Another tricky hack added to handle undefined context, we will probably be able to kick this out if we have a cleaner drawer management

Hopefully we are good now... This is a nightmare 😆

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: 0

♻️ Duplicate comments (1)
src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx (1)

37-41: Fix nested button and first-click bug: use Drawer.Close asChild

Without asChild, Drawer.Close renders its own button and wraps the Chakra Button, producing nested buttons and the reported “extra click” behavior on mobile.

-        <Drawer.Close>
+        <Drawer.Close asChild>
           <Button {...restProps} onClick={handleConnect} isDisabled={false} colorScheme='blue'>
             {translate('common.connectWallet')}
           </Button>
         </Drawer.Close>

Additionally, consider dropping isDisabled={false} here to honor upstream disabled state (see next comment).

🧹 Nitpick comments (3)
src/context/DialogContextProvider/DialogContextProvider.tsx (1)

21-31: Add explicit return type and align no-op setter signature in useSafeDialog

This tightens typings per guidelines and prevents subtle widening; also makes the no-op setter conform to DialogContextType.

-export const useSafeDialog = () => {
+export const useSafeDialog = (): DialogContextType => {
   const context = useContext(DialogContext)
   return (
     context ?? {
       isOpen: false,
-      setIsOpen: () => {},
+      setIsOpen: (_: boolean) => {},
     }
   )
 }
src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx (2)

33-36: Avoid using generic dialog isOpen to infer Drawer context

This hack can inadvertently wrap the Button in Drawer.Close when a non-Drawer modal is open, risking unintended close attempts. Keep for now if needed, but scope it to a drawer-specific signal ASAP (e.g., a DrawerContext or prop from parents rendered inside a Drawer).

Would you like a follow-up ticket to replace this with a Drawer-specific context flag?


38-38: Honor upstream disabled prop on Connect button

Forcing isDisabled={false} ignores restProps.isDisabled. If callers intentionally disable the button, that will be bypassed.

-          <Button {...restProps} onClick={handleConnect} isDisabled={false} colorScheme='blue'>
+          <Button {...restProps} onClick={handleConnect} colorScheme='blue'>

Mirror the same behavior for the non-drawer path (Lines 46–49).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f9ffaab and f8a32cf.

📒 Files selected for processing (2)
  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx (2 hunks)
  • src/context/DialogContextProvider/DialogContextProvider.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{ts,tsx}: ALWAYS use Result<T, E> pattern for error handling in swappers and APIs
ALWAYS use Ok() and Err() from @sniptt/monads for monadic error handling
ALWAYS use custom error classes from @shapeshiftoss/errors
ALWAYS provide meaningful error codes for internationalization
ALWAYS include relevant details in error objects
ALWAYS wrap async operations in try-catch blocks
ALWAYS use AsyncResultOf utility for converting promises to Results
ALWAYS provide fallback error handling
ALWAYS use timeoutMonadic for API calls
ALWAYS provide appropriate timeout values for API calls
ALWAYS handle timeout errors gracefully
ALWAYS validate inputs before processing
ALWAYS provide clear validation error messages
ALWAYS use early returns for validation failures
ALWAYS log errors for debugging
ALWAYS use structured logging for errors
ALWAYS include relevant context in error logs
Throwing errors instead of using monadic patterns is an anti-pattern
Missing try-catch blocks for async operations is an anti-pattern
Generic error messages without context are an anti-pattern
Not handling specific error types is an anti-pattern
Missing timeout handling is an anti-pattern
No input validation is an anti-pattern
Poor error logging is an anti-pattern
Using any for error types is an anti-pattern
Missing error codes for internationalization is an anti-pattern
No fallback error handling is an anti-pattern
Console.error without structured logging is an anti-pattern

**/*.{ts,tsx}: ALWAYS use camelCase for variables, functions, and methods
ALWAYS use descriptive names that explain the purpose for variables and functions
ALWAYS use verb prefixes for functions that perform actions
ALWAYS use PascalCase for types, interfaces, and enums
ALWAYS use descriptive names that indicate the structure for types, interfaces, and enums
ALWAYS use suffixes like Props, State, Config, Type when appropriate for types and interfaces
ALWAYS use UPPER_SNAKE_CASE for constants and configuration values
ALWAYS use d...

Files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
  • src/context/DialogContextProvider/DialogContextProvider.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.tsx: ALWAYS wrap components in error boundaries
ALWAYS provide user-friendly fallback components in error boundaries
ALWAYS log errors for debugging in error boundaries
ALWAYS use useErrorToast hook for displaying errors
ALWAYS provide translated error messages in error toasts
ALWAYS handle different error types appropriately in error toasts
Missing error boundaries in React components is an anti-pattern

**/*.tsx: ALWAYS use PascalCase for React component names
ALWAYS use descriptive names that indicate the component's purpose
ALWAYS match the component name to the file name
Flag components without PascalCase
Flag default exports for components

Files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
  • src/context/DialogContextProvider/DialogContextProvider.tsx
**/*

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*: ALWAYS use appropriate file extensions
Flag files without kebab-case

Files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
  • src/context/DialogContextProvider/DialogContextProvider.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

**/*.{tsx,jsx}: ALWAYS use useMemo for expensive computations, object/array creations, and filtered data
ALWAYS use useMemo for derived values and computed properties
ALWAYS use useMemo for conditional values and simple transformations
ALWAYS use useCallback for event handlers and functions passed as props
ALWAYS use useCallback for any function that could be passed as a prop or dependency
ALWAYS include all dependencies in useEffect, useMemo, useCallback dependency arrays
NEVER use // eslint-disable-next-line react-hooks/exhaustive-deps unless absolutely necessary
ALWAYS explain why dependencies are excluded if using eslint disable
ALWAYS use named exports for components
NEVER use default exports for components
KEEP component files under 200 lines when possible
BREAK DOWN large components into smaller, reusable pieces
EXTRACT complex logic into custom hooks
USE local state for component-level state
LIFT state up when needed across multiple components
USE Context for avoiding prop drilling
ALWAYS wrap components in error boundaries for production
ALWAYS handle async errors properly
ALWAYS provide user-friendly error messages
ALWAYS use virtualization for lists with 100+ items
ALWAYS implement proper key props for list items
ALWAYS lazy load heavy components
ALWAYS use React.lazy for code splitting
Components receiving props are wrapped with memo

Files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
  • src/context/DialogContextProvider/DialogContextProvider.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

USE Redux only for global state shared across multiple places

Files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
  • src/context/DialogContextProvider/DialogContextProvider.tsx
🧠 Learnings (11)
📓 Common learnings
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx:7-7
Timestamp: 2025-08-27T09:47:06.275Z
Learning: In shapeshift/web project, NeOMakinG consistently prefers to defer UI/UX improvements and refactoring work (like the Drawer.Close hack fix in ButtonWalletPredicate.tsx) to follow-up PRs rather than expanding the scope of feature PRs, even when the improvements would enhance robustness.
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx:104-105
Timestamp: 2025-08-22T13:16:12.721Z
Learning: In src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx, the guard `if (!actions[actionId]) return` before upserting completed reward distributions is intentional product behavior. NeOMakinG confirmed that the system should only show completion notifications for reward distributions that were previously seen in a pending state, not for distributions the user missed during the pending phase.
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.879Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.
📚 Learning: 2025-08-27T09:47:06.275Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx:7-7
Timestamp: 2025-08-27T09:47:06.275Z
Learning: In shapeshift/web project, NeOMakinG consistently prefers to defer UI/UX improvements and refactoring work (like the Drawer.Close hack fix in ButtonWalletPredicate.tsx) to follow-up PRs rather than expanding the scope of feature PRs, even when the improvements would enhance robustness.

Applied to files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
📚 Learning: 2025-08-25T23:32:13.876Z
Learnt from: premiumjibles
PR: shapeshift/web#10361
File: src/pages/Markets/components/CardWithSparkline.tsx:83-92
Timestamp: 2025-08-25T23:32:13.876Z
Learning: In shapeshift/web PR #10361, premiumjibles considered the nested button accessibility issue (ChartErrorFallback retry Button inside Card rendered as Button in CardWithSparkline.tsx) out of scope for the error boundaries feature PR, consistent with deferring minor a11y improvements to follow-up PRs.

Applied to files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
📚 Learning: 2025-08-27T09:47:59.394Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/Modal/components/DialogCloseButton.tsx:29-37
Timestamp: 2025-08-27T09:47:59.394Z
Learning: In shapeshift/web, for DialogCloseButton.tsx, the onClick handler must remain on Drawer.Close (not moved to IconButton with asChild) because stopPropagation() needs to be called at the Drawer.Close level to properly handle drawer closing behavior; moving event handling to the child IconButton would break the event flow that Drawer.Close expects.

Applied to files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
📚 Learning: 2025-08-08T15:00:49.887Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.

Applied to files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
📚 Learning: 2025-08-08T14:59:40.422Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/pages/Explore/ExploreCategory.tsx:231-238
Timestamp: 2025-08-08T14:59:40.422Z
Learning: In src/pages/Explore/ExploreCategory.tsx, for the PageHeader filter trigger, NeOMakinG considers changing a clickable Chakra Icon to IconButton too nitpicky for this PR and prefers to keep the current Icon-based trigger; such minor a11y/UI nitpicks should be deferred to a follow-up if needed.

Applied to files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
📚 Learning: 2025-08-27T09:51:36.901Z
Learnt from: NeOMakinG
PR: shapeshift/web#10376
File: packages/contracts/src/viemClient.ts:39-40
Timestamp: 2025-08-27T09:51:36.901Z
Learning: NeOMakinG confirmed that issues flagged by CodeRabbit that predate the PR changes and are unrelated to the PR's core objective should be addressed separately, not within the current PR scope.

Applied to files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
📚 Learning: 2025-08-08T11:41:22.794Z
Learnt from: NeOMakinG
PR: shapeshift/web#10234
File: src/components/MultiHopTrade/hooks/useGetTradeQuotes/hooks/useTrackTradeQuotes.ts:42-86
Timestamp: 2025-08-08T11:41:22.794Z
Learning: NeOMakinG prefers not to include refactors in move-only PRs; such suggestions should be deferred to follow-up issues instead of being applied within the same PR.

Applied to files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
📚 Learning: 2025-08-25T12:59:43.842Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/Fox/components/RFOXSection.tsx:234-248
Timestamp: 2025-08-25T12:59:43.842Z
Learning: NeOMakinG considers suggestions to refactor working modal state management from useState to useDisclosure as too nitpicky, preferring to keep such cosmetic code improvements out of feature PRs unless they address actual issues.

Applied to files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
📚 Learning: 2025-08-08T15:00:22.321Z
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/MultiHopTrade/components/TradeInput/components/HighlightedTokens.tsx:14-14
Timestamp: 2025-08-08T15:00:22.321Z
Learning: In shapeshift/web reviews for NeOMakinG, avoid nitpicks to change deep-relative imports to '@/…' alias paths within feature/non-refactor PRs; defer such style-only changes to a dedicated follow-up refactor unless they fix an issue.

Applied to files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
📚 Learning: 2025-08-22T13:00:44.879Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.879Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.

Applied to files:

  • src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
🧬 Code graph analysis (1)
src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx (2)
src/context/DialogContextProvider/DialogContextProvider.tsx (1)
  • useSafeDialog (23-31)
src/theme/theme.tsx (1)
  • breakpoints (37-44)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install and Cache
🔇 Additional comments (1)
src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx (1)

26-27: Good: responsive gating and safe context access

Using useSafeDialog with useMediaQuery(ssr: false) avoids SSR hydration mismatches and crashes outside providers.

Copy link
Collaborator

@premiumjibles premiumjibles left a comment

Choose a reason for hiding this comment

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

Looks good! Let's bang this schlep of a PR in 🤌

https://jam.dev/c/e1e6e62d-2bb8-43c0-bc86-2b3b098af1db

@NeOMakinG NeOMakinG enabled auto-merge (squash) August 29, 2025 07:16
@NeOMakinG NeOMakinG merged commit 0656f4a into develop Aug 29, 2025
3 of 4 checks passed
@NeOMakinG NeOMakinG deleted the rfox-fox-ecosystem branch August 29, 2025 07:23
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 (2)
src/components/Modal/components/Dialog.tsx (2)

95-101: Fix event listener cleanup; current removeEventListener never detaches.

New arrow functions create different references; focus traps will leak across unmounts on mobile.

Apply this diff:

-    document.addEventListener('focusin', e => e.stopImmediatePropagation())
-    document.addEventListener('focusout', e => e.stopImmediatePropagation())
+    const stopFocusEvent = (e: Event) => e.stopImmediatePropagation()
+    document.addEventListener('focusin', stopFocusEvent)
+    document.addEventListener('focusout', stopFocusEvent)
 
     return () => {
-      document.removeEventListener('focusin', e => e.stopImmediatePropagation())
-      document.removeEventListener('focusout', e => e.stopImmediatePropagation())
+      document.removeEventListener('focusin', stopFocusEvent)
+      document.removeEventListener('focusout', stopFocusEvent)
     }

106-115: Use onOpenChange instead of onClose on Drawer.Root and standardize the snap point

Replace the onClose prop with onOpenChange={handleOpenChange} on Drawer.Root and update activeSnapPoint to DEFAULT_SNAP_POINT to rely on the intended API for open/close events:

       <Drawer.Root
         repositionInputs={isFullScreen ? true : false}
         open={isDialogOpen}
-        onClose={onClose}
-        activeSnapPoint={snapPoint}
+        onOpenChange={handleOpenChange}
+        activeSnapPoint={DEFAULT_SNAP_POINT}
         modal
       >
♻️ Duplicate comments (2)
src/assets/translations/en/main.json (2)

1367-1367: Apostrophe style nit (’ vs ').

Not blocking; prior feedback already covered this and author deferred.


2730-2733: Grammar: prefer “Claim Withdrawal”.

Use the noun form for clarity (matches FOXY too). Same prior suggestion applies here.

Apply:

-    "claimWithdraw": "Claim Withdraw",
+    "claimWithdraw": "Claim Withdrawal",
🧹 Nitpick comments (2)
src/components/Modal/components/Dialog.tsx (2)

45-45: Rename constant to UPPER_SNAKE_CASE per guidelines.

Use a descriptive name and update usages.

Apply this diff:

-const snapPoint = 0.5
+const DEFAULT_SNAP_POINT = 0.5

57-66: Optional: guard window access for SSR-safety.

If SSR ever enters this codepath, window is undefined. Minor, but easy to harden.

Apply this diff:

-  const [viewportHeight, setViewportHeight] = useState(window.visualViewport?.height)
+  const [viewportHeight, setViewportHeight] = useState<number | undefined>(undefined)
@@
-    window.visualViewport?.addEventListener('resize', updateViewportHeight)
+    updateViewportHeight()
+    window.visualViewport?.addEventListener('resize', updateViewportHeight)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f8a32cf and 0229e6a.

📒 Files selected for processing (8)
  • .env (1 hunks)
  • src/Routes/RoutesCommon.tsx (4 hunks)
  • src/assets/translations/en/main.json (5 hunks)
  • src/components/AssetSearch/components/AssetRow.tsx (1 hunks)
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx (3 hunks)
  • src/components/Modal/components/Dialog.tsx (5 hunks)
  • src/pages/Explore/Explore.tsx (3 hunks)
  • src/state/slices/actionSlice/types.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/pages/Explore/Explore.tsx
  • src/components/AssetSearch/components/AssetRow.tsx
  • src/components/Layout/Header/ActionCenter/ActionCenter.tsx
  • src/Routes/RoutesCommon.tsx
  • src/state/slices/actionSlice/types.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{ts,tsx}: ALWAYS use Result<T, E> pattern for error handling in swappers and APIs
ALWAYS use Ok() and Err() from @sniptt/monads for monadic error handling
ALWAYS use custom error classes from @shapeshiftoss/errors
ALWAYS provide meaningful error codes for internationalization
ALWAYS include relevant details in error objects
ALWAYS wrap async operations in try-catch blocks
ALWAYS use AsyncResultOf utility for converting promises to Results
ALWAYS provide fallback error handling
ALWAYS use timeoutMonadic for API calls
ALWAYS provide appropriate timeout values for API calls
ALWAYS handle timeout errors gracefully
ALWAYS validate inputs before processing
ALWAYS provide clear validation error messages
ALWAYS use early returns for validation failures
ALWAYS log errors for debugging
ALWAYS use structured logging for errors
ALWAYS include relevant context in error logs
Throwing errors instead of using monadic patterns is an anti-pattern
Missing try-catch blocks for async operations is an anti-pattern
Generic error messages without context are an anti-pattern
Not handling specific error types is an anti-pattern
Missing timeout handling is an anti-pattern
No input validation is an anti-pattern
Poor error logging is an anti-pattern
Using any for error types is an anti-pattern
Missing error codes for internationalization is an anti-pattern
No fallback error handling is an anti-pattern
Console.error without structured logging is an anti-pattern

**/*.{ts,tsx}: ALWAYS use camelCase for variables, functions, and methods
ALWAYS use descriptive names that explain the purpose for variables and functions
ALWAYS use verb prefixes for functions that perform actions
ALWAYS use PascalCase for types, interfaces, and enums
ALWAYS use descriptive names that indicate the structure for types, interfaces, and enums
ALWAYS use suffixes like Props, State, Config, Type when appropriate for types and interfaces
ALWAYS use UPPER_SNAKE_CASE for constants and configuration values
ALWAYS use d...

Files:

  • src/components/Modal/components/Dialog.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.tsx: ALWAYS wrap components in error boundaries
ALWAYS provide user-friendly fallback components in error boundaries
ALWAYS log errors for debugging in error boundaries
ALWAYS use useErrorToast hook for displaying errors
ALWAYS provide translated error messages in error toasts
ALWAYS handle different error types appropriately in error toasts
Missing error boundaries in React components is an anti-pattern

**/*.tsx: ALWAYS use PascalCase for React component names
ALWAYS use descriptive names that indicate the component's purpose
ALWAYS match the component name to the file name
Flag components without PascalCase
Flag default exports for components

Files:

  • src/components/Modal/components/Dialog.tsx
**/*

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*: ALWAYS use appropriate file extensions
Flag files without kebab-case

Files:

  • src/components/Modal/components/Dialog.tsx
  • src/assets/translations/en/main.json
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

**/*.{tsx,jsx}: ALWAYS use useMemo for expensive computations, object/array creations, and filtered data
ALWAYS use useMemo for derived values and computed properties
ALWAYS use useMemo for conditional values and simple transformations
ALWAYS use useCallback for event handlers and functions passed as props
ALWAYS use useCallback for any function that could be passed as a prop or dependency
ALWAYS include all dependencies in useEffect, useMemo, useCallback dependency arrays
NEVER use // eslint-disable-next-line react-hooks/exhaustive-deps unless absolutely necessary
ALWAYS explain why dependencies are excluded if using eslint disable
ALWAYS use named exports for components
NEVER use default exports for components
KEEP component files under 200 lines when possible
BREAK DOWN large components into smaller, reusable pieces
EXTRACT complex logic into custom hooks
USE local state for component-level state
LIFT state up when needed across multiple components
USE Context for avoiding prop drilling
ALWAYS wrap components in error boundaries for production
ALWAYS handle async errors properly
ALWAYS provide user-friendly error messages
ALWAYS use virtualization for lists with 100+ items
ALWAYS implement proper key props for list items
ALWAYS lazy load heavy components
ALWAYS use React.lazy for code splitting
Components receiving props are wrapped with memo

Files:

  • src/components/Modal/components/Dialog.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

USE Redux only for global state shared across multiple places

Files:

  • src/components/Modal/components/Dialog.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx:7-7
Timestamp: 2025-08-27T09:47:06.275Z
Learning: In shapeshift/web project, NeOMakinG consistently prefers to defer UI/UX improvements and refactoring work (like the Drawer.Close hack fix in ButtonWalletPredicate.tsx) to follow-up PRs rather than expanding the scope of feature PRs, even when the improvements would enhance robustness.
Learnt from: NeOMakinG
PR: shapeshift/web#10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx:104-105
Timestamp: 2025-08-22T13:16:12.721Z
Learning: In src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx, the guard `if (!actions[actionId]) return` before upserting completed reward distributions is intentional product behavior. NeOMakinG confirmed that the system should only show completion notifications for reward distributions that were previously seen in a pending state, not for distributions the user missed during the pending phase.
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.879Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.
📚 Learning: 2025-08-27T09:47:59.394Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/components/Modal/components/DialogCloseButton.tsx:29-37
Timestamp: 2025-08-27T09:47:59.394Z
Learning: In shapeshift/web, for DialogCloseButton.tsx, the onClick handler must remain on Drawer.Close (not moved to IconButton with asChild) because stopPropagation() needs to be called at the Drawer.Close level to properly handle drawer closing behavior; moving event handling to the child IconButton would break the event flow that Drawer.Close expects.

Applied to files:

  • src/components/Modal/components/Dialog.tsx
📚 Learning: 2025-08-27T09:51:36.901Z
Learnt from: NeOMakinG
PR: shapeshift/web#10376
File: packages/contracts/src/viemClient.ts:39-40
Timestamp: 2025-08-27T09:51:36.901Z
Learning: NeOMakinG confirmed that issues flagged by CodeRabbit that predate the PR changes and are unrelated to the PR's core objective should be addressed separately, not within the current PR scope.

Applied to files:

  • src/assets/translations/en/main.json
📚 Learning: 2025-08-22T13:00:44.879Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.879Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.

Applied to files:

  • src/assets/translations/en/main.json
📚 Learning: 2025-08-06T08:58:09.096Z
Learnt from: gomesalexandre
PR: shapeshift/web#10187
File: src/assets/translations/en/main.json:205-206
Timestamp: 2025-08-06T08:58:09.096Z
Learning: gomesalexandre considers translation key consolidation (like multiple keys for "view on explorer" functionality) as nice-to-have improvements rather than critical issues. The team doesn't prioritize i18n consistency across the board currently, and they prefer to handle such consolidation suggestions at their own discretion based on other priorities.

Applied to files:

  • src/assets/translations/en/main.json
📚 Learning: 2025-08-22T13:03:36.919Z
Learnt from: NeOMakinG
PR: shapeshift/web#10323
File: src/pages/Explore/Explore.tsx:110-113
Timestamp: 2025-08-22T13:03:36.919Z
Learning: In src/Routes/RoutesCommon.tsx, the /fox-ecosystem route is already properly gated using disable: !getConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE, which prevents direct navigation when the VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE flag is false.

Applied to files:

  • .env
🧬 Code graph analysis (1)
src/components/Modal/components/Dialog.tsx (1)
src/context/DialogContextProvider/DialogContextProvider.tsx (1)
  • useDialog (13-19)
🪛 dotenv-linter (3.3.0)
.env

[warning] 191-191: [UnorderedKey] The VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE key should go before the VITE_FEATURE_TX_HISTORY_BYE_BYE key

(UnorderedKey)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Call / Static
🔇 Additional comments (5)
src/assets/translations/en/main.json (2)

1126-1136: rFOX claim and reward distribution strings: LGTM.

Keys and placeholders look correct and consistent.


3144-3152: Action Center reward distribution copy: LGTM.

Consistent with new trade keys; placeholders are correct.

src/components/Modal/components/Dialog.tsx (3)

4-4: LGTM: useCallback import is appropriate.

Used by handleClose; no issues.


55-55: LGTM: context destructure is correct.

Alias is clear and matches intent.


114-114: Good: stopPropagation on overlay click.

This aligns with the drawer-stacking workaround and prevents click-through.

VITE_FEATURE_THORCHAIN_TCY_WIDGET=true

VITE_FEATURE_TX_HISTORY_BYE_BYE=false
VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=false
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Order the new feature flag before TX_HISTORY to satisfy dotenv-linter.

Reorder to keep keys sorted and fix the UnorderedKey warning.

Apply:

-VITE_FEATURE_TX_HISTORY_BYE_BYE=false
-VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=false
+VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=false
+VITE_FEATURE_TX_HISTORY_BYE_BYE=false

Run to verify usage across envs and mocks:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Occurrences of VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE (expect .env=false, .env.development=true, tests default=false)"
rg -nC1 --hidden --glob '!**/node_modules/**' 'VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE'
echo
echo "Check config exposure and preferencesSlice wiring"
rg -nP --hidden --glob '!**/node_modules/**' '(getConfig\(\)\.VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE|RfoxFoxEcosystemPage)' src | sed -n '1,120p'

Length of output: 3738


Order feature flags alphabetically to satisfy dotenv-linter.
Reorder keys in environment files to keep them sorted and fix UnorderedKey warnings.

Apply:

.env
-VITE_FEATURE_TX_HISTORY_BYE_BYE=false
-VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=false
+VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=false
+VITE_FEATURE_TX_HISTORY_BYE_BYE=false
.env.development
-VITE_FEATURE_TX_HISTORY_BYE_BYE=true
-VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=true
+VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=true
+VITE_FEATURE_TX_HISTORY_BYE_BYE=true
📝 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
VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=false
# .env (around line 190)
VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=false
VITE_FEATURE_TX_HISTORY_BYE_BYE=false
# .env.development
VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE=true
VITE_FEATURE_TX_HISTORY_BYE_BYE=true
🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 191-191: [UnorderedKey] The VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE key should go before the VITE_FEATURE_TX_HISTORY_BYE_BYE key

(UnorderedKey)

🤖 Prompt for AI Agents
In .env around line 191, the VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE key is out of
alphabetical order causing dotenv-linter UnorderedKey warnings; reorder the
environment variables so all keys are sorted alphabetically (i.e., move this
line to its correct position among the other VITE_FEATURE_* entries), preserve
the exact key/value and file formatting, and save the file to resolve the linter
warning.

Comment on lines +1124 to +1125
"feeExplainer": "Quote includes a %{feePercentage}% ShapeShift fee",
"claim": {
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

Conflicting fee messaging (“ShapeShift fee”) vs existing “ShapeShift does not add fees on trades”.

Avoid misleading copy; make neutral until product confirms.

Apply:

-    "feeExplainer": "Quote includes a %{feePercentage}% ShapeShift fee",
+    "feeExplainer": "Quote includes a %{feePercentage}% fee",
📝 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
"feeExplainer": "Quote includes a %{feePercentage}% ShapeShift fee",
"claim": {
"feeExplainer": "Quote includes a %{feePercentage}% fee",
"claim": {
🤖 Prompt for AI Agents
In src/assets/translations/en/main.json around lines 1124 to 1125, the string
"feeExplainer": "Quote includes a %{feePercentage}% ShapeShift fee" conflicts
with existing neutral messaging and may mislead users; update this key to
neutral language pending product confirmation (for example: "Quote includes a
%{feePercentage}% fee" or "Quote may include a %{feePercentage}% fee") to remove
the explicit "ShapeShift" attribution, and ensure the interpolation placeholder
remains unchanged.

Comment on lines +82 to +90
const handleClose = useCallback(
(event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation()
setIsOpen(false)
onClose()
},
[onClose, setIsOpen],
)

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

Avoid double-close side effects; route close semantics through onOpenChange.

Calling onClose here plus Drawer onClose/onOpenChange can fire twice and regress the “extra click” bug.

Apply this diff:

-  const handleClose = useCallback(
-    (event: React.MouseEvent<HTMLDivElement>) => {
-      event.stopPropagation()
-      setIsOpen(false)
-      onClose()
-    },
-    [onClose, setIsOpen],
-  )
+  const handleClose = useCallback(
+    (event: React.MouseEvent<HTMLDivElement>) => {
+      event.stopPropagation()
+      setIsOpen(false)
+    },
+    [setIsOpen],
+  )
+
+  const handleOpenChange = useCallback(
+    (open: boolean) => {
+      setIsOpen(open)
+      if (!open) onClose()
+    },
+    [onClose, setIsOpen],
+  )
📝 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
const handleClose = useCallback(
(event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation()
setIsOpen(false)
onClose()
},
[onClose, setIsOpen],
)
const handleClose = useCallback(
(event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation()
setIsOpen(false)
},
[setIsOpen],
)
const handleOpenChange = useCallback(
(open: boolean) => {
setIsOpen(open)
if (!open) onClose()
},
[onClose, setIsOpen],
)
🤖 Prompt for AI Agents
In src/components/Modal/components/Dialog.tsx around lines 82 to 90, the
handleClose currently calls setIsOpen(false) and onClose() which can trigger
duplicate close semantics; instead, route all close state through the
onOpenChange handler to avoid double-close side effects. Replace the body so it
calls event.stopPropagation() and then onOpenChange(false) (remove direct
setIsOpen and onClose calls), and update the hook dependency array to include
onOpenChange instead of setIsOpen/onClose.

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.

Fox Ecosystem page

3 participants