Skip to content

Commit 6fc7404

Browse files
authored
chore(tangle-dapp): Refactor restake page with new tab navigation and content structure (#2973)
1 parent 587c073 commit 6fc7404

File tree

10 files changed

+273
-50
lines changed

10 files changed

+273
-50
lines changed

apps/tangle-dapp/src/app/app.tsx

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import '@tangle-network/ui-components/tailwind.css';
33
import '../styles.css';
44

5-
import { Route, Routes } from 'react-router';
5+
import { Navigate, Route, Routes } from 'react-router';
66
import Layout from '../containers/Layout';
77
import DashboardPage from '../pages/dashboard';
88
import BlueprintsPage from '../pages/blueprints';
@@ -16,9 +16,10 @@ import NominationPage from '../pages/nomination';
1616
import ValidatorDetailsPage from '../pages/nomination/[validatorAddress]';
1717
import NominationLayout from '../pages/nomination/layout';
1818
import NotFoundPage from '../pages/notFound';
19-
import RestakePage from '../pages/restake';
2019
import { PagePath } from '../types';
2120
import Providers from './providers';
21+
import RestakeTabContent from '../containers/restaking/RestakeTabContent';
22+
import { RestakeAction, RestakeTab } from '../constants';
2223

2324
function App() {
2425
return (
@@ -66,12 +67,40 @@ function App() {
6667
element={<LiquidStakingPage />}
6768
/>
6869

69-
<Route path={`${PagePath.RESTAKE}`} element={<RestakePage />} />
70-
71-
<Route
72-
path={`${PagePath.RESTAKE}/:action`}
73-
element={<RestakePage />}
74-
/>
70+
<Route path={PagePath.RESTAKE}>
71+
<Route
72+
index
73+
element={<Navigate to={PagePath.RESTAKE_DEPOSIT} replace />}
74+
/>
75+
<Route
76+
path={PagePath.RESTAKE_DEPOSIT}
77+
element={<RestakeTabContent tab={RestakeAction.DEPOSIT} />}
78+
/>
79+
<Route
80+
path={PagePath.RESTAKE_DELEGATE}
81+
element={<RestakeTabContent tab={RestakeAction.DELEGATE} />}
82+
/>
83+
<Route
84+
path={PagePath.RESTAKE_UNDELEGATE}
85+
element={<RestakeTabContent tab={RestakeAction.UNDELEGATE} />}
86+
/>
87+
<Route
88+
path={PagePath.RESTAKE_WITHDRAW}
89+
element={<RestakeTabContent tab={RestakeAction.WITHDRAW} />}
90+
/>
91+
<Route
92+
path={PagePath.RESTAKE_VAULT}
93+
element={<RestakeTabContent tab={RestakeTab.VAULTS} />}
94+
/>
95+
<Route
96+
path={PagePath.RESTAKE_OPERATOR}
97+
element={<RestakeTabContent tab={RestakeTab.OPERATORS} />}
98+
/>
99+
<Route
100+
path={PagePath.RESTAKE_BLUEPRINT}
101+
element={<RestakeTabContent tab={RestakeTab.BLUEPRINTS} />}
102+
/>
103+
</Route>
75104

76105
<Route path="*" element={<NotFoundPage />} />
77106
</Routes>

apps/tangle-dapp/src/constants/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ export enum LockUnlocksAtKind {
127127
BLOCK,
128128
}
129129

130+
export enum RestakeTab {
131+
RESTAKE = 'restake',
132+
VAULTS = 'vaults',
133+
OPERATORS = 'operators',
134+
BLUEPRINTS = 'blueprints',
135+
}
136+
130137
export enum RestakeAction {
131138
DEPOSIT = 'deposit',
132139
DELEGATE = 'delegate',
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { ReactNode, useCallback, type FC } from 'react';
2+
import RestakeTabs from '../../pages/restake/RestakeTabs';
3+
import { RestakeAction, RestakeTab } from '../../constants';
4+
import DepositForm from '../../pages/restake/deposit/DepositForm';
5+
import RestakeWithdrawForm from '../../pages/restake/withdraw';
6+
import RestakeDelegateForm from '../../pages/restake/delegate';
7+
import RestakeUnstakeForm from '../../pages/restake/unstake';
8+
import useRestakeDelegatorInfo from '@tangle-network/tangle-shared-ui/data/restake/useRestakeDelegatorInfo';
9+
import useRestakeOperatorMap from '@tangle-network/tangle-shared-ui/data/restake/useRestakeOperatorMap';
10+
import useRestakeTVL from '@tangle-network/tangle-shared-ui/data/restake/useRestakeTVL';
11+
import useRestakeAssets from '@tangle-network/tangle-shared-ui/data/restake/useRestakeAssets';
12+
import useRestakeAssetsTvl from '@tangle-network/tangle-shared-ui/data/restake/useRestakeAssetsTvl';
13+
import { useRestakeVaults } from '@tangle-network/tangle-shared-ui/data/restake/useRestakeVaults';
14+
import {
15+
useVaultsTableProps,
16+
VaultsTable,
17+
} from '../../components/tables/Vaults';
18+
import OperatorsTable from './OperatorsTable';
19+
import BlueprintListing from '../../pages/blueprints/BlueprintListing';
20+
import { useNavigate } from 'react-router';
21+
import { PagePath } from '../../types';
22+
import { RestakeAssetId } from '@tangle-network/tangle-shared-ui/types';
23+
import { RestakeAsset } from '@tangle-network/tangle-shared-ui/types/restake';
24+
25+
type RestakeTabOrAction = RestakeTab | RestakeAction;
26+
27+
type Props = {
28+
tab: RestakeTabOrAction;
29+
};
30+
31+
const RestakeTabContent: FC<Props> = ({ tab }) => {
32+
const { result: delegatorInfo } = useRestakeDelegatorInfo();
33+
const { result: operatorMap } = useRestakeOperatorMap();
34+
const { operatorConcentration, operatorTVL } = useRestakeTVL(
35+
operatorMap,
36+
delegatorInfo,
37+
);
38+
const navigate = useNavigate();
39+
const {
40+
assets,
41+
isLoading: isLoadingAssets,
42+
refetchErc20Balances,
43+
} = useRestakeAssets();
44+
45+
const handleRestakeClicked = useCallback(() => {
46+
navigate(PagePath.RESTAKE_DEPOSIT);
47+
}, [navigate]);
48+
49+
const getRestakeTabContent = (action: RestakeTabOrAction): ReactNode => {
50+
switch (action) {
51+
case RestakeAction.DEPOSIT:
52+
return (
53+
<DepositForm
54+
assets={assets}
55+
isLoadingAssets={isLoadingAssets}
56+
refetchErc20Balances={refetchErc20Balances}
57+
/>
58+
);
59+
case RestakeAction.WITHDRAW:
60+
return <RestakeWithdrawForm assets={assets} />;
61+
case RestakeAction.DELEGATE:
62+
return <RestakeDelegateForm assets={assets} />;
63+
case RestakeAction.UNDELEGATE:
64+
return <RestakeUnstakeForm assets={assets} />;
65+
case RestakeTab.VAULTS:
66+
return (
67+
<VaultTabContent assets={assets} isLoadingAssets={isLoadingAssets} />
68+
);
69+
case RestakeTab.OPERATORS:
70+
return (
71+
<OperatorsTable
72+
operatorConcentration={operatorConcentration}
73+
operatorMap={operatorMap}
74+
operatorTVL={operatorTVL}
75+
onRestakeClicked={handleRestakeClicked}
76+
/>
77+
);
78+
case RestakeTab.BLUEPRINTS:
79+
return <BlueprintListing />;
80+
}
81+
};
82+
83+
return (
84+
<div className="space-y-9">
85+
<RestakeTabs />
86+
{getRestakeTabContent(tab)}
87+
</div>
88+
);
89+
};
90+
91+
export default RestakeTabContent;
92+
93+
type VaultTabContentProps = {
94+
assets: Map<RestakeAssetId, RestakeAsset> | null;
95+
isLoadingAssets: boolean;
96+
};
97+
98+
const VaultTabContent = ({ assets, isLoadingAssets }: VaultTabContentProps) => {
99+
const assetsTvl = useRestakeAssetsTvl();
100+
const { result: delegatorInfo } = useRestakeDelegatorInfo();
101+
102+
const vaults = useRestakeVaults({
103+
assets,
104+
delegatorInfo,
105+
assetsTvl,
106+
});
107+
108+
const tableProps = useVaultsTableProps({
109+
delegatorDeposits: delegatorInfo?.deposits,
110+
assets,
111+
});
112+
113+
return (
114+
<VaultsTable
115+
data={vaults}
116+
tableProps={tableProps}
117+
isLoading={isLoadingAssets}
118+
/>
119+
);
120+
};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import type { PropsOf } from '@tangle-network/ui-components/types';
2+
import { FC, useMemo } from 'react';
3+
import { useLocation } from 'react-router';
4+
import { twMerge } from 'tailwind-merge';
5+
6+
import { PagePath } from '../../types';
7+
import TabListItem from '../../components/restaking/TabListItem';
8+
import TabsList from '../../components/restaking/TabsList';
9+
import { RestakeAction } from '../../constants';
10+
11+
type Props = PropsOf<'ul'>;
12+
13+
const getTabRoute = (tab: RestakeAction): PagePath => {
14+
switch (tab) {
15+
case RestakeAction.DEPOSIT:
16+
return PagePath.RESTAKE_DEPOSIT;
17+
case RestakeAction.DELEGATE:
18+
return PagePath.RESTAKE_DELEGATE;
19+
case RestakeAction.UNDELEGATE:
20+
return PagePath.RESTAKE_UNDELEGATE;
21+
case RestakeAction.WITHDRAW:
22+
return PagePath.RESTAKE_WITHDRAW;
23+
}
24+
};
25+
26+
const RestakeActionTabs: FC<Props> = (props) => {
27+
const location = useLocation();
28+
29+
const activeTab = useMemo(() => {
30+
return Object.values(RestakeAction).find(
31+
(tab) => location.pathname === getTabRoute(tab),
32+
);
33+
}, [location.pathname]);
34+
35+
return (
36+
<TabsList {...props} className={twMerge('mb-4', props.className)}>
37+
{Object.values(RestakeAction).map((tab, idx) => (
38+
<TabListItem
39+
href={getTabRoute(tab)}
40+
key={`${tab}-${idx}`}
41+
isActive={activeTab === tab}
42+
// Hide separator when the tab is directly previous to the active tab
43+
hideSeparator={
44+
activeTab &&
45+
Object.values(RestakeAction).indexOf(activeTab) - 1 === idx
46+
}
47+
>
48+
{`${tab[0].toUpperCase()}${tab.substring(1)}`}
49+
</TabListItem>
50+
))}
51+
</TabsList>
52+
);
53+
};
54+
55+
export default RestakeActionTabs;

apps/tangle-dapp/src/pages/restake/RestakeTabs.tsx

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,64 @@
1-
import type { PropsOf } from '@tangle-network/ui-components/types';
21
import { FC, useMemo } from 'react';
3-
import { useLocation } from 'react-router';
2+
import { Link, useLocation } from 'react-router';
43
import { twMerge } from 'tailwind-merge';
54

65
import { PagePath } from '../../types';
7-
import TabListItem from '../../components/restaking/TabListItem';
86
import TabsList from '../../components/restaking/TabsList';
9-
import { RestakeAction } from '../../constants';
7+
import { RestakeTab, RestakeAction } from '../../constants';
8+
import { TabsRoot } from '@tangle-network/ui-components/components/Tabs';
9+
import { Typography } from '@tangle-network/ui-components/typography';
1010

11-
type Props = PropsOf<'ul'>;
12-
13-
const getTabRoute = (tab: RestakeAction): PagePath => {
11+
const getTabRoute = (tab: RestakeTab): PagePath => {
1412
switch (tab) {
15-
case RestakeAction.DEPOSIT:
13+
case RestakeTab.RESTAKE:
1614
return PagePath.RESTAKE_DEPOSIT;
17-
case RestakeAction.DELEGATE:
18-
return PagePath.RESTAKE_DELEGATE;
19-
case RestakeAction.UNDELEGATE:
20-
return PagePath.RESTAKE_UNDELEGATE;
21-
case RestakeAction.WITHDRAW:
22-
return PagePath.RESTAKE_WITHDRAW;
15+
case RestakeTab.VAULTS:
16+
return PagePath.RESTAKE_VAULT;
17+
case RestakeTab.OPERATORS:
18+
return PagePath.RESTAKE_OPERATOR;
19+
case RestakeTab.BLUEPRINTS:
20+
return PagePath.RESTAKE_BLUEPRINT;
2321
}
2422
};
2523

26-
const RestakeTabs: FC<Props> = (props) => {
24+
const RestakeTabs: FC = () => {
2725
const location = useLocation();
2826

2927
const activeTab = useMemo(() => {
30-
return Object.values(RestakeAction).find(
31-
(tab) => location.pathname === getTabRoute(tab),
32-
);
28+
return Object.values(RestakeTab).find((tab) => {
29+
const isRestakeAction = Object.values(RestakeAction).some((tabValue) =>
30+
location.pathname.includes(tabValue),
31+
);
32+
33+
if (isRestakeAction && tab === RestakeTab.RESTAKE) {
34+
return true;
35+
}
36+
37+
return location.pathname === getTabRoute(tab);
38+
});
3339
}, [location.pathname]);
3440

3541
return (
36-
<TabsList {...props} className={twMerge('mb-4', props.className)}>
37-
{Object.values(RestakeAction).map((tab, idx) => (
38-
<TabListItem
39-
href={getTabRoute(tab)}
40-
key={`${tab}-${idx}`}
41-
isActive={activeTab === tab}
42-
// Hide separator when the tab is directly previous to the active tab
43-
hideSeparator={
44-
activeTab &&
45-
Object.values(RestakeAction).indexOf(activeTab) - 1 === idx
46-
}
47-
>
48-
{`${tab[0].toUpperCase()}${tab.substring(1)}`}
49-
</TabListItem>
50-
))}
51-
</TabsList>
42+
<TabsRoot>
43+
<TabsList className="!bg-transparent space-x-8">
44+
{Object.values(RestakeTab).map((tab, idx) => (
45+
<Link
46+
to={getTabRoute(tab)}
47+
key={`${tab}-${idx}`}
48+
className={twMerge(
49+
'transition-colors pb-1 border-b-2',
50+
tab === activeTab
51+
? 'border-mono-200 dark:border-mono-0 text-mono-200 dark:text-mono-0'
52+
: 'text-mono-120 hover:text-mono-140 border-transparent',
53+
)}
54+
>
55+
<Typography variant="h5" fw="bold" className="!text-inherit">
56+
{`${tab[0].toUpperCase()}${tab.substring(1)}`}
57+
</Typography>
58+
</Link>
59+
))}
60+
</TabsList>
61+
</TabsRoot>
5262
);
5363
};
5464

apps/tangle-dapp/src/pages/restake/delegate/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import type { DelegationFormFields } from '../../../types/restake';
4141
import filterBy from '../../../utils/filterBy';
4242
import parseChainUnits from '../../../utils/parseChainUnits';
4343
import Form from '../Form';
44-
import RestakeTabs from '../RestakeTabs';
44+
import RestakeActionTabs from '../RestakeActionTabs';
4545
import SupportedChainModal from '../SupportedChainModal';
4646
import useSwitchChain from '../useSwitchChain';
4747
import ActionButton from './ActionButton';
@@ -309,7 +309,7 @@ const RestakeDelegateForm: FC<Props> = ({ assets }) => {
309309

310310
return (
311311
<StyleContainer className="md:min-w-[512px]">
312-
<RestakeTabs />
312+
<RestakeActionTabs />
313313

314314
<Card withShadow tightPadding>
315315
<Form onSubmit={handleSubmit(onSubmit)}>

apps/tangle-dapp/src/pages/restake/deposit/DepositForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { DepositFormFields } from '../../../types/restake';
2626
import filterBy from '../../../utils/filterBy';
2727
import parseChainUnits from '../../../utils/parseChainUnits';
2828
import Form from '../Form';
29-
import RestakeTabs from '../RestakeTabs';
29+
import RestakeActionTabs from '../RestakeActionTabs';
3030
import ActionButton from './ActionButton';
3131
import Details from './Details';
3232
import SourceChainInput from './SourceChainInput';
@@ -214,7 +214,7 @@ const DepositForm: FC<Props> = ({
214214

215215
return (
216216
<StyleContainer className="md:min-w-[512px]">
217-
<RestakeTabs />
217+
<RestakeActionTabs />
218218

219219
<Card withShadow tightPadding>
220220
<Form {...props} ref={formRef} onSubmit={handleSubmit(onSubmit)}>

0 commit comments

Comments
 (0)