Add chaos labs launch incentives panel (#182)
* add chaos lab incentives panel * add seen state * external link dialog * better disconnected state
This commit is contained in:
@@ -38,6 +38,7 @@ import {
|
||||
HelpCircleIcon,
|
||||
HideIcon,
|
||||
HistoryIcon,
|
||||
LeaderboardIcon,
|
||||
LinkOutIcon,
|
||||
LockIcon,
|
||||
LogoShortIcon,
|
||||
@@ -111,6 +112,7 @@ export enum IconName {
|
||||
HelpCircle = 'HelpCircle',
|
||||
Hide = 'Hide',
|
||||
History = 'History',
|
||||
Leaderboard = 'Leaderboard',
|
||||
LinkOut = 'LinkOut',
|
||||
Lock = 'Lock',
|
||||
LogoShort = 'LogoShort',
|
||||
@@ -185,6 +187,7 @@ const icons = {
|
||||
[IconName.HelpCircle]: HelpCircleIcon,
|
||||
[IconName.Hide]: HideIcon,
|
||||
[IconName.History]: HistoryIcon,
|
||||
[IconName.Leaderboard]: LeaderboardIcon,
|
||||
[IconName.LinkOut]: LinkOutIcon,
|
||||
[IconName.Lock]: LockIcon,
|
||||
[IconName.LogoShort]: LogoShortIcon,
|
||||
|
||||
@@ -3,6 +3,7 @@ export enum DialogTypes {
|
||||
Deposit = 'Deposit',
|
||||
DisconnectWallet = 'DisconnectWallet',
|
||||
ExchangeOffline = 'ExchangeOffline',
|
||||
ExternalLink = 'ExternalLink',
|
||||
FillDetails = 'FillDetails',
|
||||
Help = 'Help',
|
||||
ExternalNavKeplr = 'ExternalNavKeplr',
|
||||
|
||||
@@ -22,6 +22,7 @@ export enum LocalStorageKey {
|
||||
SelectedTheme = 'dydx.SelectedTheme',
|
||||
SelectedTradeLayout = 'dydx.SelectedTradeLayout',
|
||||
TradingViewChartConfig = 'dydx.TradingViewChartConfig',
|
||||
HasSeenLaunchIncentives = 'dydx.HasSeenLaunchIncentives',
|
||||
}
|
||||
|
||||
export const LOCAL_STORAGE_VERSIONS = {
|
||||
|
||||
@@ -220,7 +220,7 @@ export const notificationTypes: NotificationTypeConfig[] = [
|
||||
body: stringGetter({
|
||||
key: 'NOTIFICATIONS.RELEASE_REWARDS_AND_FULL_TRADING.BODY',
|
||||
params: {
|
||||
BLOGPOST: (
|
||||
DOS_BLOGPOST: (
|
||||
<$Link
|
||||
href="https://www.dydxopsdao.com/blog/deep-dive-full-trading"
|
||||
target="_blank"
|
||||
@@ -229,8 +229,7 @@ export const notificationTypes: NotificationTypeConfig[] = [
|
||||
{stringGetter({ key: STRING_KEYS.HERE })}
|
||||
</$Link>
|
||||
),
|
||||
// todo: update localization to flip the two
|
||||
DOS_BLOGPOST: (
|
||||
TRADING_BLOGPOST: (
|
||||
<$Link
|
||||
href="https://dydx.exchange/blog/v4-full-trading"
|
||||
target="_blank"
|
||||
|
||||
@@ -32,6 +32,7 @@ export { default as GiftboxIcon } from './giftbox.svg';
|
||||
export { default as HelpCircleIcon } from './help-circle.svg';
|
||||
export { default as HideIcon } from './hide.svg';
|
||||
export { default as HistoryIcon } from './history.svg';
|
||||
export { default as LeaderboardIcon } from './leaderboard.svg';
|
||||
export { default as LinkOutIcon } from './link-out.svg';
|
||||
export { default as LockIcon } from './lock.svg';
|
||||
export { default as LogoShortIcon } from './logo-short';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="13" height="14" viewBox="0 0 13 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.0996094 2.60156C0.0996094 2.07113 0.310323 1.56242 0.685396 1.18735C1.06047 0.812276 1.56918 0.601563 2.09961 0.601562H10.8996C11.43 0.601563 11.9387 0.812276 12.3138 1.18735C12.6889 1.56242 12.8996 2.07113 12.8996 2.60156C12.8996 3.132 12.6889 3.6407 12.3138 4.01578C11.9387 4.39085 11.43 4.60156 10.8996 4.60156H2.09961C1.56918 4.60156 1.06047 4.39085 0.685396 4.01578C0.310323 3.6407 0.0996094 3.132 0.0996094 2.60156ZM0.699609 6.26796C0.540479 6.26796 0.387867 6.33118 0.275345 6.4437C0.162823 6.55622 0.0996094 6.70883 0.0996094 6.86796C0.0996094 7.02709 0.162823 7.1797 0.275345 7.29223C0.387867 7.40475 0.540479 7.46796 0.699609 7.46796H12.2996C12.4587 7.46796 12.6114 7.40475 12.7239 7.29223C12.8364 7.1797 12.8996 7.02709 12.8996 6.86796C12.8996 6.70883 12.8364 6.55622 12.7239 6.4437C12.6114 6.33118 12.4587 6.26796 12.2996 6.26796H0.699609ZM0.699609 9.13196C0.540479 9.13196 0.387867 9.19518 0.275345 9.3077C0.162823 9.42022 0.0996094 9.57283 0.0996094 9.73196C0.0996094 9.89109 0.162823 10.0437 0.275345 10.1562C0.387867 10.2687 0.540479 10.332 0.699609 10.332H12.2996C12.4587 10.332 12.6114 10.2687 12.7239 10.1562C12.8364 10.0437 12.8996 9.89109 12.8996 9.73196C12.8996 9.57283 12.8364 9.42022 12.7239 9.3077C12.6114 9.19518 12.4587 9.13196 12.2996 9.13196H0.699609ZM0.699609 12.0016C0.540479 12.0016 0.387867 12.0648 0.275345 12.1773C0.162823 12.2898 0.0996094 12.4424 0.0996094 12.6016C0.0996094 12.7607 0.162823 12.9133 0.275345 13.0258C0.387867 13.1383 0.540479 13.2016 0.699609 13.2016H12.2996C12.4587 13.2016 12.6114 13.1383 12.7239 13.0258C12.8364 12.9133 12.8996 12.7607 12.8996 12.6016C12.8996 12.4424 12.8364 12.2898 12.7239 12.1773C12.6114 12.0648 12.4587 12.0016 12.2996 12.0016H0.699609Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -11,6 +11,7 @@ import { DepositDialog } from '@/views/dialogs/DepositDialog';
|
||||
import { DisconnectDialog } from '@/views/dialogs/DisconnectDialog';
|
||||
import { ExchangeOfflineDialog } from '@/views/dialogs/ExchangeOfflineDialog';
|
||||
import { HelpDialog } from '@/views/dialogs/HelpDialog';
|
||||
import { ExternalLinkDialog } from '@/views/dialogs/ExternalLinkDialog';
|
||||
import { ExternalNavKeplrDialog } from '@/views/dialogs/ExternalNavKeplrDialog';
|
||||
import { MnemonicExportDialog } from '@/views/dialogs/MnemonicExportDialog';
|
||||
import { MobileSignInDialog } from '@/views/dialogs/MobileSignInDialog';
|
||||
@@ -53,6 +54,7 @@ export const DialogManager = () => {
|
||||
[DialogTypes.FillDetails]: <FillDetailsDialog {...modalProps} />,
|
||||
[DialogTypes.Help]: <HelpDialog {...modalProps} />,
|
||||
[DialogTypes.ExternalNavKeplr]: <ExternalNavKeplrDialog {...modalProps} />,
|
||||
[DialogTypes.ExternalLink]: <ExternalLinkDialog {...modalProps} />,
|
||||
[DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />,
|
||||
[DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />,
|
||||
[DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { ButtonShape } from '@/constants/buttons';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
@@ -8,6 +8,10 @@ import { STRING_KEYS } from '@/constants/localization';
|
||||
import { AppRoute } from '@/constants/routes';
|
||||
import { LogoShortIcon, BellStrokeIcon } from '@/icons';
|
||||
|
||||
import { headerMixins } from '@/styles/headerMixins';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import breakpoints from '@/styles/breakpoints';
|
||||
|
||||
import { useTokenConfigs, useStringGetter, useURLConfigs } from '@/hooks';
|
||||
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
@@ -21,10 +25,7 @@ import { NotificationsMenu } from '@/views/menus/NotificationsMenu';
|
||||
import { LanguageSelector } from '@/views/menus/LanguageSelector';
|
||||
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
|
||||
import { headerMixins } from '@/styles/headerMixins';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import breakpoints from '@/styles/breakpoints';
|
||||
import { getHasSeenLaunchIncentives } from '@/state/configsSelectors';
|
||||
|
||||
export const HeaderDesktop = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
@@ -32,6 +33,8 @@ export const HeaderDesktop = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
|
||||
const hasSeenLaunchIncentives = useSelector(getHasSeenLaunchIncentives);
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
group: 'navigation',
|
||||
@@ -55,6 +58,7 @@ export const HeaderDesktop = () => {
|
||||
value: chainTokenLabel,
|
||||
label: chainTokenLabel,
|
||||
href: `/${chainTokenLabel}`,
|
||||
slotAfter: !hasSeenLaunchIncentives && <Styled.UnreadIndicator />,
|
||||
},
|
||||
{
|
||||
value: 'MORE',
|
||||
@@ -238,3 +242,10 @@ Styled.IconButton = styled(IconButton)<{ size?: string }>`
|
||||
--button-icon-size: 1rem;
|
||||
--button-padding: 0 0.5em;
|
||||
`;
|
||||
|
||||
Styled.UnreadIndicator = styled.div`
|
||||
width: 0.4375rem;
|
||||
height: 0.4375rem;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-accent);
|
||||
`;
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { ElementType } from 'react';
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import breakpoints from '@/styles/breakpoints';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { useAccountBalance, useAccounts, useTokenConfigs, useStringGetter } from '@/hooks';
|
||||
|
||||
@@ -116,12 +117,17 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Panel = styled(Panel)`
|
||||
--panel-paddingX: 1.5rem;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--panel-paddingY: 1.5rem;
|
||||
--panel-content-paddingY: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Header = styled.div`
|
||||
${layoutMixins.spacedRow}
|
||||
gap: 1rem;
|
||||
padding: 1rem 1.5rem 0;
|
||||
padding: var(--panel-paddingY) var(--panel-paddingX) 0;
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useQuery } from 'react-query';
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { ButtonAction } from '@/constants/buttons';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
|
||||
import breakpoints from '@/styles/breakpoints';
|
||||
import { useAccounts, useBreakpoints, useStringGetter } from '@/hooks';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Panel } from '@/components/Panel';
|
||||
import { Button } from '@/components/Button';
|
||||
|
||||
import { Output, OutputType } from '@/components/Output';
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { Tag, TagSize } from '@/components/Tag';
|
||||
|
||||
import { markLaunchIncentivesSeen } from '@/state/configs';
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
|
||||
import { log } from '@/lib/telemetry';
|
||||
|
||||
export const LaunchIncentivesPanel = () => {
|
||||
const { isNotTablet } = useBreakpoints();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(markLaunchIncentivesSeen());
|
||||
}, []);
|
||||
|
||||
return isNotTablet ? (
|
||||
<Styled.Panel slotHeader={<LaunchIncentivesTitle />} slotRight={<EstimatedRewards />}>
|
||||
<LaunchIncentivesContent />
|
||||
</Styled.Panel>
|
||||
) : (
|
||||
<Styled.Panel>
|
||||
<Styled.Column>
|
||||
<EstimatedRewards />
|
||||
<LaunchIncentivesTitle />
|
||||
<LaunchIncentivesContent />
|
||||
</Styled.Column>
|
||||
</Styled.Panel>
|
||||
);
|
||||
};
|
||||
|
||||
const LaunchIncentivesTitle = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
return (
|
||||
<Styled.Title>
|
||||
{stringGetter({
|
||||
key: STRING_KEYS.LAUNCH_INCENTIVES_TITLE,
|
||||
params: {
|
||||
FOR_V4: <Styled.ForV4>{stringGetter({ key: STRING_KEYS.FOR_V4 })}</Styled.ForV4>,
|
||||
},
|
||||
})}
|
||||
<Styled.NewTag size={TagSize.Medium}>{stringGetter({ key: STRING_KEYS.NEW })}</Styled.NewTag>
|
||||
</Styled.Title>
|
||||
);
|
||||
};
|
||||
|
||||
const EstimatedRewards = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { dydxAddress } = useAccounts();
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
enabled: !!dydxAddress,
|
||||
queryKey: `launch_incentives_rewards_${dydxAddress ?? ''}`,
|
||||
queryFn: async () => {
|
||||
if (!dydxAddress) return undefined;
|
||||
const resp = await fetch(`https://cloud.chaoslabs.co/query/api/dydx/points/${dydxAddress}`);
|
||||
return (await resp.json())?.incentivePoints;
|
||||
},
|
||||
onError: (error: Error) => log('LaunchIncentives/fetchPoints', error),
|
||||
});
|
||||
|
||||
return (
|
||||
<Styled.EstimatedRewardsCard>
|
||||
<Styled.EstimatedRewardsCardContent>
|
||||
<div>
|
||||
<span>{stringGetter({ key: STRING_KEYS.ESTIMATED_REWARDS })}</span>
|
||||
<Styled.Season>
|
||||
{stringGetter({
|
||||
key: STRING_KEYS.LAUNCH_INCENTIVES_SEASON_NUM,
|
||||
params: {
|
||||
SEASON_NUMBER: 1,
|
||||
},
|
||||
})}
|
||||
</Styled.Season>
|
||||
</div>
|
||||
|
||||
<Styled.Points>
|
||||
<Output type={OutputType.Number} value={data} isLoading={isLoading} fractionDigits={2} />
|
||||
{data !== undefined && stringGetter({ key: STRING_KEYS.POINTS })}
|
||||
</Styled.Points>
|
||||
</Styled.EstimatedRewardsCardContent>
|
||||
|
||||
<Styled.Image src="/rewards-stars.svg" />
|
||||
</Styled.EstimatedRewardsCard>
|
||||
);
|
||||
};
|
||||
|
||||
const LaunchIncentivesContent = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<Styled.Column>
|
||||
<Styled.Description>
|
||||
{stringGetter({ key: STRING_KEYS.LAUNCH_INCENTIVES_DESCRIPTION })}{' '}
|
||||
</Styled.Description>
|
||||
<Styled.ChaosLabsLogo src="/logos/chaos-labs.svg" />
|
||||
<Styled.ButtonRow>
|
||||
<Styled.AboutButton
|
||||
action={ButtonAction.Base}
|
||||
onClick={() => {
|
||||
dispatch(
|
||||
openDialog({
|
||||
type: DialogTypes.ExternalLink,
|
||||
dialogProps: { link: 'https://dydx.exchange/blog/v4-full-trading' },
|
||||
})
|
||||
);
|
||||
}}
|
||||
slotRight={<Styled.LinkOutIcon iconName={IconName.LinkOut} />}
|
||||
>
|
||||
{stringGetter({ key: STRING_KEYS.ABOUT })}
|
||||
</Styled.AboutButton>
|
||||
<Styled.Button
|
||||
action={ButtonAction.Primary}
|
||||
onClick={() => {
|
||||
dispatch(
|
||||
openDialog({
|
||||
type: DialogTypes.ExternalLink,
|
||||
dialogProps: { link: 'https://community.chaoslabs.xyz/dydx-v4/risk/leaderboard' },
|
||||
})
|
||||
);
|
||||
}}
|
||||
slotRight={<Styled.LinkOutIcon iconName={IconName.LinkOut} />}
|
||||
slotLeft={<Icon iconName={IconName.Leaderboard} />}
|
||||
>
|
||||
{stringGetter({ key: STRING_KEYS.LEADERBOARD })}
|
||||
</Styled.Button>
|
||||
</Styled.ButtonRow>
|
||||
</Styled.Column>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Panel = styled(Panel)`
|
||||
--panel-paddingY: 1rem;
|
||||
--panel-paddingX: 1.5rem;
|
||||
|
||||
background-color: var(--color-layer-4);
|
||||
width: 100%;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--panel-paddingY: 1.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.ForV4 = styled.span`
|
||||
color: var(--color-text-0);
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
${layoutMixins.inlineRow}
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
|
||||
@media ${breakpoints.notTablet} {
|
||||
padding: var(--panel-paddingY) var(--panel-paddingX) 0;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Description = styled.div`
|
||||
color: var(--color-text-0);
|
||||
--link-color: var(--color-text-1);
|
||||
|
||||
a {
|
||||
display: inline;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.25rem;
|
||||
|
||||
::before {
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.ButtonRow = styled.div`
|
||||
${layoutMixins.inlineRow}
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
a:last-child {
|
||||
--button-width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Button = styled(Button)`
|
||||
--button-padding: 0 1rem;
|
||||
`;
|
||||
|
||||
Styled.LinkOutIcon = styled(Icon)`
|
||||
color: var(--color-text-1);
|
||||
`;
|
||||
|
||||
Styled.AboutButton = styled(Styled.Button)`
|
||||
--button-textColor: var(--color-text-2);
|
||||
--button-backgroundColor: var(--color-layer-6);
|
||||
--button-border: solid var(--border-width) var(--color-layer-7);
|
||||
`;
|
||||
|
||||
Styled.Column = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 0.5rem;
|
||||
`;
|
||||
|
||||
Styled.EstimatedRewardsCard = styled.div`
|
||||
${layoutMixins.spacedRow}
|
||||
padding: 1rem 1.25rem;
|
||||
min-width: 21.25rem;
|
||||
height: calc(100% - calc(1.5rem * 2));
|
||||
margin: 1.5rem;
|
||||
|
||||
background-color: var(--color-layer-5);
|
||||
background-image: url('/dots-background.svg');
|
||||
background-size: cover;
|
||||
|
||||
border-radius: 0.75rem;
|
||||
border: solid var(--border-width) var(--color-layer-6);
|
||||
color: var(--color-text-1);
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.EstimatedRewardsCardContent = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1rem;
|
||||
height: 100%;
|
||||
justify-content: space-between;
|
||||
|
||||
div {
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 0.15rem;
|
||||
font: var(--font-medium-book);
|
||||
|
||||
:first-child {
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.BackgroundDots = styled.img`
|
||||
position: absolute;
|
||||
`;
|
||||
|
||||
Styled.Season = styled.span`
|
||||
font: var(--font-small-book);
|
||||
color: var(--color-text-1);
|
||||
`;
|
||||
|
||||
Styled.Points = styled.span`
|
||||
${layoutMixins.inlineRow}
|
||||
gap: 0.25rem;
|
||||
font: var(--font-large-book);
|
||||
color: var(--color-text-0);
|
||||
|
||||
output {
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Image = styled.img`
|
||||
position: relative;
|
||||
float: right;
|
||||
|
||||
width: 5.25rem;
|
||||
height: auto;
|
||||
`;
|
||||
|
||||
Styled.ChaosLabsLogo = styled.img`
|
||||
height: 1.25rem;
|
||||
align-self: start;
|
||||
`;
|
||||
|
||||
Styled.NewTag = styled(Tag)`
|
||||
color: var(--color-accent);
|
||||
background-color: var(--color-accent-faded);
|
||||
`;
|
||||
@@ -7,6 +7,8 @@ import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||
|
||||
import { useAccountBalance, useBreakpoints, useStringGetter } from '@/hooks';
|
||||
|
||||
import { breakpoints } from '@/styles';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Details } from '@/components/Details';
|
||||
@@ -134,13 +136,22 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
||||
Styled.MigratePanel = styled(Panel)`
|
||||
--panel-paddingX: 1.5rem;
|
||||
width: 100%;
|
||||
|
||||
background-image: url('/dots-background.svg');
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--panel-paddingY: 1.5rem;
|
||||
--panel-content-paddingY: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
|
||||
padding: 1rem 1.5rem 0;
|
||||
padding: var(--panel-paddingY) var(--panel-paddingX) 0;
|
||||
`;
|
||||
|
||||
Styled.MigrateAction = styled.div`
|
||||
@@ -164,6 +175,13 @@ Styled.Token = styled(Output)`
|
||||
Styled.Description = styled.div`
|
||||
color: var(--color-text-0);
|
||||
--link-color: var(--color-text-1);
|
||||
|
||||
a {
|
||||
display: inline;
|
||||
::before {
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Column = styled.div`
|
||||
|
||||
@@ -19,6 +19,7 @@ import { openDialog } from '@/state/dialogs';
|
||||
|
||||
import { DYDXBalancePanel } from './DYDXBalancePanel';
|
||||
import { MigratePanel } from './MigratePanel';
|
||||
import { LaunchIncentivesPanel } from './LaunchIncentivesPanel';
|
||||
|
||||
export const RewardsPage = () => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -39,9 +40,20 @@ export const RewardsPage = () => {
|
||||
return (
|
||||
<Styled.Page>
|
||||
{import.meta.env.VITE_V3_TOKEN_ADDRESS && <MigratePanel />}
|
||||
<Styled.PanelRow>
|
||||
{isTablet && <DYDXBalancePanel />}
|
||||
|
||||
{isTablet ? (
|
||||
<>
|
||||
<LaunchIncentivesPanel />
|
||||
<DYDXBalancePanel />
|
||||
</>
|
||||
) : (
|
||||
<Styled.PanelRowIncentivesAndBalance>
|
||||
<LaunchIncentivesPanel />
|
||||
<DYDXBalancePanel />
|
||||
</Styled.PanelRowIncentivesAndBalance>
|
||||
)}
|
||||
|
||||
<Styled.PanelRow>
|
||||
<Styled.Panel
|
||||
slotHeader={<Styled.Title>{stringGetter({ key: STRING_KEYS.GOVERNANCE })}</Styled.Title>}
|
||||
slotRight={panelArrow}
|
||||
@@ -67,8 +79,6 @@ export const RewardsPage = () => {
|
||||
</Link>
|
||||
</Styled.Description>
|
||||
</Styled.Panel>
|
||||
|
||||
{isNotTablet && <DYDXBalancePanel />}
|
||||
</Styled.PanelRow>
|
||||
</Styled.Page>
|
||||
);
|
||||
@@ -99,6 +109,11 @@ Styled.Page = styled.div`
|
||||
|
||||
Styled.Panel = styled(Panel)`
|
||||
--panel-paddingX: 1.5rem;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--panel-paddingY: 1.5rem;
|
||||
--panel-content-paddingY: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
@@ -110,20 +125,29 @@ Styled.Title = styled.h3`
|
||||
Styled.Description = styled.div`
|
||||
color: var(--color-text-0);
|
||||
--link-color: var(--color-text-1);
|
||||
|
||||
a {
|
||||
display: inline;
|
||||
::before {
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.PanelRow = styled.div`
|
||||
${layoutMixins.gridEqualColumns}
|
||||
gap: 1.5rem;
|
||||
|
||||
align-items: flex-start;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
grid-auto-flow: row;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.PanelRowIncentivesAndBalance = styled(Styled.PanelRow)`
|
||||
grid-template-columns: 2fr 1fr;
|
||||
`;
|
||||
|
||||
Styled.IconButton = styled(IconButton)`
|
||||
color: var(--color-text-0);
|
||||
--color-border: var(--color-layer-6);
|
||||
|
||||
+10
-1
@@ -17,6 +17,7 @@ export interface ConfigsState {
|
||||
feeTiers?: kollections.List<FeeTier>;
|
||||
feeDiscounts?: FeeDiscount[];
|
||||
network?: NetworkConfigs;
|
||||
hasSeenLaunchIncentives: boolean;
|
||||
}
|
||||
|
||||
const DOCUMENT_THEME_MAP = {
|
||||
@@ -43,6 +44,10 @@ const initialState: ConfigsState = {
|
||||
feeDiscounts: undefined,
|
||||
feeTiers: undefined,
|
||||
network: undefined,
|
||||
hasSeenLaunchIncentives: getLocalStorage({
|
||||
key: LocalStorageKey.HasSeenLaunchIncentives,
|
||||
defaultValue: false,
|
||||
}),
|
||||
};
|
||||
|
||||
changeTheme(initialState.appTheme);
|
||||
@@ -60,7 +65,11 @@ export const configsSlice = createSlice({
|
||||
...state,
|
||||
...action.payload,
|
||||
}),
|
||||
markLaunchIncentivesSeen: (state: ConfigsState) => {
|
||||
setLocalStorage({ key: LocalStorageKey.HasSeenLaunchIncentives, value: true });
|
||||
state.hasSeenLaunchIncentives = true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setAppTheme, setConfigs } = configsSlice.actions;
|
||||
export const { setAppTheme, setConfigs, markLaunchIncentivesSeen } = configsSlice.actions;
|
||||
|
||||
@@ -5,3 +5,6 @@ export const getAppTheme = (state: RootState) => state.configs.appTheme;
|
||||
export const getFeeTiers = (state: RootState) => state.configs.feeTiers?.toArray();
|
||||
|
||||
export const getFeeDiscounts = (state: RootState) => state.configs.feeDiscounts;
|
||||
|
||||
export const getHasSeenLaunchIncentives = (state: RootState) =>
|
||||
state.configs.hasSeenLaunchIncentives;
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
|
||||
--color-gradient-positive: hsla(158, 49%, 48%, 0.16);
|
||||
--color-gradient-negative: hsla(0, 100%, 66%, 0.16);
|
||||
--color-accent-faded: hsla(var(--theme-classic-hue-purple), 100%, 70%, 0.16);
|
||||
|
||||
--color-risk-low: var(--theme-classic-color-green);
|
||||
--color-risk-medium: var(--theme-classic-color-yellow);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { ButtonAction, ButtonType } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { useStringGetter } from '@/hooks';
|
||||
|
||||
import { Button } from '@/components/Button';
|
||||
import { Dialog } from '@/components/Dialog';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
type ElementProps = {
|
||||
link: string;
|
||||
linkDescription?: string;
|
||||
setIsOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export const ExternalLinkDialog = ({ setIsOpen, link, linkDescription }: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
return (
|
||||
<Dialog
|
||||
isOpen
|
||||
setIsOpen={setIsOpen}
|
||||
title={stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE })}
|
||||
description={
|
||||
linkDescription ?? stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE_DESCRIPTION })
|
||||
}
|
||||
>
|
||||
<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 })}
|
||||
</Button>
|
||||
</Styled.Content>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Content = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1rem;
|
||||
|
||||
font: var(--font-base-book);
|
||||
`;
|
||||
Reference in New Issue
Block a user