Skip to content

Comments

fix/PRO-3690/change-token-atlas-buy-to-pulse#407

Merged
RanaBug merged 2 commits intostagingfrom
fix/PRO-3690/change-token-atlas-buy-to-pulse
Sep 24, 2025
Merged

fix/PRO-3690/change-token-atlas-buy-to-pulse#407
RanaBug merged 2 commits intostagingfrom
fix/PRO-3690/change-token-atlas-buy-to-pulse

Conversation

@RanaBug
Copy link
Collaborator

@RanaBug RanaBug commented Sep 24, 2025

Description

  • Clicking on buy in token atlas direct to Pulse app

How Has This Been Tested?

  • Existing Unit tests and Manual Testing

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Summary by CodeRabbit

  • New Features

    • Deep linking from Token Atlas to Pulse now preselects token and chain via URL parameters for a one-click Buy flow.
  • Improvements

    • Smarter search behavior: validates asset parameter, auto-opens only when appropriate, and shows a loading indicator during background search.
    • Seamless state handoff in the Buy flow to keep token and chain selection consistent across screens.
    • Faster entry when arriving from Token Atlas by skipping the intro animation.

@RanaBug RanaBug requested a review from IAmKio September 24, 2025 12:21
@RanaBug RanaBug self-assigned this Sep 24, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 24, 2025

Walkthrough

Implements token-atlas → pulse deep-link flow: updates Pulse AppWrapper, HomeScreen, and Buy to parse URL params, run background token search, auto-select token/chain, and adjust search modal behavior. TokenGraphColumn now builds pulse URLs and receives selectedToken props. Tests updated (Pulse, Exchange), Authorized animation bypass added, and test store wired to API slice.

Changes

Cohort / File(s) Summary
Pulse App wrapper & routing
src/apps/pulse/components/App/AppWrapper.tsx
Adds query handling for from, asset; suppresses search modal when from=token-atlas; validates asset (address/symbol); forwards new props setBuyToken, setChains to HomeScreen and Search.
Pulse HomeScreen + tests
src/apps/pulse/components/App/HomeScreen.tsx, src/apps/pulse/components/App/tests/HomeScreen.test.tsx
HomeScreen props extended with setBuyToken, setChains; forwarded to Buy. Tests refactored to use TestWrapper, updated props/mocks and provider stack.
Pulse Buy flow + tests
src/apps/pulse/components/Buy/Buy.tsx, src/apps/pulse/components/Buy/tests/Buy.test.tsx
Buy reads URL (asset, blockchain, from), triggers background search via useGetSearchTokensQuery, auto-selects token, sets chain via setChains, exposes setBuyToken. Adds loading state. Tests mock API hook, use TestWrapper, and extend props/state.
Token Atlas navigation + usage
src/apps/token-atlas/components/TokenGraphColumn/TokenGraphColumn.tsx, src/apps/token-atlas/index.tsx
TokenGraphColumn now takes selectedToken, isWrappedOrNativeToken, getSymbol; constructs pulse URL /pulse?asset=...&blockchain=...&from=token-atlas; removes internal state selection. Index updated to pass new props.
Authorized animation tweak
src/containers/Authorized.tsx
Skips entrance animation when from=token-atlas is present in URL.
Shared test infra
src/test-utils/testUtils.tsx
Registers pillarXApiSearchTokens reducer/middleware in test store.
Exchange test mock
src/apps/the-exchange/components/CardsSwap/test/CardSwap.test.tsx
Changes mock of pillarXApiSearchTokens to async factory augmenting original module; preserves original exports while overriding useGetSearchTokensQuery.

Sequence Diagram(s)

sequenceDiagram
  participant TA as Token Atlas
  participant User
  participant Router
  participant Pulse.AppWrapper
  participant Pulse.Buy
  participant API as pillarXApiSearchTokens

  User->>TA: Click "Buy"
  TA->>Router: Navigate to /pulse?asset=...&blockchain=...&from=token-atlas
  Router->>Pulse.AppWrapper: Mount with query
  Note over Pulse.AppWrapper: If from=token-atlas then don't open search modal
  Pulse.AppWrapper->>Pulse.Buy: Render with setBuyToken, setChains

  Pulse.Buy->>API: useGetSearchTokensQuery(filters from URL)
  API-->>Pulse.Buy: { data, isLoading }
  alt Results ready
    Pulse.Buy->>Pulse.Buy: Build SelectedToken
    Pulse.Buy->>Pulse.AppWrapper: setBuyToken(token)
    Pulse.Buy->>Pulse.AppWrapper: setChains(chain)
    Pulse.Buy->>Pulse.Buy: Stop searching UI
  else Loading/No results
    Pulse.Buy->>Pulse.Buy: Show loading or fallback UI
  end
Loading
sequenceDiagram
  participant Router
  participant Authorized

  Router->>Authorized: Mount with location.search
  alt from=token-atlas
    Authorized->>Authorized: setShowAnimation(false)
  else other sources
    Authorized->>Authorized: Show animation then hide after timer
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • IAmKio
  • vignesha22

Poem

A hop from Atlas to Pulse we go,
Query crumbs guide where tokens flow.
I twitch my nose—auto-pick, how nice!
Chains align, no loading ice.
Tests burrow deep, mocks in tow—
Carrot commits, green lights glow. 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly identifies the primary change (redirecting Token Atlas "buy" to the Pulse app) and matches the changeset contents; it uses branch-style formatting (fix/PRO-3690/...) rather than a natural sentence but remains directly related and informative.
Description Check ✅ Passed The PR description follows the repository template and includes a concise Description, a How Has This Been Tested section, and Types of changes, correctly stating the intent (redirect Buy to Pulse) and that unit tests and manual testing were used.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/PRO-3690/change-token-atlas-buy-to-pulse

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f12ec08 and 9419129.

📒 Files selected for processing (1)
  • src/apps/pulse/components/Buy/tests/Buy.test.tsx (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/apps/pulse/components/Buy/tests/Buy.test.tsx
⏰ 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). (3)
  • GitHub Check: lint
  • GitHub Check: unit-tests
  • GitHub Check: build

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Sep 24, 2025

Deploying x with  Cloudflare Pages  Cloudflare Pages

Latest commit: 9419129
Status: ✅  Deploy successful!
Preview URL: https://395e5950.x-e62.pages.dev
Branch Preview URL: https://fix-pro-3690-change-token-at.x-e62.pages.dev

View logs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/apps/pulse/components/Buy/Buy.tsx (3)

338-400: Consider improving error handling and token validation.

The token auto-selection logic is comprehensive, but could benefit from better error handling and validation. The token mapping logic correctly handles both address and symbol searches.

Consider adding error handling for the search process:

 // Auto-select token when search results are ready
 useEffect(() => {
+  if (fromTokenAtlas && tokenToSearch && !token && !isSearchLoading && !searchData?.result?.data) {
+    // Handle case where search returns no results
+    console.warn('No search results found for token:', tokenToSearch);
+    setIsSearchingToken(false);
+    return;
+  }
+
   if (
     fromTokenAtlas &&
     tokenToSearch &&
     !token &&
     searchData?.result?.data &&
     !isSearchLoading
   ) {
     const foundToken = searchData.result.data.find(
       (searchToken: TokenAssetResponse | PairResponse | undefined) => {
+        if (!searchToken) return false;
+        
         if (isAddress(tokenToSearch)) {
           return (
             (
               searchToken as TokenAssetResponse
             )?.contracts?.[0]?.toLowerCase() === tokenToSearch.toLowerCase()
           );
         }
         return (
           (searchToken as TokenAssetResponse)?.symbol?.toLowerCase() ===
           tokenToSearch.toLowerCase()
         );
       }
     );

     if (foundToken && 'name' in foundToken && setBuyToken) {
       const chainIdFromUrl = blockchain
         ? chainNameToChainIdTokensData(blockchain)
         : 1;

+      // Validate essential token data before creating token object
+      if (!foundToken.name || !foundToken.symbol || !foundToken.contracts?.[0]) {
+        console.warn('Incomplete token data from search:', foundToken);
+        setIsSearchingToken(false);
+        return;
+      }

       const tokenToSelect = {
         name: foundToken.name,
         symbol: foundToken.symbol,
         logo: foundToken.logo ?? '',
         address: foundToken.contracts?.[0] || '',
         chainId: chainIdFromUrl,
         decimals: Array.isArray(foundToken.decimals)
           ? foundToken.decimals[0] || 18
           : foundToken.decimals || 18,
         usdValue: foundToken.price?.toString() || '0',
         dailyPriceChange: 0,
       };

       setBuyToken(tokenToSelect as SelectedToken);

       // Set the correct chain based on the blockchain parameter
       if (blockchain) {
         setChains(blockchain as MobulaChainNames);
       }

       setIsSearchingToken(false);
+    } else {
+      // Handle case where token is not found in search results
+      console.warn('Token not found in search results:', tokenToSearch);
+      setIsSearchingToken(false);
     }
   }
 }, [

363-366: Unify chain-id fallback — don't silently default to Ethereum (1)

chainNameToChainIdTokensData (src/services/tokensData.ts) returns '' for unknown names; in src/apps/pulse/components/Buy/Buy.tsx (lines 363–366) you do const chainIdFromUrl = blockchain ? chainNameToChainIdTokensData(blockchain) : 1; — this silently treats missing/invalid blockchain as Ethereum and conflicts with other modules that treat invalid mapping as 0/throw. Validate the mapping and handle invalid chains explicitly (e.g. const id = chainNameToChainIdTokensData(blockchain) ?? 0 and guard/throw), or make an explicit, documented default (e.g. use getChainId(MobulaChainNames.Ethereum)) if Ethereum is intended.


75-94: Verify search API hook implementation and add explicit error handling.

I checked src/services/pillarXApiSearchTokens.ts — getSearchTokens uses fetchBaseQuery wrapped with retry(maxRetries:5) and returns MobulaApiResponse but has no transformResponse or custom error handling. In src/apps/pulse/components/Buy/Buy.tsx the hook only uses data/isLoading and guards with searchData?.result?.data && !isSearchLoading, but does not handle isError/error or isFetching. Recommend: surface hook errors (check isError/error), handle isFetching consistently, and either normalize the API response in the service (transformResponse / default shape) or add explicit runtime guards and user-facing error UI in Buy.tsx.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 282110b and f12ec08.

📒 Files selected for processing (10)
  • src/apps/pulse/components/App/AppWrapper.tsx (2 hunks)
  • src/apps/pulse/components/App/HomeScreen.tsx (4 hunks)
  • src/apps/pulse/components/App/tests/HomeScreen.test.tsx (5 hunks)
  • src/apps/pulse/components/Buy/Buy.tsx (6 hunks)
  • src/apps/pulse/components/Buy/tests/Buy.test.tsx (5 hunks)
  • src/apps/the-exchange/components/CardsSwap/test/CardSwap.test.tsx (1 hunks)
  • src/apps/token-atlas/components/TokenGraphColumn/TokenGraphColumn.tsx (2 hunks)
  • src/apps/token-atlas/index.tsx (1 hunks)
  • src/containers/Authorized.tsx (1 hunks)
  • src/test-utils/testUtils.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-03-28T09:22:22.712Z
Learnt from: RanaBug
PR: pillarwallet/x#275
File: src/apps/the-exchange/components/DropdownTokensList/DropdownTokenList.tsx:180-195
Timestamp: 2025-03-28T09:22:22.712Z
Learning: In the Exchange app, `swapTokenList` and `receiveTokenList` are derived from `searchTokenResult` when search is active, so including `searchToken` in the useEffect dependency array that uses these lists would be redundant as the lists will update when search results change.

Applied to files:

  • src/apps/pulse/components/App/HomeScreen.tsx
  • src/apps/pulse/components/App/AppWrapper.tsx
  • src/apps/pulse/components/Buy/Buy.tsx
  • src/test-utils/testUtils.tsx
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
PR: pillarwallet/x#374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/apps/pulse/components/Buy/tests/Buy.test.tsx
  • src/apps/pulse/components/App/tests/HomeScreen.test.tsx
  • src/apps/pulse/components/Buy/Buy.tsx
🧬 Code graph analysis (5)
src/apps/pulse/components/App/HomeScreen.tsx (1)
src/apps/pulse/types/tokens.ts (1)
  • SelectedToken (1-10)
src/apps/token-atlas/components/TokenGraphColumn/TokenGraphColumn.tsx (2)
src/apps/token-atlas/types/types.tsx (1)
  • SelectedTokenType (14-23)
src/services/tokensData.ts (1)
  • chainIdToChainNameTokensData (210-231)
src/apps/pulse/components/Buy/tests/Buy.test.tsx (2)
src/test-utils/testUtils.tsx (1)
  • TestWrapper (117-135)
src/apps/pulse/components/Buy/Buy.tsx (1)
  • Buy (56-679)
src/apps/pulse/components/App/tests/HomeScreen.test.tsx (2)
src/test-utils/testUtils.tsx (1)
  • TestWrapper (117-135)
src/apps/pulse/components/App/HomeScreen.tsx (1)
  • HomeScreen (45-412)
src/apps/pulse/components/Buy/Buy.tsx (4)
src/apps/pulse/types/tokens.ts (1)
  • SelectedToken (1-10)
src/apps/pulse/utils/constants.ts (1)
  • getChainId (28-49)
src/types/api.ts (2)
  • TokenAssetResponse (549-568)
  • PairResponse (570-656)
src/services/tokensData.ts (1)
  • chainNameToChainIdTokensData (234-255)
⏰ 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). (3)
  • GitHub Check: lint
  • GitHub Check: unit-tests
  • GitHub Check: build
🔇 Additional comments (19)
src/containers/Authorized.tsx (1)

50-66: LGTM! Proper token-atlas flow optimization.

The early exit when coming from token-atlas appropriately skips the loading animation to provide a smoother user experience. The implementation correctly preserves the existing timer logic for all other flows.

src/test-utils/testUtils.tsx (1)

20-22: LGTM! Enhanced test store for API testing.

The integration of pillarXApiSearchTokens API slice enables proper testing of the token search functionality introduced in the PR. The reducer and middleware are correctly configured in the test store.

Also applies to: 42-45

src/apps/the-exchange/components/CardsSwap/test/CardSwap.test.tsx (1)

92-106: LGTM! Improved mock pattern for better test isolation.

The async mock factory with importOriginal preserves all original module exports while only overriding the specific function needed for testing. This approach is more maintainable and reduces the risk of breaking changes in the mock.

src/apps/token-atlas/components/TokenGraphColumn/TokenGraphColumn.tsx (2)

41-46: LGTM! Prop-driven architecture improves component reusability.

The component now receives token data and utility functions through props instead of internal state access, making it more testable and flexible for different contexts.

Also applies to: 48-54


215-231: LGTM! Proper asset parameter derivation for token-atlas integration.

The logic correctly handles different token types:

  • Wrapped/native tokens use the symbol via getSymbol()
  • Non-zero address tokens use the contract address
  • Fallback to symbol for other cases

The navigation to /pulse with proper query parameters enables seamless token-atlas to Pulse app integration.

src/apps/token-atlas/index.tsx (1)

299-302: LGTM! Proper prop wiring for TokenGraphColumn.

The required props selectedToken, isWrappedOrNativeToken, and getSymbol are correctly passed from the parent component's context, enabling the new navigation functionality in TokenGraphColumn.

src/apps/pulse/components/App/tests/HomeScreen.test.tsx (2)

20-22: LGTM! Proper test wrapper and extended props coverage.

The migration to TestWrapper aligns with the updated test infrastructure, and the new mock props (setBuyToken, chains, setChains) cover the enhanced HomeScreen interface for token-atlas integration.

Also applies to: 39-42


44-54: LGTM! Consistent test wrapper usage.

All test rendering consistently uses TestWrapper which provides the necessary providers and store configuration for the enhanced component functionality.

Also applies to: 79-92, 129-137, 247-255

src/apps/pulse/components/App/HomeScreen.tsx (2)

18-18: LGTM! Proper type imports and prop additions.

The MobulaChainNames import and new props (setBuyToken, setChains) enable upstream state management for token selection from child components.

Also applies to: 41-43, 53-55


384-386: LGTM! Proper prop forwarding to Buy component.

The setBuyToken and setChains props are correctly passed to the Buy component, enabling the token-atlas flow to update parent state when tokens are auto-selected.

src/apps/pulse/components/Buy/tests/Buy.test.tsx (3)

15-16: LGTM! Proper imports for enhanced Buy component.

The MobulaChainNames and TestWrapper imports support the new token selection and chain management functionality in the Buy component.

Also applies to: 20-22


32-39: **LGTM! Consistent mock approach.**The mock approach using vi.mock with a function that returns the mocked implementation is consistent with Vitest best practices. The mock correctly returns the expected shape for useGetSearchTokensQuery.


148-151: LGTM! Comprehensive test prop coverage and wrapper consistency.

The new mock props (setBuyToken, chains, setChains) align with the Buy component's enhanced interface for token-atlas integration. The consistent use of TestWrapper ensures proper test environment setup with all required providers.

Also applies to: 175-181, 189-197

src/apps/pulse/components/App/AppWrapper.tsx (2)

42-60: LGTM! Well-structured token-atlas conditional routing logic.

The conditional logic correctly handles the token-atlas flow by preventing the search modal from opening when users come from token-atlas, allowing the Buy component to handle the search and token selection instead. The validation logic for addresses and symbols is appropriate with reasonable length constraints.


84-85: LGTM! Proper prop threading for token-atlas integration.

The new props setBuyToken and setChains are correctly passed to HomeScreen to enable the token-atlas flow where Buy component can update the selected token and chain context.

src/apps/pulse/components/Buy/Buy.tsx (4)

13-33: LGTM! Comprehensive imports for token-atlas integration.

All necessary imports are properly added to support the new functionality including React Router hooks, Viem utilities, search API services, and type definitions.


52-54: LGTM! Proper interface expansion for token-atlas props.

The BuyProps interface correctly adds the required props setBuyToken and setChains to support the token-atlas flow where the Buy component needs to update parent state.


332-336: LGTM! Clean trigger logic for token-atlas search.

The effect properly initializes the search state when coming from token-atlas with valid parameters and no existing token selection.


519-536: LGTM! Clean loading state implementation.

The conditional rendering properly shows a loading spinner with "Searching..." text when the token-atlas search is in progress, providing good user feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants