Skip to content

Commit 2d8d451

Browse files
committed
feat: add Boost tab to Market page
1 parent 0036178 commit 2d8d451

File tree

11 files changed

+143
-89
lines changed

11 files changed

+143
-89
lines changed

.changeset/wild-berries-say.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@venusprotocol/evm": minor
3+
---
4+
5+
add Boost tab to Market page

apps/evm/src/components/ButtonGroup/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { TertiaryButton } from '@venusprotocol/ui';
33
import { useStyles } from './styles';
44

55
export interface ButtonGroupProps {
6-
buttonLabels: string[];
6+
buttonLabels: React.ReactNode[];
77
activeButtonIndex: number;
88
onButtonClick: (newIndex: number) => void;
99
fullWidth?: boolean;

apps/evm/src/components/Tabs/index.tsx

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
/** @jsxImportSource @emotion/react */
2+
import { cn } from '@venusprotocol/ui';
3+
import { Delimiter } from 'components';
24
import { type Tab, type TabNavType, useTabs } from 'hooks/useTabs';
35
import { ButtonGroup } from '../ButtonGroup';
46
import { useStyles } from './styles';
57

68
export interface TabsProps {
79
tabs: Tab[];
10+
variant?: 'primary' | 'secondary';
811
onTabChange?: (newIndex: number) => void;
912
navType?: TabNavType;
1013
initialActiveTabId?: string;
1114
className?: string;
15+
headerClassName?: string;
1216
}
1317

1418
export const Tabs = ({
1519
tabs,
20+
variant = 'primary',
1621
onTabChange,
1722
initialActiveTabId,
1823
className,
24+
headerClassName,
1925
navType = 'state',
2026
}: TabsProps) => {
2127
const styles = useStyles();
@@ -36,16 +42,50 @@ export const Tabs = ({
3642
};
3743

3844
return (
39-
<div className={className}>
40-
<ButtonGroup
41-
buttonLabels={tabs.map(({ title }) => title)}
42-
css={styles.buttonsContainer}
43-
activeButtonIndex={tabs.findIndex(tab => activeTab.id === tab.id)}
44-
onButtonClick={handleChange}
45-
fullWidth
46-
/>
47-
48-
{activeTab.content}
45+
<div className={cn('space-y-6', className)}>
46+
{variant === 'primary' ? (
47+
<ButtonGroup
48+
buttonLabels={tabs.map(({ title }) => title)}
49+
css={styles.buttonsContainer}
50+
className={headerClassName}
51+
activeButtonIndex={tabs.findIndex(tab => activeTab.id === tab.id)}
52+
onButtonClick={handleChange}
53+
fullWidth
54+
/>
55+
) : (
56+
<div className="relative">
57+
<div
58+
className={cn(
59+
'flex text-sm gap-x-4 scrollbar-hidden overflow-y-auto sm:gap-x-6 md:overflow-y-visible',
60+
headerClassName,
61+
)}
62+
>
63+
{tabs.map((tab, index) => (
64+
<button onClick={() => handleChange(index)} type="button" key={tab.id}>
65+
<p
66+
className={cn(
67+
'mb-2 font-semibold whitespace-nowrap',
68+
activeTab.id === tab.id ? 'text-offWhite' : 'text-grey',
69+
)}
70+
>
71+
{tab.title}
72+
</p>
73+
74+
<div
75+
className={cn(
76+
'w-full h-[5px] rounded-t-[2px]',
77+
activeTab.id === tab.id ? 'bg-blue' : 'bg-transparent',
78+
)}
79+
/>
80+
</button>
81+
))}
82+
</div>
83+
84+
<Delimiter className="-mt-[1px] -z-[1]" />
85+
</div>
86+
)}
87+
88+
<div>{activeTab.content}</div>
4989
</div>
5090
);
5191
};

apps/evm/src/components/Tabs/styles.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export const useStyles = () => {
88
buttonsContainer: css`
99
display: flex;
1010
align-items: center;
11-
margin-bottom: ${theme.spacing(6)};
1211
width: 100%;
1312
1413
> button {

apps/evm/src/hooks/useIsFeatureEnabled/index.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,16 @@ export const featureFlags = {
141141
ChainId.UNICHAIN_SEPOLIA,
142142
],
143143
transactionHistory: [ChainId.BSC_MAINNET, ChainId.ETHEREUM, ChainId.UNICHAIN_MAINNET],
144+
leveragedPositions: [
145+
ChainId.BSC_TESTNET,
146+
ChainId.OPBNB_TESTNET,
147+
ChainId.SEPOLIA,
148+
ChainId.ARBITRUM_SEPOLIA,
149+
ChainId.ZKSYNC_SEPOLIA,
150+
ChainId.OPTIMISM_SEPOLIA,
151+
ChainId.BASE_SEPOLIA,
152+
ChainId.UNICHAIN_SEPOLIA,
153+
],
144154
};
145155

146156
export type FeatureFlag = keyof typeof featureFlags;

apps/evm/src/hooks/useTabs/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export interface UseTabsInput<T extends Tab> {
1111

1212
export interface Tab extends Record<string, any> {
1313
id: string;
14-
title: string;
14+
title: React.ReactNode;
1515
content: React.ReactNode;
1616
}
1717

apps/evm/src/libs/translations/translations/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,8 @@
650650
},
651651
"operationForm": {
652652
"availableAmount": "Available",
653+
"boostTabAltText": "Rocket icon",
654+
"boostTabTitle": "Boost",
653655
"borrowTabTitle": "Borrow",
654656
"error": {
655657
"borrowCapReached": "The borrow cap of {{assetBorrowCap}} has been reached for this pool. You can not borrow from this market anymore until loans are repaid or its borrow cap is increased.",

apps/evm/src/pages/Account/Tabs/index.tsx

Lines changed: 0 additions & 45 deletions
This file was deleted.

apps/evm/src/pages/Account/index.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { cn } from '@venusprotocol/ui';
2+
13
import { useGetPools, useGetTokenUsdPrice, useGetVaiRepayApr, useGetVaults } from 'clients/api';
2-
import { Page, Spinner } from 'components';
4+
import { Page, Spinner, Tabs } from 'components';
35
import { Redirect } from 'containers/Redirect';
46
import { useGetHomePagePath } from 'hooks/useGetHomePagePath';
57
import { useGetUserPrimeInfo } from 'hooks/useGetUserPrimeInfo';
@@ -14,7 +16,6 @@ import { Pools } from './Pools';
1416
import { PrimeBanner } from './PrimeBanner';
1517
import { Settings } from './Settings';
1618
import { Summary } from './Summary';
17-
import { Tabs } from './Tabs';
1819
import { Transactions } from './Transactions';
1920
import { Vaults } from './Vaults';
2021
import { useExtractData } from './useExtractData';
@@ -171,7 +172,13 @@ export const Account: React.FC = () => {
171172
/>
172173
</div>
173174

174-
<Tabs tabs={tabs} />
175+
<Tabs
176+
tabs={tabs}
177+
className="lg:space-y-8"
178+
headerClassName={cn('text-md sm:text-lg')}
179+
navType="searchParam"
180+
variant="secondary"
181+
/>
175182
</Page>
176183
);
177184
};

apps/evm/src/pages/Market/OperationForm/index.tsx

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1+
import { cn } from '@venusprotocol/ui';
2+
import type { Address } from 'viem';
3+
14
import { type ModalProps, Tabs } from 'components';
25
import AssetAccessor from 'containers/AssetAccessor';
6+
import { useIsFeatureEnabled } from 'hooks/useIsFeatureEnabled';
7+
import type { Tab } from 'hooks/useTabs';
38
import { useTranslation } from 'libs/translations';
49
import type { VToken } from 'types';
5-
6-
import type { Tab } from 'hooks/useTabs';
7-
import type { Address } from 'viem';
810
import BorrowForm from './BorrowForm';
911
import NativeTokenBalanceWrapper from './NativeTokenBalanceWrapper';
1012
import RepayForm from './RepayForm';
1113
import SupplyForm from './SupplyForm';
1214
import WithdrawForm from './WithdrawForm';
15+
import rocketIconSrc from './rocket.svg';
1316

1417
export interface OperationFormProps {
1518
vToken: VToken;
@@ -26,6 +29,10 @@ export const OperationForm: React.FC<OperationFormProps> = ({
2629
}) => {
2730
const { t } = useTranslation();
2831

32+
const isLeveragedPositionsFeatureEnabled = useIsFeatureEnabled({
33+
name: 'leveragedPositions',
34+
});
35+
2936
const tabs: Tab[] = [
3037
{
3138
id: 'supply',
@@ -81,31 +88,50 @@ export const OperationForm: React.FC<OperationFormProps> = ({
8188
</AssetAccessor>
8289
),
8390
},
84-
{
85-
id: 'repay',
86-
title: t('operationForm.repayTabTitle'),
87-
content: (
88-
<AssetAccessor
89-
vToken={vToken}
90-
poolComptrollerAddress={poolComptrollerAddress}
91-
action="repay"
92-
>
93-
{({ asset, pool }) => (
94-
<NativeTokenBalanceWrapper asset={asset} pool={pool}>
95-
{({ asset, pool, userTokenWrappedBalanceMantissa }) => (
96-
<RepayForm
97-
asset={asset}
98-
pool={pool}
99-
userTokenWrappedBalanceMantissa={userTokenWrappedBalanceMantissa}
100-
onSubmitSuccess={onSubmitSuccess}
101-
/>
102-
)}
103-
</NativeTokenBalanceWrapper>
104-
)}
105-
</AssetAccessor>
106-
),
107-
},
10891
];
10992

110-
return <Tabs tabs={tabs} initialActiveTabId={initialActiveTabId} navType="searchParam" />;
93+
if (isLeveragedPositionsFeatureEnabled) {
94+
tabs.push({
95+
id: 'boost',
96+
title: (
97+
<div className="flex items-center gap-x-1">
98+
<img src={rocketIconSrc} className="size-4" alt={t('operationForm.boostTabAltText')} />
99+
100+
<span className="text-[#65EEE0]">{t('operationForm.boostTabTitle')}</span>
101+
</div>
102+
),
103+
content: <>Coming soon</>,
104+
});
105+
}
106+
107+
tabs.push({
108+
id: 'repay',
109+
title: t('operationForm.repayTabTitle'),
110+
content: (
111+
<AssetAccessor vToken={vToken} poolComptrollerAddress={poolComptrollerAddress} action="repay">
112+
{({ asset, pool }) => (
113+
<NativeTokenBalanceWrapper asset={asset} pool={pool}>
114+
{({ asset, pool, userTokenWrappedBalanceMantissa }) => (
115+
<RepayForm
116+
asset={asset}
117+
pool={pool}
118+
userTokenWrappedBalanceMantissa={userTokenWrappedBalanceMantissa}
119+
onSubmitSuccess={onSubmitSuccess}
120+
/>
121+
)}
122+
</NativeTokenBalanceWrapper>
123+
)}
124+
</AssetAccessor>
125+
),
126+
});
127+
128+
return (
129+
<Tabs
130+
tabs={tabs}
131+
initialActiveTabId={initialActiveTabId}
132+
navType="searchParam"
133+
variant={isLeveragedPositionsFeatureEnabled ? 'secondary' : 'primary'}
134+
headerClassName={cn(isLeveragedPositionsFeatureEnabled && 'sm:gap-x-8 lg:gap-x-6')}
135+
/>
136+
);
111137
};

0 commit comments

Comments
 (0)