Repository: OneKeyHQ/app-monorepo
Pull request #7333: feat: Market detail page 3
Author: login: limichange
Created: 2025-05-15T01:05:55Z | Merged: 2025-05-15T06:44:21Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Added support for custom header left and right items in tab page headers, enabling flexible header customization.
  - Introduced new components for market detail views, including MarketDetailHeader, TokenDetailHeader, TokenDetailHeaderSkeleton, and TokenPriceChart, providing enhanced and modular UI elements.
  - Added a custom hook for managing market token detail data with refresh capability.

- **Refactor**
  - Simplified the Market Detail view by delegating data fetching and UI composition to new modular components and hooks, improving maintainability and clarity.

- **Chores**
  - Consolidated and re-exported components for easier and more organized imports.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/packages/kit/src/components/TabPageHeader/HeaderLeft.tsx b/packages/kit/src/components/TabPageHeader/HeaderLeft.tsx
index 1c11dcfcbb56..1d08890841e1 100644
--- a/packages/kit/src/components/TabPageHeader/HeaderLeft.tsx
+++ b/packages/kit/src/components/TabPageHeader/HeaderLeft.tsx
@@ -1,4 +1,4 @@
-import { useMemo, useState } from 'react';
+import { type ReactNode, useMemo, useState } from 'react';
 
 import { useIntl } from 'react-intl';
 
@@ -23,12 +23,22 @@ import {
 } from '../AccountSelector';
 import { useSpotlight } from '../Spotlight';
 
+export function HeaderLeftCloseButton() {
+  return (
+    <Page.Close>
+      <NavBackButton />
+    </Page.Close>
+  );
+}
+
 export function HeaderLeft({
   sceneName,
   tabRoute,
+  customHeaderLeftItems,
 }: {
   sceneName: EAccountSelectorSceneName;
   tabRoute: ETabRoutes;
+  customHeaderLeftItems?: ReactNode;
 }) {
   const intl = useIntl();
   const { tourTimes, tourVisited } = useSpotlight(
@@ -49,12 +59,11 @@ export function HeaderLeft({
     [isFocus, tourTimes],
   );
   const items = useMemo(() => {
+    if (customHeaderLeftItems) {
+      return customHeaderLeftItems;
+    }
     if (sceneName === EAccountSelectorSceneName.homeUrlAccount) {
-      return (
-        <Page.Close>
-          <NavBackButton />
-        </Page.Close>
-      );
+      return <HeaderLeftCloseButton />;
     }
 
     const accountSelectorTrigger = (
@@ -106,7 +115,15 @@ export function HeaderLeft({
         />
       </XStack>
     );
-  }, [gtMd, intl, sceneName, spotlightVisible, tabRoute, tourVisited]);
+  }, [
+    gtMd,
+    intl,
+    sceneName,
+    spotlightVisible,
+    tabRoute,
+    tourVisited,
+    customHeaderLeftItems,
+  ]);
   return (
     <AccountSelectorProviderMirror
       enabledNum={[0]}
diff --git a/packages/kit/src/components/TabPageHeader/HeaderRight.tsx b/packages/kit/src/components/TabPageHeader/HeaderRight.tsx
index f765fb051eae..9c50eb3e7ae9 100644
--- a/packages/kit/src/components/TabPageHeader/HeaderRight.tsx
+++ b/packages/kit/src/components/TabPageHeader/HeaderRight.tsx
@@ -1,4 +1,4 @@
-import { useCallback, useMemo } from 'react';
+import { type ReactNode, useCallback, useMemo } from 'react';
 
 import { useIntl } from 'react-intl';
 
@@ -107,11 +107,11 @@ function NotificationsButton() {
   );
 }
 
-function MoreAction() {
+export function MoreAction() {
   return <MoreActionButton key="more-action" />;
 }
 
-function SelectorTrigger() {
+export function SelectorTrigger() {
   return (
     <NetworkSelectorTriggerHome
       num={0}
@@ -137,19 +137,25 @@ function PeopleAction() {
   );
 }
 
-function SearchInput() {
+export function SearchInput() {
   const { gtLg } = useMedia();
   return <UniversalSearchInput size={gtLg ? 'large' : 'small'} />;
 }
 
 export function HeaderRight({
   tabRoute,
+  customHeaderRightItems,
 }: {
   sceneName: EAccountSelectorSceneName;
   tabRoute: ETabRoutes;
+  customHeaderRightItems?: ReactNode;
 }) {
   const isHorizontal = useIsHorizontalLayout();
   const items = useMemo(() => {
+    if (customHeaderRightItems) {
+      return customHeaderRightItems;
+    }
+
     const fixedItems = (
       <>
         {isHorizontal ? <NotificationsButton /> : null}
@@ -195,7 +201,7 @@ export function HeaderRight({
       default:
         break;
     }
-  }, [isHorizontal, tabRoute]);
+  }, [isHorizontal, tabRoute, customHeaderRightItems]);
   return (
     <HeaderButtonGroup
       testID="Wallet-Page-Header-Right"
diff --git a/packages/kit/src/components/TabPageHeader/index.native.tsx b/packages/kit/src/components/TabPageHeader/index.native.tsx
index cdacdaf5da18..3105d8a06e31 100644
--- a/packages/kit/src/components/TabPageHeader/index.native.tsx
+++ b/packages/kit/src/components/TabPageHeader/index.native.tsx
@@ -3,7 +3,6 @@ import { useMemo } from 'react';
 import { Page, View, XStack, useSafeAreaInsets } from '@onekeyhq/components';
 import { PageHeaderDivider } from '@onekeyhq/components/src/layouts/Page/PageHeader';
 import platformEnv from '@onekeyhq/shared/src/platformEnv';
-import { useDebugComponentRemountLog } from '@onekeyhq/shared/src/utils/debug/debugUtils';
 
 import { HomeTokenListProviderMirror } from '../../views/Home/components/HomeTokenListProvider/HomeTokenListProviderMirror';
 
@@ -14,16 +13,25 @@ import { HeaderTitle } from './HeaderTitle';
 
 import type { ITabPageHeaderProp } from './type';
 
-export function TabPageHeader({ sceneName, tabRoute }: ITabPageHeaderProp) {
+export function TabPageHeader({
+  sceneName,
+  tabRoute,
+  customHeaderRightItems,
+  customHeaderLeftItems,
+}: ITabPageHeaderProp) {
   const { top } = useSafeAreaInsets();
 
   const headerRight = useMemo(() => {
     return (
       <HomeTokenListProviderMirror>
-        <HeaderRight sceneName={sceneName} tabRoute={tabRoute} />
+        <HeaderRight
+          sceneName={sceneName}
+          tabRoute={tabRoute}
+          customHeaderRightItems={customHeaderRightItems}
+        />
       </HomeTokenListProviderMirror>
     );
-  }, [sceneName, tabRoute]);
+  }, [sceneName, tabRoute, customHeaderRightItems]);
 
   return (
     <>
@@ -36,7 +44,11 @@ export function TabPageHeader({ sceneName, tabRoute }: ITabPageHeaderProp) {
         {...(top || platformEnv.isNativeAndroid ? { mt: top || '$2' } : {})}
       >
         <View>
-          <HeaderLeft sceneName={sceneName} tabRoute={tabRoute} />
+          <HeaderLeft
+            sceneName={sceneName}
+            tabRoute={tabRoute}
+            customHeaderLeftItems={customHeaderLeftItems}
+          />
         </View>
         <View>
           <HeaderTitle sceneName={sceneName} />
diff --git a/packages/kit/src/components/TabPageHeader/index.tsx b/packages/kit/src/components/TabPageHeader/index.tsx
index 8938d0cc4351..00fb7190a273 100644
--- a/packages/kit/src/components/TabPageHeader/index.tsx
+++ b/packages/kit/src/components/TabPageHeader/index.tsx
@@ -13,10 +13,21 @@ import { HeaderTitle } from './HeaderTitle';
 
 import type { ITabPageHeaderProp } from './type';
 
-export function TabPageHeader({ sceneName, tabRoute }: ITabPageHeaderProp) {
+export function TabPageHeader({
+  sceneName,
+  tabRoute,
+  customHeaderRightItems,
+  customHeaderLeftItems,
+}: ITabPageHeaderProp) {
   const renderHeaderLeft = useCallback(
-    () => <HeaderLeft sceneName={sceneName} tabRoute={tabRoute} />,
-    [sceneName, tabRoute],
+    () => (
+      <HeaderLeft
+        sceneName={sceneName}
+        tabRoute={tabRoute}
+        customHeaderLeftItems={customHeaderLeftItems}
+      />
+    ),
+    [sceneName, tabRoute, customHeaderLeftItems],
   );
 
   const { config } = useAccountSelectorContextData();
@@ -26,11 +37,15 @@ export function TabPageHeader({ sceneName, tabRoute }: ITabPageHeaderProp) {
       config ? (
         <HomeTokenListProviderMirror>
           <AccountSelectorProviderMirror enabledNum={[0]} config={config}>
-            <HeaderRight sceneName={sceneName} tabRoute={tabRoute} />
+            <HeaderRight
+              sceneName={sceneName}
+              tabRoute={tabRoute}
+              customHeaderRightItems={customHeaderRightItems}
+            />
           </AccountSelectorProviderMirror>
         </HomeTokenListProviderMirror>
       ) : null,
-    [config, sceneName, tabRoute],
+    [config, sceneName, tabRoute, customHeaderRightItems],
   );
 
   const renderHeaderTitle = useCallback(
diff --git a/packages/kit/src/components/TabPageHeader/type.ts b/packages/kit/src/components/TabPageHeader/type.ts
index 06716a3b57e9..b0c9a4b262bb 100644
--- a/packages/kit/src/components/TabPageHeader/type.ts
+++ b/packages/kit/src/components/TabPageHeader/type.ts
@@ -7,4 +7,6 @@ export interface ITabPageHeaderProp {
   children?: ReactNode;
   sceneName: EAccountSelectorSceneName;
   tabRoute: ETabRoutes;
+  customHeaderRightItems?: ReactNode;
+  customHeaderLeftItems?: ReactNode;
 }
diff --git a/packages/kit/src/views/Market/MarketDetailV2/MarketDetailV2.tsx b/packages/kit/src/views/Market/MarketDetailV2/MarketDetailV2.tsx
index 318295580b8b..66b78b581bbc 100644
--- a/packages/kit/src/views/Market/MarketDetailV2/MarketDetailV2.tsx
+++ b/packages/kit/src/views/Market/MarketDetailV2/MarketDetailV2.tsx
@@ -1,146 +1,35 @@
-import { useCallback, useEffect, useMemo, useState } from 'react';
-
-import { CommonActions, StackActions } from '@react-navigation/native';
+import { useMemo } from 'react';
 
 import type { IPageScreenProps } from '@onekeyhq/components';
 import {
   EPageType,
-  HeaderIconButton,
-  NavBackButton,
   Page,
   ScrollView,
-  SizableText,
-  Skeleton,
-  View,
   XStack,
   YStack,
   useDeferredPromise,
   useMedia,
   usePageType,
-  useShare,
 } from '@onekeyhq/components';
 import { EJotaiContextStoreNames } from '@onekeyhq/kit-bg/src/states/jotai/atoms';
-import { EOneKeyDeepLinkPath } from '@onekeyhq/shared/src/consts/deeplinkConsts';
-import { EWatchlistFrom } from '@onekeyhq/shared/src/logger/scopes/market/scenes/token';
-import platformEnv from '@onekeyhq/shared/src/platformEnv';
-import { ETabMarketRoutes } from '@onekeyhq/shared/src/routes';
-import type { ITabMarketParamList } from '@onekeyhq/shared/src/routes';
-import timerUtils from '@onekeyhq/shared/src/utils/timerUtils';
-import uriUtils from '@onekeyhq/shared/src/utils/uriUtils';
+import type {
+  ETabMarketRoutes,
+  ITabMarketParamList,
+} from '@onekeyhq/shared/src/routes';
 import { EAccountSelectorSceneName } from '@onekeyhq/shared/types';
-import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
 
-import backgroundApiProxy from '../../../background/instance/backgroundApiProxy';
 import { AccountSelectorProviderMirror } from '../../../components/AccountSelector';
-import { OpenInAppButton } from '../../../components/OpenInAppButton';
-import useAppNavigation from '../../../hooks/useAppNavigation';
-import { usePromiseResult } from '../../../hooks/usePromiseResult';
-import { useActiveAccount } from '../../../states/jotai/contexts/accountSelector';
-import { MarketDetailOverview } from '../components/MarketDetailOverview';
-import { MarketHomeHeaderSearchBar } from '../components/MarketHomeHeaderSearchBar';
-import { MarketStar } from '../components/MarketStar';
-import { MarketTokenIcon } from '../components/MarketTokenIcon';
-import { MarketTokenPrice } from '../components/MarketTokenPrice';
-import { MarketTradeButton } from '../components/MarketTradeButton';
-import { PriceChangePercentage } from '../components/PriceChangePercentage';
 import { TokenDetailTabs } from '../components/TokenDetailTabs';
-import { TokenPriceChart } from '../components/TokenPriceChart';
-import { buildMarketFullUrl } from '../marketUtils';
 import { MarketWatchListProviderMirror } from '../MarketWatchListProviderMirror';
 
-import { SwapPanel } from './components/SwapPanel';
-
-function TokenDetailHeader({
-  coinGeckoId,
-  token: responseToken,
-}: {
-  coinGeckoId: string;
-  token: IMarketTokenDetail;
-}) {
-  const { gtMd: gtMdMedia } = useMedia();
-
-  const pageType = usePageType();
-
-  const {
-    activeAccount: { wallet },
-  } = useActiveAccount({
-    num: 0,
-  });
-
-  const gtMd = pageType === EPageType.modal ? false : gtMdMedia;
-
-  const { result: token } = usePromiseResult(
-    () => backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(coinGeckoId),
-    [coinGeckoId],
-    {
-      pollingInterval: timerUtils.getTimeDurationMs({ seconds: 45 }),
-      initResult: responseToken,
-    },
-  );
-  const {
-    name,
-    symbol,
-    stats: { performance, currentPrice, lastUpdated },
-  } = token;
-  return (
-    <YStack
-      px="$5"
-      $md={{ minHeight: 150 }}
-      {...(pageType === EPageType.modal ? { minHeight: 150 } : null)}
-    >
-      <YStack flex={1}>
-        <SizableText size="$headingMd" color="$textSubdued">
-          {name}
-        </SizableText>
-        <XStack ai="center" jc="space-between" pt="$2">
-          <MarketTokenPrice
-            size="$heading3xl"
-            price={currentPrice}
-            tokenName={name}
-            tokenSymbol={symbol}
-            lastUpdated={lastUpdated}
-          />
-          <MarketStar
-            coingeckoId={coinGeckoId}
-            mr="$-2"
-            size="medium"
-            from={EWatchlistFrom.details}
-          />
-        </XStack>
-        <PriceChangePercentage pt="$0.5" width="100%">
-          {performance.priceChangePercentage24h}
-        </PriceChangePercentage>
-      </YStack>
-      <MarketTradeButton
-        coinGeckoId={coinGeckoId}
-        token={token}
-        wallet={wallet}
-      />
-      {gtMd ? <MarketDetailOverview token={token} /> : null}
-    </YStack>
-  );
-}
-
-function SkeletonHeader() {
-  return (
-    <YStack>
-      <Skeleton w="$24" h="$4" />
-      <View pt="$5" pb="$3.5">
-        <Skeleton w="$40" h="$7" />
-      </View>
-      <Skeleton w="$24" h="$3" />
-    </YStack>
-  );
-}
-
-function SkeletonHeaderOverItemItem() {
-  return (
-    <YStack gap="$2" flexGrow={1} flexBasis={0}>
-      <Skeleton w="$10" h="$3" />
-      <Skeleton w="$24" h="$3" />
-    </YStack>
-  );
-}
+import {
+  TokenDetailHeader as LegacyTokenDetailHeader,
+  MarketDetailHeader,
+  TokenPriceChart as NewTokenPriceChart,
+  SwapPanel,
+  TokenDetailHeaderSkeleton,
+} from './components';
+import { useMarketDetailData } from './useMarketDetailData';
 
 function MarketDetail({
   route,
@@ -152,163 +41,29 @@ function MarketDetail({
 
   const gtMd = pageType === EPageType.modal ? false : gtMdMedia;
 
-  const [isRefreshing, setIsRefreshing] = useState(false);
-
-  const [tokenDetail, setTokenDetail] = useState<
-    IMarketTokenDetail | undefined
-  >(undefined);
-
-  const fetchMarketTokenDetail = useCallback(async () => {
-    const response =
-      await backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(
-        coinGeckoId,
-      );
-    setTokenDetail(response);
-  }, [coinGeckoId]);
-
-  const onRefresh = useCallback(async () => {
-    setIsRefreshing(true);
-    await fetchMarketTokenDetail();
-    setIsRefreshing(false);
-  }, [fetchMarketTokenDetail]);
-
-  useEffect(() => {
-    void fetchMarketTokenDetail();
-  }, [fetchMarketTokenDetail]);
-
-  const renderHeaderTitle = useCallback(
-    () => (
-      <XStack gap="$2">
-        <MarketTokenIcon uri={tokenDetail?.image || ''} size="sm" />
-        <SizableText>{tokenDetail?.symbol?.toUpperCase()}</SizableText>
-      </XStack>
-    ),
-    [tokenDetail?.image, tokenDetail?.symbol],
-  );
-  const { shareText } = useShare();
-
-  const buildDeepLinkUrl = useCallback(
-    () =>
-      uriUtils.buildDeepLinkUrl({
-        path: EOneKeyDeepLinkPath.market_detail,
-        query: {
-          coinGeckoId,
-        },
-      }),
-    [coinGeckoId],
-  );
-
-  const buildFullUrl = useCallback(
-    async () => buildMarketFullUrl({ coinGeckoId }),
-    [coinGeckoId],
-  );
-
-  const renderHeaderRight = useCallback(
-    () => (
-      <XStack gap="$6" ai="center">
-        {!platformEnv.isExtensionUiPopup && !platformEnv.isNative ? (
-          <OpenInAppButton
-            buildDeepLinkUrl={buildDeepLinkUrl}
-            buildFullUrl={buildFullUrl}
-          />
-        ) : null}
-        <HeaderIconButton
-          icon="ShareOutline"
-          onPress={async () => {
-            const url = buildMarketFullUrl({ coinGeckoId });
-            await shareText(url);
-          }}
-        />
-        {gtMd ? <MarketHomeHeaderSearchBar /> : null}
-      </XStack>
-    ),
-    [buildDeepLinkUrl, buildFullUrl, coinGeckoId, gtMd, shareText],
-  );
-
-  const navigation = useAppNavigation();
-
-  const popPage = useCallback(() => {
-    navigation.dispatch((state) => {
-      if (state.routes.length > 1) {
-        return StackActions.pop(state.routes.length);
-      }
-      return CommonActions.reset({
-        index: 0,
-        routes: [
-          {
-            name: ETabMarketRoutes.TabMarket,
-          },
-        ],
-      });
-    });
-  }, [navigation]);
-
-  const renderHeaderLeft = useCallback(
-    () => <NavBackButton onPress={popPage} />,
-    [popPage],
-  );
+  const { tokenDetail, isRefreshing, onRefresh } =
+    useMarketDetailData(coinGeckoId);
 
   const tokenDetailHeader = useMemo(() => {
     if (tokenDetail) {
       return (
-        <TokenDetailHeader coinGeckoId={coinGeckoId} token={tokenDetail} />
+        <LegacyTokenDetailHeader
+          coinGeckoId={coinGeckoId}
+          token={tokenDetail}
+        />
       );
     }
-    return (
-      <YStack px="$5">
-        {gtMd ? (
-          <YStack gap="$12" width={392}>
-            <SkeletonHeader />
-            <YStack gap="$3">
-              <Skeleton w={252} h="$3" />
-            </YStack>
-            <YStack gap="$6">
-              <XStack>
-                <SkeletonHeaderOverItemItem />
-                <SkeletonHeaderOverItemItem />
-              </XStack>
-              <XStack>
-                <SkeletonHeaderOverItemItem />
-                <SkeletonHeaderOverItemItem />
-              </XStack>
-              <XStack>
-                <SkeletonHeaderOverItemItem />
-                <SkeletonHeaderOverItemItem />
-              </XStack>
-            </YStack>
-            <YStack gap="$6">
-              <Skeleton w="$10" h="$3" />
-              <Skeleton w={252} h="$3" />
-              <Skeleton w={252} h="$3" />
-              <Skeleton w={252} h="$3" />
-            </YStack>
-          </YStack>
-        ) : (
-          <YStack gap="$6" pt="$1">
-            <SkeletonHeader />
-            <XStack>
-              <SkeletonHeaderOverItemItem />
-              <SkeletonHeaderOverItemItem />
-              <SkeletonHeaderOverItemItem />
-            </XStack>
-          </YStack>
-        )}
-      </YStack>
-    );
+    return <TokenDetailHeaderSkeleton gtMd={gtMd} />;
   }, [coinGeckoId, gtMd, tokenDetail]);
 
   const defer = useDeferredPromise();
 
   const tokenPriceChart = useMemo(
     () => (
-      <TokenPriceChart
-        isFetching={!tokenDetail}
-        tickers={tokenDetail?.tickers}
-        fallbackToChart={!!tokenDetail?.fallbackToChart}
-        tvPlatform={tokenDetail?.tvPlatform}
+      <NewTokenPriceChart
+        tokenDetail={tokenDetail}
         coinGeckoId={coinGeckoId}
         defer={defer}
-        symbol={tokenDetail?.symbol}
       />
     ),
     [coinGeckoId, defer, tokenDetail],
@@ -316,10 +71,10 @@ function MarketDetail({
 
   return (
     <Page>
-      <Page.Header
-        headerTitle={renderHeaderTitle}
-        headerRight={renderHeaderRight}
-        headerLeft={renderHeaderLeft}
+      <MarketDetailHeader
+        tokenDetail={tokenDetail}
+        coinGeckoId={coinGeckoId}
+        gtMd={gtMd}
       />
       <Page.Body>
         <SwapPanel />
diff --git a/packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/MarketDetailHeader.tsx b/packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/MarketDetailHeader.tsx
new file mode 100644
index 000000000000..8d643f4a0bb0
--- /dev/null
+++ b/packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/MarketDetailHeader.tsx
@@ -0,0 +1,119 @@
+import { useCallback } from 'react';
+
+import {
+  CommonActions,
+  StackActions,
+  type StackNavigationState,
+} from '@react-navigation/native';
+
+import {
+  HeaderIconButton,
+  NavBackButton,
+  Page,
+  SizableText,
+  XStack,
+  useShare,
+} from '@onekeyhq/components';
+import { OpenInAppButton } from '@onekeyhq/kit/src/components/OpenInAppButton';
+import useAppNavigation from '@onekeyhq/kit/src/hooks/useAppNavigation';
+import { MarketHomeHeaderSearchBar } from '@onekeyhq/kit/src/views/Market/components/MarketHomeHeaderSearchBar';
+import { MarketTokenIcon } from '@onekeyhq/kit/src/views/Market/components/MarketTokenIcon';
+import { buildMarketFullUrl as buildMarketFullUrlUtil } from '@onekeyhq/kit/src/views/Market/marketUtils';
+import { EOneKeyDeepLinkPath } from '@onekeyhq/shared/src/consts/deeplinkConsts';
+import platformEnv from '@onekeyhq/shared/src/platformEnv';
+import { ETabMarketRoutes } from '@onekeyhq/shared/src/routes';
+import uriUtils from '@onekeyhq/shared/src/utils/uriUtils';
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+interface IMarketDetailHeaderProps {
+  tokenDetail: IMarketTokenDetail | undefined;
+  coinGeckoId: string;
+  gtMd: boolean;
+}
+
+export function MarketDetailHeader({
+  tokenDetail,
+  coinGeckoId,
+  gtMd,
+}: IMarketDetailHeaderProps) {
+  const navigation = useAppNavigation();
+
+  const renderHeaderTitle = useCallback(
+    () => (
+      <XStack gap="$2" alignItems="center">
+        <MarketTokenIcon uri={tokenDetail?.image || ''} size="sm" />
+        <SizableText>{tokenDetail?.symbol?.toUpperCase()}</SizableText>
+      </XStack>
+    ),
+    [tokenDetail?.image, tokenDetail?.symbol],
+  );
+
+  const { shareText } = useShare();
+
+  const buildDeepLinkUrl = useCallback(
+    () =>
+      uriUtils.buildDeepLinkUrl({
+        path: EOneKeyDeepLinkPath.market_detail,
+        query: {
+          coinGeckoId,
+        },
+      }),
+    [coinGeckoId],
+  );
+
+  const buildFullUrl = useCallback(
+    async () => buildMarketFullUrlUtil({ coinGeckoId }),
+    [coinGeckoId],
+  );
+
+  const renderHeaderRight = useCallback(
+    () => (
+      <XStack gap="$6" ai="center">
+        {!platformEnv.isExtensionUiPopup && !platformEnv.isNative ? (
+          <OpenInAppButton
+            buildDeepLinkUrl={buildDeepLinkUrl}
+            buildFullUrl={buildFullUrl}
+          />
+        ) : null}
+        <HeaderIconButton
+          icon="ShareOutline"
+          onPress={async () => {
+            const url = buildMarketFullUrlUtil({ coinGeckoId });
+            await shareText(url);
+          }}
+        />
+        {gtMd ? <MarketHomeHeaderSearchBar /> : null}
+      </XStack>
+    ),
+    [buildDeepLinkUrl, buildFullUrl, coinGeckoId, gtMd, shareText],
+  );
+
+  const popPage = useCallback(() => {
+    navigation.dispatch((state: StackNavigationState<any>) => {
+      if (state.routes.length > 1) {
+        return StackActions.pop(state.routes.length);
+      }
+      return CommonActions.reset({
+        index: 0,
+        routes: [
+          {
+            name: ETabMarketRoutes.TabMarket,
+          },
+        ],
+      });
+    });
+  }, [navigation]);
+
+  const renderHeaderLeft = useCallback(
+    () => <NavBackButton onPress={popPage} />,
+    [popPage],
+  );
+
+  return (
+    <Page.Header
+      headerTitle={renderHeaderTitle}
+      headerRight={renderHeaderRight}
+      headerLeft={renderHeaderLeft}
+    />
+  );
+}
diff --git a/packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/index.ts b/packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/index.ts
new file mode 100644
index 000000000000..d47508caadbf
--- /dev/null
+++ b/packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/index.ts
@@ -0,0 +1 @@
+export * from './MarketDetailHeader';
diff --git a/packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeader.tsx b/packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeader.tsx
new file mode 100644
index 000000000000..2dc8de59456f
--- /dev/null
+++ b/packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeader.tsx
@@ -0,0 +1,91 @@
+import {
+  EPageType,
+  SizableText,
+  XStack,
+  YStack,
+  useMedia,
+  usePageType,
+} from '@onekeyhq/components';
+import backgroundApiProxy from '@onekeyhq/kit/src/background/instance/backgroundApiProxy';
+import { usePromiseResult } from '@onekeyhq/kit/src/hooks/usePromiseResult';
+import { useActiveAccount } from '@onekeyhq/kit/src/states/jotai/contexts/accountSelector';
+import { MarketDetailOverview } from '@onekeyhq/kit/src/views/Market/components/MarketDetailOverview';
+import { MarketStar } from '@onekeyhq/kit/src/views/Market/components/MarketStar';
+import { MarketTokenPrice } from '@onekeyhq/kit/src/views/Market/components/MarketTokenPrice';
+import { MarketTradeButton } from '@onekeyhq/kit/src/views/Market/components/MarketTradeButton';
+import { PriceChangePercentage } from '@onekeyhq/kit/src/views/Market/components/PriceChangePercentage';
+import { EWatchlistFrom } from '@onekeyhq/shared/src/logger/scopes/market/scenes/token';
+import timerUtils from '@onekeyhq/shared/src/utils/timerUtils';
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+export function TokenDetailHeader({
+  coinGeckoId,
+  token: responseToken,
+}: {
+  coinGeckoId: string;
+  token: IMarketTokenDetail;
+}) {
+  const { gtMd: gtMdMedia } = useMedia();
+
+  const pageType = usePageType();
+
+  const {
+    activeAccount: { wallet },
+  } = useActiveAccount({
+    num: 0,
+  });
+
+  const gtMd = pageType === EPageType.modal ? false : gtMdMedia;
+
+  const { result: token } = usePromiseResult(
+    () => backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(coinGeckoId),
+    [coinGeckoId],
+    {
+      pollingInterval: timerUtils.getTimeDurationMs({ seconds: 45 }),
+      initResult: responseToken,
+    },
+  );
+  const {
+    name,
+    symbol,
+    stats: { performance, currentPrice, lastUpdated },
+  } = token;
+  return (
+    <YStack
+      bg="$green4"
+      px="$5"
+      $md={{ minHeight: 150 }}
+      {...(pageType === EPageType.modal ? { minHeight: 150 } : null)}
+    >
+      <YStack flex={1}>
+        <SizableText size="$headingMd" color="$textSubdued">
+          {name}
+        </SizableText>
+        <XStack ai="center" jc="space-between" pt="$2">
+          <MarketTokenPrice
+            size="$heading3xl"
+            price={currentPrice}
+            tokenName={name}
+            tokenSymbol={symbol}
+            lastUpdated={lastUpdated}
+          />
+          <MarketStar
+            coingeckoId={coinGeckoId}
+            mr="$-2"
+            size="medium"
+            from={EWatchlistFrom.details}
+          />
+        </XStack>
+        <PriceChangePercentage pt="$0.5" width="100%">
+          {performance.priceChangePercentage24h}
+        </PriceChangePercentage>
+      </YStack>
+      <MarketTradeButton
+        coinGeckoId={coinGeckoId}
+        token={token}
+        wallet={wallet}
+      />
+      {gtMd ? <MarketDetailOverview token={token} /> : null}
+    </YStack>
+  );
+}
diff --git a/packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeaderSkeleton.tsx b/packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeaderSkeleton.tsx
new file mode 100644
index 000000000000..b82de280b967
--- /dev/null
+++ b/packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeaderSkeleton.tsx
@@ -0,0 +1,72 @@
+import { Skeleton, View, XStack, YStack } from '@onekeyhq/components';
+
+function SkeletonHeader() {
+  return (
+    <YStack>
+      <Skeleton w="$24" h="$4" />
+      <View pt="$5" pb="$3.5">
+        <Skeleton w="$40" h="$7" />
+      </View>
+      <Skeleton w="$24" h="$3" />
+    </YStack>
+  );
+}
+
+function SkeletonHeaderOverItemItem() {
+  return (
+    <YStack gap="$2" flexGrow={1} flexBasis={0}>
+      <Skeleton w="$10" h="$3" />
+      <Skeleton w="$24" h="$3" />
+    </YStack>
+  );
+}
+
+interface ITokenDetailHeaderSkeletonProps {
+  gtMd: boolean;
+}
+
+export function TokenDetailHeaderSkeleton({
+  gtMd,
+}: ITokenDetailHeaderSkeletonProps) {
+  return (
+    <YStack px="$5">
+      {gtMd ? (
+        <YStack gap="$12" width={392}>
+          <SkeletonHeader />
+          <YStack gap="$3">
+            <Skeleton w={252} h="$3" />
+          </YStack>
+          <YStack gap="$6">
+            <XStack>
+              <SkeletonHeaderOverItemItem />
+              <SkeletonHeaderOverItemItem />
+            </XStack>
+            <XStack>
+              <SkeletonHeaderOverItemItem />
+              <SkeletonHeaderOverItemItem />
+            </XStack>
+            <XStack>
+              <SkeletonHeaderOverItemItem />
+              <SkeletonHeaderOverItemItem />
+            </XStack>
+          </YStack>
+          <YStack gap="$6">
+            <Skeleton w="$10" h="$3" />
+            <Skeleton w={252} h="$3" />
+            <Skeleton w={252} h="$3" />
+            <Skeleton w={252} h="$3" />
+          </YStack>
+        </YStack>
+      ) : (
+        <YStack gap="$6" pt="$1">
+          <SkeletonHeader />
+          <XStack>
+            <SkeletonHeaderOverItemItem />
+            <SkeletonHeaderOverItemItem />
+            <SkeletonHeaderOverItemItem />
+          </XStack>
+        </YStack>
+      )}
+    </YStack>
+  );
+}
diff --git a/packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/index.ts b/packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/index.ts
new file mode 100644
index 000000000000..80d5e761c677
--- /dev/null
+++ b/packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/index.ts
@@ -0,0 +1,2 @@
+export * from './TokenDetailHeader';
+export * from './TokenDetailHeaderSkeleton';
diff --git a/packages/kit/src/views/Market/MarketDetailV2/components/TokenPriceChart/TokenPriceChart.tsx b/packages/kit/src/views/Market/MarketDetailV2/components/TokenPriceChart/TokenPriceChart.tsx
new file mode 100644
index 000000000000..5113ea311390
--- /dev/null
+++ b/packages/kit/src/views/Market/MarketDetailV2/components/TokenPriceChart/TokenPriceChart.tsx
@@ -0,0 +1,29 @@
+import type { ComponentProps } from 'react';
+
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+import { TokenPriceChart as TokenPriceChartBase } from '../../../components/TokenPriceChart';
+
+type ITokenPriceChartProps = {
+  coinGeckoId: string;
+  tokenDetail?: IMarketTokenDetail;
+  defer: ComponentProps<typeof TokenPriceChartBase>['defer'];
+};
+
+export function TokenPriceChart({
+  coinGeckoId,
+  tokenDetail,
+  defer,
+}: ITokenPriceChartProps) {
+  return (
+    <TokenPriceChartBase
+      isFetching={!tokenDetail}
+      tickers={tokenDetail?.tickers}
+      fallbackToChart={!!tokenDetail?.fallbackToChart}
+      tvPlatform={tokenDetail?.tvPlatform}
+      coinGeckoId={coinGeckoId}
+      defer={defer}
+      symbol={tokenDetail?.symbol}
+    />
+  );
+}
diff --git a/packages/kit/src/views/Market/MarketDetailV2/components/TokenPriceChart/index.ts b/packages/kit/src/views/Market/MarketDetailV2/components/TokenPriceChart/index.ts
new file mode 100644
index 000000000000..b29b0c40bac7
--- /dev/null
+++ b/packages/kit/src/views/Market/MarketDetailV2/components/TokenPriceChart/index.ts
@@ -0,0 +1 @@
+export * from './TokenPriceChart';
diff --git a/packages/kit/src/views/Market/MarketDetailV2/components/index.ts b/packages/kit/src/views/Market/MarketDetailV2/components/index.ts
new file mode 100644
index 000000000000..e90dfbfa6b88
--- /dev/null
+++ b/packages/kit/src/views/Market/MarketDetailV2/components/index.ts
@@ -0,0 +1,4 @@
+export * from './SwapPanel';
+export * from './TokenPriceChart';
+export * from './TokenDetailHeader';
+export * from './MarketDetailHeader';
diff --git a/packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts b/packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts
new file mode 100644
index 000000000000..6e43442e34e7
--- /dev/null
+++ b/packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts
@@ -0,0 +1,37 @@
+import { useCallback, useEffect, useState } from 'react';
+
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+import backgroundApiProxy from '../../../background/instance/backgroundApiProxy';
+
+export function useMarketDetailData(coinGeckoId: string) {
+  const [tokenDetail, setTokenDetail] = useState<
+    IMarketTokenDetail | undefined
+  >(undefined);
+  const [isRefreshing, setIsRefreshing] = useState(false);
+
+  const fetchMarketTokenDetail = useCallback(async () => {
+    const response =
+      await backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(
+        coinGeckoId,
+      );
+    setTokenDetail(response);
+  }, [coinGeckoId]);
+
+  useEffect(() => {
+    void fetchMarketTokenDetail();
+  }, [fetchMarketTokenDetail]);
+
+  const onRefresh = useCallback(async () => {
+    setIsRefreshing(true);
+    await fetchMarketTokenDetail();
+    setIsRefreshing(false);
+  }, [fetchMarketTokenDetail]);
+
+  return {
+    tokenDetail,
+    fetchMarketTokenDetail,
+    isRefreshing,
+    onRefresh,
+  };
+}


Reviews:
user: login: originalix
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-15T01:15:09Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 10**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/MarketDetailV2.tsx (1)</summary><blockquote>

`100-106`: _🧹 Nitpick (assertive)_

**Refresh logic only wired for small screens**

`isRefreshing` and `onRefresh` are passed to `TokenDetailTabs` only in the mobile branch.  
On desktop (`gtMd` true) users can’t pull to refresh. Decide if desktop should refresh too or document why not.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between c3559087acac08daffec7cbc2677b9c3a1d60dc1 and c5614466c3c0729c677c622ec773ec3f2976cae4.

</details>

<details>
<summary>📒 Files selected for processing (15)</summary>

* `packages/kit/src/components/TabPageHeader/HeaderLeft.tsx` (4 hunks)
* `packages/kit/src/components/TabPageHeader/HeaderRight.tsx` (4 hunks)
* `packages/kit/src/components/TabPageHeader/index.native.tsx` (2 hunks)
* `packages/kit/src/components/TabPageHeader/index.tsx` (2 hunks)
* `packages/kit/src/components/TabPageHeader/type.ts` (1 hunks)
* `packages/kit/src/views/Market/MarketDetailV2/MarketDetailV2.tsx` (2 hunks)
* `packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/MarketDetailHeader.tsx` (1 hunks)
* `packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/index.ts` (1 hunks)
* `packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeader.tsx` (1 hunks)
* `packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeaderSkeleton.tsx` (1 hunks)
* `packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/index.ts` (1 hunks)
* `packages/kit/src/views/Market/MarketDetailV2/components/TokenPriceChart/TokenPriceChart.tsx` (1 hunks)
* `packages/kit/src/views/Market/MarketDetailV2/components/TokenPriceChart/index.ts` (1 hunks)
* `packages/kit/src/views/Market/MarketDetailV2/components/index.ts` (1 hunks)
* `packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (7)</summary>

<details>
<summary>packages/kit/src/components/TabPageHeader/HeaderLeft.tsx (1)</summary><blockquote>

<details>
<summary>packages/components/src/layouts/Page/index.tsx (1)</summary>

* `Page` (87-96)

</details>

</blockquote></details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts (2)</summary><blockquote>

<details>
<summary>packages/shared/types/market.ts (1)</summary>

* `IMarketTokenDetail` (130-147)

</details>
<details>
<summary>packages/kit-bg/src/services/ServiceMarket.ts (1)</summary>

* `fetchMarketTokenDetail` (100-118)

</details>

</blockquote></details>
<details>
<summary>packages/kit/src/components/TabPageHeader/index.native.tsx (3)</summary><blockquote>

<details>
<summary>packages/kit/src/components/TabPageHeader/type.ts (1)</summary>

* `ITabPageHeaderProp` (6-12)

</details>
<details>
<summary>packages/kit/src/components/TabPageHeader/HeaderRight.tsx (1)</summary>

* `HeaderRight` (145-215)

</details>
<details>
<summary>packages/kit/src/components/TabPageHeader/HeaderLeft.tsx (1)</summary>

* `HeaderLeft` (34-138)

</details>

</blockquote></details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/components/TokenPriceChart/TokenPriceChart.tsx (1)</summary><blockquote>

<details>
<summary>packages/shared/types/market.ts (1)</summary>

* `IMarketTokenDetail` (130-147)

</details>

</blockquote></details>
<details>
<summary>packages/kit/src/components/TabPageHeader/HeaderRight.tsx (2)</summary><blockquote>

<details>
<summary>packages/kit/src/components/TabPageHeader/MoreActionButton.tsx (1)</summary>

* `MoreActionButton` (560-566)

</details>
<details>
<summary>packages/kit/src/components/TabPageHeader/UniversalSearchInput.tsx (1)</summary>

* `UniversalSearchInput` (21-70)

</details>

</blockquote></details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/MarketDetailV2.tsx (4)</summary><blockquote>

<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts (1)</summary>

* `useMarketDetailData` (7-37)

</details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeaderSkeleton.tsx (1)</summary>

* `TokenDetailHeaderSkeleton` (28-72)

</details>
<details>
<summary>packages/components/src/layouts/Page/index.tsx (1)</summary>

* `Page` (87-96)

</details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/MarketDetailHeader.tsx (1)</summary>

* `MarketDetailHeader` (34-119)

</details>

</blockquote></details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/MarketDetailHeader.tsx (5)</summary><blockquote>

<details>
<summary>packages/shared/types/market.ts (1)</summary>

* `IMarketTokenDetail` (130-147)

</details>
<details>
<summary>packages/kit/src/views/Market/components/MarketTokenIcon.tsx (1)</summary>

* `MarketTokenIcon` (17-17)

</details>
<details>
<summary>packages/shared/src/polyfills/polyfillsPlatform.js (1)</summary>

* `platformEnv` (6-6)

</details>
<details>
<summary>packages/kit/src/views/Market/components/MarketHomeHeaderSearchBar.tsx (1)</summary>

* `MarketHomeHeaderSearchBar` (12-46)

</details>
<details>
<summary>packages/components/src/layouts/Page/index.tsx (1)</summary>

* `Page` (87-96)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (34)</summary><blockquote>

<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/index.ts (1)</summary>

`1-3`: **Clean re-export pattern.**

Standard index file that bundles related components. Makes imports cleaner elsewhere.

</details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/index.ts (1)</summary>

`1-2`: **Good modular structure.**

Follows the barrel export pattern. Makes component usage more intuitive.

</details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/components/TokenPriceChart/index.ts (1)</summary>

`1-2`: **Clean export structure.**

Consistent with other components. Simplifies imports in parent components.

</details>
<details>
<summary>packages/kit/src/components/TabPageHeader/type.ts (1)</summary>

`10-11`: **Good interface extension.**

New properties allow flexible header customization. Optional fields maintain backward compatibility.

</details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/components/index.ts (1)</summary>

`1-5`: **Clean export structure for Market components**

This index file creates a single import point for all Market detail components, simplifying imports elsewhere in the codebase. This is a good pattern that reduces import clutter.

</details>
<details>
<summary>packages/kit/src/components/TabPageHeader/index.tsx (4)</summary>

`16-21`: **Props signature updated to support custom header items**

The component now accepts two new props that allow for custom header content, making the component more flexible.

---

`22-31`: **Correctly updated dependency array for renderHeaderLeft**

The useCallback dependency array properly includes the new customHeaderLeftItems prop, ensuring the callback refreshes when this prop changes.

---

`35-48`: **HeaderRight now supports custom items**

The HeaderRight component now receives the customHeaderRightItems prop, allowing for custom header content on the right side.

---

`48-49`: **Properly updated dependency array for renderHeaderRight**

The dependency array correctly includes customHeaderRightItems to ensure memoization works properly.

</details>
<details>
<summary>packages/kit/src/components/TabPageHeader/index.native.tsx (3)</summary>

`16-21`: **Native header updated with new props**

Good job keeping the native implementation in sync with the web version by adding the same props.

---

`24-34`: **HeaderRight implementation updated**

The headerRight memo now includes the customHeaderRightItems prop in its dependencies for proper updates.

---

`46-52`: **HeaderLeft now accepts custom items**

The HeaderLeft component correctly receives the customHeaderLeftItems prop, maintaining consistency with the web implementation.

</details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/components/TokenPriceChart/TokenPriceChart.tsx (2)</summary>

`1-12`: **Clean props type definition**

The props interface clearly defines what the component expects, and smartly reuses the defer prop type from the base component.

---

`13-29`: **Well-structured wrapper component**

This component effectively wraps TokenPriceChartBase, handling the conditional mapping of tokenDetail properties. Using the nullish coalescing operator (?) for optional properties is a good practice.

</details>
<details>
<summary>packages/kit/src/components/TabPageHeader/HeaderLeft.tsx (4)</summary>

`26-32`: **Well-structured reusable component.**

This new `HeaderLeftCloseButton` component provides a clean abstraction for a common UI pattern. It wraps the `NavBackButton` in a `Page.Close` component, making it easier to reuse across the app.

---

`37-41`: **Good addition of customization capability.**

Adding the `customHeaderLeftItems` prop enhances flexibility, letting consumers pass custom header items when needed. The type definition is appropriate.

---

`61-67`: **Clean implementation of conditional rendering.**

The priority check for custom items is well placed, and the component now uses the newly created `HeaderLeftCloseButton` which improves code reuse.

---

`118-126`: **Properly updated dependency array.**

The `useMemo` dependency array correctly includes the new `customHeaderLeftItems` prop, ensuring the memoized value recalculates when this prop changes.

</details>
<details>
<summary>packages/kit/src/components/TabPageHeader/HeaderRight.tsx (6)</summary>

`110-112`: **Good component extraction and export.**

Exporting `MoreAction` makes this UI element reusable in other components, improving code organization.

---

`114-122`: **Smart extraction of selector trigger.**

Exporting the `SelectorTrigger` component enables reuse and improves modularity in the codebase.

---

`140-143`: **Clean search input extraction.**

The `SearchInput` component is now properly exported and encapsulates responsive behavior based on screen size.

---

`147-151`: **Consistent prop pattern for customization.**

Adding `customHeaderRightItems` follows the same pattern as in HeaderLeft, creating a consistent API across components.

---

`155-157`: **Proper priority for custom items.**

The implementation correctly prioritizes custom header items when provided, keeping the internal logic intact for default cases.

---

`204-204`: **Updated dependency array correctly.**

The dependency array properly includes the new `customHeaderRightItems` prop, ensuring memoization works as expected.

</details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/MarketDetailHeader.tsx (5)</summary>

`28-32`: **Clear interface definition.**

The props interface is well-defined with explicit types for all properties, making the component API clear and self-documenting.

---

`41-49`: **Clean header title implementation.**

The header title callback correctly shows the token icon and symbol, with proper dependencies in the useCallback.

---

`53-67`: **Well-structured URL building functions.**

Both deep link and full URL building functions are properly memoized with correct dependencies. This separation makes the code more maintainable.

---

`69-89`: **Comprehensive header right implementation.**

The header right section intelligently handles platform-specific rendering and includes proper sharing functionality. The dependency array is complete.

---

`112-118`: **Clean header composition.**

The component returns a well-structured `Page.Header` with all three sections (title, right, left) properly implemented as callbacks.

</details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts (3)</summary>

`7-11`: **Clear state initialization.**

The hook properly initializes state for token details and refresh status with appropriate types.

---

`25-29`: **Well-implemented refresh function.**

The refresh function correctly manages the refreshing state and calls the fetch function.

---

`31-36`: **Clean return object.**

The hook returns a well-structured object with all necessary properties and functions.

</details>
<details>
<summary>packages/kit/src/views/Market/MarketDetailV2/MarketDetailV2.tsx (2)</summary>

`44-57`: **Memo depends on component identity**

`useMemo` for `tokenDetailHeader` depends on `LegacyTokenDetailHeader`’s identity.  
After the refactor above, include the component in the dependency list or move JSX outside `useMemo`; otherwise React may reuse stale output when the header component itself changes.

---

`61-69`: **Chart gets `undefined` token on first render**

`TokenPriceChart` receives `tokenDetail={tokenDetail}` even when `tokenDetail` is still `undefined`.  
Confirm that the chart handles this gracefully (shows a spinner, etc.)—otherwise add a guard like you did for the header.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T01:17:43Z
user: login: limichange
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T01:42:42Z
user: login: sidmorizon
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T06:36:23Z

Review comments:
diff_hunk: @@ -0,0 +1,37 @@
+import { useCallback, useEffect, useState } from 'react';
+
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+import backgroundApiProxy from '../../../background/instance/backgroundApiProxy';
+
+export function useMarketDetailData(coinGeckoId: string) {
+  const [tokenDetail, setTokenDetail] = useState<
+    IMarketTokenDetail | undefined
+  >(undefined);
+  const [isRefreshing, setIsRefreshing] = useState(false);
+
+  const fetchMarketTokenDetail = useCallback(async () => {
+    const response =
+      await backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(
+        coinGeckoId,
+      );
+    setTokenDetail(response);
+  }, [coinGeckoId]);
path: packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts
user: login: originalix
body: 详情页的数据考虑跟其他模块设计一致放 jotai 里么，子组件获取详情页数据会简洁方便一点

包括在 header 里的 45s 的轮询逻辑，逻辑统一后能更新全页面的数据
created_at: 2025-05-15T01:14:46Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,119 @@
+import { useCallback } from 'react';
+
+import {
+  CommonActions,
+  StackActions,
+  type StackNavigationState,
+} from '@react-navigation/native';
+
+import {
+  HeaderIconButton,
+  NavBackButton,
+  Page,
+  SizableText,
+  XStack,
+  useShare,
+} from '@onekeyhq/components';
+import { OpenInAppButton } from '@onekeyhq/kit/src/components/OpenInAppButton';
+import useAppNavigation from '@onekeyhq/kit/src/hooks/useAppNavigation';
+import { MarketHomeHeaderSearchBar } from '@onekeyhq/kit/src/views/Market/components/MarketHomeHeaderSearchBar';
+import { MarketTokenIcon } from '@onekeyhq/kit/src/views/Market/components/MarketTokenIcon';
+import { buildMarketFullUrl as buildMarketFullUrlUtil } from '@onekeyhq/kit/src/views/Market/marketUtils';
+import { EOneKeyDeepLinkPath } from '@onekeyhq/shared/src/consts/deeplinkConsts';
+import platformEnv from '@onekeyhq/shared/src/platformEnv';
+import { ETabMarketRoutes } from '@onekeyhq/shared/src/routes';
+import uriUtils from '@onekeyhq/shared/src/utils/uriUtils';
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+interface IMarketDetailHeaderProps {
+  tokenDetail: IMarketTokenDetail | undefined;
+  coinGeckoId: string;
+  gtMd: boolean;
+}
+
+export function MarketDetailHeader({
+  tokenDetail,
+  coinGeckoId,
+  gtMd,
+}: IMarketDetailHeaderProps) {
+  const navigation = useAppNavigation();
+
+  const renderHeaderTitle = useCallback(
+    () => (
+      <XStack gap="$2" alignItems="center">
+        <MarketTokenIcon uri={tokenDetail?.image || ''} size="sm" />
+        <SizableText>{tokenDetail?.symbol?.toUpperCase()}</SizableText>
+      </XStack>
+    ),
+    [tokenDetail?.image, tokenDetail?.symbol],
+  );
+
+  const { shareText } = useShare();
+
+  const buildDeepLinkUrl = useCallback(
+    () =>
+      uriUtils.buildDeepLinkUrl({
+        path: EOneKeyDeepLinkPath.market_detail,
+        query: {
+          coinGeckoId,
+        },
+      }),
+    [coinGeckoId],
+  );
+
+  const buildFullUrl = useCallback(
+    async () => buildMarketFullUrlUtil({ coinGeckoId }),
+    [coinGeckoId],
+  );
+
+  const renderHeaderRight = useCallback(
+    () => (
+      <XStack gap="$6" ai="center">
+        {!platformEnv.isExtensionUiPopup && !platformEnv.isNative ? (
+          <OpenInAppButton
+            buildDeepLinkUrl={buildDeepLinkUrl}
+            buildFullUrl={buildFullUrl}
+          />
+        ) : null}
+        <HeaderIconButton
+          icon="ShareOutline"
+          onPress={async () => {
+            const url = buildMarketFullUrlUtil({ coinGeckoId });
+            await shareText(url);
+          }}
+        />
+        {gtMd ? <MarketHomeHeaderSearchBar /> : null}
+      </XStack>
+    ),
+    [buildDeepLinkUrl, buildFullUrl, coinGeckoId, gtMd, shareText],
+  );
+
+  const popPage = useCallback(() => {
+    navigation.dispatch((state: StackNavigationState<any>) => {
+      if (state.routes.length > 1) {
+        return StackActions.pop(state.routes.length);
+      }
+      return CommonActions.reset({
+        index: 0,
+        routes: [
+          {
+            name: ETabMarketRoutes.TabMarket,
+          },
+        ],
+      });
+    });
+  }, [navigation]);
path: packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/MarketDetailHeader.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider extracting navigation logic.**

The `popPage` function handles complex navigation logic. Consider moving this to a separate hook for better reusability and testing.

```diff
+// In a separate hook file
+export function usePopToRootOrReset(resetRouteName: string) {
+  const navigation = useAppNavigation();
+  
+  return useCallback(() => {
+    navigation.dispatch((state: StackNavigationState<any>) => {
+      if (state.routes.length > 1) {
+        return StackActions.pop(state.routes.length);
+      }
+      return CommonActions.reset({
+        index: 0,
+        routes: [{ name: resetRouteName }],
+      });
+    });
+  }, [navigation, resetRouteName]);
+}

// In MarketDetailHeader.tsx
-const popPage = useCallback(() => {
-  navigation.dispatch((state: StackNavigationState<any>) => {
-    if (state.routes.length > 1) {
-      return StackActions.pop(state.routes.length);
-    }
-    return CommonActions.reset({
-      index: 0,
-      routes: [
-        {
-          name: ETabMarketRoutes.TabMarket,
-        },
-      ],
-    });
-  });
-}, [navigation]);
+const popPage = usePopToRootOrReset(ETabMarketRoutes.TabMarket);
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion
  const popPage = usePopToRootOrReset(ETabMarketRoutes.TabMarket);
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In
packages/kit/src/views/Market/MarketDetailV2/components/MarketDetailHeader/MarketDetailHeader.tsx
around lines 91 to 105, the popPage function contains complex navigation logic
that should be extracted into a separate custom hook. Create a new hook, for
example usePopPageNavigation, that encapsulates this logic and returns the
popPage function. Replace the current inline popPage definition with a call to
this new hook to improve reusability and make the navigation logic easier to
test independently.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T01:17:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+import { useCallback, useEffect, useState } from 'react';
+
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+import backgroundApiProxy from '../../../background/instance/backgroundApiProxy';
+
+export function useMarketDetailData(coinGeckoId: string) {
+  const [tokenDetail, setTokenDetail] = useState<
+    IMarketTokenDetail | undefined
+  >(undefined);
+  const [isRefreshing, setIsRefreshing] = useState(false);
+
+  const fetchMarketTokenDetail = useCallback(async () => {
+    const response =
+      await backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(
+        coinGeckoId,
+      );
+    setTokenDetail(response);
+  }, [coinGeckoId]);
path: packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add error handling to fetch function.**

The fetch function should handle potential API errors to prevent the app from crashing if the request fails.

```diff
const fetchMarketTokenDetail = useCallback(async () => {
+  try {
    const response =
      await backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(
        coinGeckoId,
      );
    setTokenDetail(response);
+  } catch (error) {
+    console.error('Failed to fetch market token detail:', error);
+    // Consider setting an error state here or showing a toast notification
+  }
}, [coinGeckoId]);
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion
  const fetchMarketTokenDetail = useCallback(async () => {
    try {
      const response =
        await backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(
          coinGeckoId,
        );
      setTokenDetail(response);
    } catch (error) {
      console.error('Failed to fetch market token detail:', error);
      // Consider setting an error state here or showing a toast notification
    }
  }, [coinGeckoId]);
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts around
lines 13 to 19, the fetchMarketTokenDetail function lacks error handling for the
API call. Wrap the await call to
backgroundApiProxy.serviceMarket.fetchMarketTokenDetail in a try-catch block,
and in the catch block handle any errors gracefully, such as logging the error
or setting an error state, to prevent the app from crashing if the request
fails.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T01:17:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+import { useCallback, useEffect, useState } from 'react';
+
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+import backgroundApiProxy from '../../../background/instance/backgroundApiProxy';
+
+export function useMarketDetailData(coinGeckoId: string) {
+  const [tokenDetail, setTokenDetail] = useState<
+    IMarketTokenDetail | undefined
+  >(undefined);
+  const [isRefreshing, setIsRefreshing] = useState(false);
+
+  const fetchMarketTokenDetail = useCallback(async () => {
+    const response =
+      await backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(
+        coinGeckoId,
+      );
+    setTokenDetail(response);
+  }, [coinGeckoId]);
+
+  useEffect(() => {
+    void fetchMarketTokenDetail();
+  }, [fetchMarketTokenDetail]);
path: packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider adding loading state.**

The hook only tracks refresh state but not initial loading. Add a loading state for better UX during initial fetch.

```diff
+const [isLoading, setIsLoading] = useState(true);

const fetchMarketTokenDetail = useCallback(async () => {
+  if (!isRefreshing) setIsLoading(true);
  try {
    const response =
      await backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(
        coinGeckoId,
      );
    setTokenDetail(response);
  } catch (error) {
    console.error('Failed to fetch market token detail:', error);
  } finally {
+    setIsLoading(false);
  }
}, [coinGeckoId, isRefreshing]);

// Include isLoading in the return object
return {
  tokenDetail,
  fetchMarketTokenDetail,
  isRefreshing,
  onRefresh,
+  isLoading,
};
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion
import { useState, useCallback, useEffect } from 'react';
// … other imports …

export function useMarketDetailData(coinGeckoId: string) {
  const [tokenDetail, setTokenDetail] = useState<TokenDetailType | null>(null);
  const [isRefreshing, setIsRefreshing] = useState(false);
+ const [isLoading, setIsLoading] = useState(true);

  const fetchMarketTokenDetail = useCallback(async () => {
+   if (!isRefreshing) {
+     setIsLoading(true);
+   }
    try {
      const response =
        await backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(
          coinGeckoId,
        );
      setTokenDetail(response);
    } catch (error) {
      console.error('Failed to fetch market token detail:', error);
    } finally {
      setIsRefreshing(false);
+     setIsLoading(false);
    }
  }, [coinGeckoId, isRefreshing]);

  useEffect(() => {
    void fetchMarketTokenDetail();
  }, [fetchMarketTokenDetail]);

  return {
    tokenDetail,
    fetchMarketTokenDetail,
    isRefreshing,
    onRefresh,
+   isLoading,
  };
}
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts around
lines 21 to 23, the useEffect calls fetchMarketTokenDetail without tracking an
initial loading state, only refresh state is tracked. Add a loading state
variable to indicate when the initial fetch is in progress, set it to true
before calling fetchMarketTokenDetail and set it to false after the fetch
completes. Update the component to use this loading state to improve UX during
the initial data load.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T01:17:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,72 @@
+import { Skeleton, View, XStack, YStack } from '@onekeyhq/components';
+
+function SkeletonHeader() {
+  return (
+    <YStack>
+      <Skeleton w="$24" h="$4" />
+      <View pt="$5" pb="$3.5">
+        <Skeleton w="$40" h="$7" />
+      </View>
+      <Skeleton w="$24" h="$3" />
+    </YStack>
+  );
+}
+
+function SkeletonHeaderOverItemItem() {
+  return (
+    <YStack gap="$2" flexGrow={1} flexBasis={0}>
+      <Skeleton w="$10" h="$3" />
+      <Skeleton w="$24" h="$3" />
+    </YStack>
+  );
+}
path: packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeaderSkeleton.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Name is awkward and stutters**

`SkeletonHeaderOverItemItem` repeats “Item”.  
A concise name like `SkeletonHeaderOverItem` is clearer.

  
```diff
-function SkeletonHeaderOverItemItem() {
+function SkeletonHeaderOverItem() {
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion
function SkeletonHeaderOverItem() {
  return (
    <YStack gap="$2" flexGrow={1} flexBasis={0}>
      <Skeleton w="$10" h="$3" />
      <Skeleton w="$24" h="$3" />
    </YStack>
  );
}
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In
packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeaderSkeleton.tsx
around lines 15 to 22, rename the function SkeletonHeaderOverItemItem to
SkeletonHeaderOverItem to remove the redundant repetition of "Item" and improve
clarity. Update all references to this function accordingly to maintain
consistency.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T01:17:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,72 @@
+import { Skeleton, View, XStack, YStack } from '@onekeyhq/components';
+
+function SkeletonHeader() {
+  return (
+    <YStack>
+      <Skeleton w="$24" h="$4" />
+      <View pt="$5" pb="$3.5">
+        <Skeleton w="$40" h="$7" />
+      </View>
+      <Skeleton w="$24" h="$3" />
+    </YStack>
+  );
+}
+
+function SkeletonHeaderOverItemItem() {
+  return (
+    <YStack gap="$2" flexGrow={1} flexBasis={0}>
+      <Skeleton w="$10" h="$3" />
+      <Skeleton w="$24" h="$3" />
+    </YStack>
+  );
+}
+
+interface ITokenDetailHeaderSkeletonProps {
+  gtMd: boolean;
+}
+
+export function TokenDetailHeaderSkeleton({
+  gtMd,
+}: ITokenDetailHeaderSkeletonProps) {
+  return (
+    <YStack px="$5">
+      {gtMd ? (
+        <YStack gap="$12" width={392}>
+          <SkeletonHeader />
+          <YStack gap="$3">
+            <Skeleton w={252} h="$3" />
+          </YStack>
+          <YStack gap="$6">
+            <XStack>
+              <SkeletonHeaderOverItemItem />
+              <SkeletonHeaderOverItemItem />
+            </XStack>
+            <XStack>
+              <SkeletonHeaderOverItemItem />
+              <SkeletonHeaderOverItemItem />
+            </XStack>
+            <XStack>
+              <SkeletonHeaderOverItemItem />
+              <SkeletonHeaderOverItemItem />
+            </XStack>
+          </YStack>
+          <YStack gap="$6">
+            <Skeleton w="$10" h="$3" />
+            <Skeleton w={252} h="$3" />
+            <Skeleton w={252} h="$3" />
+            <Skeleton w={252} h="$3" />
+          </YStack>
+        </YStack>
+      ) : (
+        <YStack gap="$6" pt="$1">
+          <SkeletonHeader />
+          <XStack>
+            <SkeletonHeaderOverItemItem />
+            <SkeletonHeaderOverItemItem />
+            <SkeletonHeaderOverItemItem />
+          </XStack>
+        </YStack>
+      )}
+    </YStack>
+  );
+}
path: packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeaderSkeleton.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Unnecessary re-renders**

`TokenDetailHeaderSkeleton` re-computes its whole JSX tree on every parent render.  
Wrap it with `React.memo` (or export `memo(TokenDetailHeaderSkeleton)`) to avoid wasted cycles while loading.

  
```diff
-export function TokenDetailHeaderSkeleton({ gtMd }: ITokenDetailHeaderSkeletonProps) {
+export const TokenDetailHeaderSkeleton = React.memo(function TokenDetailHeaderSkeleton({
+  gtMd,
+}: ITokenDetailHeaderSkeletonProps) {
   …
-}
+});
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion
export const TokenDetailHeaderSkeleton = React.memo(function TokenDetailHeaderSkeleton({
  gtMd,
}: ITokenDetailHeaderSkeletonProps) {
  return (
    <YStack px="$5">
      {gtMd ? (
        <YStack gap="$12" width={392}>
          <SkeletonHeader />
          <YStack gap="$3">
            <Skeleton w={252} h="$3" />
          </YStack>
          <YStack gap="$6">
            <XStack>
              <SkeletonHeaderOverItemItem />
              <SkeletonHeaderOverItemItem />
            </XStack>
            <XStack>
              <SkeletonHeaderOverItemItem />
              <SkeletonHeaderOverItemItem />
            </XStack>
            <XStack>
              <SkeletonHeaderOverItemItem />
              <SkeletonHeaderOverItemItem />
            </XStack>
          </YStack>
          <YStack gap="$6">
            <Skeleton w="$10" h="$3" />
            <Skeleton w={252} h="$3" />
            <Skeleton w={252} h="$3" />
            <Skeleton w={252} h="$3" />
          </YStack>
        </YStack>
      ) : (
        <YStack gap="$6" pt="$1">
          <SkeletonHeader />
          <XStack>
            <SkeletonHeaderOverItemItem />
            <SkeletonHeaderOverItemItem />
            <SkeletonHeaderOverItemItem />
          </XStack>
        </YStack>
      )}
    </YStack>
  );
});
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In
packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeaderSkeleton.tsx
around lines 28 to 72, the TokenDetailHeaderSkeleton component re-renders
unnecessarily on every parent render. To fix this, wrap the
TokenDetailHeaderSkeleton function component with React.memo before exporting
it, which will memoize the component and prevent redundant re-renders when props
have not changed.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T01:17:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,72 @@
+import { Skeleton, View, XStack, YStack } from '@onekeyhq/components';
+
+function SkeletonHeader() {
+  return (
+    <YStack>
+      <Skeleton w="$24" h="$4" />
+      <View pt="$5" pb="$3.5">
+        <Skeleton w="$40" h="$7" />
+      </View>
+      <Skeleton w="$24" h="$3" />
+    </YStack>
+  );
+}
+
+function SkeletonHeaderOverItemItem() {
+  return (
+    <YStack gap="$2" flexGrow={1} flexBasis={0}>
+      <Skeleton w="$10" h="$3" />
+      <Skeleton w="$24" h="$3" />
+    </YStack>
+  );
+}
+
+interface ITokenDetailHeaderSkeletonProps {
+  gtMd: boolean;
+}
+
+export function TokenDetailHeaderSkeleton({
+  gtMd,
+}: ITokenDetailHeaderSkeletonProps) {
+  return (
+    <YStack px="$5">
+      {gtMd ? (
+        <YStack gap="$12" width={392}>
+          <SkeletonHeader />
+          <YStack gap="$3">
+            <Skeleton w={252} h="$3" />
+          </YStack>
+          <YStack gap="$6">
+            <XStack>
+              <SkeletonHeaderOverItemItem />
+              <SkeletonHeaderOverItemItem />
+            </XStack>
+            <XStack>
+              <SkeletonHeaderOverItemItem />
+              <SkeletonHeaderOverItemItem />
+            </XStack>
+            <XStack>
+              <SkeletonHeaderOverItemItem />
+              <SkeletonHeaderOverItemItem />
+            </XStack>
+          </YStack>
+          <YStack gap="$6">
+            <Skeleton w="$10" h="$3" />
+            <Skeleton w={252} h="$3" />
+            <Skeleton w={252} h="$3" />
+            <Skeleton w={252} h="$3" />
+          </YStack>
path: packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeaderSkeleton.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Mixed sizing units hurt consistency**

You mix raw numbers (`252`) and token strings (`"$3"`, `"$10"`).  
Stick to one convention so designers and devs know exactly which scale you’re on.

  
Example:  

```diff
-<Skeleton w={252} h="$3" />
+<Skeleton w="$64" h="$3" />   // or another design-token width
```

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In
packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeaderSkeleton.tsx
between lines 37 and 58, the sizing units for width and height are
inconsistently mixed between raw numbers (e.g., 252) and token strings (e.g.,
"$3", "$10"). To fix this, convert all raw numeric sizes to use the token string
format consistent with the design system scale, ensuring uniformity and clarity
for designers and developers.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T01:17:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,91 @@
+import {
+  EPageType,
+  SizableText,
+  XStack,
+  YStack,
+  useMedia,
+  usePageType,
+} from '@onekeyhq/components';
+import backgroundApiProxy from '@onekeyhq/kit/src/background/instance/backgroundApiProxy';
+import { usePromiseResult } from '@onekeyhq/kit/src/hooks/usePromiseResult';
+import { useActiveAccount } from '@onekeyhq/kit/src/states/jotai/contexts/accountSelector';
+import { MarketDetailOverview } from '@onekeyhq/kit/src/views/Market/components/MarketDetailOverview';
+import { MarketStar } from '@onekeyhq/kit/src/views/Market/components/MarketStar';
+import { MarketTokenPrice } from '@onekeyhq/kit/src/views/Market/components/MarketTokenPrice';
+import { MarketTradeButton } from '@onekeyhq/kit/src/views/Market/components/MarketTradeButton';
+import { PriceChangePercentage } from '@onekeyhq/kit/src/views/Market/components/PriceChangePercentage';
+import { EWatchlistFrom } from '@onekeyhq/shared/src/logger/scopes/market/scenes/token';
+import timerUtils from '@onekeyhq/shared/src/utils/timerUtils';
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+export function TokenDetailHeader({
+  coinGeckoId,
+  token: responseToken,
+}: {
+  coinGeckoId: string;
+  token: IMarketTokenDetail;
+}) {
+  const { gtMd: gtMdMedia } = useMedia();
+
+  const pageType = usePageType();
+
+  const {
+    activeAccount: { wallet },
+  } = useActiveAccount({
+    num: 0,
+  });
+
+  const gtMd = pageType === EPageType.modal ? false : gtMdMedia;
+
+  const { result: token } = usePromiseResult(
+    () => backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(coinGeckoId),
+    [coinGeckoId],
+    {
+      pollingInterval: timerUtils.getTimeDurationMs({ seconds: 45 }),
+      initResult: responseToken,
+    },
+  );
path: packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeader.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Silent fetch failures**

`usePromiseResult` swallows rejections by default.  If the network call fails, the UI freezes on stale data with no hint.  
Consider passing an `onError` callback to surface an error toast or retry option.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In
packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeader.tsx
around lines 40 to 47, the usePromiseResult hook currently swallows fetch errors
silently, causing the UI to freeze on stale data without any user feedback. To
fix this, add an onError callback to the usePromiseResult options that handles
fetch failures by showing an error toast or providing a retry mechanism,
ensuring users are informed of network issues.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T01:17:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,91 @@
+import {
+  EPageType,
+  SizableText,
+  XStack,
+  YStack,
+  useMedia,
+  usePageType,
+} from '@onekeyhq/components';
+import backgroundApiProxy from '@onekeyhq/kit/src/background/instance/backgroundApiProxy';
+import { usePromiseResult } from '@onekeyhq/kit/src/hooks/usePromiseResult';
+import { useActiveAccount } from '@onekeyhq/kit/src/states/jotai/contexts/accountSelector';
+import { MarketDetailOverview } from '@onekeyhq/kit/src/views/Market/components/MarketDetailOverview';
+import { MarketStar } from '@onekeyhq/kit/src/views/Market/components/MarketStar';
+import { MarketTokenPrice } from '@onekeyhq/kit/src/views/Market/components/MarketTokenPrice';
+import { MarketTradeButton } from '@onekeyhq/kit/src/views/Market/components/MarketTradeButton';
+import { PriceChangePercentage } from '@onekeyhq/kit/src/views/Market/components/PriceChangePercentage';
+import { EWatchlistFrom } from '@onekeyhq/shared/src/logger/scopes/market/scenes/token';
+import timerUtils from '@onekeyhq/shared/src/utils/timerUtils';
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+export function TokenDetailHeader({
+  coinGeckoId,
+  token: responseToken,
+}: {
+  coinGeckoId: string;
+  token: IMarketTokenDetail;
+}) {
+  const { gtMd: gtMdMedia } = useMedia();
+
+  const pageType = usePageType();
+
+  const {
+    activeAccount: { wallet },
+  } = useActiveAccount({
+    num: 0,
+  });
+
+  const gtMd = pageType === EPageType.modal ? false : gtMdMedia;
+
+  const { result: token } = usePromiseResult(
+    () => backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(coinGeckoId),
+    [coinGeckoId],
+    {
+      pollingInterval: timerUtils.getTimeDurationMs({ seconds: 45 }),
+      initResult: responseToken,
+    },
+  );
+  const {
+    name,
+    symbol,
+    stats: { performance, currentPrice, lastUpdated },
+  } = token;
+  return (
+    <YStack
+      bg="$green4"
+      px="$5"
+      $md={{ minHeight: 150 }}
+      {...(pageType === EPageType.modal ? { minHeight: 150 } : null)}
+    >
+      <YStack flex={1}>
+        <SizableText size="$headingMd" color="$textSubdued">
+          {name}
+        </SizableText>
+        <XStack ai="center" jc="space-between" pt="$2">
+          <MarketTokenPrice
+            size="$heading3xl"
+            price={currentPrice}
+            tokenName={name}
+            tokenSymbol={symbol}
+            lastUpdated={lastUpdated}
+          />
+          <MarketStar
+            coingeckoId={coinGeckoId}
+            mr="$-2"
+            size="medium"
+            from={EWatchlistFrom.details}
+          />
path: packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeader.tsx
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Hard-coded margin hack**

`mr="$-2"` on `MarketStar` feels like a spacing hack that breaks if icon size changes.  
Prefer adjusting the parent layout or adding a proper prop in `MarketStar`.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In
packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeader.tsx
around lines 73 to 77, the margin-right property `mr="$-2"` on the MarketStar
component is a hard-coded spacing hack that may break if the icon size changes.
To fix this, remove the hard-coded margin and instead adjust the parent
container's layout or add a dedicated spacing prop to the MarketStar component
to handle spacing more robustly and responsively.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T01:17:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,91 @@
+import {
+  EPageType,
+  SizableText,
+  XStack,
+  YStack,
+  useMedia,
+  usePageType,
+} from '@onekeyhq/components';
+import backgroundApiProxy from '@onekeyhq/kit/src/background/instance/backgroundApiProxy';
+import { usePromiseResult } from '@onekeyhq/kit/src/hooks/usePromiseResult';
+import { useActiveAccount } from '@onekeyhq/kit/src/states/jotai/contexts/accountSelector';
+import { MarketDetailOverview } from '@onekeyhq/kit/src/views/Market/components/MarketDetailOverview';
+import { MarketStar } from '@onekeyhq/kit/src/views/Market/components/MarketStar';
+import { MarketTokenPrice } from '@onekeyhq/kit/src/views/Market/components/MarketTokenPrice';
+import { MarketTradeButton } from '@onekeyhq/kit/src/views/Market/components/MarketTradeButton';
+import { PriceChangePercentage } from '@onekeyhq/kit/src/views/Market/components/PriceChangePercentage';
+import { EWatchlistFrom } from '@onekeyhq/shared/src/logger/scopes/market/scenes/token';
+import timerUtils from '@onekeyhq/shared/src/utils/timerUtils';
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+export function TokenDetailHeader({
+  coinGeckoId,
+  token: responseToken,
+}: {
+  coinGeckoId: string;
+  token: IMarketTokenDetail;
+}) {
+  const { gtMd: gtMdMedia } = useMedia();
+
+  const pageType = usePageType();
+
+  const {
+    activeAccount: { wallet },
+  } = useActiveAccount({
+    num: 0,
+  });
+
+  const gtMd = pageType === EPageType.modal ? false : gtMdMedia;
+
+  const { result: token } = usePromiseResult(
+    () => backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(coinGeckoId),
+    [coinGeckoId],
+    {
+      pollingInterval: timerUtils.getTimeDurationMs({ seconds: 45 }),
+      initResult: responseToken,
+    },
+  );
+  const {
path: packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeader.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Possible crash when API returns undefined fields**

`token` is assumed to always contain `stats`, but the first fetch could return `undefined` or a transient error.  
A destructuring like

```ts
const {
  stats: { performance, currentPrice, lastUpdated },
} = token;
```

will throw if `stats` is missing.

  
Guard first:

```diff
-const { name, symbol, stats: { performance, currentPrice, lastUpdated } } = token;
+if (!token?.stats) return null; // or show a fallback
+const {
+  name,
+  symbol,
+  stats: { performance, currentPrice, lastUpdated },
+} = token;
```

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In
packages/kit/src/views/Market/MarketDetailV2/components/TokenDetailHeader/TokenDetailHeader.tsx
around lines 38 to 48, the code destructures the stats property from token
without checking if token or token.stats is defined, which can cause a crash if
the API returns undefined or incomplete data. To fix this, add a guard to ensure
token and token.stats exist before destructuring, using optional chaining or
conditional checks to safely access performance, currentPrice, and lastUpdated.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T01:17:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,146 +1,35 @@
-import { useCallback, useEffect, useMemo, useState } from 'react';
-
-import { CommonActions, StackActions } from '@react-navigation/native';
+import { useMemo } from 'react';
 
 import type { IPageScreenProps } from '@onekeyhq/components';
 import {
   EPageType,
-  HeaderIconButton,
-  NavBackButton,
   Page,
   ScrollView,
-  SizableText,
-  Skeleton,
-  View,
   XStack,
   YStack,
   useDeferredPromise,
   useMedia,
   usePageType,
-  useShare,
 } from '@onekeyhq/components';
 import { EJotaiContextStoreNames } from '@onekeyhq/kit-bg/src/states/jotai/atoms';
-import { EOneKeyDeepLinkPath } from '@onekeyhq/shared/src/consts/deeplinkConsts';
-import { EWatchlistFrom } from '@onekeyhq/shared/src/logger/scopes/market/scenes/token';
-import platformEnv from '@onekeyhq/shared/src/platformEnv';
-import { ETabMarketRoutes } from '@onekeyhq/shared/src/routes';
-import type { ITabMarketParamList } from '@onekeyhq/shared/src/routes';
-import timerUtils from '@onekeyhq/shared/src/utils/timerUtils';
-import uriUtils from '@onekeyhq/shared/src/utils/uriUtils';
+import type {
+  ETabMarketRoutes,
+  ITabMarketParamList,
+} from '@onekeyhq/shared/src/routes';
 import { EAccountSelectorSceneName } from '@onekeyhq/shared/types';
-import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
 
-import backgroundApiProxy from '../../../background/instance/backgroundApiProxy';
 import { AccountSelectorProviderMirror } from '../../../components/AccountSelector';
-import { OpenInAppButton } from '../../../components/OpenInAppButton';
-import useAppNavigation from '../../../hooks/useAppNavigation';
-import { usePromiseResult } from '../../../hooks/usePromiseResult';
-import { useActiveAccount } from '../../../states/jotai/contexts/accountSelector';
-import { MarketDetailOverview } from '../components/MarketDetailOverview';
-import { MarketHomeHeaderSearchBar } from '../components/MarketHomeHeaderSearchBar';
-import { MarketStar } from '../components/MarketStar';
-import { MarketTokenIcon } from '../components/MarketTokenIcon';
-import { MarketTokenPrice } from '../components/MarketTokenPrice';
-import { MarketTradeButton } from '../components/MarketTradeButton';
-import { PriceChangePercentage } from '../components/PriceChangePercentage';
 import { TokenDetailTabs } from '../components/TokenDetailTabs';
-import { TokenPriceChart } from '../components/TokenPriceChart';
-import { buildMarketFullUrl } from '../marketUtils';
 import { MarketWatchListProviderMirror } from '../MarketWatchListProviderMirror';
 
-import { SwapPanel } from './components/SwapPanel';
-
-function TokenDetailHeader({
-  coinGeckoId,
-  token: responseToken,
-}: {
-  coinGeckoId: string;
-  token: IMarketTokenDetail;
-}) {
-  const { gtMd: gtMdMedia } = useMedia();
-
-  const pageType = usePageType();
-
-  const {
-    activeAccount: { wallet },
-  } = useActiveAccount({
-    num: 0,
-  });
-
-  const gtMd = pageType === EPageType.modal ? false : gtMdMedia;
-
-  const { result: token } = usePromiseResult(
-    () => backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(coinGeckoId),
-    [coinGeckoId],
-    {
-      pollingInterval: timerUtils.getTimeDurationMs({ seconds: 45 }),
-      initResult: responseToken,
-    },
-  );
-  const {
-    name,
-    symbol,
-    stats: { performance, currentPrice, lastUpdated },
-  } = token;
-  return (
-    <YStack
-      px="$5"
-      $md={{ minHeight: 150 }}
-      {...(pageType === EPageType.modal ? { minHeight: 150 } : null)}
-    >
-      <YStack flex={1}>
-        <SizableText size="$headingMd" color="$textSubdued">
-          {name}
-        </SizableText>
-        <XStack ai="center" jc="space-between" pt="$2">
-          <MarketTokenPrice
-            size="$heading3xl"
-            price={currentPrice}
-            tokenName={name}
-            tokenSymbol={symbol}
-            lastUpdated={lastUpdated}
-          />
-          <MarketStar
-            coingeckoId={coinGeckoId}
-            mr="$-2"
-            size="medium"
-            from={EWatchlistFrom.details}
-          />
-        </XStack>
-        <PriceChangePercentage pt="$0.5" width="100%">
-          {performance.priceChangePercentage24h}
-        </PriceChangePercentage>
-      </YStack>
-      <MarketTradeButton
-        coinGeckoId={coinGeckoId}
-        token={token}
-        wallet={wallet}
-      />
-      {gtMd ? <MarketDetailOverview token={token} /> : null}
-    </YStack>
-  );
-}
-
-function SkeletonHeader() {
-  return (
-    <YStack>
-      <Skeleton w="$24" h="$4" />
-      <View pt="$5" pb="$3.5">
-        <Skeleton w="$40" h="$7" />
-      </View>
-      <Skeleton w="$24" h="$3" />
-    </YStack>
-  );
-}
-
-function SkeletonHeaderOverItemItem() {
-  return (
-    <YStack gap="$2" flexGrow={1} flexBasis={0}>
-      <Skeleton w="$10" h="$3" />
-      <Skeleton w="$24" h="$3" />
-    </YStack>
-  );
-}
+import {
+  TokenDetailHeader as LegacyTokenDetailHeader,
+  MarketDetailHeader,
+  TokenPriceChart as NewTokenPriceChart,
+  SwapPanel,
+  TokenDetailHeaderSkeleton,
+} from './components';
path: packages/kit/src/views/Market/MarketDetailV2/MarketDetailV2.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**“Legacy” / “New” prefixes create cognitive load**

The imports rename components to `LegacyTokenDetailHeader` and `NewTokenPriceChart`.  
They are actually the current, recommended components—there is no legacy or new inside this file.  
Use their real names to avoid confusion for future maintainers.

  
```diff
-import {
-  TokenDetailHeader as LegacyTokenDetailHeader,
-  MarketDetailHeader,
-  TokenPriceChart as NewTokenPriceChart,
+import {
+  TokenDetailHeader,
+  MarketDetailHeader,
+  TokenPriceChart,
```
…and update usages below.  
Clean names read better and remove the “is this still used?” question.


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion
import {
  TokenDetailHeader,
  MarketDetailHeader,
  TokenPriceChart,
  SwapPanel,
  TokenDetailHeaderSkeleton,
} from './components';
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/kit/src/views/Market/MarketDetailV2/MarketDetailV2.tsx around lines
25 to 31, the imports rename components with "Legacy" and "New" prefixes which
cause confusion. Remove these prefixes and import the components using their
original names as exported. Then update all usages of these components in the
file to use the original names to improve clarity and maintainability.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T01:17:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,37 @@
+import { useCallback, useEffect, useState } from 'react';
+
+import type { IMarketTokenDetail } from '@onekeyhq/shared/types/market';
+
+import backgroundApiProxy from '../../../background/instance/backgroundApiProxy';
+
+export function useMarketDetailData(coinGeckoId: string) {
+  const [tokenDetail, setTokenDetail] = useState<
+    IMarketTokenDetail | undefined
+  >(undefined);
+  const [isRefreshing, setIsRefreshing] = useState(false);
+
+  const fetchMarketTokenDetail = useCallback(async () => {
+    const response =
+      await backgroundApiProxy.serviceMarket.fetchMarketTokenDetail(
+        coinGeckoId,
+      );
+    setTokenDetail(response);
+  }, [coinGeckoId]);
path: packages/kit/src/views/Market/MarketDetailV2/useMarketDetailData.ts
user: login: limichange
body: ok 可以。
created_at: 2025-05-15T01:42:42Z
author_association: CONTRIBUTOR

PR comments:
user: login: codesandbox[bot]
created_at: 2025-05-15T01:05:58Z
body: #### Review or Edit in CodeSandbox

Open the branch in <a href="https://codesandbox.io/p/github/OneKeyHQ/app-monorepo/market-detail-page-3?mode=review&utm_source=gh_app">Web Editor</a> &bull; <a href="https://codesandbox.io/p/vscode?owner=OneKeyHQ&repo=app-monorepo&branch=market-detail-page-3&utm_source=gh_app">VS Code</a> &bull; <a href="https://codesandbox.io/p/vscode?owner=OneKeyHQ&repo=app-monorepo&branch=market-detail-page-3&insiders=true&utm_source=gh_app">Insiders</a><br>
Open <a href="https://codesandbox.io/p/devtool/preview/OneKeyHQ/app-monorepo/market-detail-page-3?task=preview&port=51423&redirect=true&utm_source=gh_app">Preview</a>

<!-- open-in-codesandbox:complete -->

author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-05-15T01:06:03Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update refactors the market detail view and header components. It introduces new hooks and modular components for headers, token details, and charts. The TabPageHeader now supports custom header items via new props. Several new files provide skeletons, wrappers, and re-exports for easier component management.

## Changes

| File(s)                                                                                          | Change Summary                                                                                                                                                                                                                   |
|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| TabPageHeader/HeaderLeft.tsx, HeaderRight.tsx, type.ts, index.native.tsx, index.tsx              | Added optional props for custom left/right header items; exported new components; updated hooks and props to support customization.                                                        |
| MarketDetailV2/MarketDetailV2.tsx                                                                | Refactored to use new hooks and modular header components; removed inline state and logic; simplified control flow.                                                                        |
| MarketDetailV2/components/MarketDetailHeader/MarketDetailHeader.tsx, index.ts                    | Added new header component for market details; new index file for re-export.                                                                                                               |
| MarketDetailV2/components/TokenDetailHeader/TokenDetailHeader.tsx, TokenDetailHeaderSkeleton.tsx, index.ts | Added detailed token header and skeleton components; index file re-exports both.                                                                                                           |
| MarketDetailV2/components/TokenPriceChart/TokenPriceChart.tsx, index.ts                          | Added wrapper for token price chart; index file re-exports.                                                                                                                                |
| MarketDetailV2/components/index.ts                                                               | New index file re-exports all major market detail components for easier imports.                                                                                                           |
| MarketDetailV2/useMarketDetailData.ts                                                            | Added new hook to fetch and manage token detail state, including refresh logic.                                                                                                            |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant MarketDetailV2
    participant useMarketDetailData
    participant MarketDetailHeader
    participant TokenDetailHeader
    participant TokenPriceChart

    User->>MarketDetailV2: View market detail page
    MarketDetailV2->>useMarketDetailData: Fetch token detail by coinGeckoId
    useMarketDetailData-->>MarketDetailV2: Return tokenDetail, isRefreshing, onRefresh
    MarketDetailV2->>MarketDetailHeader: Pass tokenDetail, coinGeckoId, gtMd
    MarketDetailHeader->>TokenDetailHeader: Render with tokenDetail
    MarketDetailHeader->>TokenPriceChart: Render with coinGeckoId, tokenDetail
    User->>MarketDetailHeader: Interact with header (back, share, open in app)
```

```mermaid
sequenceDiagram
    participant ParentComponent
    participant TabPageHeader
    participant HeaderLeft
    participant HeaderRight

    ParentComponent->>TabPageHeader: Pass customHeaderLeftItems, customHeaderRightItems
    TabPageHeader->>HeaderLeft: Pass customHeaderLeftItems
    TabPageHeader->>HeaderRight: Pass customHeaderRightItems
    HeaderLeft-->>TabPageHeader: Render left header (custom if provided)
    HeaderRight-->>TabPageHeader: Render right header (custom if provided)
```

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

---

> [!NOTE]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=OneKeyHQ/app-monorepo&utm_content=7333):

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

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

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

### CodeRabbit Configuration File (`.coderabbit.yaml`)

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

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR