Liquid Staking CTA (#281)
* 🚧 feat(token-page): Scaffold sidebar components for token page * 🚧 feat(staking/governance): Scaffold adding Panels to Staking and Governance page * ✨ Update and add icons * 🧱 Add strideZoneApp to shared config * 🧱 Add dialogs, update ExternalLinkDialog * 🚧 Add New Panels/Update paths * Fix mobile Profile Panel * 💄 fix desktop padding * ✨ Add New tag * 💄 Highlight tag on StrideStakingPanel * 💄 Single columns for panels on mobile breakpoint * 💄 fix import nits and alignment nits on panels * Add circular keplr icon
This commit is contained in:
parent
26b426c9e9
commit
1a41ccaf2a
File diff suppressed because it is too large
Load Diff
BIN
public/third-party/keplr.png
vendored
Normal file
BIN
public/third-party/keplr.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
public/third-party/stride.png
vendored
Normal file
BIN
public/third-party/stride.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@ -52,9 +52,9 @@ const AlertsPage = lazy(() => import('@/pages/AlertsPage'));
|
||||
const ProfilePage = lazy(() => import('@/pages/Profile'));
|
||||
const SettingsPage = lazy(() => import('@/pages/settings/Settings'));
|
||||
const TradePage = lazy(() => import('@/pages/trade/Trade'));
|
||||
const RewardsPage = lazy(() => import('@/pages/rewards/RewardsPage'));
|
||||
const TermsOfUsePage = lazy(() => import('@/pages/TermsOfUsePage'));
|
||||
const PrivacyPolicyPage = lazy(() => import('@/pages/PrivacyPolicyPage'));
|
||||
const TokenPage = lazy(() => import('@/pages/token/Token'));
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
@ -87,7 +87,7 @@ const Content = () => {
|
||||
<Route path={MarketsRoute.New} element={<NewMarket />} />
|
||||
<Route path={AppRoute.Markets} element={<MarketsPage />} />
|
||||
</Route>
|
||||
<Route path={`/${chainTokenLabel}`} element={<RewardsPage />} />
|
||||
<Route path={`/${chainTokenLabel}/*`} element={<TokenPage />} />
|
||||
{isTablet && (
|
||||
<>
|
||||
<Route path={AppRoute.Alerts} element={<AlertsPage />} />
|
||||
@ -207,6 +207,7 @@ Styled.Content = styled.div<{ isShowingHeader: boolean; isShowingFooter: boolean
|
||||
|
||||
Styled.Main = styled.main`
|
||||
${layoutMixins.contentSectionAttached}
|
||||
box-shadow: none;
|
||||
|
||||
grid-area: Main;
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
|
||||
type ElementProps = {
|
||||
title: string;
|
||||
subtitle?: React.ReactNode;
|
||||
slotLeft?: React.ReactNode;
|
||||
slotRight?: React.ReactNode;
|
||||
};
|
||||
|
||||
@ -16,14 +17,16 @@ type StyleProps = {
|
||||
export const ContentSectionHeader = ({
|
||||
title,
|
||||
subtitle,
|
||||
slotLeft,
|
||||
slotRight,
|
||||
className,
|
||||
}: ElementProps & StyleProps) => (
|
||||
<Styled.ContentSectionHeader className={className}>
|
||||
<div>
|
||||
{slotLeft}
|
||||
<Styled.Header>
|
||||
{title && <h3>{title}</h3>}
|
||||
{subtitle && <p>{subtitle}</p>}
|
||||
</div>
|
||||
</Styled.Header>
|
||||
{slotRight}
|
||||
</Styled.ContentSectionHeader>
|
||||
);
|
||||
@ -40,9 +43,15 @@ Styled.ContentSectionHeader = styled.header<StyleProps>`
|
||||
|
||||
padding: 1rem var(--header-horizontal-padding);
|
||||
|
||||
> div {
|
||||
${layoutMixins.column}
|
||||
@media ${breakpoints.tablet} {
|
||||
flex-wrap: wrap;
|
||||
--header-horizontal-padding: 1.25rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Header = styled.div`
|
||||
${layoutMixins.column}
|
||||
flex: 1;
|
||||
|
||||
h3 {
|
||||
color: var(--color-text-2);
|
||||
@ -54,9 +63,4 @@ Styled.ContentSectionHeader = styled.header<StyleProps>`
|
||||
font: var(--font-small-book);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
flex-wrap: wrap;
|
||||
--header-horizontal-padding: 1.25rem;
|
||||
}
|
||||
`;
|
||||
|
||||
@ -25,6 +25,7 @@ import {
|
||||
CoinsIcon,
|
||||
CommentIcon,
|
||||
CopyIcon,
|
||||
CurrencySignIcon,
|
||||
DepositIcon,
|
||||
DepthChartIcon,
|
||||
DiscordIcon,
|
||||
@ -35,6 +36,7 @@ import {
|
||||
FundingChartIcon,
|
||||
GearIcon,
|
||||
GiftboxIcon,
|
||||
GovernanceIcon,
|
||||
HelpCircleIcon,
|
||||
HideIcon,
|
||||
HistoryIcon,
|
||||
@ -70,6 +72,7 @@ import {
|
||||
StarIcon,
|
||||
SunIcon,
|
||||
TerminalIcon,
|
||||
TokenIcon,
|
||||
TradeIcon,
|
||||
TransferIcon,
|
||||
TriangleIcon,
|
||||
@ -103,6 +106,7 @@ export enum IconName {
|
||||
Coins = 'Coins',
|
||||
Comment = 'Comment',
|
||||
Copy = 'Copy',
|
||||
CurrencySign = 'CurrencySign',
|
||||
Deposit = 'Deposit',
|
||||
DepthChart = 'DepthChart',
|
||||
Discord = 'Discord',
|
||||
@ -113,6 +117,7 @@ export enum IconName {
|
||||
FundingChart = 'FundingChart',
|
||||
Gear = 'Gear',
|
||||
Giftbox = 'Giftbox',
|
||||
Governance = 'Governance',
|
||||
HelpCircle = 'HelpCircle',
|
||||
Hide = 'Hide',
|
||||
History = 'History',
|
||||
@ -149,6 +154,7 @@ export enum IconName {
|
||||
Sun = 'Sun',
|
||||
Terminal = 'Terminal',
|
||||
TogglesMenu = 'TogglesMenu',
|
||||
Token = 'Token',
|
||||
Trade = 'Trade',
|
||||
Transfer = 'Transfer',
|
||||
Triangle = 'Triangle',
|
||||
@ -182,6 +188,7 @@ const icons = {
|
||||
[IconName.Coins]: CoinsIcon,
|
||||
[IconName.Comment]: CommentIcon,
|
||||
[IconName.Copy]: CopyIcon,
|
||||
[IconName.CurrencySign]: CurrencySignIcon,
|
||||
[IconName.Deposit]: DepositIcon,
|
||||
[IconName.DepthChart]: DepthChartIcon,
|
||||
[IconName.Discord]: DiscordIcon,
|
||||
@ -192,6 +199,7 @@ const icons = {
|
||||
[IconName.FundingChart]: FundingChartIcon,
|
||||
[IconName.Gear]: GearIcon,
|
||||
[IconName.Giftbox]: GiftboxIcon,
|
||||
[IconName.Governance]: GovernanceIcon,
|
||||
[IconName.HelpCircle]: HelpCircleIcon,
|
||||
[IconName.Hide]: HideIcon,
|
||||
[IconName.History]: HistoryIcon,
|
||||
@ -227,6 +235,7 @@ const icons = {
|
||||
[IconName.Sun]: SunIcon,
|
||||
[IconName.Terminal]: TerminalIcon,
|
||||
[IconName.TogglesMenu]: TogglesMenuIcon,
|
||||
[IconName.Token]: TokenIcon,
|
||||
[IconName.Trade]: TradeIcon,
|
||||
[IconName.Transfer]: TransferIcon,
|
||||
[IconName.Triangle]: TriangleIcon,
|
||||
|
||||
@ -106,8 +106,6 @@ Styled.Container = styled.div`
|
||||
--stickyArea1-leftGap: var(--border-width);
|
||||
min-height: var(--stickyArea-height);
|
||||
|
||||
${layoutMixins.withOuterAndInnerBorders}
|
||||
|
||||
display: grid;
|
||||
grid-template: var(--withSidebar-gridTemplate);
|
||||
`;
|
||||
@ -120,6 +118,7 @@ Styled.Side = styled.aside`
|
||||
${layoutMixins.sticky}
|
||||
max-height: var(--stickyArea-height);
|
||||
backdrop-filter: none;
|
||||
background-color: var(--color-layer-2);
|
||||
|
||||
${layoutMixins.stack}
|
||||
`;
|
||||
|
||||
@ -5,6 +5,7 @@ export enum DialogTypes {
|
||||
DisplaySettings = 'DisplaySettings',
|
||||
ExchangeOffline = 'ExchangeOffline',
|
||||
ExternalLink = 'ExternalLink',
|
||||
ExternalNavStride = 'ExternalNavStride',
|
||||
FillDetails = 'FillDetails',
|
||||
Help = 'Help',
|
||||
ExternalNavKeplr = 'ExternalNavKeplr',
|
||||
|
||||
@ -29,6 +29,12 @@ export enum HistoryRoute {
|
||||
Payments = 'payments',
|
||||
}
|
||||
|
||||
export enum TokenRoute {
|
||||
TradingRewards = 'trading-rewards',
|
||||
StakingRewards = 'staking-rewards',
|
||||
Governance = 'governance',
|
||||
}
|
||||
|
||||
export enum MobileSettingsRoute {
|
||||
Language = 'language',
|
||||
Notifications = 'notifications',
|
||||
|
||||
@ -33,7 +33,7 @@ export const useMarketsData = (
|
||||
return Object.values(allPerpetualMarkets)
|
||||
.filter(isTruthy)
|
||||
.map((marketData) => ({
|
||||
asset: allAssets[marketData.assetId],
|
||||
asset: allAssets[marketData.assetId] ?? {},
|
||||
tickSizeDecimals: marketData.configs?.tickSizeDecimals,
|
||||
...marketData,
|
||||
...marketData.perpetual,
|
||||
@ -46,9 +46,10 @@ export const useMarketsData = (
|
||||
|
||||
if (searchFilter) {
|
||||
return filtered.filter(
|
||||
({ asset }) =>
|
||||
({ asset, id }) =>
|
||||
asset?.name?.toLocaleLowerCase().includes(searchFilter.toLowerCase()) ||
|
||||
asset?.id?.toLocaleLowerCase().includes(searchFilter.toLowerCase())
|
||||
asset?.id?.toLocaleLowerCase().includes(searchFilter.toLowerCase()) ||
|
||||
id?.toLocaleLowerCase().includes(searchFilter.toLowerCase())
|
||||
);
|
||||
}
|
||||
return filtered;
|
||||
|
||||
@ -21,6 +21,7 @@ export interface LinksConfigs {
|
||||
newMarketProposalLearnMore: string;
|
||||
stakingLearnMore?: string;
|
||||
keplrDashboard?: string;
|
||||
strideZoneApp?: string;
|
||||
accountExportLearnMore?: string;
|
||||
walletLearnMore?: string;
|
||||
}
|
||||
@ -46,6 +47,7 @@ export const useURLConfigs = (): LinksConfigs => {
|
||||
newMarketProposalLearnMore: linksConfigs.newMarketProposalLearnMore || FALLBACK_URL,
|
||||
stakingLearnMore: linksConfigs.stakingLearnMore || FALLBACK_URL,
|
||||
keplrDashboard: linksConfigs.keplrDashboard || FALLBACK_URL,
|
||||
strideZoneApp: linksConfigs.strideZoneApp || FALLBACK_URL,
|
||||
accountExportLearnMore: linksConfigs.accountExportLearnMore || FALLBACK_URL,
|
||||
walletLearnMore: linksConfigs.walletLearnMore || FALLBACK_URL,
|
||||
};
|
||||
|
||||
@ -36,8 +36,8 @@ const ChaosLabsIcon: React.FC = () => {
|
||||
fill={fills[1]}
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M47.6624 4.38721C46.5167 4.38721 45.5396 4.77222 44.7521 5.54468C43.9625 6.31199 43.5687 7.27749 43.5687 8.41904C43.5687 9.55454 43.9632 10.5194 44.7514 11.2929L44.7527 11.2939C45.5403 12.0594 46.5173 12.4406 47.6624 12.4406C48.8074 12.4406 49.7845 12.0594 50.572 11.2939L50.5734 11.2929C51.3616 10.5194 51.756 9.55454 51.756 8.41904C51.756 7.27749 51.3623 6.312 50.5727 5.54469C49.7851 4.77223 48.8081 4.38721 47.6624 4.38721ZM45.4207 8.41904C45.4207 7.76676 45.6364 7.22501 46.0671 6.77549C46.5039 6.32689 47.0294 6.10561 47.6624 6.10561C48.2954 6.10561 48.8168 6.32697 49.2462 6.77466L49.2476 6.77617C49.6857 7.22615 49.9034 7.7677 49.9034 8.41904C49.9034 9.06265 49.6864 9.60104 49.2476 10.0516L49.2462 10.0531C48.8168 10.5008 48.2954 10.7222 47.6624 10.7222C47.0294 10.7222 46.5039 10.5009 46.0671 10.0523C45.6357 9.60218 45.4207 9.06359 45.4207 8.41904Z"
|
||||
fill={fills[1]}
|
||||
/>
|
||||
@ -50,8 +50,8 @@ const ChaosLabsIcon: React.FC = () => {
|
||||
fill={fills[1]}
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M40.3586 4.56055H38.6659L35.2261 12.3667H37.1862L37.9072 10.7289H41.1173L41.8383 12.3667H43.7984L40.3586 4.56055ZM38.6686 9.00014L39.5126 7.08737L40.3559 9.00014H38.6686Z"
|
||||
fill={fills[1]}
|
||||
/>
|
||||
|
||||
4
src/icons/currency-sign.svg
Normal file
4
src/icons/currency-sign.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.9255 9.70119C10.9255 8.14969 9.99456 7.61773 8.13272 7.39612C6.80283 7.21876 6.53685 6.86416 6.53685 6.24348C6.53685 5.62279 6.98018 5.22393 7.86674 5.22393C8.66467 5.22393 9.10801 5.48991 9.32962 6.15485C9.37398 6.28784 9.50697 6.37647 9.63996 6.37647H10.3492C10.5265 6.37647 10.6595 6.24348 10.6595 6.06623V6.02186C10.4822 5.04658 9.68422 4.29301 8.66467 4.20439V3.14047C8.66467 2.96312 8.53169 2.83013 8.31007 2.78577H7.64513C7.46777 2.78577 7.33479 2.91876 7.29042 3.14047V4.16002C5.96053 4.33737 5.11834 5.22393 5.11834 6.33221C5.11834 7.79508 6.0049 8.3713 7.86674 8.59302C9.10801 8.81463 9.50697 9.08061 9.50697 9.78992C9.50697 10.4992 8.88629 10.9868 8.0441 10.9868C6.89145 10.9868 6.49249 10.4991 6.3595 9.83418C6.31524 9.65693 6.18225 9.5682 6.04926 9.5682H5.29559C5.11834 9.5682 4.98535 9.70119 4.98535 9.87854V9.92291C5.1626 11.0311 5.87191 11.829 7.33479 12.0507V13.1146C7.33479 13.2919 7.46778 13.4249 7.68939 13.4692H8.35433C8.53169 13.4692 8.66468 13.3363 8.70904 13.1146V12.0507C10.0389 11.829 10.9255 10.8981 10.9255 9.70119Z" fill="currentColor"/>
|
||||
<path d="M5.73821 14.3561C2.2805 13.1149 0.507281 9.25822 1.79291 5.84477C2.45786 3.98293 3.92073 2.56441 5.73821 1.89947C5.91557 1.81085 6.00419 1.67786 6.00419 1.45614V0.835558C6.00419 0.658204 5.91557 0.525216 5.73821 0.480957C5.69385 0.480957 5.60522 0.480957 5.56086 0.525215C1.34958 1.8551 -0.955596 6.33247 0.374292 10.5437C1.17223 13.0262 3.07843 14.9324 5.56086 15.7303C5.73821 15.8189 5.91557 15.7303 5.95982 15.553C6.00419 15.5087 6.00419 15.4643 6.00419 15.3757V14.755C6.00419 14.622 5.8712 14.4448 5.73821 14.3561ZM10.4372 0.525215C10.2598 0.436592 10.0825 0.525216 10.0382 0.70257C9.99385 0.746935 9.99385 0.791193 9.99385 0.879924V1.5005C9.99385 1.67786 10.1268 1.8551 10.2598 1.94383C13.7175 3.18499 15.4908 7.04167 14.2051 10.4551C13.5402 12.317 12.0773 13.7355 10.2598 14.4004C10.0825 14.489 9.99385 14.622 9.99385 14.8438V15.4643C9.99385 15.6417 10.0825 15.7747 10.2598 15.8189C10.3042 15.8189 10.3928 15.8189 10.4372 15.7747C14.6485 14.4448 16.9536 9.96742 15.6238 5.75615C14.8258 3.22936 12.8752 1.32315 10.4372 0.525215Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
3
src/icons/governance.svg
Normal file
3
src/icons/governance.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 13.5V8M10.5 13.5V8M5.5 13.5V8M2 5.5L8 1.5L14 5.5M13 13.5V6.388C11.3459 6.12904 9.67421 5.99931 8 6C6.29933 6 4.62933 6.13333 3 6.388V13.5M2 13.5H14M8 4H8.00533V4.00533H8V4Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 360 B |
@ -1,4 +1,3 @@
|
||||
import ChaosLabsIcon from './chaos-labs';
|
||||
export { default as AddressConnectorIcon } from './address-connector.svg';
|
||||
export { default as ArrowIcon } from './arrow.svg';
|
||||
export { default as Bar3Icon } from './bar3.svg';
|
||||
@ -20,6 +19,7 @@ export { default as CoinMarketCapIcon } from './logos/coinmarketcap.svg';
|
||||
export { default as CoinsIcon } from './coins.svg';
|
||||
export { default as CommentIcon } from './comment.svg';
|
||||
export { default as CopyIcon } from './copy.svg';
|
||||
export { default as CurrencySignIcon } from './currency-sign.svg';
|
||||
export { default as DepositIcon } from './deposit.svg';
|
||||
export { default as DepthChartIcon } from './depth-chart.svg';
|
||||
export { default as DiscordIcon } from './discord.svg';
|
||||
@ -29,6 +29,7 @@ export { default as FileIcon } from './file.svg';
|
||||
export { default as FundingChartIcon } from './funding-chart.svg';
|
||||
export { default as GearIcon } from './gear.svg';
|
||||
export { default as GiftboxIcon } from './giftbox.svg';
|
||||
export { default as GovernanceIcon } from './governance.svg';
|
||||
export { default as HelpCircleIcon } from './help-circle.svg';
|
||||
export { default as HideIcon } from './hide.svg';
|
||||
export { default as HistoryIcon } from './history.svg';
|
||||
@ -60,6 +61,7 @@ export { default as StarIcon } from './star.svg';
|
||||
export { default as SunIcon } from './sun.svg';
|
||||
export { default as TerminalIcon } from './terminal.svg';
|
||||
export { default as TogglesMenuIcon } from './toggles-menu.svg';
|
||||
export { default as TokenIcon } from './token.svg';
|
||||
export { default as TradeIcon } from './trade.svg';
|
||||
export { default as TransferIcon } from './transfer.svg';
|
||||
export { default as TriangleIcon } from './triangle.svg';
|
||||
|
||||
5
src/icons/token.svg
Normal file
5
src/icons/token.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.78448 0L0.498535 11.999H3.0425L11.3714 0H8.78448Z" fill="currentColor"/>
|
||||
<path d="M3.30742 0L5.74539 3.53569L4.47339 5.46426L0.710449 0H3.30742Z" fill="currentColor"/>
|
||||
<path d="M9.03109 12L6.32812 8.08934L7.73738 6.21436L11.575 12H9.03109Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 378 B |
@ -30,6 +30,7 @@ import { OrderDetailsDialog } from '@/views/dialogs/DetailsDialog/OrderDetailsDi
|
||||
import { FillDetailsDialog } from '@/views/dialogs/DetailsDialog/FillDetailsDialog';
|
||||
import { NewMarketMessageDetailsDialog } from '@/views/dialogs/NewMarketMessageDetailsDialog';
|
||||
import { NewMarketAgreementDialog } from '@/views/dialogs/NewMarketAgreementDialog';
|
||||
import { ExternalNavStrideDialog } from '@/views/dialogs/ExternalNavStrideDialog';
|
||||
|
||||
export const DialogManager = () => {
|
||||
const dispatch = useDispatch();
|
||||
@ -59,6 +60,7 @@ export const DialogManager = () => {
|
||||
[DialogTypes.Help]: <HelpDialog {...modalProps} />,
|
||||
[DialogTypes.ExternalNavKeplr]: <ExternalNavKeplrDialog {...modalProps} />,
|
||||
[DialogTypes.ExternalLink]: <ExternalLinkDialog {...modalProps} />,
|
||||
[DialogTypes.ExternalNavStride]: <ExternalNavStrideDialog {...modalProps} />,
|
||||
[DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />,
|
||||
[DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />,
|
||||
[DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />,
|
||||
|
||||
@ -4,9 +4,15 @@ import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useEnsName } from 'wagmi';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ButtonSize } from '@/constants/buttons';
|
||||
import { TransferType } from '@/constants/abacus';
|
||||
|
||||
import { OnboardingState } from '@/constants/account';
|
||||
import { ButtonSize } from '@/constants/buttons';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { AppRoute, PortfolioRoute, HistoryRoute } from '@/constants/routes';
|
||||
import { wallets } from '@/constants/wallets';
|
||||
import { useAccounts, useStringGetter, useTokenConfigs } from '@/hooks';
|
||||
import { breakpoints } from '@/styles';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Details } from '@/components/Details';
|
||||
@ -16,13 +22,6 @@ import { IconButton, type IconButtonProps } from '@/components/IconButton';
|
||||
import { Panel } from '@/components/Panel';
|
||||
import { Toolbar } from '@/components/Toolbar';
|
||||
|
||||
import { OnboardingState } from '@/constants/account';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { AppRoute, PortfolioRoute, HistoryRoute } from '@/constants/routes';
|
||||
import { wallets } from '@/constants/wallets';
|
||||
import { useAccounts, useStringGetter, useTokenConfigs } from '@/hooks';
|
||||
|
||||
import {
|
||||
getHistoricalTradingRewardsForCurrentWeek,
|
||||
getOnboardingState,
|
||||
@ -32,10 +31,12 @@ import { openDialog } from '@/state/dialogs';
|
||||
import { isTruthy } from '@/lib/isTruthy';
|
||||
import { truncateAddress } from '@/lib/wallet';
|
||||
|
||||
import { DYDXBalancePanel } from './rewards/DYDXBalancePanel';
|
||||
import { MigratePanel } from './rewards/MigratePanel';
|
||||
import { GovernancePanel } from './rewards/GovernancePanel';
|
||||
import { StakingPanel } from './rewards/StakingPanel';
|
||||
import { DYDXBalancePanel } from './token/rewards/DYDXBalancePanel';
|
||||
import { MigratePanel } from './token/rewards/MigratePanel';
|
||||
import { GovernancePanel } from './token/rewards/GovernancePanel';
|
||||
import { StakingPanel } from './token/staking/StakingPanel';
|
||||
import { StrideStakingPanel } from './token/staking/StrideStakingPanel';
|
||||
import { NewMarketsPanel } from './token/rewards/NewMarketsPanel';
|
||||
|
||||
const ENS_CHAIN_ID = 1; // Ethereum
|
||||
|
||||
@ -236,7 +237,9 @@ const Profile = () => {
|
||||
</Styled.HistoryPanel>
|
||||
|
||||
<Styled.GovernancePanel />
|
||||
<Styled.NewMarketsPanel />
|
||||
<Styled.StakingPanel />
|
||||
<Styled.StrideStakingPanel />
|
||||
</Styled.MobileProfileLayout>
|
||||
);
|
||||
};
|
||||
@ -249,11 +252,12 @@ Styled.MobileProfileLayout = styled.div`
|
||||
${layoutMixins.contentContainerPage}
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
padding: 1.25rem 0.9rem;
|
||||
max-width: 100vw;
|
||||
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
grid-template-areas:
|
||||
'header header'
|
||||
'actions actions'
|
||||
@ -262,8 +266,23 @@ Styled.MobileProfileLayout = styled.div`
|
||||
'balance balance'
|
||||
'rewards fees'
|
||||
'history history'
|
||||
'governance governance'
|
||||
'staking staking';
|
||||
'governance newMarkets'
|
||||
'keplr stride';
|
||||
|
||||
@media ${breakpoints.mobile} {
|
||||
grid-template-areas:
|
||||
'header header'
|
||||
'actions actions'
|
||||
'settings help'
|
||||
'migrate migrate'
|
||||
'balance balance'
|
||||
'rewards fees'
|
||||
'history history'
|
||||
'governance governance'
|
||||
'newMarkets newMarkets'
|
||||
'keplr keplr'
|
||||
'stride stride';
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Header = styled.header`
|
||||
@ -431,5 +450,13 @@ Styled.GovernancePanel = styled(GovernancePanel)`
|
||||
`;
|
||||
|
||||
Styled.StakingPanel = styled(StakingPanel)`
|
||||
grid-area: staking;
|
||||
grid-area: keplr;
|
||||
`;
|
||||
|
||||
Styled.NewMarketsPanel = styled(NewMarketsPanel)`
|
||||
grid-area: newMarkets;
|
||||
`;
|
||||
|
||||
Styled.StrideStakingPanel = styled(StrideStakingPanel)`
|
||||
grid-area: stride;
|
||||
`;
|
||||
|
||||
@ -45,7 +45,8 @@ export default () => {
|
||||
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
|
||||
const { nativeTokenBalance } = useAccountBalance();
|
||||
|
||||
const { numTotalPositions, numTotalOpenOrders } = useSelector(getTradeInfoNumbers, shallowEqual) || {};
|
||||
const { numTotalPositions, numTotalOpenOrders } =
|
||||
useSelector(getTradeInfoNumbers, shallowEqual) || {};
|
||||
const numPositions = shortenNumberForDisplay(numTotalPositions);
|
||||
const numOrders = shortenNumberForDisplay(numTotalOpenOrders);
|
||||
|
||||
@ -119,30 +120,43 @@ export default () => {
|
||||
items: [
|
||||
{
|
||||
value: PortfolioRoute.Overview,
|
||||
slotBefore: <Styled.Icon iconName={IconName.Overview} />,
|
||||
slotBefore: (
|
||||
<Styled.IconContainer>
|
||||
<Icon iconName={IconName.Overview} />
|
||||
</Styled.IconContainer>
|
||||
),
|
||||
label: stringGetter({ key: STRING_KEYS.OVERVIEW }),
|
||||
href: PortfolioRoute.Overview,
|
||||
},
|
||||
{
|
||||
value: PortfolioRoute.Positions,
|
||||
slotBefore: <Styled.Icon iconName={IconName.Positions} />,
|
||||
slotBefore: (
|
||||
<Styled.IconContainer>
|
||||
<Icon iconName={IconName.Positions} />
|
||||
</Styled.IconContainer>
|
||||
),
|
||||
label: (
|
||||
<>
|
||||
{stringGetter({ key: STRING_KEYS.POSITIONS })}
|
||||
{numPositions > 0 && (
|
||||
<Tag type={TagType.Number}> {numPositions} </Tag>
|
||||
)}
|
||||
{numPositions &&
|
||||
(typeof numPositions === 'string' || numPositions > 0) && (
|
||||
<Tag type={TagType.Number}> {numPositions} </Tag>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
href: PortfolioRoute.Positions,
|
||||
},
|
||||
{
|
||||
value: PortfolioRoute.Orders,
|
||||
slotBefore: <Styled.Icon iconName={IconName.OrderPending} />,
|
||||
slotBefore: (
|
||||
<Styled.IconContainer>
|
||||
<Icon iconName={IconName.OrderPending} />
|
||||
</Styled.IconContainer>
|
||||
),
|
||||
label: (
|
||||
<>
|
||||
{stringGetter({ key: STRING_KEYS.ORDERS })}
|
||||
{numOrders > 0 && (
|
||||
{numOrders && (typeof numOrders === 'string' || numOrders > 0) && (
|
||||
<Tag type={TagType.Number}> {numOrders} </Tag>
|
||||
)}
|
||||
</>
|
||||
@ -151,13 +165,21 @@ export default () => {
|
||||
},
|
||||
{
|
||||
value: PortfolioRoute.Fees,
|
||||
slotBefore: <Styled.Icon iconName={IconName.Calculator} />,
|
||||
slotBefore: (
|
||||
<Styled.IconContainer>
|
||||
<Icon iconName={IconName.Calculator} />
|
||||
</Styled.IconContainer>
|
||||
),
|
||||
label: stringGetter({ key: STRING_KEYS.FEES }),
|
||||
href: PortfolioRoute.Fees,
|
||||
},
|
||||
{
|
||||
value: PortfolioRoute.History,
|
||||
slotBefore: <Styled.Icon iconName={IconName.History} />,
|
||||
slotBefore: (
|
||||
<Styled.IconContainer>
|
||||
<Icon iconName={IconName.History} />
|
||||
</Styled.IconContainer>
|
||||
),
|
||||
label: stringGetter({ key: STRING_KEYS.HISTORY }),
|
||||
href: PortfolioRoute.History,
|
||||
},
|
||||
@ -232,16 +254,14 @@ Styled.NavigationMenu = styled(NavigationMenu)`
|
||||
padding-top: 0;
|
||||
`;
|
||||
|
||||
Styled.Icon = styled(Icon)`
|
||||
--icon-backgroundColor: var(--color-layer-4);
|
||||
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
|
||||
margin-left: -0.25em;
|
||||
|
||||
box-sizing: content-box;
|
||||
background-color: var(--icon-backgroundColor);
|
||||
Styled.IconContainer = styled.div`
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
font-size: 0.75rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: var(--color-layer-4);
|
||||
border-radius: 50%;
|
||||
padding: 0.25em;
|
||||
margin-left: -0.25rem;
|
||||
`;
|
||||
|
||||
60
src/pages/token/Governance.tsx
Normal file
60
src/pages/token/Governance.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { useStringGetter } from '@/hooks';
|
||||
import { breakpoints } from '@/styles';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { DetachedSection } from '@/components/ContentSection';
|
||||
import { ContentSectionHeader } from '@/components/ContentSectionHeader';
|
||||
|
||||
import { GovernancePanel } from './rewards/GovernancePanel';
|
||||
import { NewMarketsPanel } from './rewards/NewMarketsPanel';
|
||||
|
||||
export default () => {
|
||||
const stringGetter = useStringGetter();
|
||||
|
||||
return (
|
||||
<DetachedSection>
|
||||
<Styled.HeaderSection>
|
||||
<ContentSectionHeader
|
||||
title={stringGetter({ key: STRING_KEYS.GOVERNANCE })}
|
||||
subtitle="Participate in the ecosystem by voting on Governance proposals or submitting your own."
|
||||
/>
|
||||
</Styled.HeaderSection>
|
||||
|
||||
<Styled.ContentWrapper>
|
||||
<Styled.Row>
|
||||
<GovernancePanel />
|
||||
<NewMarketsPanel />
|
||||
</Styled.Row>
|
||||
</Styled.ContentWrapper>
|
||||
</DetachedSection>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.HeaderSection = styled.section`
|
||||
${layoutMixins.contentSectionDetached}
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1rem;
|
||||
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.ContentWrapper = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1.5rem;
|
||||
max-width: 80rem;
|
||||
padding: 0 1rem;
|
||||
`;
|
||||
|
||||
Styled.Row = styled.div`
|
||||
gap: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
`;
|
||||
126
src/pages/token/Token.tsx
Normal file
126
src/pages/token/Token.tsx
Normal file
@ -0,0 +1,126 @@
|
||||
import { Suspense, lazy } from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { TokenRoute } from '@/constants/routes';
|
||||
import { useBreakpoints, useStringGetter } from '@/hooks';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { LoadingSpace } from '@/components/Loading/LoadingSpinner';
|
||||
import { NavigationMenu } from '@/components/NavigationMenu';
|
||||
import { WithSidebar } from '@/components/WithSidebar';
|
||||
|
||||
const RewardsPage = lazy(() => import('./rewards/RewardsPage'));
|
||||
const StakingPage = lazy(() => import('./staking/StakingPage'));
|
||||
const GovernancePage = lazy(() => import('./Governance'));
|
||||
|
||||
export default () => {
|
||||
const { isTablet } = useBreakpoints();
|
||||
const stringGetter = useStringGetter();
|
||||
|
||||
const routesComponent = (
|
||||
<Suspense fallback={<LoadingSpace id="token-page" />}>
|
||||
<Routes>
|
||||
<Route path={TokenRoute.TradingRewards} element={<RewardsPage />} />
|
||||
<Route path={TokenRoute.StakingRewards} element={<StakingPage />} />
|
||||
<Route path={TokenRoute.Governance} element={<GovernancePage />} />
|
||||
<Route path="*" element={<Navigate to={TokenRoute.TradingRewards} replace />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
return (
|
||||
<WithSidebar
|
||||
sidebar={
|
||||
isTablet ? null : (
|
||||
<Styled.SideBar>
|
||||
<Styled.NavigationMenu
|
||||
items={[
|
||||
{
|
||||
group: 'views',
|
||||
groupLabel: stringGetter({ key: STRING_KEYS.VIEWS }),
|
||||
items: [
|
||||
{
|
||||
value: TokenRoute.TradingRewards,
|
||||
slotBefore: (
|
||||
<Styled.IconContainer>
|
||||
<Icon iconName={IconName.Token} />
|
||||
</Styled.IconContainer>
|
||||
),
|
||||
label: stringGetter({ key: STRING_KEYS.TRADING_REWARDS }),
|
||||
href: TokenRoute.TradingRewards,
|
||||
},
|
||||
{
|
||||
value: TokenRoute.StakingRewards,
|
||||
slotBefore: (
|
||||
<Styled.IconContainer>
|
||||
<Icon iconName={IconName.CurrencySign} />
|
||||
</Styled.IconContainer>
|
||||
),
|
||||
label: 'Staking Rewards', // stringGetter({ key: STRING_KEYS.STAKING_REWARDS }),
|
||||
href: TokenRoute.StakingRewards,
|
||||
tag: stringGetter({ key: STRING_KEYS.NEW }),
|
||||
},
|
||||
{
|
||||
value: TokenRoute.Governance,
|
||||
slotBefore: (
|
||||
<Styled.IconContainer>
|
||||
<Icon iconName={IconName.Governance} />
|
||||
</Styled.IconContainer>
|
||||
),
|
||||
label: stringGetter({ key: STRING_KEYS.GOVERNANCE }),
|
||||
href: TokenRoute.Governance,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Styled.SideBar>
|
||||
)
|
||||
}
|
||||
>
|
||||
{routesComponent}
|
||||
</WithSidebar>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.SideBar = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
justify-content: space-between;
|
||||
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
Styled.Footer = styled.div`
|
||||
${layoutMixins.row}
|
||||
flex-wrap: wrap;
|
||||
|
||||
padding: 1rem;
|
||||
|
||||
gap: 0.5rem;
|
||||
|
||||
> button {
|
||||
flex-grow: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.NavigationMenu = styled(NavigationMenu)`
|
||||
padding: 0.5rem;
|
||||
padding-top: 0;
|
||||
`;
|
||||
|
||||
Styled.IconContainer = styled.div`
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
font-size: 0.75rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: var(--color-layer-4);
|
||||
border-radius: 50%;
|
||||
margin-left: -0.25rem;
|
||||
`;
|
||||
@ -14,13 +14,14 @@ import { Link } from '@/components/Link';
|
||||
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
|
||||
export const GovernancePanel = () => {
|
||||
export const GovernancePanel = ({ className }: { className?: string }) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const dispatch = useDispatch();
|
||||
const { governanceLearnMore } = useURLConfigs();
|
||||
|
||||
return (
|
||||
<Panel
|
||||
className={className}
|
||||
slotHeaderContent={
|
||||
<Styled.Title>{stringGetter({ key: STRING_KEYS.GOVERNANCE })}</Styled.Title>
|
||||
}
|
||||
@ -72,4 +73,4 @@ Styled.Title = styled.h3`
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
margin-bottom: -1rem;
|
||||
`;
|
||||
`;
|
||||
@ -28,7 +28,7 @@ import { log } from '@/lib/telemetry';
|
||||
|
||||
const SEASON_NUMBER = 2;
|
||||
|
||||
export const LaunchIncentivesPanel = () => {
|
||||
export const LaunchIncentivesPanel = ({ className }: { className?: string }) => {
|
||||
const { isNotTablet } = useBreakpoints();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@ -37,11 +37,15 @@ export const LaunchIncentivesPanel = () => {
|
||||
}, []);
|
||||
|
||||
return isNotTablet ? (
|
||||
<Styled.Panel slotHeader={<LaunchIncentivesTitle />} slotRight={<EstimatedRewards />}>
|
||||
<Styled.Panel
|
||||
className={className}
|
||||
slotHeader={<LaunchIncentivesTitle />}
|
||||
slotRight={<EstimatedRewards />}
|
||||
>
|
||||
<LaunchIncentivesContent />
|
||||
</Styled.Panel>
|
||||
) : (
|
||||
<Styled.Panel>
|
||||
<Styled.Panel className={className}>
|
||||
<Styled.Column>
|
||||
<EstimatedRewards />
|
||||
<LaunchIncentivesTitle />
|
||||
@ -19,21 +19,22 @@ import { Tag } from '@/components/Tag';
|
||||
import { MustBigNumber } from '@/lib/numbers';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
export const NewMarketsPanel = () => {
|
||||
export const NewMarketsPanel = ({ className }: { className?: string }) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const navigate = useNavigate();
|
||||
const { hasPotentialMarketsData } = usePotentialMarkets();
|
||||
const { chainTokenDecimals, chainTokenLabel } = useTokenConfigs();
|
||||
const { newMarketProposal } = useGovernanceVariables();
|
||||
const initialDepositAmountDecimals = isMainnet ? 0 : 11;
|
||||
const initialDepositAmountBN = MustBigNumber(newMarketProposal.initialDepositAmount).div(
|
||||
Number(`1e${chainTokenDecimals}`)
|
||||
);
|
||||
const initialDepositAmountDecimals = isMainnet ? 0 : chainTokenDecimals;
|
||||
|
||||
if (!hasPotentialMarketsData) return null;
|
||||
|
||||
return (
|
||||
<Panel
|
||||
className={className}
|
||||
slotHeaderContent={
|
||||
<Styled.Title>
|
||||
{stringGetter({ key: STRING_KEYS.ADD_A_MARKET })}
|
||||
@ -10,6 +10,8 @@ import { breakpoints } from '@/styles';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { BackButton } from '@/components/BackButton';
|
||||
import { DetachedSection } from '@/components/ContentSection';
|
||||
import { ContentSectionHeader } from '@/components/ContentSectionHeader';
|
||||
|
||||
import { testFlags } from '@/lib/testFlags';
|
||||
|
||||
@ -19,9 +21,6 @@ import { MigratePanel } from './MigratePanel';
|
||||
import { RewardsHelpPanel } from './RewardsHelpPanel';
|
||||
import { TradingRewardsSummaryPanel } from './TradingRewardsSummaryPanel';
|
||||
import { RewardHistoryPanel } from './RewardHistoryPanel';
|
||||
import { GovernancePanel } from './GovernancePanel';
|
||||
import { StakingPanel } from './StakingPanel';
|
||||
import { NewMarketsPanel } from './NewMarketsPanel';
|
||||
|
||||
const RewardsPage = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
@ -29,46 +28,45 @@ const RewardsPage = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Styled.Page>
|
||||
<div>
|
||||
{isTablet && (
|
||||
<Styled.MobileHeader>
|
||||
<BackButton onClick={() => navigate(AppRoute.Profile)} />
|
||||
{stringGetter({ key: STRING_KEYS.TRADING_REWARDS })}
|
||||
</Styled.MobileHeader>
|
||||
<ContentSectionHeader
|
||||
title={stringGetter({ key: STRING_KEYS.TRADING_REWARDS })}
|
||||
slotLeft={<BackButton onClick={() => navigate(AppRoute.Profile)} />}
|
||||
/>
|
||||
)}
|
||||
<Styled.GridLayout
|
||||
showTradingRewards={testFlags.showTradingRewards}
|
||||
showMigratePanel={import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet}
|
||||
>
|
||||
{import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet && <Styled.MigratePanel />}
|
||||
<DetachedSection>
|
||||
<Styled.GridLayout
|
||||
showTradingRewards={testFlags.showTradingRewards}
|
||||
showMigratePanel={import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet}
|
||||
>
|
||||
{import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet && <Styled.MigratePanel />}
|
||||
|
||||
{isTablet ? (
|
||||
<Styled.LaunchIncentivesPanel />
|
||||
) : (
|
||||
<>
|
||||
{isTablet ? (
|
||||
<Styled.LaunchIncentivesPanel />
|
||||
<Styled.DYDXBalancePanel />
|
||||
</>
|
||||
)}
|
||||
) : (
|
||||
<>
|
||||
<Styled.LaunchIncentivesPanel />
|
||||
<Styled.DYDXBalancePanel />
|
||||
</>
|
||||
)}
|
||||
|
||||
{testFlags.showTradingRewards && (
|
||||
<Styled.TradingRewardsColumn>
|
||||
<TradingRewardsSummaryPanel />
|
||||
{isTablet && <RewardsHelpPanel />}
|
||||
<RewardHistoryPanel />
|
||||
</Styled.TradingRewardsColumn>
|
||||
)}
|
||||
{testFlags.showTradingRewards && (
|
||||
<Styled.TradingRewardsColumn>
|
||||
<TradingRewardsSummaryPanel />
|
||||
{isTablet && <RewardsHelpPanel />}
|
||||
<RewardHistoryPanel />
|
||||
</Styled.TradingRewardsColumn>
|
||||
)}
|
||||
|
||||
{isNotTablet && (
|
||||
<Styled.OtherColumn showTradingRewards={testFlags.showTradingRewards}>
|
||||
<NewMarketsPanel />
|
||||
<GovernancePanel />
|
||||
<StakingPanel />
|
||||
<RewardsHelpPanel />
|
||||
</Styled.OtherColumn>
|
||||
)}
|
||||
</Styled.GridLayout>
|
||||
</Styled.Page>
|
||||
{isNotTablet && (
|
||||
<Styled.OtherColumn showTradingRewards={testFlags.showTradingRewards}>
|
||||
<RewardsHelpPanel />
|
||||
</Styled.OtherColumn>
|
||||
)}
|
||||
</Styled.GridLayout>
|
||||
</DetachedSection>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -76,27 +74,6 @@ export default RewardsPage;
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Page = styled.div`
|
||||
${layoutMixins.contentContainerPage}
|
||||
padding: 2rem;
|
||||
align-items: center;
|
||||
|
||||
> * {
|
||||
--content-max-width: 80rem;
|
||||
max-width: min(calc(100vw - 4rem), var(--content-max-width));
|
||||
}
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--stickyArea-topHeight: var(--page-header-height-mobile);
|
||||
padding: 0 1rem 1rem;
|
||||
|
||||
> * {
|
||||
max-width: calc(100vw - 2rem);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.MobileHeader = styled.header`
|
||||
${layoutMixins.contentSectionDetachedScrollable}
|
||||
${layoutMixins.stickyHeader}
|
||||
@ -113,6 +90,7 @@ Styled.GridLayout = styled.div<{ showTradingRewards?: boolean; showMigratePanel?
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: var(--gap);
|
||||
max-width: 80rem;
|
||||
|
||||
> * {
|
||||
gap: var(--gap);
|
||||
@ -123,24 +101,39 @@ Styled.GridLayout = styled.div<{ showTradingRewards?: boolean; showMigratePanel?
|
||||
? css`
|
||||
grid-template-areas:
|
||||
'migrate migrate'
|
||||
'incentives balance'
|
||||
'incentives incentives'
|
||||
'balance balance'
|
||||
'rewards other';
|
||||
`
|
||||
: showTradingRewards
|
||||
? css`
|
||||
grid-template-areas: 'incentives balance' 'rewards other';
|
||||
grid-template-areas:
|
||||
'incentives balance'
|
||||
'rewards other';
|
||||
`
|
||||
: showMigratePanel
|
||||
? css`
|
||||
grid-template-areas: 'migrate migrate' 'incentives balance' 'other other';
|
||||
grid-template-areas:
|
||||
'migrate migrate'
|
||||
'incentives incentives'
|
||||
'balance balance'
|
||||
'other other';
|
||||
`
|
||||
: css`
|
||||
grid-template-areas: 'incentives balance' 'other other';
|
||||
grid-template-areas:
|
||||
'incentives balance'
|
||||
'other other';
|
||||
`};
|
||||
|
||||
@media ${breakpoints.notTablet} {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--gap: 1rem;
|
||||
grid-template-columns: 1fr;
|
||||
width: calc(100vw - 2rem);
|
||||
margin: 0 auto;
|
||||
|
||||
${({ showTradingRewards }) =>
|
||||
showTradingRewards
|
||||
@ -187,13 +180,3 @@ Styled.OtherColumn = styled.div<{ showTradingRewards?: boolean }>`
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
Styled.RewardHistoryHeader = styled.div`
|
||||
h3 {
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
|
||||
padding: 1rem 1.5rem 0;
|
||||
margin-bottom: -0.5rem;
|
||||
`;
|
||||
@ -3,8 +3,8 @@ import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { useStringGetter, useTokenConfigs } from '@/hooks';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { AssetIcon } from '@/components/AssetIcon';
|
||||
import { Details } from '@/components/Details';
|
||||
@ -18,7 +18,10 @@ import abacusStateManager from '@/lib/abacus';
|
||||
export const TradingRewardsSummaryPanel = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
const currentWeekTradingReward = useSelector(getHistoricalTradingRewardsForCurrentWeek, shallowEqual);
|
||||
const currentWeekTradingReward = useSelector(
|
||||
getHistoricalTradingRewardsForCurrentWeek,
|
||||
shallowEqual
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
abacusStateManager.refreshHistoricalTradingRewards();
|
||||
67
src/pages/token/staking/StakingPage.tsx
Normal file
67
src/pages/token/staking/StakingPage.tsx
Normal file
@ -0,0 +1,67 @@
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { breakpoints } from '@/styles';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { DetachedSection } from '@/components/ContentSection';
|
||||
import { ContentSectionHeader } from '@/components/ContentSectionHeader';
|
||||
|
||||
import { StakingPanel } from './StakingPanel';
|
||||
import { StrideStakingPanel } from './StrideStakingPanel';
|
||||
import { DYDXBalancePanel } from '../rewards/DYDXBalancePanel';
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<DetachedSection>
|
||||
<Styled.HeaderSection>
|
||||
<ContentSectionHeader
|
||||
title="Staking Rewards"
|
||||
subtitle="Stake to earn APR. Unstaking can take up to 30 days."
|
||||
/>
|
||||
</Styled.HeaderSection>
|
||||
|
||||
<Styled.ContentWrapper>
|
||||
<Styled.Row>
|
||||
<Styled.InnerRow>
|
||||
<StrideStakingPanel />
|
||||
<StakingPanel />
|
||||
</Styled.InnerRow>
|
||||
<DYDXBalancePanel />
|
||||
</Styled.Row>
|
||||
</Styled.ContentWrapper>
|
||||
</DetachedSection>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.HeaderSection = styled.section`
|
||||
${layoutMixins.contentSectionDetached}
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1rem;
|
||||
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.ContentWrapper = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1.5rem;
|
||||
max-width: 80rem;
|
||||
padding: 0 1rem;
|
||||
`;
|
||||
|
||||
Styled.Row = styled.div`
|
||||
gap: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
`;
|
||||
|
||||
Styled.InnerRow = styled.div`
|
||||
gap: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
height: fit-content;
|
||||
`;
|
||||
@ -1,7 +1,6 @@
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { ButtonAction, ButtonSize } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
|
||||
@ -14,22 +13,19 @@ import { Link } from '@/components/Link';
|
||||
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
|
||||
export const StakingPanel = () => {
|
||||
export const StakingPanel = ({ className }: { className?: string }) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const dispatch = useDispatch();
|
||||
const { stakingLearnMore } = useURLConfigs();
|
||||
|
||||
return (
|
||||
<Panel
|
||||
slotHeaderContent={<Styled.Title>{stringGetter({ key: STRING_KEYS.STAKING })}</Styled.Title>}
|
||||
slotRight={
|
||||
<Styled.Arrow>
|
||||
<Styled.IconButton
|
||||
action={ButtonAction.Base}
|
||||
iconName={IconName.Arrow}
|
||||
size={ButtonSize.Small}
|
||||
/>
|
||||
</Styled.Arrow>
|
||||
<Styled.Panel
|
||||
className={className}
|
||||
slotHeaderContent={
|
||||
<Styled.Header>
|
||||
<Styled.Title>Stake with Keplr</Styled.Title>
|
||||
<Styled.Img src="/third-party/keplr.png" alt={stringGetter({ key: STRING_KEYS.KEPLR })} />
|
||||
</Styled.Header>
|
||||
}
|
||||
onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
|
||||
>
|
||||
@ -39,12 +35,40 @@ export const StakingPanel = () => {
|
||||
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||
</Link>
|
||||
</Styled.Description>
|
||||
</Panel>
|
||||
</Styled.Panel>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Panel = styled(Panel)`
|
||||
align-items: start;
|
||||
|
||||
header {
|
||||
justify-content: unset;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Header = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
`;
|
||||
|
||||
Styled.Img = styled.img`
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin-left: 0.5rem;
|
||||
`;
|
||||
|
||||
Styled.Description = styled.div`
|
||||
color: var(--color-text-0);
|
||||
--link-color: var(--color-text-1);
|
||||
@ -61,13 +85,3 @@ Styled.IconButton = styled(IconButton)`
|
||||
color: var(--color-text-0);
|
||||
--color-border: var(--color-layer-6);
|
||||
`;
|
||||
|
||||
Styled.Arrow = styled.div`
|
||||
padding-right: 1.5rem;
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
margin-bottom: -1rem;
|
||||
`;
|
||||
94
src/pages/token/staking/StrideStakingPanel.tsx
Normal file
94
src/pages/token/staking/StrideStakingPanel.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { useStringGetter, useTokenConfigs, useURLConfigs } from '@/hooks';
|
||||
|
||||
import { IconButton } from '@/components/IconButton';
|
||||
import { Link } from '@/components/Link';
|
||||
import { Panel } from '@/components/Panel';
|
||||
import { Tag } from '@/components/Tag';
|
||||
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
|
||||
export const StrideStakingPanel = ({ className }: { className?: string }) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const dispatch = useDispatch();
|
||||
const { stakingLearnMore } = useURLConfigs();
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
|
||||
return (
|
||||
<Styled.Panel
|
||||
className={className}
|
||||
slotHeaderContent={
|
||||
<Styled.Header>
|
||||
<Styled.Title>
|
||||
Liquid Stake with Stride
|
||||
<Tag isHighlighted>{stringGetter({ key: STRING_KEYS.NEW })}</Tag>
|
||||
</Styled.Title>
|
||||
<Styled.Img src="/third-party/stride.png" alt="Stride" />
|
||||
</Styled.Header>
|
||||
}
|
||||
onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavStride }))}
|
||||
>
|
||||
<Styled.Description>
|
||||
{`Stake your ${chainTokenLabel} tokens for st${chainTokenLabel} which you can deploy around the ecosystem.`}
|
||||
<Link href={stakingLearnMore} onClick={(e) => e.stopPropagation()}>
|
||||
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||
</Link>
|
||||
</Styled.Description>
|
||||
</Styled.Panel>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Panel = styled(Panel)`
|
||||
align-items: start;
|
||||
|
||||
header {
|
||||
justify-content: unset;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Header = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5ch;
|
||||
`;
|
||||
|
||||
Styled.Img = styled.img`
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin-left: 0.5rem;
|
||||
`;
|
||||
|
||||
Styled.Description = styled.div`
|
||||
color: var(--color-text-0);
|
||||
--link-color: var(--color-text-1);
|
||||
|
||||
a {
|
||||
display: inline;
|
||||
::before {
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.IconButton = styled(IconButton)`
|
||||
color: var(--color-text-0);
|
||||
--color-border: var(--color-layer-6);
|
||||
`;
|
||||
@ -1,3 +1,4 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { ButtonAction, ButtonType } from '@/constants/buttons';
|
||||
@ -10,18 +11,26 @@ import { Dialog } from '@/components/Dialog';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
type ElementProps = {
|
||||
buttonText?: ReactNode;
|
||||
link: string;
|
||||
linkDescription?: string;
|
||||
title?: ReactNode;
|
||||
setIsOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export const ExternalLinkDialog = ({ setIsOpen, link, linkDescription }: ElementProps) => {
|
||||
export const ExternalLinkDialog = ({
|
||||
setIsOpen,
|
||||
buttonText,
|
||||
link,
|
||||
linkDescription,
|
||||
title,
|
||||
}: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
return (
|
||||
<Dialog
|
||||
isOpen
|
||||
setIsOpen={setIsOpen}
|
||||
title={stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE })}
|
||||
title={title ?? stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE })}
|
||||
description={
|
||||
linkDescription ?? stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE_DESCRIPTION })
|
||||
}
|
||||
@ -29,7 +38,7 @@ export const ExternalLinkDialog = ({ setIsOpen, link, linkDescription }: Element
|
||||
<Styled.Content>
|
||||
<p>{stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE_DISCLAIMER })}.</p>
|
||||
<Button type={ButtonType.Link} action={ButtonAction.Primary} href={link}>
|
||||
{stringGetter({ key: STRING_KEYS.CONTINUE })}
|
||||
{buttonText ?? stringGetter({ key: STRING_KEYS.CONTINUE })}
|
||||
</Button>
|
||||
</Styled.Content>
|
||||
</Dialog>
|
||||
|
||||
137
src/views/dialogs/ExternalNavStrideDialog.tsx
Normal file
137
src/views/dialogs/ExternalNavStrideDialog.tsx
Normal file
@ -0,0 +1,137 @@
|
||||
import { useCallback } from 'react';
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { useBreakpoints, useStringGetter, useURLConfigs } from '@/hooks';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Button } from '@/components/Button';
|
||||
import { Dialog, DialogPlacement } from '@/components/Dialog';
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { IconButton } from '@/components/IconButton';
|
||||
|
||||
import { closeDialog, openDialog } from '@/state/dialogs';
|
||||
|
||||
type ElementProps = {
|
||||
setIsOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export const ExternalNavStrideDialog = ({ setIsOpen }: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { strideZoneApp, accountExportLearnMore } = useURLConfigs();
|
||||
const dispatch = useDispatch();
|
||||
const { isTablet } = useBreakpoints();
|
||||
|
||||
const openExternalNavDialog = useCallback(() => {
|
||||
dispatch(closeDialog());
|
||||
dispatch(
|
||||
openDialog({
|
||||
type: DialogTypes.ExternalLink,
|
||||
dialogProps: {
|
||||
buttonText: (
|
||||
<Styled.Span>
|
||||
Liquid Stake on Stride <Icon iconName={IconName.LinkOut} />
|
||||
</Styled.Span>
|
||||
),
|
||||
link: strideZoneApp,
|
||||
title: 'Liquid staking and leaving website',
|
||||
},
|
||||
})
|
||||
);
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
isOpen
|
||||
setIsOpen={setIsOpen}
|
||||
title={stringGetter({ key: STRING_KEYS.HAVE_YOU_EXPORTED })}
|
||||
placement={isTablet ? DialogPlacement.FullScreen : DialogPlacement.Default}
|
||||
>
|
||||
<Styled.Content>
|
||||
<Styled.Button
|
||||
type={ButtonType.Button}
|
||||
size={ButtonSize.XLarge}
|
||||
onClick={openExternalNavDialog}
|
||||
>
|
||||
<span>
|
||||
<strong>{stringGetter({ key: STRING_KEYS.YES })}</strong>, Navigate to Stride Zone.
|
||||
</span>
|
||||
|
||||
<Styled.IconButton
|
||||
action={ButtonAction.Base}
|
||||
iconName={IconName.Arrow}
|
||||
size={ButtonSize.XSmall}
|
||||
/>
|
||||
</Styled.Button>
|
||||
|
||||
<Styled.Button
|
||||
type={ButtonType.Link}
|
||||
size={ButtonSize.XLarge}
|
||||
href={accountExportLearnMore}
|
||||
>
|
||||
<span>
|
||||
{stringGetter({
|
||||
key: STRING_KEYS.LEARN_TO_EXPORT,
|
||||
params: {
|
||||
STRONG_NO: <strong>{stringGetter({ key: STRING_KEYS.NO })}</strong>,
|
||||
},
|
||||
})}
|
||||
</span>
|
||||
|
||||
<Styled.IconButton
|
||||
action={ButtonAction.Base}
|
||||
iconName={IconName.Arrow}
|
||||
size={ButtonSize.XSmall}
|
||||
/>
|
||||
</Styled.Button>
|
||||
</Styled.Content>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.TextToggle = styled.div`
|
||||
${layoutMixins.stickyFooter}
|
||||
color: var(--color-accent);
|
||||
cursor: pointer;
|
||||
|
||||
margin-top: auto;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Content = styled.div`
|
||||
${layoutMixins.stickyArea0}
|
||||
--stickyArea0-bottomHeight: 2rem;
|
||||
--stickyArea0-bottomGap: 1rem;
|
||||
--stickyArea0-totalInsetBottom: 0.5rem;
|
||||
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1rem;
|
||||
`;
|
||||
|
||||
Styled.Button = styled(Button)`
|
||||
--button-font: var(--font-base-book);
|
||||
--button-padding: 0 1.5rem;
|
||||
|
||||
gap: 0;
|
||||
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
Styled.IconButton = styled(IconButton)`
|
||||
color: var(--color-text-0);
|
||||
--color-border: var(--color-layer-6);
|
||||
`;
|
||||
|
||||
Styled.Span = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5ch;
|
||||
`;
|
||||
@ -1,4 +1,4 @@
|
||||
import { memo } from 'react';
|
||||
import { ElementType, memo } from 'react';
|
||||
import styled, { AnyStyledComponent, css } from 'styled-components';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import type { Dispatch } from '@reduxjs/toolkit';
|
||||
@ -106,7 +106,7 @@ export const AccountMenu = () => {
|
||||
{walletType && (
|
||||
<Styled.SourceIcon>
|
||||
<Styled.ConnectorIcon iconName={IconName.AddressConnector} />
|
||||
<Icon iconComponent={wallets[walletType].icon} />
|
||||
<Icon iconComponent={wallets[walletType].icon as ElementType} />
|
||||
</Styled.SourceIcon>
|
||||
)}
|
||||
<Styled.Column>
|
||||
@ -220,7 +220,7 @@ export const AccountMenu = () => {
|
||||
{onboardingState === OnboardingState.WalletConnected ? (
|
||||
<Styled.WarningIcon iconName={IconName.Warning} />
|
||||
) : onboardingState === OnboardingState.AccountConnected ? (
|
||||
walletType && <Icon iconComponent={wallets[walletType].icon} />
|
||||
walletType && <Icon iconComponent={wallets[walletType].icon as ElementType} />
|
||||
) : null}
|
||||
{!isTablet && <Styled.Address>{truncateAddress(dydxAddress)}</Styled.Address>}
|
||||
</Styled.DropdownMenu>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user