-
Notifications
You must be signed in to change notification settings - Fork 198
feat: merge rfox into rfox ecosystem #10323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughWalkthroughAdds 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
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
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Assessment against linked issues
Out-of-scope changes
Possibly related PRs
Suggested reviewers
Poem
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 unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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_PAGEbeforeVITE_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=truesrc/components/Layout/Header/ActionCenter/ActionCenter.tsx (2)
188-190: Fix malformed CSS calc() on Mobile drawer heightThere'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 EmptyStateWhen some mapped actions resolve to
null(e.g., missing order in LimitOrder),maybeActionCards.lengthremains > 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
newContractBalanceOfCryptoBaseUnitcan 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 flagFor consistency with other flags (e.g.
RFOX,RFOX_LP,FoxPageRFOX), please rename all instances ofRfoxFoxEcosystemPagetoRFOXFoxEcosystemPage. This touches type definitions, config keys, mocks, selectors, hook calls, and route logic:• src/config.ts
–VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGEgetConfig• 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 274Suggested 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 numberamountCryptoPrecision 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 callbackhandleAccountIdChange 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.
📒 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.tssrc/pages/RFOX/components/StakeModal.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/config.tssrc/pages/RFOX/components/Stake/components/StakeSummary.tsxsrc/hooks/useActionCenterSubscribers/useActionCenterSubscribers.tssrc/pages/Fox/FoxEcosystemPage.tsxsrc/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsxsrc/pages/RFOX/hooks/useCooldownPeriodQuery.tssrc/pages/RFOX/components/Unstake/components/UnstakeSummary.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/components/Stake/StakeInput.tsxsrc/pages/RFOX/components/UnstakeModal.tsxsrc/pages/Explore/Explore.tsxsrc/pages/RFOX/components/Unstake/UnstakeInput.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/state/slices/actionSlice/types.tssrc/test/mocks/store.tssrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/Routes/RoutesCommon.tsxsrc/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.tssrc/pages/RFOX/components/StakeModal.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/config.tssrc/pages/RFOX/components/Stake/components/StakeSummary.tsxsrc/hooks/useActionCenterSubscribers/useActionCenterSubscribers.tssrc/pages/Fox/FoxEcosystemPage.tsxsrc/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsxsrc/pages/RFOX/hooks/useCooldownPeriodQuery.tssrc/pages/RFOX/components/Unstake/components/UnstakeSummary.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/components/Stake/StakeInput.tsxsrc/pages/RFOX/components/UnstakeModal.tsxsrc/pages/Explore/Explore.tsxsrc/pages/RFOX/components/Unstake/UnstakeInput.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/state/slices/actionSlice/types.tssrc/test/mocks/store.tssrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/Routes/RoutesCommon.tsxsrc/state/slices/preferencesSlice/preferencesSlice.tssrc/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.tssrc/pages/RFOX/components/StakeModal.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/config.tssrc/pages/RFOX/components/Stake/components/StakeSummary.tsxsrc/hooks/useActionCenterSubscribers/useActionCenterSubscribers.tssrc/pages/Fox/FoxEcosystemPage.tsxsrc/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsxsrc/pages/RFOX/hooks/useCooldownPeriodQuery.tssrc/pages/RFOX/components/Unstake/components/UnstakeSummary.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/components/Stake/StakeInput.tsxsrc/pages/RFOX/components/UnstakeModal.tsxsrc/pages/Explore/Explore.tsxsrc/pages/RFOX/components/Unstake/UnstakeInput.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/state/slices/actionSlice/types.tssrc/test/mocks/store.tssrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/Routes/RoutesCommon.tsxsrc/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.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/pages/RFOX/components/Stake/components/StakeSummary.tsxsrc/pages/Fox/FoxEcosystemPage.tsxsrc/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsxsrc/pages/RFOX/components/Unstake/components/UnstakeSummary.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/components/Stake/StakeInput.tsxsrc/pages/RFOX/components/UnstakeModal.tsxsrc/pages/Explore/Explore.tsxsrc/pages/RFOX/components/Unstake/UnstakeInput.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/Routes/RoutesCommon.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)
**/*.{tsx,jsx}: ALWAYS useuseMemofor expensive computations, object/array creations, and filtered data
ALWAYS useuseMemofor derived values and computed properties
ALWAYS useuseMemofor conditional values and simple transformations
ALWAYS useuseCallbackfor event handlers and functions passed as props
ALWAYS useuseCallbackfor any function that could be passed as a prop or dependency
ALWAYS include all dependencies inuseEffect,useMemo,useCallbackdependency arrays
NEVER use// eslint-disable-next-line react-hooks/exhaustive-depsunless 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 withmemo
Files:
src/pages/RFOX/components/StakeModal.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/pages/RFOX/components/Stake/components/StakeSummary.tsxsrc/pages/Fox/FoxEcosystemPage.tsxsrc/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsxsrc/pages/RFOX/components/Unstake/components/UnstakeSummary.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/components/Stake/StakeInput.tsxsrc/pages/RFOX/components/UnstakeModal.tsxsrc/pages/Explore/Explore.tsxsrc/pages/RFOX/components/Unstake/UnstakeInput.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/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.tsxsrc/hooks/useActionCenterSubscribers/useActionCenterSubscribers.tssrc/pages/RFOX/components/Unstake/hooks/useRfoxUnstake.tsxsrc/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 componentsI’ve verified that within
src/pages/RFOX/components/Stake/:
- Neither
StakeInput.tsxnorStakeConfirm.tsxinvokesetStepIndexdirectly—these components merely pass it through to child props.- The
StakeandStakeRouteswrappers only forwardsetStepIndex(never call it internally).- All RFOX consumers of
StakeRouteProps(e.g. inWidget.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
cooldownPeriodDataaligns 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 0To prevent undefined metadata for zero-cooldown contracts and simplify downstream logic (including i18n messages), default
cooldownPeriodSecondsto0. TheGenericTransactionMetadatatype already definescooldownPeriodSeconds?: number, so using?? 0preserves 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.
DialogCloseButtontoggles dialog state via context; confirm it also triggers theonClosehandler passed toDialogso the parent state remains in sync and any cleanup logic runs.Manual check:
- Open the Stake modal.
- Click the close button; assert parent
isOpenstate flips to false and any parentonCloseside 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 keysI ran lightweight grep searches and did not find any references to
trade.claimortrade.rewardDistributionin 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.*andtrade.rewardDistribution.*entries fromsrc/assets/translations/en/main.jsonto reduce maintenance overhead.
• If they are in use, refactor those components to reuse the existingactionCenter.claim.*andactionCenter.rewardDistribution.*keys instead of duplicating copy.src/test/mocks/store.ts (1)
162-163: LGTM: mock feature flag added.
RfoxFoxEcosystemPage: falsemirrors the new config flag and keeps tests deterministic.src/components/Layout/Header/ActionCenter/ActionCenter.tsx (3)
23-23: rFOX Reward Distribution card integration looks correctImport path and naming are consistent with the component location; no API surface changes. Good addition.
102-104: Switch case correctly wires RewardDistributionThe 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 boundaryPer 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 integrationThe
VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGEflag is properly defined, used throughout the codebase, and mocked in tests with a default offalse, 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 flowImport path and naming are consistent.
src/pages/RFOX/components/Unstake/components/UnstakeSummary.tsx (1)
51-53: Alignment with new cooldownPeriod data shape is correctUsing
data: cooldownPeriodDataandisCooldownPeriodSuccessmatches the updated hook API and keeps the UI logic clean.src/state/slices/preferencesSlice/preferencesSlice.ts (1)
215-217: Flag wiring LGTMThe 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 onCloseDialogCloseButton 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 LGTMFee-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 UIGood composition: status-aware badge, precise crypto amount formatting with fromBaseUnit and a clean separation of title/description.
27-33: Add explicit component typeAnnotate 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 enabledThe conditional
disable: !VITE_FEATURE_RFOX || VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGElooks correct and prevents duplicate entry points. LGTM.
265-266: Disable legacy /fox page when the new Ecosystem page is enabledThis 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 scopedThe 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 withundefineduntil the account mapping resolves. Ensure the context setter toleratesundefinedand 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 UXConditional 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 boundaryGuidelines 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
useErrorToastin the nearest parent if needed.src/state/slices/actionSlice/types.ts (5)
27-28: Enum extension looks good
ActionType.RewardDistributionaddition is consistent and scoped.
98-99: Good addition: normalized seconds field for cooldownsAdding
cooldownPeriodSeconds?: numberhelps consumers avoid parsing strings.
156-163: RewardDistributionAction shape is clear and future-proofThe metadata nest is consistent with other action types. LGTM.
171-171: Union wiring is correctIncluding
RewardDistributionActioninActionunion enables exhaustiveness checks downstream.
211-214: Type guard is accurateThe predicate mirrors the metadata nesting pattern used elsewhere.
src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx (1)
68-90: Notification dedupe logic looks solidUsing
toast.isActive(actionId)prevents duplicate notifications;actionSlice.upsertActionpreservescreatedAtwhile refreshingupdatedAt. Good balance between idempotence and freshness.src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx (3)
70-76: LGTM on icon/status/CTA wiringGood 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 anduseErrorToast, 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
withErrorBoundaryor<ErrorBoundary>component).
4-5: fromNow() requires the dayjs relativeTime plugin—ensure it’s initialized before useWe verified that you do extend
relativeTimein src/lib/utils/time.ts and in several ActionCard components, but RewardDistributionActionCard.tsx neither imports the plugin nor callsdayjs.extend(relativeTime). Unless your app’s root (e.g.index.tsxorApp.tsx) importssrc/lib/utils/time.tson startup,dayjs(action.updatedAt).fromNow()will returnundefinedhere.Please confirm one of the following:
- Your bootstrap file (e.g.
src/index.tsxorsrc/App.tsx) hasimport '@/lib/utils/time'(which callsdayjs.extend(relativeTime)).- If not, either
- Add
import 'dayjs/plugin/relativeTime'anddayjs.extend(relativeTime)at the top of RewardDistributionActionCard.tsx, or- Import your global
utils/time.tsin 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)
src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
Outdated
Show resolved
Hide resolved
src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
Outdated
Show resolved
Hide resolved
...nents/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
Show resolved
Hide resolved
premiumjibles
left a comment
There was a problem hiding this 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.
https://jam.dev/c/c6c7d0e2-8c9e-47f4-b9b0-fa57cd3b83ab
https://jam.dev/c/bd5b007e-33ab-4224-91f6-4352868ff4fc
...nents/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
Outdated
Show resolved
Hide resolved
src/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsx
Outdated
Show resolved
Hide resolved
src/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsx
Outdated
Show resolved
Hide resolved
src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx
Outdated
Show resolved
Hide resolved
Removed the button if the flag is on! in 14c3cee |
There was a problem hiding this 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 frominitiate.Dispatching
marketApi.endpoints.findByAssetId.initiate(...)without cleanup can leave subscriptions active if this component re-mounts with differentstakingAssetId. Preferprefetchor store the result andunsubscribein 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 undefinedWhile 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 enabledThe
/rfox/*entry insrc/Routes/RoutesCommon.tsx(around line 249) currently has nodisableflag, so even whenVITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGEis 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 calcThe 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; usetransparent(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 fileImporting 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 helperThis was already raised for this file; if you decide to keep using
@shapeshiftoss/utilshere, feel free to ignore.
37-53: Placeholder fallback for i18n components when asset is unavailablePreviously discussed; leaving as-is per confirmation that
runeAssetis guaranteed to be defined when this renders.
78-90: Resilient tx link source and collapsibility booleanEarlier suggestion to use
action.rewardDistributionMetadata.txHash ?? distribution.txIdand simplifyisCollapsablewas declined—acknowledging and not re-requesting changes.src/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsx (2)
21-21: Use undefined instead of null for Chakra toast durationPreviously flagged; leaving as-is per your preference to keep consistency with existing patterns.
104-105: Skip-upsert on complete when pending was unseen is intentionalAcknowledging 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.
📒 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.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/pages/Fox/components/FoxTokenHeader.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/pages/RFOX/components/StakeModal.tsxsrc/Routes/RoutesCommon.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/state/slices/actionSlice/types.tssrc/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.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/pages/Fox/components/FoxTokenHeader.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/pages/RFOX/components/StakeModal.tsxsrc/Routes/RoutesCommon.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/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.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/pages/Fox/components/FoxTokenHeader.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/pages/RFOX/components/StakeModal.tsxsrc/Routes/RoutesCommon.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/state/slices/actionSlice/types.tssrc/pages/RFOX/components/UnstakeModal.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)
**/*.{tsx,jsx}: ALWAYS useuseMemofor expensive computations, object/array creations, and filtered data
ALWAYS useuseMemofor derived values and computed properties
ALWAYS useuseMemofor conditional values and simple transformations
ALWAYS useuseCallbackfor event handlers and functions passed as props
ALWAYS useuseCallbackfor any function that could be passed as a prop or dependency
ALWAYS include all dependencies inuseEffect,useMemo,useCallbackdependency arrays
NEVER use// eslint-disable-next-line react-hooks/exhaustive-depsunless 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 withmemo
Files:
src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/pages/Fox/components/FoxTokenHeader.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/pages/RFOX/components/StakeModal.tsxsrc/Routes/RoutesCommon.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/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.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/pages/Fox/components/FoxTokenHeader.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/pages/RFOX/components/StakeModal.tsxsrc/Routes/RoutesCommon.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/state/slices/actionSlice/types.tssrc/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.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/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.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/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.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/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.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/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.tsxsrc/components/Layout/Header/ActionCenter/components/GenericTransactionActionCard.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/components/Layout/Header/ActionCenter/ActionCenter.tsxsrc/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.tsxsrc/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.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/components/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/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.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/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.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/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.tsxsrc/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.tsxsrc/components/Layout/Header/ActionCenter/components/RewardDistributionActionCard.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/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.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/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.tsxsrc/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.tsxsrc/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.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/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.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/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.tsxsrc/pages/RFOX/hooks/useRfoxRewardDistributionActionSubscriber.tsxsrc/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.tsxsrc/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
useFeatureFlagand 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
/rfoxroute and addresses the previously noted blocking issue. No change requested.
55-55: Flag key, usage, and route gating verified — approving changesAll checks passed:
- RfoxFoxEcosystemPage is declared in
preferencesSlice(type and initial state via getConfig) and in test mocks.useFeatureFlag('RfoxFoxEcosystemPage')is used consistently inExplore.tsx,FoxTokenHeader.tsx, andRFOXSection.tsx.- In
src/Routes/RoutesCommon.tsx, the/fox-ecosystemroute is correctly disabled whenVITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGEis false, and the/foxroute 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.
DialogCloseButtontoggles internal dialog context; ensure it also triggers theonClosepassed toDialogso the parent’sisOpenstate updates correctly (especially on mobile path whereDialogCloseButtonhandles its own click). If not guaranteed by the provider, passonClosetoDialogCloseButtonand 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
DialogCloseButtonresults inonClosebeing invoked soisOpenin 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 intendedVITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGEconfig. 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
stakingAssetAccountIdin 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 APIReturning 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 correctDisabling /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 flowsExtracting 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 correctRendering 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 explicitGiven 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 correctThe predicate is straightforward and consistent with other guards.
src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx (2)
64-78: Icon selection logic LGTMCorrectly 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 homepageIf
transactionMetadata.txHashis missing/empty,getTxLinkwill 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 globallyA top-level call to
dayjs.extend(relativeTime)exists insrc/components/Layout/Header/ActionCenter/components/ActionCard.tsx(line 11), and becauseRewardDistributionActionCardimportsActionCard, the plugin is registered before anyfromNow()calls. No changes are needed.
...nents/Layout/Header/ActionCenter/components/Notifications/RewardDistributionNotification.tsx
Show resolved
Hide resolved
src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
Show resolved
Hide resolved
src/components/Layout/Header/ActionCenter/components/RfoxInitiatedActionCard.tsx
Outdated
Show resolved
Hide resolved
|
@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. Which also made me realise it doesn't seem like this new page includes a claim section? Where should this go now?
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?
Here's manage accounts for comparison 👇
Code rabbit is also making babies with your PR 🐇 🐇 🐇 |
That's some nice catch, brang back the notion of 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 |
There was a problem hiding this 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).
removeEventListeneruses 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-isExplicit boolean state with small callbacks is perfectly readable here. A prior suggestion to use
useDisclosurewas 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), notnone.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 useLocationType the router state to remove the
ascast and get compiler-checked access toselectedUnstakingRequest.- 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/throwsIf
distributionRateByStakingContract[getStakingContract(stakingAssetId)]is ever undefined (e.g., missing key or unexpected stakingAssetId),bn(...).times(distributionRate)can yield an invalid value. DefaultingdistributionRateto 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) + ] ?? 0src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsx (2)
35-48: DRY up the conditional navigation to avoid duplication and future driftBoth 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/indexCurrent 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.Elementexcludes 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?.heightmay be undefined initially, yieldingheight: undefinedwhenisFullScreenis true. Fall back towindow.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
isDisablingPropagationis true, both the ChakraBoxandCustomDrawerOverlayclose on click. This can double-invokeonClose. 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.
mutationFnreturnsundefinedon missing wallet/accountNumber and relies on implicit React Query error handling for thrown errors. Consider adding anonErrorto 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
walletis null orstakingAssetAccountNumberisundefined, the mutation returnsundefinedimmediately 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
isDisablingPropagationflag toDialogas you did inStakeModal.- <Dialog isOpen={isOpen} onClose={handleClose} height='auto'> + <Dialog isOpen={isOpen} onClose={handleClose} height='auto' isDisablingPropagation>
4-5: ImportuseNavigatefromreact-router-domfor 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.
selectedUnstakingRequestisn’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).
handleCloseunconditionally 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 duplicationBuild 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 guardLine 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 itemsUsing 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 timedayjs(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.
📒 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.tssrc/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsxsrc/pages/RFOX/components/ClaimModal.tsxsrc/pages/RFOX/components/Claim/ClaimConfirm.tsxsrc/pages/RFOX/components/Claim/ClaimSelect.tsxsrc/components/Modal/components/Dialog.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/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.tssrc/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsxsrc/pages/RFOX/components/ClaimModal.tsxsrc/pages/RFOX/components/Claim/ClaimConfirm.tsxsrc/pages/RFOX/components/Claim/ClaimSelect.tsxsrc/components/Modal/components/Dialog.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/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.tssrc/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsxsrc/pages/RFOX/components/ClaimModal.tsxsrc/pages/RFOX/components/Claim/ClaimConfirm.tsxsrc/pages/RFOX/components/Claim/ClaimSelect.tsxsrc/components/Modal/components/Dialog.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/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.tsxsrc/pages/RFOX/components/ClaimModal.tsxsrc/pages/RFOX/components/Claim/ClaimConfirm.tsxsrc/pages/RFOX/components/Claim/ClaimSelect.tsxsrc/components/Modal/components/Dialog.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/pages/RFOX/components/StakeModal.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)
**/*.{tsx,jsx}: ALWAYS useuseMemofor expensive computations, object/array creations, and filtered data
ALWAYS useuseMemofor derived values and computed properties
ALWAYS useuseMemofor conditional values and simple transformations
ALWAYS useuseCallbackfor event handlers and functions passed as props
ALWAYS useuseCallbackfor any function that could be passed as a prop or dependency
ALWAYS include all dependencies inuseEffect,useMemo,useCallbackdependency arrays
NEVER use// eslint-disable-next-line react-hooks/exhaustive-depsunless 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 withmemo
Files:
src/components/Layout/Header/ActionCenter/components/RfoxClaimActionCard.tsxsrc/pages/RFOX/components/ClaimModal.tsxsrc/pages/RFOX/components/Claim/ClaimConfirm.tsxsrc/pages/RFOX/components/Claim/ClaimSelect.tsxsrc/components/Modal/components/Dialog.tsxsrc/pages/Fox/components/RFOXSection.tsxsrc/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.tssrc/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.tsxsrc/pages/RFOX/components/Claim/ClaimSelect.tsxsrc/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.tsxsrc/pages/RFOX/components/Claim/ClaimConfirm.tsxsrc/pages/RFOX/components/Claim/ClaimSelect.tsxsrc/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 goodUsing 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
UnstakingRequesttype import is correct.Also applies to: 48-48
118-123: Good: deep-link account synchronizationSyncing
stakingAssetAccountIdfrom a deep-linkedselectedUnstakingRequestensures the context is correct when arriving from Action Center.
124-128: Good: claim modal auto-open is guarded to avoid loopsInitializing
isClaimModalOpenand the follow-up effect usingusePreviousensures 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 intentRendering 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 navigationClosing
ClaimModalnavigates to/fox-ecosystem(in the modal component) with a delayed push; that can add extra history entries and affect back-button UX. Validate whether usingreplace: trueis 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 neededThe
useFeatureFlag('RfoxFoxEcosystemPage')usage in RFOXSection.tsx exactly matches:
- The preferences slice key
RfoxFoxEcosystemPageinpreferencesSlice.ts(mapped fromgetConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE)- The environment variable
VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGEinconfig.ts- The route gating in
RoutesCommon.tsx(disable: !getConfig().VITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGE)- The test mock
RfoxFoxEcosystemPageinsrc/test/mocks/store.tsAll 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 — LGTMUsing useFeatureFlag with a string literal key is consistent with the codebase conventions and the new flag.
26-26: Good boolean naming and correct flag wiringisRFOXFoxEcosystemPageEnabled 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 entryuses a wildcard ({ path: '/fox-ecosystem/*', main: FoxEcosystemPage, … }*) to catch all subpaths, including/:index/confirm.- In
src/pages/Fox/FoxEcosystemPage.tsx, the page renders<RFOXSection />.- The
<RFOXSection />component (insrc/pages/Fox/components/RFOXSection.tsx) reads
location.state?.selectedUnstakingRequestand opens the Claim modal based on that state.No changes needed—the gated route covers
/fox-ecosystem/:index/confirm, and the target component consumesstate.selectedUnstakingRequestas 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 theDrawer.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
isDisablingPropagationtrue/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
isDisablingPropagationhere addresses the double-modal/pointer-events issue discussed in the PR. Header/body composition looks consistent, and background is correctly set totransparent.src/pages/RFOX/components/Claim/ClaimConfirm.tsx (2)
100-107: Feature-flagged back navigation is correct.The fallback to
RfoxRoute.Claimkeeps legacy behavior when the ecosystem page is disabled.
68-72:headerComponentprop 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 callThis 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 integrationImporting 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-widefromNow() 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/confirmis available and correctly gated
- The route definition for
/fox-ecosystem/*exists insrc/Routes/RoutesCommon.tsx(lines 273–276) and will match/fox-ecosystem/:claimId/confirm.- In
src/pages/RFOX/components/Claim/ClaimSelect.tsx, whenisRFOXFoxEcosystemPageEnabledis true the code navigates to/fox-ecosystem/${claimId}/confirmand passes theselectedUnstakingRequestin navigation state.src/pages/RFOX/components/Claim/ClaimConfirm.tsxcorrectly declares and consumes theselectedUnstakingRequestprop via React Router state.- The feature flag hook
useFeatureFlag('RfoxFoxEcosystemPage')is used in both components, and the env varVITE_FEATURE_RFOX_FOX_ECOSYSTEM_PAGEis wired throughconfig.ts, the preferences slice, andRoutesCommon.tsx.No changes required here.
|
@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? |
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 |
|
@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 |
There was a problem hiding this 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 neededHard-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 undefinedChakra 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.
📒 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.tsxsrc/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.tsxsrc/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.tsxsrc/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)
**/*.{tsx,jsx}: ALWAYS useuseMemofor expensive computations, object/array creations, and filtered data
ALWAYS useuseMemofor derived values and computed properties
ALWAYS useuseMemofor conditional values and simple transformations
ALWAYS useuseCallbackfor event handlers and functions passed as props
ALWAYS useuseCallbackfor any function that could be passed as a prop or dependency
ALWAYS include all dependencies inuseEffect,useMemo,useCallbackdependency arrays
NEVER use// eslint-disable-next-line react-hooks/exhaustive-depsunless 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 withmemo
Files:
src/components/Modal/components/DialogCloseButton.tsxsrc/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.tsxsrc/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 requireasChild
I scanned all.tsxfiles forDrawer.ClosewrappingIconButton/Buttonelements without theasChildprop and found no other occurrences. The change inDialogCloseButton.tsxis 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 usedAdditions of useMediaQuery, Drawer, useDialog, and breakpoints are purposeful and correctly referenced.
Also applies to: 5-5, 7-7, 10-10
There was a problem hiding this 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 issuesGuard 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.
📒 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 useuseMemofor expensive computations, object/array creations, and filtered data
ALWAYS useuseMemofor derived values and computed properties
ALWAYS useuseMemofor conditional values and simple transformations
ALWAYS useuseCallbackfor event handlers and functions passed as props
ALWAYS useuseCallbackfor any function that could be passed as a prop or dependency
ALWAYS include all dependencies inuseEffect,useMemo,useCallbackdependency arrays
NEVER use// eslint-disable-next-line react-hooks/exhaustive-depsunless 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 withmemo
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 requestThis ensures context is aligned with the deep link. LGTM.
124-128: Correct: open Claim modal once when a deep link appearsThe previous-value check prevents re-opens. LGTM.
173-178: Good fix: avoid overriding deep-linked accountIdThe early return prevents context thrash and preserves claim flow. LGTM.
282-311: Feature-flag gating of actions is correctCleanly 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 closeGiven 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.
|
@NeOMakinG if we add
|
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 😆 |
There was a problem hiding this 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 asChildWithout 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 useSafeDialogThis 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 contextThis 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 buttonForcing
isDisabled={false}ignoresrestProps.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.
📒 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.tsxsrc/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.tsxsrc/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.tsxsrc/context/DialogContextProvider/DialogContextProvider.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)
**/*.{tsx,jsx}: ALWAYS useuseMemofor expensive computations, object/array creations, and filtered data
ALWAYS useuseMemofor derived values and computed properties
ALWAYS useuseMemofor conditional values and simple transformations
ALWAYS useuseCallbackfor event handlers and functions passed as props
ALWAYS useuseCallbackfor any function that could be passed as a prop or dependency
ALWAYS include all dependencies inuseEffect,useMemo,useCallbackdependency arrays
NEVER use// eslint-disable-next-line react-hooks/exhaustive-depsunless 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 withmemo
Files:
src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsxsrc/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.tsxsrc/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 accessUsing useSafeDialog with useMediaQuery(ssr: false) avoids SSR hydration mismatches and crashes outside providers.
premiumjibles
left a comment
There was a problem hiding this 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 🤌
There was a problem hiding this 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 pointReplace 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.
📒 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.tsxsrc/assets/translations/en/main.json
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)
**/*.{tsx,jsx}: ALWAYS useuseMemofor expensive computations, object/array creations, and filtered data
ALWAYS useuseMemofor derived values and computed properties
ALWAYS useuseMemofor conditional values and simple transformations
ALWAYS useuseCallbackfor event handlers and functions passed as props
ALWAYS useuseCallbackfor any function that could be passed as a prop or dependency
ALWAYS include all dependencies inuseEffect,useMemo,useCallbackdependency arrays
NEVER use// eslint-disable-next-line react-hooks/exhaustive-depsunless 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 withmemo
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 |
There was a problem hiding this comment.
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=falseRun 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.
| 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.
| "feeExplainer": "Quote includes a %{feePercentage}% ShapeShift fee", | ||
| "claim": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| "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.
| const handleClose = useCallback( | ||
| (event: React.MouseEvent<HTMLDivElement>) => { | ||
| event.stopPropagation() | ||
| setIsOpen(false) | ||
| onClose() | ||
| }, | ||
| [onClose, setIsOpen], | ||
| ) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.











Description
Issue (if applicable)
closes #10264
Risk
Low
Testing
Engineering
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
Screenshots (if applicable)
Summary by CodeRabbit
New Features
Documentation / Content
Chores