mirror of
https://github.com/zhigang1992/wallet.git
synced 2026-01-12 22:53:27 +08:00
fix: use routes over state for home tabs, closes #3518
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
import { useTabState } from '@app/store/ui/ui.hooks';
|
||||
|
||||
export function useHomeTabs() {
|
||||
const [activeTab, setActiveTab] = useTabState('HOME_TABS');
|
||||
const setActiveTabBalances = () => setActiveTab(0);
|
||||
const setActiveTabActivity = () => setActiveTab(1);
|
||||
return {
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
setActiveTabBalances,
|
||||
setActiveTabActivity,
|
||||
};
|
||||
}
|
||||
@@ -1,10 +1,3 @@
|
||||
import { QueryKey, hashQueryKey } from '@tanstack/react-query';
|
||||
import hash from 'object-hash';
|
||||
|
||||
export function makeLocalDataKey(params: QueryKey): string {
|
||||
return hash(hashQueryKey([params, VERSION]));
|
||||
}
|
||||
|
||||
// LocalStorage keys kept across sign-in/signout sessions
|
||||
const PERSISTENT_LOCAL_DATA: string[] = [];
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ function TabButton({ isActive, label, ...rest }: { isActive?: boolean; label: st
|
||||
borderRadius="10px"
|
||||
color={isActive ? color('text-title') : color('text-caption')}
|
||||
fontSize={1}
|
||||
textTransform="capitalize"
|
||||
fontWeight={isActive ? 500 : 400}
|
||||
as="button"
|
||||
_hover={!isActive ? { color: color('text-title') } : undefined}
|
||||
@@ -69,7 +70,7 @@ export function Tabs({
|
||||
{tabs.map((tab, index) => (
|
||||
<TabButton
|
||||
onClick={() => onTabClick(index)}
|
||||
data-testid={`tab-${tab.slug}`}
|
||||
data-testid={`tab-${tab.label}`}
|
||||
isActive={activeTab === index}
|
||||
key={tab.slug}
|
||||
label={tab.label}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Box, Stack, StackProps, Text } from '@stacks/ui';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
|
||||
import { Box, Stack, Text } from '@stacks/ui';
|
||||
import { HomePageSelectorsLegacy } from '@tests-legacy/page-objects/home.selectors';
|
||||
|
||||
import { useBtcAssetBalance } from '@app/common/hooks/balance/btc/use-btc-balance';
|
||||
@@ -20,11 +22,12 @@ import { PendingBrc20TransferList } from '../pending-brc-20-transfers/pending-br
|
||||
import { BitcoinFungibleTokenAssetList } from './components/bitcoin-fungible-tokens-asset-list';
|
||||
import { StacksFungibleTokenAssetList } from './components/stacks-fungible-token-asset-list';
|
||||
|
||||
interface BalancesListProps extends StackProps {
|
||||
interface BalancesListContextState {
|
||||
address: string;
|
||||
}
|
||||
|
||||
export function BalancesList({ address, ...props }: BalancesListProps) {
|
||||
export function BalancesList(): React.JSX.Element {
|
||||
const { address } = useOutletContext<BalancesListContextState>();
|
||||
const stacksFtAssetBalances = useStacksFungibleTokenAssetBalancesAnchoredWithMetadata(address);
|
||||
const isBitcoinEnabled = useConfigBitcoinEnabled();
|
||||
const btcAddress = useCurrentAccountNativeSegwitAddressIndexZero();
|
||||
@@ -50,12 +53,7 @@ export function BalancesList({ address, ...props }: BalancesListProps) {
|
||||
) : undefined;
|
||||
|
||||
return (
|
||||
<Stack
|
||||
pb="extra-loose"
|
||||
spacing="loose"
|
||||
data-testid={HomePageSelectorsLegacy.BalancesList}
|
||||
{...props}
|
||||
>
|
||||
<Stack pb="extra-loose" spacing="loose" data-testid={HomePageSelectorsLegacy.BalancesList}>
|
||||
{isBitcoinEnabled && (
|
||||
<CryptoCurrencyAssetItem
|
||||
assetBalance={btcAvailableAssetBalance}
|
||||
@@ -70,7 +68,7 @@ export function BalancesList({ address, ...props }: BalancesListProps) {
|
||||
address={address}
|
||||
additionalBalanceInfo={stxAdditionalBalanceInfo}
|
||||
additionalUsdBalanceInfo={stxAdditionalUsdBalanceInfo}
|
||||
icon={<StxAvatar {...props} />}
|
||||
icon={<StxAvatar />}
|
||||
/>
|
||||
<StacksFungibleTokenAssetList assetBalances={stacksFtAssetBalances} />
|
||||
{whenWallet({
|
||||
|
||||
@@ -16,7 +16,7 @@ interface ChooseFeeTabsProps extends StackProps {
|
||||
export function ChooseFeeTabs(props: ChooseFeeTabsProps) {
|
||||
const { feesList, customFee, ...rest } = props;
|
||||
const analytics = useAnalytics();
|
||||
|
||||
// TODO #4013: Refactor this to use routes for tabs like home-tabs
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
|
||||
const setActiveTabTracked = (index: number) => {
|
||||
|
||||
@@ -7,7 +7,6 @@ import { truncateMiddle } from '@stacks/ui-utils';
|
||||
import { RouteUrls } from '@shared/route-urls';
|
||||
|
||||
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
|
||||
import { useHomeTabs } from '@app/common/hooks/use-home-tabs';
|
||||
import { formatMoneyPadded } from '@app/common/money/format-money';
|
||||
import { delay } from '@app/common/utils';
|
||||
import { FormAddressDisplayer } from '@app/components/address-displayer/form-address-displayer';
|
||||
@@ -25,7 +24,6 @@ import { useGenerateRetrieveTaprootFundsTx } from './use-generate-retrieve-tapro
|
||||
|
||||
export function RetrieveTaprootToNativeSegwit() {
|
||||
const navigate = useNavigate();
|
||||
const { setActiveTabActivity } = useHomeTabs();
|
||||
const balance = useCurrentTaprootAccountBalance();
|
||||
const recipient = useCurrentAccountNativeSegwitAddressIndexZero();
|
||||
const uninscribedUtxos = useCurrentTaprootAccountUninscribedUtxos();
|
||||
@@ -41,8 +39,7 @@ export function RetrieveTaprootToNativeSegwit() {
|
||||
await delay(1200);
|
||||
toast.success('Transaction broadcasted succesfully');
|
||||
await delay(700);
|
||||
navigate(RouteUrls.Home);
|
||||
setActiveTabActivity();
|
||||
navigate(RouteUrls.Activity);
|
||||
void analytics.track('broadcast_retrieve_taproot_to_native_segwit');
|
||||
},
|
||||
onError(e) {
|
||||
|
||||
@@ -1,66 +1,51 @@
|
||||
import { Suspense } from 'react';
|
||||
import { Suspense, useCallback, useMemo } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
import { Box, Flex, SlideFade, Stack } from '@stacks/ui';
|
||||
import { Box, Stack } from '@stacks/ui';
|
||||
import type { StackProps } from '@stacks/ui';
|
||||
|
||||
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
|
||||
import { useHomeTabs } from '@app/common/hooks/use-home-tabs';
|
||||
import { RouteUrls } from '@shared/route-urls';
|
||||
|
||||
import { LoadingSpinner } from '@app/components/loading-spinner';
|
||||
import { Tabs } from '@app/components/tabs';
|
||||
|
||||
interface HomeTabsProps extends StackProps {
|
||||
balances: React.JSX.Element;
|
||||
activity: React.JSX.Element;
|
||||
children: React.JSX.Element;
|
||||
}
|
||||
// TODO #4013: Abstract this to generic RouteTab once choose-fee-tab updated
|
||||
export function HomeTabs({ children }: HomeTabsProps) {
|
||||
const navigate = useNavigate();
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const ANALYTICS_PATH = ['/balances', '/activity'];
|
||||
const tabs = useMemo(
|
||||
() => [
|
||||
{ slug: RouteUrls.Home, label: 'balances' },
|
||||
{ slug: RouteUrls.Activity, label: 'activity' },
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
export function HomeTabs(props: HomeTabsProps) {
|
||||
const { balances, activity, ...rest } = props;
|
||||
const analytics = useAnalytics();
|
||||
const getActiveTab = useCallback(
|
||||
() => tabs.findIndex(tab => tab.slug === pathname),
|
||||
[tabs, pathname]
|
||||
);
|
||||
|
||||
const { activeTab, setActiveTab } = useHomeTabs();
|
||||
|
||||
const setActiveTabTracked = (index: number) => {
|
||||
void analytics.page('view', ANALYTICS_PATH[index]);
|
||||
setActiveTab(index);
|
||||
};
|
||||
const setActiveTab = useCallback(
|
||||
(index: number) => navigate(tabs[index]?.slug),
|
||||
[navigate, tabs]
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack flexGrow={1} mt="loose" spacing="extra-loose" {...rest}>
|
||||
<Stack flexGrow={1} mt="loose" spacing="extra-loose">
|
||||
<Tabs
|
||||
tabs={[
|
||||
{ slug: 'balances', label: 'Balances' },
|
||||
{ slug: 'activity', label: 'Activity' },
|
||||
]}
|
||||
activeTab={activeTab}
|
||||
onTabClick={setActiveTabTracked}
|
||||
tabs={tabs}
|
||||
activeTab={getActiveTab()}
|
||||
onTabClick={setActiveTab}
|
||||
width={['100%', '193px']}
|
||||
/>
|
||||
<Flex position="relative" flexGrow={1}>
|
||||
{activeTab === 0 && (
|
||||
<Suspense fallback={<LoadingSpinner pb="72px" />}>
|
||||
<SlideFade in={activeTab === 0}>
|
||||
{styles => (
|
||||
<Box style={styles} width="100%">
|
||||
{balances}
|
||||
</Box>
|
||||
)}
|
||||
</SlideFade>
|
||||
</Suspense>
|
||||
)}
|
||||
{activeTab === 1 && (
|
||||
<Suspense fallback={<LoadingSpinner pb="72px" />}>
|
||||
<SlideFade in={activeTab === 1}>
|
||||
{styles => (
|
||||
<Box width="100%" style={styles}>
|
||||
{activity}
|
||||
</Box>
|
||||
)}
|
||||
</SlideFade>
|
||||
</Suspense>
|
||||
)}
|
||||
</Flex>
|
||||
<Suspense fallback={<LoadingSpinner pb="72px" />}>
|
||||
<Box width="100%">{children}</Box>
|
||||
</Suspense>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import { useOnboardingState } from '@app/common/hooks/auth/use-onboarding-state'
|
||||
import { useOnMount } from '@app/common/hooks/use-on-mount';
|
||||
import { useRouteHeader } from '@app/common/hooks/use-route-header';
|
||||
import { Header } from '@app/components/header';
|
||||
import { ActivityList } from '@app/features/activity-list/activity-list';
|
||||
import { BalancesList } from '@app/features/balances-list/balances-list';
|
||||
import { InAppMessages } from '@app/features/hiro-messages/in-app-messages';
|
||||
import { SuggestedFirstSteps } from '@app/features/suggested-first-steps/suggested-first-steps';
|
||||
import { HomeActions } from '@app/pages/home/components/home-actions';
|
||||
@@ -48,8 +46,9 @@ function HomeContainer({ account }: HomeContainerProps) {
|
||||
currentAccount={<CurrentAccount />}
|
||||
actions={<HomeActions />}
|
||||
>
|
||||
<HomeTabs balances={<BalancesList address={account.address} />} activity={<ActivityList />} />
|
||||
<Outlet />
|
||||
<HomeTabs>
|
||||
<Outlet context={{ address: account.address }} />
|
||||
</HomeTabs>
|
||||
</HomeLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import { RouteUrls } from '@shared/route-urls';
|
||||
|
||||
import { BroadcastErrorDrawer } from '@app/components/broadcast-error-drawer/broadcast-error-drawer';
|
||||
import { LoadingSpinner } from '@app/components/loading-spinner';
|
||||
import { ActivityList } from '@app/features/activity-list/activity-list';
|
||||
import { BalancesList } from '@app/features/balances-list/balances-list';
|
||||
import { Container } from '@app/features/container/container';
|
||||
import { EditNonceDrawer } from '@app/features/edit-nonce-drawer/edit-nonce-drawer';
|
||||
import { IncreaseFeeDrawer } from '@app/features/increase-fee-drawer/increase-fee-drawer';
|
||||
@@ -97,6 +99,9 @@ function useAppRoutes() {
|
||||
</AccountGate>
|
||||
}
|
||||
>
|
||||
<Route index element={<BalancesList />} />
|
||||
<Route path={RouteUrls.Activity} element={<ActivityList />} />
|
||||
|
||||
<Route path={RouteUrls.RetriveTaprootFunds} element={<RetrieveTaprootToNativeSegwit />} />
|
||||
|
||||
<Route path={RouteUrls.IncreaseFee} element={<IncreaseFeeDrawer />}>
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
showSettingsStore,
|
||||
showSwitchAccountsState,
|
||||
showTxSettingsCallback,
|
||||
tabState,
|
||||
} from './ui';
|
||||
|
||||
export function useShowHighFeeConfirmationState() {
|
||||
@@ -31,10 +30,6 @@ export function useLoadingState(key: string) {
|
||||
return useAtom(loadingState(key));
|
||||
}
|
||||
|
||||
export function useTabState(key: string) {
|
||||
return useAtom(tabState(key));
|
||||
}
|
||||
|
||||
export function useErrorStackTraceState() {
|
||||
return useAtom(errorStackTraceState);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import { atom } from 'jotai';
|
||||
import { atomFamily, atomWithStorage } from 'jotai/utils';
|
||||
|
||||
import { makeLocalDataKey } from '@app/common/store-utils';
|
||||
|
||||
export const tabState = atomFamily(param => {
|
||||
const anAtom = atomWithStorage<number>(makeLocalDataKey(['HOME_TABS', param]), 0);
|
||||
anAtom.debugLabel = `TABS__${param}`;
|
||||
return anAtom;
|
||||
});
|
||||
import { atomFamily } from 'jotai/utils';
|
||||
|
||||
type LoadingState = 'idle' | 'loading';
|
||||
|
||||
@@ -28,4 +20,4 @@ export const showTxSettingsCallback = atom<(() => Promise<void>) | undefined>(un
|
||||
|
||||
export const errorStackTraceState = atom<string | null>(null);
|
||||
|
||||
export const routeHeaderState = atom<JSX.Element | null>(null);
|
||||
export const routeHeaderState = atom<React.JSX.Element | null>(null);
|
||||
|
||||
@@ -25,6 +25,7 @@ export enum RouteUrls {
|
||||
|
||||
// Active wallet routes
|
||||
Home = '/',
|
||||
Activity = '/activity',
|
||||
AddNetwork = '/add-network',
|
||||
ChooseAccount = '/choose-account',
|
||||
Fund = '/fund',
|
||||
|
||||
Reference in New Issue
Block a user