fix: use routes over state for home tabs, closes #3518

This commit is contained in:
Pete Watters
2023-07-11 16:05:27 +01:00
parent 0d8e72ff96
commit cb2f317636
12 changed files with 55 additions and 102 deletions

View File

@@ -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,
};
}

View File

@@ -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[] = [];

View File

@@ -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}

View File

@@ -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({

View File

@@ -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) => {

View File

@@ -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) {

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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 />}>

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -25,6 +25,7 @@ export enum RouteUrls {
// Active wallet routes
Home = '/',
Activity = '/activity',
AddNetwork = '/add-network',
ChooseAccount = '/choose-account',
Fund = '/fund',