Compare commits

..
Author SHA1 Message Date
asiaznik b297ffda64 fix: fauceted assets 2023-02-24 17:51:10 +01:00
asiaznik b49009e716 chore: e2e tests, page title changes 2023-02-24 17:40:50 +01:00
asiaznik 97a8751ab1 fix: lint 2023-02-24 16:24:05 +01:00
asiaznik 43169659a6 chore: validators tweaks 2023-02-24 16:24:05 +01:00
asiaznik 48cdb44e9d feat(explorer): validators page 2023-02-24 16:24:05 +01:00
74 changed files with 138092 additions and 138724 deletions
@@ -19,12 +19,12 @@ const EmptyList = ({ heading, label }: EmptyListProps) => {
<div className="mt-4">
{heading ? (
<h1 className="font-alpha calt text-xl uppercase text-center leading-relaxed">
<h1 className="font-alpha text-xl uppercase text-center leading-relaxed">
{heading}
</h1>
) : null}
{label ? (
<p className="font-alpha calt text-gray-500 text-center">{label}</p>
<p className="font-alpha text-gray-500 text-center">{label}</p>
) : null}
</div>
</div>
@@ -184,8 +184,9 @@ export const MarketDetails = ({
content: (
<>
<p className="text-xs mb-4">
{`For liquidity orders to count towards a commitment, they must be
within the liquidity monitoring bounds.`}
{`For liquidity orders count towards a commitment they have to be
within either the liquidity or price monitoring bounds (whichever is
tighter).`}
</p>
<p className="text-xs mb-4">
{`The liquidity price range is a ${liquidityPriceRange} difference from the mid
@@ -248,7 +249,7 @@ export const MarketDetails = ({
<>
{panels.map((p) => (
<div className="mb-3">
<h2 className="font-alpha calt text-xl">{p.title}</h2>
<h2 className="font-alpha text-xl">{p.title}</h2>
{p.content}
</div>
))}
@@ -83,7 +83,7 @@ const NestedDataListItem = ({
});
const titleClasses = classNames({
'text-xl pl-4 border-l-4 font-alpha calt': hasChildren,
'text-xl pl-4 border-l-4 font-alpha': hasChildren,
'text-base font-medium whitespace-nowrap': !hasChildren,
});
@@ -20,7 +20,7 @@ export const PageHeader = ({
copy = false,
className,
}: PageHeaderProps) => {
const titleClasses = 'text-4xl xl:text-5xl uppercase font-alpha calt';
const titleClasses = 'text-4xl xl:text-5xl uppercase font-alpha';
return (
<header className={className}>
<span className={`${titleClasses} block`}>{prefix}</span>
@@ -13,7 +13,7 @@ export const RouteTitle = ({
...props
}: RouteTitleProps) => {
const classes = classnames(
'font-alpha calt',
'font-alpha',
'text-4xl',
'uppercase',
'mb-8',
@@ -11,7 +11,7 @@ export const StatusMessage = ({
className,
...props
}: StatusMessageProps) => {
const classes = classnames('font-alpha calt text-2xl mb-28', className);
const classes = classnames('font-alpha text-2xl mb-28', className);
return (
<h3 className={classes} {...props}>
{children}
@@ -12,7 +12,7 @@ export const SubHeading = ({
...props
}: SubHeadingProps) => {
const classes = classnames(
'font-alpha calt',
'font-alpha',
'text-2xl',
'uppercase',
'mt-8 mb-2',
@@ -9,7 +9,7 @@ export type Metric = components['schemas']['vegaDispatchMetric'];
export const wrapperClasses =
'border border-vega-light-150 dark:border-vega-dark-200 rounded-md pv-2 mb-5 w-full sm:w-1/4 min-w-[200px] ';
export const headerClasses =
'bg-solid bg-vega-light-150 dark:bg-vega-dark-150 border-vega-light-150 text-center text-xl py-2 font-alpha calt';
'bg-solid bg-vega-light-150 dark:bg-vega-dark-150 border-vega-light-150 text-center text-xl py-2 font-alpha';
export type Transfer = components['schemas']['commandsv1Transfer'];
@@ -68,7 +68,7 @@ const Party = () => {
return (
<section>
<h1
className="font-alpha calt uppercase font-xl mb-4 text-vega-dark-100 dark:text-vega-light-100"
className="font-alpha uppercase font-xl mb-4 text-vega-dark-100 dark:text-vega-light-100"
data-testid="parties-header"
>
{t('Public key')}
@@ -8,7 +8,7 @@ export function Dashboard() {
<>
<div className="px-16 pt-20 pb-12 bg-greys-light-100">
<div className="max-w-screen-xl mx-auto">
<h1 className="font-alpha calt uppercase text-5xl mb-8">
<h1 className="font-alpha uppercase text-5xl mb-8">
{t('Top liquidity opportunities')}
</h1>
@@ -30,7 +30,7 @@ type Network = 'testnet' | 'mainnet';
export const Intro = ({ network = 'testnet' }: { network?: Network }) => {
return (
<div>
<p className="font-alpha calt text-2xl font-medium mb-2">
<p className="font-alpha text-2xl font-medium mb-2">
{t(
'Become a liquidity provider and earn a cut of the fees paid during trading.'
)}
@@ -92,7 +92,7 @@ export const Detail = () => {
)}
</div>
<div>
<h2 className="font-alpha calt text-2xl mb-4">
<h2 className="font-alpha text-2xl mb-4">
{t('Current Liquidity Provision')}
</h2>
<LPProvidersGrid
@@ -14,13 +14,13 @@ export const Header = ({
<div className="mb-6">
<Link to="/">
<Icon name="chevron-left" className="mr-2" />
<span className="underline font-alpha calt text-lg font-medium">
<span className="underline font-alpha text-lg font-medium">
{t('Liquidity opportunities')}
</span>
</Link>
</div>
<h1 className="font-alpha calt text-5xl mb-6">{name}</h1>
<p className="font-alpha calt text-4xl">{symbol}</p>
<h1 className="font-alpha text-5xl mb-6">{name}</h1>
<p className="font-alpha text-4xl">{symbol}</p>
</div>
);
};
@@ -44,7 +44,7 @@ export const Market = ({
<table className="w-full">
<thead>
<tr
className="text-sm text-greys-light-400 text-left font-alpha calt"
className="text-sm text-greys-light-400 text-left font-alpha"
style={{ fontFeatureSettings: "'liga' off, 'calt' off" }}
>
<th className="font-medium px-4">{t('Volume (24h)')}</th>
@@ -36,7 +36,7 @@ export const Grid = ({ isRowClickable, children, ...props }: Props) => {
return (
<AgGridReact
className={classNames('ag-theme-alpine h-full font-alpha calt', {
className={classNames('ag-theme-alpine h-full font-alpha', {
'row-hover': isRowClickable,
})}
rowHeight={92}
@@ -13,7 +13,7 @@ const Remainder = () => (
);
const COPY_CLASS =
'text-sm font-medium whitespace-nowrap text-white font-alpha calt';
'text-sm font-medium whitespace-nowrap text-white font-alpha';
const Tooltip = ({
children,
@@ -59,10 +59,10 @@ const ROWS = [
export const HealthDialog = ({ onChange, isOpen }: HealthDialogProps) => {
return (
<Dialog size="medium" open={isOpen} onChange={onChange}>
<h1 className="text-2xl mb-5 pr-2 font-medium font-alpha uppercase">
<h1 className="text-2xl mb-5 pr-2 font-medium font-alpha uppercase liga-0-calt-0">
{t('Health')}
</h1>
<p className="text-lg font-medium font-alpha mb-8">
<p className="text-lg font-medium font-alpha mb-8 liga-0-calt-0">
{t(
'Market health is a representation of market and liquidity status and how close that market is to moving from one fee level to another.'
)}
@@ -70,10 +70,10 @@ export const HealthDialog = ({ onChange, isOpen }: HealthDialogProps) => {
<table className="table-fixed">
<thead className="border-b border-greys-light-300">
<th className="w-1/2 text-left font-medium font-alpha text-base pb-4 uppercase">
<th className="w-1/2 text-left font-medium font-alpha text-base pb-4 uppercase liga-0-calt-0">
{t('Market status')}
</th>
<th className="w-1/2 text-lef font-medium font-alpha text-base pb-4 uppercase">
<th className="w-1/2 text-lef font-medium font-alpha text-base pb-4 uppercase liga-0-calt-0">
{t('Liquidity status')}
</th>
</thead>
@@ -85,10 +85,12 @@ export const HealthDialog = ({ onChange, isOpen }: HealthDialogProps) => {
<td
className={classNames('pr-4 pb-10', { 'pt-8': isFirstRow })}
>
<h2 className="font-medium font-alpha uppercase text-base">
<h2 className="font-medium font-alpha uppercase text-base liga-0-calt-0">
{t(r.title)}
</h2>
<p className="font-medium font-alpha text-lg">{t(r.copy)}</p>
<p className="font-medium font-alpha text-lg liga-0-calt-0">
{t(r.copy)}
</p>
</td>
<td
className={classNames('pl-4 pb-10', { 'pt-8': isFirstRow })}
File diff suppressed because it is too large Load Diff
-111
View File
@@ -1,111 +0,0 @@
.pre-loader {
display: flex;
width: 100%;
min-height: 100vh;
justify-content: center;
align-items: center;
}
.pre-loader .loader-item {
width: 10px;
height: 10px;
background: #000;
}
.pre-loader .pre-loader-center {
align-items: center;
display: flex;
flex-direction: column;
}
.pre-loader .pre-loader-wrapper {
width: 50px;
height: 50px;
display: flex;
flex-wrap: wrap;
}
.pre-loader .loader-item:nth-child(0) {
animation-delay: 0ms;
animation-direction: reverse;
}
.pre-loader .loader-item:first-child {
animation-delay: -50ms;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(2) {
animation-delay: 0.2s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(3) {
animation-delay: -0.6s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(4) {
animation-delay: 0.4s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(5) {
animation-delay: -0.5s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(6) {
animation-delay: 0.3s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(7) {
animation-delay: -1.4s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(8) {
animation-delay: 2s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(9) {
animation-delay: -0.9s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(10) {
animation-delay: 1.5s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(11) {
animation-delay: -0.55s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(12) {
animation-delay: 1.2s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(13) {
animation-delay: -0.65s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(14) {
animation-delay: 0.7s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(15) {
animation-delay: -3.75s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(16) {
animation-delay: 1.6s;
animation-direction: reverse;
}
.pre-loader .loader-item {
animation: flickering 0.4s linear infinite alternate;
}
@keyframes flickering {
0% {
opacity: 1;
}
25% {
opacity: 1;
}
26% {
opacity: 0;
}
to {
opacity: 0;
}
}
html.dark .pre-loader .loader-item {
background: #fff;
}
-9
View File
@@ -1,9 +0,0 @@
(function () {
var storedTheme = window.localStorage.getItem('theme');
if (
storedTheme === 'dark' ||
(!storedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
document.documentElement.classList.add('dark');
}
})();
@@ -5,7 +5,6 @@ import { VoteValue } from '@vegaprotocol/types';
import { useEffect, useState } from 'react';
import { useUserVoteQuery } from './__generated__/Vote';
import { removePaginationWrapper } from '@vegaprotocol/react-helpers';
import type { FinalizedVote } from '@vegaprotocol/governance';
export enum VoteState {
NotCast = 'NotCast',
@@ -22,14 +21,25 @@ export type Vote = {
party: { id: string };
};
export type Votes = Array<Vote | null>;
export function getUserVote(pubkey: string, yesVotes?: Votes, noVotes?: Votes) {
const yesVote = yesVotes?.find((v) => v && v.party.id === pubkey);
const noVote = noVotes?.find((v) => v && v.party.id === pubkey);
if (yesVote) {
return yesVote;
} else if (noVote) {
return noVote;
} else {
return null;
}
}
/**
* Finds the status of a users given vote in a given proposal and provides
* a function to send a vote transaction to your wallet
*/
export function useUserVote(
proposalId: string | null | undefined,
finalizedVote?: FinalizedVote | null
) {
export function useUserVote(proposalId: string | null | undefined) {
const { pubKey } = useVegaWallet();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [timeout, setTimeoutValue] = useState<any>(null);
@@ -40,27 +50,10 @@ export function useUserVote(
variables: { partyId: pubKey || '' },
skip: !pubKey || !proposalId,
});
const [userVote, setUserVote] = useState<FinalizedVote | undefined>(
undefined
);
useEffect(() => {
if (finalizedVote?.vote.value) {
setUserVote(finalizedVote);
} else if (data?.party?.votesConnection?.edges) {
// This sets the vote (if any) when the user first loads the page
setUserVote(
removePaginationWrapper(data?.party?.votesConnection?.edges).find(
({ proposalId: pId }) => proposalId === pId
)
);
}
}, [
finalizedVote?.vote.value,
data?.party?.votesConnection?.edges,
finalizedVote,
proposalId,
]);
const userVote = removePaginationWrapper(
data?.party?.votesConnection?.edges
).find(({ proposalId: pId }) => proposalId === pId);
// If user vote changes update the vote state
useEffect(() => {
@@ -22,8 +22,6 @@ describe('Vote buttons', () => {
minVoterBalance={null}
spamProtectionMinTokens={null}
currentStakeAvailable={new BigNumber(1)}
dialog={() => <div>Blah</div>}
submit={() => Promise.resolve()}
/>
</VegaWalletContext.Provider>
</MockedProvider>
@@ -45,8 +43,6 @@ describe('Vote buttons', () => {
minVoterBalance={null}
spamProtectionMinTokens={null}
currentStakeAvailable={new BigNumber(1)}
dialog={() => <div>Blah</div>}
submit={() => Promise.resolve()}
/>
</VegaWalletContext.Provider>
</MockedProvider>
@@ -79,8 +75,6 @@ describe('Vote buttons', () => {
minVoterBalance={null}
spamProtectionMinTokens={null}
currentStakeAvailable={new BigNumber(1)}
dialog={() => <div>Blah</div>}
submit={() => Promise.resolve()}
/>
</VegaWalletContext.Provider>
</MockedProvider>
@@ -103,8 +97,6 @@ describe('Vote buttons', () => {
minVoterBalance={null}
spamProtectionMinTokens={null}
currentStakeAvailable={new BigNumber(0)}
dialog={() => <div>Blah</div>}
submit={() => Promise.resolve()}
/>
</VegaWalletContext.Provider>
</MockedProvider>
@@ -128,8 +120,6 @@ describe('Vote buttons', () => {
minVoterBalance="2000000000000000000"
spamProtectionMinTokens="1000000000000000000"
currentStakeAvailable={new BigNumber(1)}
dialog={() => <div>Blah</div>}
submit={() => Promise.resolve()}
/>
</VegaWalletContext.Provider>
</MockedProvider>
@@ -155,8 +145,6 @@ describe('Vote buttons', () => {
minVoterBalance="2000000000000000000"
spamProtectionMinTokens="1000000000000000000"
currentStakeAvailable={new BigNumber(10)}
dialog={() => <div>Blah</div>}
submit={() => Promise.resolve()}
/>
</VegaWalletContext.Provider>
</MockedProvider>
@@ -179,8 +167,6 @@ describe('Vote buttons', () => {
minVoterBalance="2000000000000000000"
spamProtectionMinTokens="1000000000000000000"
currentStakeAvailable={new BigNumber(10)}
dialog={() => <div>Blah</div>}
submit={() => Promise.resolve()}
/>
</VegaWalletContext.Provider>
</MockedProvider>
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { AsyncRenderer, Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
import { addDecimal, toBigNum } from '@vegaprotocol/react-helpers';
import { useVoteSubmit } from '@vegaprotocol/governance';
import { ProposalState, VoteValue } from '@vegaprotocol/types';
import {
AppStateActionType,
@@ -15,7 +16,6 @@ import { VoteState } from './use-user-vote';
import { ProposalMinRequirements, ProposalUserAction } from '../shared';
import { VoteTransactionDialog } from './vote-transaction-dialog';
import { useVoteButtonsQuery } from './__generated__/Stake';
import type { DialogProps } from '@vegaprotocol/wallet';
interface VoteButtonsContainerProps {
voteState: VoteState | null;
@@ -24,8 +24,6 @@ interface VoteButtonsContainerProps {
proposalState: ProposalState;
minVoterBalance: string | null;
spamProtectionMinTokens: string | null;
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
dialog: (props: DialogProps) => JSX.Element;
className?: string;
}
@@ -64,12 +62,11 @@ export const VoteButtons = ({
currentStakeAvailable,
minVoterBalance,
spamProtectionMinTokens,
submit,
dialog: Dialog,
}: VoteButtonsProps) => {
const { t } = useTranslation();
const { appDispatch } = useAppState();
const { pubKey } = useVegaWallet();
const { submit, Dialog } = useVoteSubmit();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next';
import { formatDistanceToNow } from 'date-fns';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { ProposalState } from '@vegaprotocol/types';
import { useVoteSubmit, VoteProgress } from '@vegaprotocol/governance';
import { VoteProgress } from '@vegaprotocol/governance';
import { formatNumber } from '../../../../lib/format-number';
import { ConnectToVega } from '../../../../components/connect-to-vega';
import { useVoteInformation } from '../../hooks';
@@ -44,8 +44,7 @@ export const VoteDetails = ({
} = useVoteInformation({ proposal });
const { t } = useTranslation();
const { submit, Dialog, finalizedVote } = useVoteSubmit();
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
const { voteState, voteDatetime } = useUserVote(proposal?.id);
const defaultDecimals = 2;
const daysLeft = t('daysLeft', {
daysLeft: formatDistanceToNow(new Date(proposal?.terms.closingDatetime)),
@@ -189,8 +188,6 @@ export const VoteDetails = ({
minVoterBalance={minVoterBalance}
spamProtectionMinTokens={spamProtectionMinTokens}
className="flex"
submit={submit}
dialog={Dialog}
/>
)}
</section>
@@ -50,11 +50,10 @@ const DisplayReward = ({
>
<button>
<div className="flex flex-col items-start">
<span>{formatNumber(toBigNum(reward, decimals), 4)}</span>
<span>{formatNumber(toBigNum(reward, decimals))}</span>
{percentageOfTotal && (
<span className="text-vega-dark-300">
({formatNumber(percentageOfTotal, 4).toString()}
%)
({formatNumber(toBigNum(percentageOfTotal, 4)).toString()}%)
</span>
)}
</div>
@@ -8,7 +8,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '100',
percentageOfTotal: '0.1',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD' },
asset: { id: 'usd', symbol: 'USD' },
party: { id: 'blah' },
epoch: { id: '1' },
};
@@ -18,7 +18,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '50',
percentageOfTotal: '0.05',
receivedAt: new Date(),
asset: { id: 'eur', symbol: 'EUR', name: 'EUR' },
asset: { id: 'eur', symbol: 'EUR' },
party: { id: 'blah' },
epoch: { id: '2' },
};
@@ -28,7 +28,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '200',
percentageOfTotal: '0.2',
receivedAt: new Date(),
asset: { id: 'gbp', symbol: 'GBP', name: 'GBP' },
asset: { id: 'gbp', symbol: 'GBP' },
party: { id: 'blah' },
epoch: { id: '2' },
};
@@ -38,7 +38,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '100',
percentageOfTotal: '0.1',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD' },
asset: { id: 'usd', symbol: 'USD' },
party: { id: 'blah' },
epoch: { id: '1' },
};
@@ -48,7 +48,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '50',
percentageOfTotal: '0.05',
receivedAt: new Date(),
asset: { id: 'eur', symbol: 'EUR', name: 'EUR' },
asset: { id: 'eur', symbol: 'EUR' },
party: { id: 'blah' },
epoch: { id: '2' },
};
@@ -134,21 +134,21 @@ describe('generateEpochIndividualRewardsList', () => {
epoch: '2',
rewards: [
{
asset: 'GBP',
totalAmount: '200',
asset: 'EUR',
totalAmount: '50',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
amount: '200',
percentageOfTotal: '0.2',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '50',
percentageOfTotal: '0.05',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
@@ -164,20 +164,20 @@ describe('generateEpochIndividualRewardsList', () => {
},
},
{
asset: 'EUR',
totalAmount: '50',
asset: 'GBP',
totalAmount: '200',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
amount: '0',
percentageOfTotal: '0',
amount: '200',
percentageOfTotal: '0.2',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '50',
percentageOfTotal: '0.05',
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
@@ -33,7 +33,7 @@ export const generateEpochIndividualRewardsList = (
// We take the rewards and aggregate them by epoch and asset.
const epochIndividualRewards = rewards.reduce((map, reward) => {
const epochId = reward.epoch.id;
const assetName = reward.asset.name;
const assetName = reward.asset.symbol;
const rewardType = reward.rewardType;
const amount = reward.amount;
const percentageOfTotal = reward.percentageOfTotal;
@@ -70,12 +70,6 @@ export const generateEpochIndividualRewardsList = (
'0'
);
if (epoch) {
epoch.rewards = epoch.rewards.sort((a, b) => {
return new BigNumber(b.totalAmount).comparedTo(a.totalAmount);
});
}
return map;
}, new Map<string, EpochIndividualReward>());
@@ -5,7 +5,6 @@ import type {
import { removePaginationWrapper } from '@vegaprotocol/react-helpers';
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
import type { AccountType } from '@vegaprotocol/types';
import { BigNumber } from '../../../lib/bignumber';
interface EpochSummaryWithNamedReward extends EpochRewardSummaryFieldsFragment {
name: string;
@@ -121,9 +120,7 @@ export const generateEpochTotalRewardsList = (
return {
epoch: epochSummaries[0].epoch,
assetRewards: assetRewards.sort((a, b) => {
return new BigNumber(b.totalAmount).comparedTo(a.totalAmount);
}),
assetRewards,
};
});
@@ -3,7 +3,6 @@ fragment RewardFields on Reward {
asset {
id
symbol
name
}
party {
id
+2 -3
View File
@@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type RewardFieldsFragment = { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } };
export type RewardFieldsFragment = { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } };
export type DelegationFieldsFragment = { __typename?: 'Delegation', amount: string, epoch: number };
@@ -13,7 +13,7 @@ export type RewardsQueryVariables = Types.Exact<{
}>;
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null, epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } } };
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null, epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } } };
export type EpochRewardSummaryFieldsFragment = { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType };
@@ -37,7 +37,6 @@ export const RewardFieldsFragmentDoc = gql`
asset {
id
symbol
name
}
party {
id
+1 -1
View File
@@ -13,7 +13,7 @@ export const NoRewards = () => {
return (
<div className={classes}>
<SubHeading title={t('noRewardsHaveBeenDistributedYet')} />
<p className="font-alpha calt text-xl">{t('checkBackSoon')}</p>
<p className="font-alpha text-xl">{t('checkBackSoon')}</p>
</div>
);
};
@@ -190,24 +190,6 @@ describe('capsule', { tags: '@slow' }, () => {
cy.setVegaWallet();
});
it('shows node health', function () {
const market = this.market;
cy.visit(`/#/markets/${market.id}`);
cy.getByTestId('node-health')
.children()
.first()
.should('contain.text', 'Operational')
.next()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
.next()
.then(($el) => {
const blockHeight = parseInt($el.text());
// block height will increase over the course of the test run so best
// we can do here is check that its showing something sensible
expect(blockHeight).to.be.greaterThan(0);
});
});
it('can place and receive an order', function () {
const market = this.market;
cy.visit(`/#/markets/${market.id}`);
@@ -304,18 +304,4 @@ describe('home', { tags: '@regression' }, () => {
});
});
});
describe('footer', () => {
it('shows current block height', () => {
cy.visit('/');
cy.getByTestId('node-health')
.children()
.first()
.should('contain.text', 'Operational')
.next()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
.next()
.should('contain.text', '100'); // all mocked queries have x-block-height header set to 100
});
});
});
@@ -126,11 +126,18 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
});
it('price monitoring trigger displayed', () => {
cy.getByTestId(marketTitle).contains('Price monitoring bounds 1').click();
cy.get('p.col-span-1').contains('100% probability of trading');
cy.get('p.col-span-1').contains('Within 43,200 seconds');
validateMarketDataRow(0, 'Highest Price', '7.97323 ');
validateMarketDataRow(1, 'Lowest Price', '6.54701 ');
cy.getByTestId(marketTitle).contains('Price monitoring trigger 1').click();
validateMarketDataRow(0, 'Horizon Secs', '43,200');
validateMarketDataRow(1, 'Probability', '1');
validateMarketDataRow(2, 'Auction Extension Secs', '600');
});
it('price monitoring bound displayed', () => {
cy.getByTestId(marketTitle).contains('Price monitoring bound 1').click();
validateMarketDataRow(0, 'Min Valid Price', '6.54701 ');
validateMarketDataRow(1, 'Max Valid Price', '7.97323 ');
validateMarketDataRow(2, 'Reference Price', '7.22625 ');
});
@@ -84,7 +84,7 @@ export const TradeMarketHeader = ({
heading={t('Volume (24h)')}
testId="market-volume"
description={t(
'The total number of contracts traded in the last 24 hours.'
'The total amount of assets traded in the last 24 hours.'
)}
>
<Last24hVolume
+17 -25
View File
@@ -1,35 +1,20 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { NodeHealth, NodeUrl, HealthIndicator } from './footer';
describe('NodeHealth', () => {
it('controls the node switcher dialog', async () => {
const mockOnClick = jest.fn();
render(
<NodeHealth
onClick={mockOnClick}
url={'https://api.n99.somenetwork.vega.xyz'}
blockHeight={100}
blockDiff={0}
/>
);
await userEvent.click(screen.getByRole('button'));
expect(mockOnClick).toHaveBeenCalled();
});
});
import { fireEvent, render, screen } from '@testing-library/react';
import { NodeUrl, NodeHealth } from './footer';
describe('NodeUrl', () => {
it('renders correct part of node url', () => {
it('can open node switcher by clicking the node url', () => {
const mockOpenNodeSwitcher = jest.fn();
const node = 'https://api.n99.somenetwork.vega.xyz';
const expectedText = node.split('.').slice(1).join('.');
render(<NodeUrl url={node} />);
render(<NodeUrl url={node} openNodeSwitcher={mockOpenNodeSwitcher} />);
expect(screen.getByText(expectedText)).toBeInTheDocument();
fireEvent.click(screen.getByText(/n99/));
expect(mockOpenNodeSwitcher).toHaveBeenCalled();
});
});
describe('HealthIndicator', () => {
describe('NodeHealth', () => {
const mockOpenNodeSwitcher = jest.fn();
const cases = [
{ diff: 0, classname: 'bg-vega-green-550', text: 'Operational' },
{ diff: 5, classname: 'bg-warning', text: '5 Blocks behind' },
@@ -38,9 +23,16 @@ describe('HealthIndicator', () => {
it.each(cases)(
'renders correct text and indicator color for $diff block difference',
(elem) => {
render(<HealthIndicator blockDiff={elem.diff} />);
render(
<NodeHealth
blockDiff={elem.diff}
openNodeSwitcher={mockOpenNodeSwitcher}
/>
);
expect(screen.getByTestId('indicator')).toHaveClass(elem.classname);
expect(screen.getByText(elem.text)).toBeInTheDocument();
fireEvent.click(screen.getByText(elem.text));
expect(mockOpenNodeSwitcher).toHaveBeenCalled();
}
);
});
+31 -76
View File
@@ -1,8 +1,6 @@
import { useEnvironment, useNodeHealth } from '@vegaprotocol/environment';
import { t, useNavigatorOnline } from '@vegaprotocol/react-helpers';
import { Indicator, Intent } from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames';
import type { ButtonHTMLAttributes, ReactNode } from 'react';
import { ButtonLink, Indicator, Intent } from '@vegaprotocol/ui-toolkit';
import { useGlobalStore } from '../../stores';
export const Footer = () => {
@@ -10,64 +8,45 @@ export const Footer = () => {
const setNodeSwitcher = useGlobalStore(
(store) => (open: boolean) => store.update({ nodeSwitcherDialog: open })
);
const { blockDiff, datanodeBlockHeight } = useNodeHealth();
const { blockDiff } = useNodeHealth();
return (
<footer className="px-4 py-1 text-xs border-t border-default text-vega-light-300 dark:text-vega-dark-300">
{/* Pull left to align with top nav, due to button padding */}
<div className="-ml-2">
{VEGA_URL && (
<NodeHealth
url={VEGA_URL}
blockHeight={datanodeBlockHeight}
blockDiff={blockDiff}
onClick={() => setNodeSwitcher(true)}
/>
)}
<footer className="px-4 py-1 text-xs border-t border-default">
<div className="flex justify-between">
<div className="flex gap-2">
{VEGA_URL && (
<>
<NodeHealth
blockDiff={blockDiff}
openNodeSwitcher={() => setNodeSwitcher(true)}
/>
{' | '}
<NodeUrl
url={VEGA_URL}
openNodeSwitcher={() => setNodeSwitcher(true)}
/>
</>
)}
</div>
</div>
</footer>
);
};
interface NodeHealthProps {
url: string;
blockHeight: number | undefined;
blockDiff: number | null;
onClick: () => void;
}
export const NodeHealth = ({
url,
blockHeight,
blockDiff,
onClick,
}: NodeHealthProps) => {
return (
<FooterButton onClick={onClick} data-testid="node-health">
<FooterButtonPart>
<HealthIndicator blockDiff={blockDiff} />
</FooterButtonPart>
<FooterButtonPart>
<NodeUrl url={url} />
</FooterButtonPart>
<FooterButtonPart>
<span title={t('Block height')}>{blockHeight}</span>
</FooterButtonPart>
</FooterButton>
);
};
interface NodeUrlProps {
url: string;
openNodeSwitcher: () => void;
}
export const NodeUrl = ({ url }: NodeUrlProps) => {
export const NodeUrl = ({ url, openNodeSwitcher }: NodeUrlProps) => {
// get base url from api url, api sub domain
const urlObj = new URL(url);
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
return <span title={t('Connected node')}>{nodeUrl}</span>;
return <ButtonLink onClick={openNodeSwitcher}>{nodeUrl}</ButtonLink>;
};
interface HealthIndicatorProps {
interface NodeHealthProps {
openNodeSwitcher: () => void;
blockDiff: number | null;
}
@@ -75,7 +54,10 @@ interface HealthIndicatorProps {
// deemed acceptable for "Good" status
const BLOCK_THRESHOLD = 3;
export const HealthIndicator = ({ blockDiff }: HealthIndicatorProps) => {
export const NodeHealth = ({
blockDiff,
openNodeSwitcher,
}: NodeHealthProps) => {
const online = useNavigatorOnline();
let intent = Intent.Success;
@@ -94,36 +76,9 @@ export const HealthIndicator = ({ blockDiff }: HealthIndicatorProps) => {
}
return (
<span title={t('Node health')}>
<>
<Indicator variant={intent} />
{text}
</span>
);
};
type FooterButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
const FooterButton = (props: FooterButtonProps) => {
const buttonClasses = classNames(
'px-2 py-0.5 rounded-md',
'enabled:hover:bg-vega-light-150',
'dark:enabled:hover:bg-vega-dark-150'
);
return <button {...props} className={buttonClasses} />;
};
const FooterButtonPart = ({ children }: { children: ReactNode }) => {
return (
<span
className={classNames(
'relative inline-block mr-2 last:mr-0 pr-2 last:pr-0',
'last:after:hidden',
'after:content after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2',
'after:h-3 after:w-1 after:border-r',
'after:border-vega-light-300 dark:after:border-vega-dark-300'
)}
>
{children}
</span>
<ButtonLink onClick={openNodeSwitcher}>{text}</ButtonLink>
</>
);
};
@@ -9,15 +9,11 @@ export const Preloader = () => {
display: block;
width: 100%;
height: 100%;
margin: 0;
}
html.dark body{
background: #000;
}
`}
</style>
<div className="pre-loader">
<Loader />
<Loader forceTheme="light" preloader />
</div>
</>
);
+1
View File
@@ -22,6 +22,7 @@ import {
useInitializeEnv,
} from '@vegaprotocol/environment';
import './styles.css';
import './gen-styles.scss';
import { useGlobalStore, usePageTitleStore } from '../stores';
import { Footer } from '../components/footer';
import { useMemo, useState } from 'react';
+1 -2
View File
@@ -19,13 +19,12 @@ export default function Document() {
type="image/x-icon"
href="https://static.vega.xyz/favicon.ico"
/>
<script src="/theme-setter.js" type="text/javascript" async />
{['1', 'true'].includes(process.env['NX_USE_ENV_OVERRIDES'] || '') ? (
/* eslint-disable-next-line @next/next/no-sync-scripts */
<script src="/assets/env-config.js" type="text/javascript" />
) : null}
</Head>
<body className="font-alpha dark:bg-black dark:text-white">
<body className="font-alpha liga-0-calt-0">
<Main />
<NextScript />
</body>
+27 -35
View File
@@ -4,10 +4,17 @@
min-height: 100vh;
justify-content: center;
align-items: center;
.loader-item {
width: 10px;
height: 10px;
background: black;
@for $i from 0 through 16 {
.loader-item:nth-child(#{$i}) {
@if $i % 2 == 0 {
animation-delay: #{$i * 50 * random(5)}ms;
animation-direction: reverse;
} @else {
animation-delay: #{$i * -50 * random(5)}ms;
animation-direction: alternate;
}
}
}
.pre-loader-center {
align-items: center;
@@ -20,39 +27,24 @@
display: flex;
flex-wrap: wrap;
}
@for $i from 0 through 16 {
.loader-item:nth-child(#{$i}) {
@if $i % 2 == 0 {
animation-delay: #{$i * 50 * random(5)}ms;
animation-direction: reverse;
} @else {
animation-delay: #{$i * -50 * random(5)}ms;
animation-direction: alternate;
}
}
}
.loader-item {
animation: flickering 0.4s linear alternate infinite;
}
@keyframes flickering {
0% {
opacity: 1;
}
25% {
opacity: 1;
}
26% {
opacity: 0;
}
100% {
opacity: 0;
}
width: 10px;
height: 10px;
background: black;
animation: flickering 0.4s steps(2, jump-none) alternate infinite;
}
}
html.dark {
.pre-loader {
.loader-item {
background: white;
}
@keyframes flickering {
0% {
opacity: 1;
}
25% {
opacity: 1;
}
26% {
opacity: 0;
}
100% {
opacity: 0;
}
}
+71 -81
View File
@@ -5,10 +5,73 @@
justify-content: center;
align-items: center;
}
.pre-loader .loader-item {
width: 10px;
height: 10px;
background: #000;
.pre-loader .loader-item:nth-child(0) {
animation-delay: 0ms;
animation-direction: reverse;
}
.pre-loader .loader-item:first-child {
animation-delay: -0.2s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(2) {
animation-delay: 0.1s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(3) {
animation-delay: -0.15s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(4) {
animation-delay: 1s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(5) {
animation-delay: -0.25s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(6) {
animation-delay: 0.3s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(7) {
animation-delay: -1.05s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(8) {
animation-delay: 0.8s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(9) {
animation-delay: -0.9s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(10) {
animation-delay: 0.5s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(11) {
animation-delay: -2.75s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(12) {
animation-delay: 2.4s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(13) {
animation-delay: -0.65s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(14) {
animation-delay: 0.7s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(15) {
animation-delay: -3s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(16) {
animation-delay: 2.4s;
animation-direction: reverse;
}
.pre-loader .pre-loader-center {
align-items: center;
@@ -21,81 +84,11 @@
display: flex;
flex-wrap: wrap;
}
@media (prefers-color-scheme: light) {
.pre-loader .loader-item {
background: #000;
}
}
.pre-loader .loader-item:nth-child(0) {
animation-delay: 0ms;
animation-direction: reverse;
}
.pre-loader .loader-item:first-child {
animation-delay: -50ms;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(2) {
animation-delay: 0.2s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(3) {
animation-delay: -0.6s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(4) {
animation-delay: 0.4s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(5) {
animation-delay: -0.5s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(6) {
animation-delay: 0.3s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(7) {
animation-delay: -1.4s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(8) {
animation-delay: 2s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(9) {
animation-delay: -0.9s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(10) {
animation-delay: 1.5s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(11) {
animation-delay: -0.55s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(12) {
animation-delay: 1.2s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(13) {
animation-delay: -0.65s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(14) {
animation-delay: 0.7s;
animation-direction: reverse;
}
.pre-loader .loader-item:nth-child(15) {
animation-delay: -3.75s;
animation-direction: alternate;
}
.pre-loader .loader-item:nth-child(16) {
animation-delay: 1.6s;
animation-direction: reverse;
}
.pre-loader .loader-item {
animation: flickering 0.4s linear infinite alternate;
width: 10px;
height: 10px;
background: #000;
animation: flickering 0.4s steps(2, jump-none) infinite alternate;
}
@keyframes flickering {
0% {
@@ -111,6 +104,3 @@
opacity: 0;
}
}
html.dark .pre-loader .loader-item {
background: #fff;
}
-9
View File
@@ -1,9 +0,0 @@
(function () {
var storedTheme = window.localStorage.getItem('theme');
if (
storedTheme === 'dark' ||
(!storedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
document.documentElement.classList.add('dark');
}
})();
+1 -1
View File
@@ -98,7 +98,7 @@ export const AssetDetailsDialog = ({
}}
>
{content}
<p className="text-sm my-4">
<p className="text-sm mb-4">
{t(
'There is 1 unit of the settlement asset (%s) to every 1 quote unit.',
[assetSymbol]
+1 -9
View File
@@ -32,8 +32,7 @@ export const aliasGQLQuery = (
operationName: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data?: any,
errors?: Partial<GraphQLError>[],
headers?: Record<string, string>
errors?: Partial<GraphQLError>[]
) => {
if (hasOperationName(req, operationName)) {
req.alias = operationName;
@@ -41,13 +40,6 @@ export const aliasGQLQuery = (
req.reply({
statusCode: 200,
body: { ...(data && { data }), ...(errors && { errors }) },
headers: {
...req.headers,
// basic default block height header response
'x-block-height': '100',
'x-block-timestamp': Date.now().toString() + '0'.repeat(6),
...headers,
},
});
}
}
@@ -12,7 +12,7 @@ export const DealTicketButton = ({ disabled, variant }: Props) => {
const { pubKey, isReadOnly } = useVegaWallet();
const isDisabled = !pubKey || isReadOnly || disabled;
return (
<div className="mb-2">
<div className="mb-4">
<Button
variant={variant}
fill
@@ -31,7 +31,7 @@ export const DealTicketFeeDetails = ({
<div>
{details.map(({ label, value, labelDescription, symbol }) => (
<div
key={typeof label === 'string' ? label : 'value-dropdown'}
key={label}
className="text-xs mt-2 flex justify-between items-center gap-4 flex-wrap"
>
<div>
@@ -38,7 +38,7 @@ export const DealTicketLimitAmount = ({
};
return (
<div className="mb-2">
<div className="mb-6">
<div className="flex items-center gap-4">
<div className="flex-1">
<FormGroup
@@ -29,9 +29,9 @@ export const DealTicketMarketAmount = ({
: undefined;
return (
<div className="mb-2">
<div className="mb-6">
<div className="flex items-end gap-4 mb-2">
<div className="flex-1 text-sm">{t('Size')}</div>
<div className="flex-1 text-sm">Size</div>
<div />
<div className="flex-2 text-sm text-right">
{isMarketInAuction(marketData.marketTradingMode) && (
@@ -307,7 +307,7 @@ const SummaryMessage = memo(
);
if (isReadOnly) {
return (
<div className="mb-2">
<div className="mb-4">
<InputError testId="dealticket-error-message-summary">
{
'You need to connect your own wallet to start trading on this market'
@@ -318,7 +318,7 @@ const SummaryMessage = memo(
}
if (!pubKey) {
return (
<div className="mb-2">
<div className="mb-4">
<Notification
testId={'deal-ticket-connect-wallet'}
intent={Intent.Warning}
@@ -343,7 +343,7 @@ const SummaryMessage = memo(
}
if (errorMessage === SummaryValidationType.NoCollateral) {
return (
<div className="mb-2">
<div className="mb-4">
<ZeroBalanceError
asset={market.tradableInstrument.instrument.product.settlementAsset}
onClickCollateral={onClickCollateral}
@@ -356,7 +356,7 @@ const SummaryMessage = memo(
// submission render that first
if (errorMessage) {
return (
<div className="mb-2">
<div className="mb-4">
<InputError testId="dealticket-error-message-summary">
{errorMessage}
</InputError>
@@ -368,7 +368,7 @@ const SummaryMessage = memo(
// balance render the margin warning, but still allow submission
if (balanceError) {
return (
<div className="mb-2">
<div className="mb-4">
<MarginWarning balance={balance} margin={margin} asset={asset} />;
</div>
);
@@ -383,7 +383,7 @@ const SummaryMessage = memo(
].includes(marketData.marketTradingMode)
) {
return (
<div className="mb-2">
<div className="mb-4">
<Notification
intent={Intent.Warning}
testId={'dealticket-warning-auction'}
+5 -12
View File
@@ -4,16 +4,14 @@ import { useHeaderStore } from '@vegaprotocol/apollo-client';
import { useEnvironment } from './use-environment';
import { fromNanoSeconds } from '@vegaprotocol/react-helpers';
const POLL_INTERVAL = 1000;
export const useNodeHealth = () => {
const url = useEnvironment((store) => store.VEGA_URL);
const headerStore = useHeaderStore();
const headers = url ? headerStore[url] : undefined;
const { data, error, loading, startPolling, stopPolling } =
useStatisticsQuery({
fetchPolicy: 'no-cache',
});
const { data, error, loading, stopPolling } = useStatisticsQuery({
pollInterval: 1000,
fetchPolicy: 'no-cache',
});
const blockDiff = useMemo(() => {
if (!data?.statistics.blockHeight) {
@@ -30,13 +28,8 @@ export const useNodeHealth = () => {
useEffect(() => {
if (error) {
stopPolling();
return;
}
if (!('Cypress' in window)) {
startPolling(POLL_INTERVAL);
}
}, [error, startPolling, stopPolling]);
}, [error, stopPolling]);
return {
error,
@@ -5,8 +5,6 @@ import { useVoteEvent } from './use-vote-event';
import type { VoteValue } from '@vegaprotocol/types';
import type { VoteEventFieldsFragment } from './__generated__/VoteSubsciption';
export type FinalizedVote = VoteEventFieldsFragment;
export const useVoteSubmit = () => {
const { pubKey } = useVegaWallet();
const { send, transaction, setComplete, Dialog } = useVegaTransaction();
+6 -5
View File
@@ -1,10 +1,11 @@
import React from 'react';
import {
PriceCell,
VolCell,
Vol,
CumulativeVol,
addDecimalsFormatNumber,
VolumeType,
addDecimal,
} from '@vegaprotocol/react-helpers';
interface OrderbookRowProps {
@@ -41,17 +42,17 @@ export const OrderbookRow = React.memo(
}: OrderbookRowProps) => {
return (
<>
<VolCell
<Vol
testId={`bid-vol-${price}`}
value={bid}
valueFormatted={addDecimalsFormatNumber(bid, positionDecimalPlaces)}
valueFormatted={addDecimal(bid, positionDecimalPlaces)}
relativeValue={relativeBid}
type={VolumeType.bid}
/>
<VolCell
<Vol
testId={`ask-vol-${price}`}
value={ask}
valueFormatted={addDecimalsFormatNumber(ask, positionDecimalPlaces)}
valueFormatted={addDecimal(ask, positionDecimalPlaces)}
relativeValue={relativeAsk}
type={VolumeType.ask}
/>
@@ -145,7 +145,7 @@ export const Info = ({ market, onSelect }: InfoProps) => {
}}
decimalPlaces={market.decimalPlaces}
/>
<p className="text-xs mt-4">
<p className="text-xs">
{t(
'There is 1 unit of the settlement asset (%s) to every 1 quote unit (%s).',
[assetSymbol, quoteUnit]
@@ -249,7 +249,7 @@ export const Info = ({ market, onSelect }: InfoProps) => {
dtClassName="text-black dark:text-white text-ui !px-0 !font-normal"
ddClassName="text-black dark:text-white text-ui !px-0 !font-normal max-w-full"
/>
<p className="text-xs mt-4">
<p className="text-xs">
{t(
'There is 1 unit of the settlement asset (%s) to every 1 quote unit (%s).',
[assetSymbol, quoteUnit]
@@ -309,40 +309,27 @@ export const Info = ({ market, onSelect }: InfoProps) => {
),
},
...(market.priceMonitoringSettings?.parameters?.triggers || []).map(
(trigger, i) => {
const bounds = market.data?.priceMonitoringBounds?.[i];
return {
title: t(`Price monitoring bounds ${i + 1}`),
content: (
<div className="text-xs">
<div className="grid grid-cols-2 text-xs mb-4">
<p className="col-span-1">
{t('%s% probability of trading', [
formatNumber(trigger.probability * 100),
])}
</p>
<p className="col-span-1 text-right">
{t('Within %s seconds', [formatNumber(trigger.horizonSecs)])}
</p>
</div>
<div className="pl-2 pb-0 text-xs border-l-2">
{bounds && (
<MarketInfoTable
data={{
highestPrice: bounds.maxValidPrice,
lowestPrice: bounds.minValidPrice,
referencePrice: bounds.referencePrice,
}}
decimalPlaces={assetDecimals}
assetSymbol={quoteUnit}
/>
)}
</div>
</div>
),
};
}
(trigger, i) => ({
title: t(`Price monitoring trigger ${i + 1}`),
content: <MarketInfoTable data={trigger} />,
})
),
...(market.data?.priceMonitoringBounds || []).map((trigger, i) => ({
title: t(`Price monitoring bound ${i + 1}`),
content: (
<>
<MarketInfoTable
data={trigger}
decimalPlaces={market.decimalPlaces}
omits={['referencePrice', '__typename']}
/>
<MarketInfoTable
data={{ referencePrice: trigger.referencePrice }}
decimalPlaces={assetDecimals}
/>
</>
),
})),
{
title: t('Liquidity monitoring parameters'),
content: (
@@ -382,38 +369,37 @@ export const Info = ({ market, onSelect }: InfoProps) => {
content: (
<>
<p className="text-xs mb-4">
{`For liquidity orders to count towards a commitment, they must be
within the liquidity monitoring bounds.`}
{`For liquidity orders count towards a commitment they have to be
within either the liquidity or price monitoring bounds (whichever is
tighter).`}
</p>
<p className="text-xs mb-4">
{`The liquidity price range is a ${liquidityPriceRange} difference from the mid
price.`}
</p>
<div className="pl-2 pb-0 text-xs border-l-2">
<MarketInfoTable
data={{
liquidityPriceRange: `${liquidityPriceRange} of mid price`,
lowestPrice:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.minus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${quoteUnit}`,
highestPrice:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.plus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${quoteUnit}`,
}}
></MarketInfoTable>
</div>
<MarketInfoTable
data={{
liquidityPriceRange: `${liquidityPriceRange} of mid price`,
lowestPrice:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.minus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${quoteUnit}`,
highestPrice:
market.data?.midPrice &&
`${addDecimalsFormatNumber(
new BigNumber(1)
.plus(market.lpPriceRange)
.times(market.data.midPrice)
.toString(),
market.decimalPlaces
)} ${quoteUnit}`,
}}
></MarketInfoTable>
</>
),
},
@@ -63,7 +63,7 @@ export const StatsManager = ({ className }: StatsManagerProps) => {
<div className={classes}>
<h3
data-testid="stats-environment"
className="font-alpha calt uppercase text-2xl pb-8 col-span-full"
className="font-alpha uppercase text-2xl pb-8 col-span-full"
>
{(error && `/ ${error}`) ||
(data ? `/ ${VEGA_ENV}` : '/ Connecting...')}
@@ -6,7 +6,6 @@ import {
formatNumber,
formatNumberPercentage,
isNumeric,
toDecimal,
toNumberParts,
} from './number';
@@ -198,20 +197,3 @@ describe('compactNumber', () => {
}
);
});
describe('toDecimal', () => {
it.each([
{ v: 0, o: '1' },
{ v: 1, o: '0.1' },
{ v: 2, o: '0.01' },
{ v: 3, o: '0.001' },
{ v: 4, o: '0.0001' },
{ v: 5, o: '0.00001' },
{ v: 6, o: '0.000001' },
{ v: 7, o: '0.0000001' },
{ v: 8, o: '0.00000001' },
{ v: 9, o: '0.000000001' },
])('formats with toNumber given number correctly', ({ v, o }) => {
expect(toDecimal(v)).toStrictEqual(o);
});
});
@@ -1,4 +1,6 @@
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { NumericCell } from './numeric-cell';
describe('NumericCell', () => {
@@ -1,4 +1,6 @@
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { PriceFlashCell } from './price-flash-cell';
describe('<PriceFlashCell />', () => {
@@ -1,49 +0,0 @@
import { render, screen } from '@testing-library/react';
import { VolCell, VolumeType } from './vol-cell';
import * as tailwind from '@vegaprotocol/tailwindcss-config';
describe('VolCell', () => {
const significantPart = '12,345';
const decimalPart = '67';
const props = {
value: 1234567,
valueFormatted: `${significantPart}.${decimalPart}`,
type: VolumeType.ask,
testId: 'cell',
};
it('Displays formatted value', () => {
render(<VolCell {...props} />);
expect(screen.getByTestId(props.testId)).toHaveTextContent(
props.valueFormatted
);
expect(screen.getByText(decimalPart)).toBeInTheDocument();
expect(screen.getByText(decimalPart)).toHaveClass('opacity-60');
});
it('Displays 0', () => {
render(<VolCell {...props} value={0} valueFormatted="0.00" />);
expect(screen.getByTestId(props.testId)).toHaveTextContent('0.00');
});
it('Displays - if value is not a number', () => {
render(<VolCell {...props} value={null} valueFormatted="" />);
expect(screen.getByTestId(props.testId)).toHaveTextContent('-');
});
it('renders bid volume bar', () => {
render(<VolCell {...props} type={VolumeType.bid} />);
expect(screen.getByTestId('vol-bar')).toHaveClass('left-0'); // renders bid bars from the left
expect(screen.getByTestId('vol-bar')).toHaveStyle({
backgroundColor: tailwind.theme.colors.vega.green.DEFAULT,
});
});
it('renders ask volume bar', () => {
render(<VolCell {...props} type={VolumeType.ask} />);
expect(screen.getByTestId('vol-bar')).toHaveClass('right-0'); // renders ask bars from the right
expect(screen.getByTestId('vol-bar')).toHaveStyle({
backgroundColor: tailwind.theme.colors.vega.pink.DEFAULT,
});
});
});
+6 -7
View File
@@ -8,7 +8,7 @@ export enum VolumeType {
bid,
ask,
}
export interface VolCellProps {
export interface VolProps {
value: number | bigint | null | undefined;
valueFormatted: string;
relativeValue?: number;
@@ -17,21 +17,20 @@ export interface VolCellProps {
}
export interface IVolCellProps extends ICellRendererParams {
value: number | bigint | null | undefined;
valueFormatted: Omit<VolCellProps, 'value'>;
valueFormatted: Omit<VolProps, 'value'>;
}
export const BID_COLOR = tailwind.theme.colors.vega.green.DEFAULT;
export const ASK_COLOR = tailwind.theme.colors.vega.pink.DEFAULT;
export const VolCell = React.memo(
({ value, valueFormatted, relativeValue, type, testId }: VolCellProps) => {
export const Vol = React.memo(
({ value, valueFormatted, relativeValue, type, testId }: VolProps) => {
if ((!value && value !== 0) || isNaN(Number(value))) {
return <div data-testid={testId || 'vol'}>-</div>;
return <div data-testid="vol">-</div>;
}
return (
<div className="relative" data-testid={testId || 'vol'}>
<div
data-testid="vol-bar"
className={classNames(
'h-full absolute top-0 opacity-40 dark:opacity-100',
{
@@ -51,4 +50,4 @@ export const VolCell = React.memo(
}
);
VolCell.displayName = 'VolCell';
Vol.displayName = 'Vol';
+25 -3
View File
@@ -122,11 +122,33 @@ module.exports = {
fontFamily: {
mono: ['Roboto Mono', 'monospace'],
sans: [
'"Helvetica Neue", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
'"Helvetica Neue"',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'Arial',
'"Noto Sans"',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
'"Noto Color Emoji"',
],
alpha: [
'AlphaLyrae, "Helvetica Neue", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
{ fontFeatureSettings: '"calt" 0, "liga" 0' },
'AlphaLyrae',
'"Helvetica Neue"',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'Arial',
'"Noto Sans"',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
'"Noto Color Emoji"',
],
},
keyframes: {
@@ -7,13 +7,12 @@ const vegaCustomClasses = plugin(function ({ addUtilities }) {
'.calt': {
fontFeatureSettings: "'calt'",
},
'.liga-0-calt-0': {
fontFeatureSettings: "'liga' 0, 'calt' 0",
},
'.liga': {
fontFeatureSettings: "'liga'",
},
// Fix for Firefox to make it inherit font-feature-settings from the default theme
'button, input, optgroup, select, textarea': {
fontFeatureSettings: 'inherit',
},
'.syntax-highlighter-wrapper .hljs': {
fontSize: '1rem',
fontFamily: "'Roboto Mono', monospace",
+1 -1
View File
@@ -1,4 +1,4 @@
{
"name": "@vegaprotocol/ui-toolkit",
"version": "0.7.0"
"version": "0.0.1"
}
@@ -20,7 +20,7 @@ export const FormGroup = ({
labelAlign = 'left',
hideLabel = false,
}: FormGroupProps) => {
const wrapperClasses = classNames('relative mb-2', className);
const wrapperClasses = classNames('relative mb-6', className);
const labelClasses = classNames('block mb-2 text-sm', {
'text-right': labelAlign === 'right',
'sr-only': hideLabel,
@@ -24,7 +24,11 @@ export const KeyValueTable = ({
return (
<React.Fragment>
{title && <TitleTag className={`text-xl my-2`}>{title}</TitleTag>}
<div data-testid="key-value-table" {...rest} className={className}>
<div
data-testid="key-value-table"
{...rest}
className={classNames('mb-4', className)}
>
<div>
{children &&
React.Children.map(
@@ -1,28 +0,0 @@
@for $i from 0 through 16 {
.loader-item:nth-child(#{$i}) {
@if $i % 2 == 0 {
animation-delay: #{$i * 50 * random(5)}ms;
animation-direction: reverse;
} @else {
animation-delay: #{$i * -50 * random(5)}ms;
animation-direction: alternate;
}
}
}
.loader-item {
animation: flickering 0.4s linear alternate infinite;
}
@keyframes flickering {
0% {
opacity: 1;
}
25% {
opacity: 1;
}
26% {
opacity: 0;
}
100% {
opacity: 0;
}
}
@@ -1,6 +1,5 @@
import classNames from 'classnames';
import { useMemo } from 'react';
import styles from './loader.module.scss';
import { useEffect, useMemo, useState } from 'react';
const pseudoRandom = (seed: number) => {
let value = seed;
@@ -13,10 +12,27 @@ const pseudoRandom = (seed: number) => {
export interface LoaderProps {
size?: 'small' | 'large';
forceTheme?: 'dark' | 'light';
preloader?: boolean;
}
export const Loader = ({ size = 'large', forceTheme }: LoaderProps) => {
const itemClasses = classNames('loader-item', styles['loader-item'], {
export const Loader = ({
size = 'large',
forceTheme,
preloader,
}: LoaderProps) => {
const [, forceRender] = useState(false);
useEffect(() => {
const interval = preloader
? undefined
: setInterval(() => {
forceRender((x) => !x);
}, 100);
return () => clearInterval(interval);
}, [preloader]);
const itemClasses = classNames('loader-item', {
'dark:bg-white bg-black': !forceTheme,
'bg-white': forceTheme === 'dark',
'bg-black': forceTheme === 'light',
@@ -209,7 +209,7 @@ export const MaintenancePage = () => {
</div>
</div>
<div className="flex mb-2">
<span className="font-alpha calt uppercase text-xl text-center">
<span className="font-alpha uppercase text-xl text-center">
{t("We're doing some maintenance right now, check back later")}
</span>
</div>
+1 -1
View File
@@ -73,7 +73,7 @@ export const Nav = ({
{icon}
<h1
className={classNames(
'h-full flex flex-col my-0 justify-center font-alpha calt uppercase',
'h-full flex flex-col my-0 justify-center font-alpha uppercase',
{ 'text-black': isYellow, 'text-white': !isYellow }
)}
>
@@ -155,7 +155,7 @@ export const Toast = ({
'w-[320px] rounded-md overflow-hidden',
'shadow-[8px_8px_16px_0_rgba(0,0,0,0.4)]',
'text-black dark:text-white',
'font-alpha text-[14px] leading-[19px]',
'font-alpha liga-0-calt-0 text-[14px] leading-[19px]',
// background
{
'bg-vega-light-100 dark:bg-vega-dark-100 ': intent === Intent.None,
@@ -20,7 +20,7 @@ const TextSample = ({ alternatives, isAlpha, type }: Args) => {
<div
className={classNames(
'flex-grow flex flex-col justify-end text-left items-start',
{ 'font-alpha calt': isAlpha },
{ 'font-alpha': isAlpha },
[alternatives, type]
)}
>
@@ -52,7 +52,7 @@ export default {
},
alternatives: {
name: 'Font features',
options: ['none', 'calt', 'liga'],
options: ['none', 'calt', 'liga', 'liga-0-calt-0'],
control: { type: 'select' },
},
},
@@ -61,5 +61,5 @@ export default {
export const Default = Template;
Default.args = {
isAlpha: true,
alternatives: 'none',
alternatives: 'liga-0-calt-0',
};
@@ -7,7 +7,7 @@ export const ConnectDialogTitle = ({ children }: { children: ReactNode }) => {
return (
<h1
data-testid="wallet-dialog-title"
className="text-2xl uppercase mb-6 text-center font-alpha calt"
className="text-2xl uppercase mb-6 text-center font-alpha"
>
{children}
</h1>
@@ -58,7 +58,7 @@ export function ViewConnectorForm({
<Icon name={'chevron-left'} ariaLabel="back" size={4} />
</button>
<form onSubmit={handleSubmit(onSubmit)} data-testid="view-connector-form">
<h1 className="text-2xl uppercase mb-6 text-center font-alpha calt">
<h1 className="text-2xl uppercase mb-6 text-center font-alpha">
{t('VIEW AS VEGA USER')}
</h1>
<p className="mb-4">
+1 -1
View File
@@ -14,7 +14,7 @@
"vegacapsule": "vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl"
},
"engines": {
"node": ">=16.15.1"
"node": ">=16.14.0"
},
"private": true,
"dependencies": {