Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions apps/evm/src/components/Icon/icons/dotShortcut.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { SVGProps } from 'react';

const SvgDotShortcut = (props: SVGProps<SVGSVGElement>) => (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<rect x="0.5" y="0.5" width="15" height="15" rx="7.5" stroke="currentColor" />
<path
d="M4 9C4.55228 9 5 8.55228 5 8C5 7.44772 4.55228 7 4 7C3.44772 7 3 7.44772 3 8C3 8.55228 3.44772 9 4 9Z"
fill="currentColor"
/>
<path
d="M8 9C8.55228 9 9 8.55228 9 8C9 7.44772 8.55228 7 8 7C7.44772 7 7 7.44772 7 8C7 8.55228 7.44772 9 8 9Z"
fill="currentColor"
/>
<path
d="M12 9C12.5523 9 13 8.55228 13 8C13 7.44772 12.5523 7 12 7C11.4477 7 11 7.44772 11 8C11 8.55228 11.4477 9 12 9Z"
fill="currentColor"
/>
</svg>
);

export default SvgDotShortcut;
1 change: 1 addition & 0 deletions apps/evm/src/components/Icon/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export { default as checkInlineDotted } from './checkInlineDotted';
export { default as mark } from './mark';
export { default as arrowShaft } from './arrowShaft';
export { default as notice } from './notice';
export { default as dotShortcut } from './dotShortcut';
export { default as dots } from './dots';
export { default as exclamation } from './exclamation';
export { default as comment } from './comment';
Expand Down
55 changes: 55 additions & 0 deletions apps/evm/src/containers/MarketFormModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { Address } from 'viem';

import { Modal, type ModalProps, ProtectionModeIndicator } from 'components';
import { GatedAssetAcknowledgementModal } from 'containers/GatedAssetAcknowledgementModal';
import { useUserChainSettings } from 'hooks/useUserChainSettings';
// TODO: import OperationForm from containers once it is stable. There's ongoing work happening on
// it, so moving it now could generate conflicts
import { OperationForm, type OperationFormProps } from 'pages/Market/OperationForm';
import type { Asset } from 'types';

export interface MarketFormModalProps {
asset: Asset;
poolComptrollerAddress: Address;
onClose: ModalProps['handleClose'];
onSubmitSuccess?: OperationFormProps['onSubmitSuccess'];
}

export const MarketFormModal: React.FC<MarketFormModalProps> = ({
asset,
poolComptrollerAddress,
onClose,
onSubmitSuccess,
}) => {
const [userChainSettings] = useUserChainSettings();

if (asset.isGated && !userChainSettings.doNotShowGatedAssetModal) {
return <GatedAssetAcknowledgementModal onReject={onClose} />;
}

return (
<Modal
isOpen
title={
<span className="inline-flex items-center gap-x-2">
{asset.isProtectionModeEnabled && (
<ProtectionModeIndicator
variant="icon"
tokenName={asset.vToken.underlyingToken.symbol}
tokenSupplyPriceCents={asset.tokenSupplyPriceCents}
tokenBorrowPriceCents={asset.tokenBorrowPriceCents}
/>
)}
{asset.vToken.underlyingToken.symbol}
</span>
}
handleClose={onClose}
>
<OperationForm
vToken={asset.vToken}
poolComptrollerAddress={poolComptrollerAddress}
onSubmitSuccess={onSubmitSuccess}
/>
</Modal>
);
};
45 changes: 8 additions & 37 deletions apps/evm/src/containers/MarketTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,16 @@ import { cn } from '@venusprotocol/ui';
import { useMemo, useState } from 'react';
import type { Address } from 'viem';

import { Card, Modal, ProtectionModeIndicator, Table, type TableProps } from 'components';
import { Card, Table, type TableProps } from 'components';
import { routes } from 'constants/routing';
import { Controls } from 'containers/Controls';
import { GatedAssetAcknowledgementModal } from 'containers/GatedAssetAcknowledgementModal';
import { MarketFormModal } from 'containers/MarketFormModal';
import { SwitchChainNotice } from 'containers/SwitchChainNotice';
import { useBreakpointUp } from 'hooks/responsive';
import { useCollateral } from 'hooks/useCollateral';
import { useUserChainSettings } from 'hooks/useUserChainSettings';
import { handleError } from 'libs/errors';
import { useTranslation } from 'libs/translations';
import { useAccountChainId, useChainId } from 'libs/wallet';
// TODO: move OperationForm to containers once it is stable. There's ongoing work happening on it,
// so moving it now could generate conflicts
import { OperationForm } from 'pages/Market/OperationForm';
import type { Asset, EModeGroup } from 'types';
import { RowControl } from './RowControl';
import pauseIconSrc from './pause.svg';
Expand Down Expand Up @@ -77,10 +73,6 @@ export const MarketTable: React.FC<MarketTableProps> = ({

const { toggleCollateral } = useCollateral();

const [userChainSettings] = useUserChainSettings();
const shouldDisplayGatedAssetsAcknowledgementModal =
selectedAsset?.isGated && !userChainSettings.doNotShowGatedAssetModal;

// The fallback breakpoint is just to satisfy TS here, it is not actually used
const _isBreakpointUp = useBreakpointUp(breakpoint || '2xl');
const isBreakpointUp = !!breakpoint && _isBreakpointUp;
Expand Down Expand Up @@ -228,33 +220,12 @@ export const MarketTable: React.FC<MarketTableProps> = ({
{...otherTableProps}
/>

{shouldDisplayGatedAssetsAcknowledgementModal && (
<GatedAssetAcknowledgementModal onReject={() => setSelectedAsset(undefined)} />
)}

{selectedAsset && !shouldDisplayGatedAssetsAcknowledgementModal && (
<Modal
isOpen
title={
<span className="inline-flex items-center gap-x-2">
{selectedAsset.isProtectionModeEnabled && (
<ProtectionModeIndicator
variant="icon"
tokenName={selectedAsset.vToken.underlyingToken.symbol}
tokenSupplyPriceCents={selectedAsset.tokenSupplyPriceCents}
tokenBorrowPriceCents={selectedAsset.tokenBorrowPriceCents}
/>
)}
{selectedAsset.vToken.underlyingToken.symbol}
</span>
}
handleClose={handleCloseMarketModal}
>
<OperationForm
vToken={selectedAsset.vToken}
poolComptrollerAddress={poolComptrollerContractAddress}
/>
</Modal>
{selectedAsset && (
<MarketFormModal
asset={selectedAsset}
poolComptrollerAddress={poolComptrollerContractAddress}
onClose={handleCloseMarketModal}
/>
)}
</>
);
Expand Down
13 changes: 12 additions & 1 deletion apps/evm/src/libs/translations/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,18 @@
}
},
"tablesRefreshNote": "Refreshed hourly · Last refresh: {{date, distanceToNow}} ago",
"title": "Prime leaderboard"
"title": "Prime leaderboard",
"totalRewards": {
"title": "Total Prime rewards distributed this cycle"
},
"userRewards": {
"eligibleMessage": "You are currently eligible for sharing the Prime rewards during this cycle. Supply assets below to share the rewards.",
"marketActions": "Open market actions",
"notEligibleMessage": "You are currently NOT eligible for sharing the Prime rewards during this cycle. Stake XVS to compete for Prime in the next cycle.",
"primeLogoAlt": "Venus Prime logo",
"rewardShareAriaLabel": "Share of total rewards",
"title": "Your Prime rewards this cycle"
}
},
"primeStatusBanner": {
"becomePrimeTitle": "You can now become a Prime user",
Expand Down
13 changes: 12 additions & 1 deletion apps/evm/src/libs/translations/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,18 @@
}
},
"tablesRefreshNote": "1時間ごとに更新 · 最終更新:{{date, distanceToNow}}前",
"title": "Prime リーダーボード"
"title": "Prime リーダーボード",
"totalRewards": {
"title": "今サイクルに分配された Prime 報酬の総額"
},
"userRewards": {
"eligibleMessage": "今サイクルで Prime 報酬を分け合う対象になっています。下記の資産を供給して報酬を獲得しましょう。",
"marketActions": "マーケット操作を開く",
"notEligibleMessage": "今サイクルでは Prime 報酬を分け合う対象ではありません。XVS をステークして次のサイクルで Prime を目指しましょう。",
"primeLogoAlt": "Venus Prime ロゴ",
"rewardShareAriaLabel": "報酬総額に占める割合",
"title": "今サイクルのあなたの Prime 報酬"
}
},
"primeStatusBanner": {
"becomePrimeTitle": "Primeユーザーになれるようになりました",
Expand Down
13 changes: 12 additions & 1 deletion apps/evm/src/libs/translations/translations/th.json
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,18 @@
}
},
"tablesRefreshNote": "รีเฟรชทุกชั่วโมง · รีเฟรชล่าสุด: {{date, distanceToNow}} ที่แล้ว",
"title": "กระดานผู้นำ Prime"
"title": "กระดานผู้นำ Prime",
"totalRewards": {
"title": "รางวัล Prime ทั้งหมดที่แจกในรอบนี้"
},
"userRewards": {
"eligibleMessage": "คุณมีสิทธิ์ร่วมรับรางวัล Prime ในรอบนี้ จัดหาสินทรัพย์ด้านล่างเพื่อรับรางวัล",
"marketActions": "เปิดการดำเนินการของตลาด",
"notEligibleMessage": "คุณยังไม่มีสิทธิ์ร่วมรับรางวัล Prime ในรอบนี้ Stake XVS เพื่อแข่งขันรับ Prime ในรอบถัดไป",
"primeLogoAlt": "โลโก้ Venus Prime",
"rewardShareAriaLabel": "สัดส่วนของรางวัลทั้งหมด",
"title": "รางวัล Prime ของคุณในรอบนี้"
}
},
"primeStatusBanner": {
"becomePrimeTitle": "ตอนนี้คุณสามารถเป็นผู้ใช้ Prime ได้แล้ว",
Expand Down
13 changes: 12 additions & 1 deletion apps/evm/src/libs/translations/translations/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,18 @@
}
},
"tablesRefreshNote": "Saatlik yenilenir · Son yenileme: {{date, distanceToNow}} önce",
"title": "Prime lider tablosu"
"title": "Prime lider tablosu",
"totalRewards": {
"title": "Bu döngüde dağıtılan toplam Prime ödülü"
},
"userRewards": {
"eligibleMessage": "Bu döngüde Prime ödüllerini paylaşmaya uygunsun. Ödülleri paylaşmak için aşağıdan varlık sağla.",
"marketActions": "Piyasa işlemlerini aç",
"notEligibleMessage": "Bu döngüde Prime ödüllerini paylaşmaya uygun değilsin. Sonraki döngüde Prime için yarışmak üzere XVS stake et.",
"primeLogoAlt": "Venus Prime logosu",
"rewardShareAriaLabel": "Toplam ödüllerdeki pay",
"title": "Bu döngüdeki Prime ödülleriniz"
}
},
"primeStatusBanner": {
"becomePrimeTitle": "Artık Prime kullanıcısı olabilirsiniz",
Expand Down
13 changes: 12 additions & 1 deletion apps/evm/src/libs/translations/translations/vi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,18 @@
}
},
"tablesRefreshNote": "Làm mới mỗi giờ · Lần làm mới cuối: {{date, distanceToNow}} trước",
"title": "Bảng xếp hạng Prime"
"title": "Bảng xếp hạng Prime",
"totalRewards": {
"title": "Tổng thưởng Prime đã phân phối trong chu kỳ này"
},
"userRewards": {
"eligibleMessage": "Bạn hiện đủ điều kiện chia sẻ phần thưởng Prime trong chu kỳ này. Hãy cung cấp tài sản bên dưới để nhận phần thưởng.",
"marketActions": "Mở thao tác thị trường",
"notEligibleMessage": "Bạn hiện KHÔNG đủ điều kiện chia sẻ phần thưởng Prime trong chu kỳ này. Stake XVS để cạnh tranh Prime cho chu kỳ tiếp theo.",
"primeLogoAlt": "Logo Venus Prime",
"rewardShareAriaLabel": "Tỷ lệ trên tổng phần thưởng",
"title": "Phần thưởng Prime của bạn trong chu kỳ này"
}
},
"primeStatusBanner": {
"becomePrimeTitle": "Bạn đã có thể trở thành người dùng Prime",
Expand Down
13 changes: 12 additions & 1 deletion apps/evm/src/libs/translations/translations/zh-Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,18 @@
}
},
"tablesRefreshNote": "每小时刷新 · 上次刷新:{{date, distanceToNow}}前",
"title": "Prime 排行榜"
"title": "Prime 排行榜",
"totalRewards": {
"title": "本周期已分配的 Prime 总奖励"
},
"userRewards": {
"eligibleMessage": "你在本周期已具备分享 Prime 奖励的资格。在下方供给资产以分享奖励。",
"marketActions": "打开市场操作",
"notEligibleMessage": "你在本周期暂不具备分享 Prime 奖励的资格。质押 XVS 以在下一周期竞争 Prime。",
"primeLogoAlt": "Venus Prime 标志",
"rewardShareAriaLabel": "占总奖励的比例",
"title": "你本周期的 Prime 奖励"
}
},
"primeStatusBanner": {
"becomePrimeTitle": "你现在可以成为 Prime 用户",
Expand Down
13 changes: 12 additions & 1 deletion apps/evm/src/libs/translations/translations/zh-Hant.json
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,18 @@
}
},
"tablesRefreshNote": "每小時刷新 · 上次刷新:{{date, distanceToNow}}前",
"title": "Prime 排行榜"
"title": "Prime 排行榜",
"totalRewards": {
"title": "本週期已分配的 Prime 總獎勵"
},
"userRewards": {
"eligibleMessage": "你在本週期已具備分享 Prime 獎勵的資格。在下方供給資產以分享獎勵。",
"marketActions": "開啟市場操作",
"notEligibleMessage": "你在本週期暫不具備分享 Prime 獎勵的資格。質押 XVS 以在下一週期競爭 Prime。",
"primeLogoAlt": "Venus Prime 標誌",
"rewardShareAriaLabel": "佔總獎勵的比例",
"title": "你本週期的 Prime 獎勵"
}
},
"primeStatusBanner": {
"becomePrimeTitle": "你現在可以成為 Prime 用戶",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { fireEvent, screen } from '@testing-library/react';

import { assetData } from '__mocks__/models/asset';
import { renderComponent } from 'testUtils/render';

import { MarketActionsButton } from '..';

vi.mock('pages/Market/OperationForm', () => ({
OperationForm: () => <div data-testid="operation-form" />,
}));

describe('pages/PrimeLeaderboard/MarketActionsButton', () => {
it('opens the market operation modal when clicked', async () => {
const asset = assetData[0];

renderComponent(
<MarketActionsButton asset={asset} poolComptrollerAddress={asset.vToken.address} />,
);

fireEvent.click(screen.getByLabelText('Open market actions'));

expect(await screen.findByTestId('operation-form')).toBeInTheDocument();
});
});
45 changes: 45 additions & 0 deletions apps/evm/src/pages/PrimeLeaderboard/MarketActionsButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useState } from 'react';
import type { Address } from 'viem';

import { Icon } from 'components';
import { MarketFormModal } from 'containers/MarketFormModal';
import { useTranslation } from 'libs/translations';
import type { Asset } from 'types';

export interface MarketActionsButtonProps {
asset: Asset;
poolComptrollerAddress: Address;
}

export const MarketActionsButton: React.FC<MarketActionsButtonProps> = ({
asset,
poolComptrollerAddress,
}) => {
const { t } = useTranslation();
const [isModalOpen, setIsModalOpen] = useState(false);

return (
<>
<button
type="button"
aria-label={t('primeLeaderboard.userRewards.marketActions')}
className="group ml-2 shrink-0 cursor-pointer"
onClick={() => setIsModalOpen(true)}
>
<Icon
name="dotShortcut"
className="text-light-grey transition-colors group-hover:text-white"
/>
</button>

{isModalOpen && (
<MarketFormModal
asset={asset}
poolComptrollerAddress={poolComptrollerAddress}
onClose={() => setIsModalOpen(false)}
onSubmitSuccess={() => setIsModalOpen(false)}
/>
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { screen } from '@testing-library/react';

import { usdc } from '__mocks__/models/tokens';
import { renderComponent } from 'testUtils/render';
import { MarketRewardRow } from '..';

describe('pages/PrimeLeaderboard/MarketRewardRow', () => {
it('renders the token, reward amount and trailing content', () => {
renderComponent(
<MarketRewardRow token={usdc} rewardsCents={28_040_000} totalRewardsCents={46_230_000}>
<span>3.78%</span>
</MarketRewardRow>,
);

expect(screen.getByText(usdc.symbol)).toBeInTheDocument();
expect(screen.getByText('$280.4K')).toBeInTheDocument();
expect(screen.getByText('3.78%')).toBeInTheDocument();
});
});
Loading
Loading