fix(#315): misc styling and ui fixes for the token app
* chore: add callout loaders and input lozenges * fix: text colors and nav heading * fix: text color for home links * chore: fix spacing of wallets * chore: fix missing translation keys * chore: add loader to pending associatino tx callout, fix spacing of text within callout * chore: make sure etherscan links open in a new tab * fix: redemption page * fix: spacing of rewards tables list * fix: link styles on withdraw page * fix: styles for withdrawal table * fix: footer links * fix: staking page links and spacing * fix: translations * fix: spacing of callout title, spacing of staking connect step * fix: vesting page title * fix: proposals list spacing * fix: proposal page and vote details * chore: update translation of metamask wallet connection button * chore: delete unused files * chore: dont nest buttons inside links * chore: lint * fix: title test after text change
This commit is contained in:
parent
458e9cafc3
commit
a66be425be
@ -6,7 +6,7 @@ describe('token', () => {
|
||||
it('should always have a header title based on environment', () => {
|
||||
cy.get('[data-testid="header-title"]', { timeout: 8000 }).should(
|
||||
'have.text',
|
||||
`${fairgroundSet ? 'Fairground token' : '$VEGA TOKEN'}`
|
||||
`${fairgroundSet ? 'Fairground token' : 'VEGA TOKEN'}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -1,14 +1,12 @@
|
||||
import './i18n';
|
||||
|
||||
import React from 'react';
|
||||
import { ApolloProvider } from '@apollo/client';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
|
||||
import { AppLoader } from './app-loader';
|
||||
import { AppBanner } from './components/app-banner';
|
||||
import { AppFooter } from './components/app-footer';
|
||||
import { BalanceManager } from './components/balance-manager';
|
||||
import { EthWallet } from './components/eth-wallet';
|
||||
import { GraphQlProvider } from './components/graphql-provider';
|
||||
import { TemplateSidebar } from './components/page-templates/template-sidebar';
|
||||
import { TransactionModal } from './components/transactions-modal';
|
||||
import { VegaWallet } from './components/vega-wallet';
|
||||
@ -21,11 +19,12 @@ import { Connectors } from './lib/web3-connectors';
|
||||
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
|
||||
import { client } from './lib/apollo-client';
|
||||
|
||||
function App() {
|
||||
const sideBar = React.useMemo(() => [<EthWallet />, <VegaWallet />], []);
|
||||
return (
|
||||
<GraphQlProvider>
|
||||
<ApolloProvider client={client}>
|
||||
<Router>
|
||||
<EnvironmentProvider>
|
||||
<AppStateProvider>
|
||||
@ -55,7 +54,7 @@ function App() {
|
||||
</AppStateProvider>
|
||||
</EnvironmentProvider>
|
||||
</Router>
|
||||
</GraphQlProvider>
|
||||
</ApolloProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
import { Links } from '../../config';
|
||||
@ -13,8 +14,12 @@ export const AppFooter = () => {
|
||||
i18nKey="footerLinksText"
|
||||
components={{
|
||||
/* eslint-disable */
|
||||
feedbackLink: <a href={Links.FEEDBACK} />,
|
||||
githubLink: <a href={Links.GITHUB} />,
|
||||
feedbackLink: (
|
||||
<Link className="text-white underline" href={Links.FEEDBACK} />
|
||||
),
|
||||
githubLink: (
|
||||
<Link className="text-white underline" href={Links.GITHUB} />
|
||||
),
|
||||
/* eslint-enable */
|
||||
}}
|
||||
/>
|
||||
|
@ -9,7 +9,7 @@ interface BulletHeaderProps {
|
||||
export const BulletHeader = ({ tag, children, style }: BulletHeaderProps) => {
|
||||
return React.createElement(
|
||||
tag,
|
||||
{ className: 'mt-24 pt-8 pb-20 uppercase', style },
|
||||
{ className: 'mt-24 pt-8 pb-20 uppercase text-white', style },
|
||||
<>
|
||||
<span className="inline-block w-[12px] h-[12px] mr-12 bg-white" />
|
||||
{children}
|
||||
|
@ -1,39 +0,0 @@
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
} from '../../contexts/app-state/app-state-context';
|
||||
import { Ethereum } from '../icons';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface EthWalletContainerProps {
|
||||
children: (address: string) => React.ReactElement;
|
||||
}
|
||||
|
||||
export const EthWalletContainer = ({ children }: EthWalletContainerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { appDispatch } = useAppState();
|
||||
const { account } = useWeb3React();
|
||||
|
||||
if (!account) {
|
||||
return (
|
||||
<Button
|
||||
data-testid="connect-to-eth-btn"
|
||||
onClick={() =>
|
||||
appDispatch({
|
||||
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
|
||||
isOpen: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
<div>{t('connectEthWallet')}</div>
|
||||
<Ethereum />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return children(account);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './eth-wallet-container';
|
@ -163,15 +163,15 @@ const ConnectedKey = () => {
|
||||
/>
|
||||
)}
|
||||
<WalletCardActions>
|
||||
<Link style={{ flex: 1 }} to={`${Routes.STAKING}/associate`}>
|
||||
<Button variant="primary" className="w-full">
|
||||
<Link className="flex-1" to={`${Routes.STAKING}/associate`}>
|
||||
<span className="flex items-center justify-center w-full text-center px-28 border h-28">
|
||||
{t('associate')}
|
||||
</Button>
|
||||
</span>
|
||||
</Link>
|
||||
<Link style={{ flex: 1 }} to={`${Routes.STAKING}/disassociate`}>
|
||||
<Button variant="primary" className="w-full">
|
||||
<Link className="flex-1" to={`${Routes.STAKING}/disassociate`}>
|
||||
<span className="flex items-center justify-center w-full px-28 border h-28">
|
||||
{t('disassociate')}
|
||||
</Button>
|
||||
</span>
|
||||
</Link>
|
||||
</WalletCardActions>
|
||||
</>
|
||||
@ -187,7 +187,7 @@ export const EthWallet = () => {
|
||||
return (
|
||||
<WalletCard>
|
||||
<WalletCardHeader>
|
||||
<h1 className="text-h3 px-8 uppercase">{t('ethereumKey')}</h1>
|
||||
<h1 className="text-h3 uppercase">{t('ethereumKey')}</h1>
|
||||
{account && (
|
||||
<div className="font-mono px-4 text-right">
|
||||
<div>{truncateMiddle(account)}</div>
|
||||
@ -215,8 +215,8 @@ export const EthWallet = () => {
|
||||
{account ? (
|
||||
<ConnectedKey />
|
||||
) : (
|
||||
<Button
|
||||
className="fill button-secondary--inverted"
|
||||
<button
|
||||
className="w-full px-28 border h-28"
|
||||
onClick={() =>
|
||||
appDispatch({
|
||||
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
|
||||
@ -226,7 +226,7 @@ export const EthWallet = () => {
|
||||
data-test-id="connect-to-eth-wallet-button"
|
||||
>
|
||||
{t('connectEthWalletToAssociate')}
|
||||
</Button>
|
||||
</button>
|
||||
)}
|
||||
{account && (
|
||||
<WalletCardActions>
|
||||
|
@ -1,12 +0,0 @@
|
||||
import { ApolloProvider } from '@apollo/client';
|
||||
import React from 'react';
|
||||
|
||||
import { client } from '../../lib/apollo-client';
|
||||
|
||||
export const GraphQlProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return <ApolloProvider client={client}>{children}</ApolloProvider>;
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './graphql-provider';
|
@ -7,7 +7,7 @@ export const Heading = ({ title }: HeadingProps) => {
|
||||
|
||||
return (
|
||||
<header className="my-0 mx-auto">
|
||||
<h1 className="font-alpha font-normal text-h3 uppercase m-0 mb-4">
|
||||
<h1 className="font-alpha calt text-h3 text-white uppercase mb-4">
|
||||
{title}
|
||||
</h1>
|
||||
</header>
|
||||
|
@ -1 +0,0 @@
|
||||
export * from './loader';
|
@ -1,35 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
interface LoaderProps {
|
||||
invert?: boolean;
|
||||
}
|
||||
|
||||
export const Loader = ({ invert = false }: LoaderProps) => {
|
||||
const [, forceRender] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
forceRender((x) => !x);
|
||||
}, 100);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<span className="flex flex-row flex-wrap w-[15px] h-[15px]">
|
||||
{new Array(9).fill(null).map((_, i) => {
|
||||
return (
|
||||
<span
|
||||
key={i}
|
||||
style={{
|
||||
opacity: Math.random() > 0.5 ? 1 : 0,
|
||||
}}
|
||||
className={`block w-5 h-5 opacity-0 ${
|
||||
invert ? 'bg-black' : 'bg-white'
|
||||
}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
);
|
||||
};
|
@ -15,7 +15,7 @@ const ProgressContents = ({
|
||||
}) => (
|
||||
<div
|
||||
className={`flex justify-between py-2 font-mono ${
|
||||
light ? 'gap-0 px-0 text-black' : 'gap-y-0 gap-x-4 px-4 text-black-60'
|
||||
light ? 'gap-0 px-0 text-black' : 'gap-y-0 gap-x-4 px-4'
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
|
@ -5,14 +5,13 @@ import debounce from 'lodash/debounce';
|
||||
import React from 'react';
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, NavLink } from 'react-router-dom';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
import { Flags } from '../../config';
|
||||
import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
} from '../../contexts/app-state/app-state-context';
|
||||
import vegaWhite from '../../images/vega_white.png';
|
||||
import { Routes } from '../../routes/router-config';
|
||||
import { EthWallet } from '../eth-wallet';
|
||||
import { VegaWallet } from '../vega-wallet';
|
||||
@ -36,14 +35,14 @@ export const Nav = () => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`p-8 ${
|
||||
className={`p-16 ${
|
||||
inverted
|
||||
? 'bg-clouds bg-no-repeat bg-cover bg-vega-yellow'
|
||||
: 'border-b-white border-b-1'
|
||||
}`}
|
||||
>
|
||||
{isDesktop && <NavHeader fairground={inverted} />}
|
||||
<div className="flex justify-between items-center mx-auto gap-12 lg:justify-start lg:ml-8">
|
||||
<div className="flex justify-between items-center mx-auto gap-12 lg:justify-start">
|
||||
{!isDesktop && <NavHeader fairground={inverted} />}
|
||||
<div className="flex gap-12 lg:flex-auto">
|
||||
{isDesktop ? (
|
||||
@ -61,76 +60,10 @@ const NavHeader = ({ fairground }: { fairground: boolean }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="h-[30px] inline-flex items-center ml-8 lg:h-40 uppercase">
|
||||
<Link to="/">
|
||||
{fairground ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="33"
|
||||
height="20"
|
||||
viewBox="0 0 200 116"
|
||||
fill="none"
|
||||
data-testid="fairground-icon"
|
||||
>
|
||||
<g clip-path="url(#clip0)">
|
||||
<path
|
||||
d="M70.5918 32.8569L70.5918 22.7932L60.5254 22.7932L60.5254 32.8569L70.5918 32.8569Z"
|
||||
fill="black"
|
||||
></path>
|
||||
<path
|
||||
d="M80.6641 83.2006L80.6641 73.1377L70.5977 73.1377L70.5977 83.2006L80.6641 83.2006Z"
|
||||
fill="black"
|
||||
></path>
|
||||
<path
|
||||
d="M70.5918 93.2409L70.5918 83.1772L60.5254 83.1772L60.5254 93.2409L70.5918 93.2409Z"
|
||||
fill="black"
|
||||
></path>
|
||||
<path
|
||||
d="M100.797 93.2636L100.797 73.1377L90.7305 73.1377L90.7305 93.2636L100.797 93.2636Z"
|
||||
fill="black"
|
||||
></path>
|
||||
<path
|
||||
d="M90.7285 103.33L90.7285 93.2671L80.662 93.2671L80.662 103.33L90.7285 103.33Z"
|
||||
fill="black"
|
||||
></path>
|
||||
<path
|
||||
d="M90.7285 22.8026L90.7285 12.74L80.662 12.74L80.662 22.8026L90.7285 22.8026Z"
|
||||
fill="black"
|
||||
></path>
|
||||
<path
|
||||
d="M110.869 12.6108L110.869 2.54785L100.803 2.54785L100.803 12.6108L110.869 12.6108Z"
|
||||
fill="black"
|
||||
></path>
|
||||
<path
|
||||
d="M120.934 103.326L120.934 73.1377L110.867 73.1377L110.867 103.326L120.934 103.326Z"
|
||||
fill="black"
|
||||
></path>
|
||||
<path
|
||||
d="M110.869 113.384L110.869 103.321L100.803 103.321L100.803 113.384L110.869 113.384Z"
|
||||
fill="black"
|
||||
></path>
|
||||
<path
|
||||
d="M161.328 52.9855L161.328 42.9226L151.262 42.9226L151.262 52.9855L161.328 52.9855Z"
|
||||
fill="black"
|
||||
></path>
|
||||
<path
|
||||
d="M20.133 83.187L30.3354 83.187L30.3354 73.124L40.4017 73.124L40.4017 63.0613L50.4681 63.0613L50.4681 73.124L60.5345 73.124L60.5345 63.0613L70.6008 63.0613L80.6672 63.0613L131.135 63.0613L131.135 113.376L161.334 113.376L161.334 103.313L171.4 103.313L171.4 93.25L181.467 93.25L181.467 83.187L191.533 83.187L191.533 63.0613L181.467 63.0613L181.467 73.1241L171.4 73.1241L171.4 83.187L161.334 83.187L161.334 73.1241L171.4 73.1241L171.4 63.0613L161.334 63.0613L151.268 63.0613L141.201 63.0613L141.201 52.9983L141.201 32.8726L161.334 32.8726L171.4 32.8726L171.4 63.0613L181.467 63.0613L181.467 52.9983L191.533 52.9983L191.533 32.8726L181.467 32.8726L181.467 22.8096L171.4 22.8096L171.4 12.7467L161.334 12.7467L161.334 2.54785L141.201 2.54785L131.135 2.54785L131.135 52.9983L120.933 52.9983L120.933 12.7467L110.866 12.7467L110.866 52.9983L100.8 52.9983L100.8 22.8096L90.7336 22.8096L90.7336 52.9983L80.6672 52.9983L80.6672 32.8726L70.6008 32.8726L70.6008 52.9983L60.5345 52.9983L60.5345 42.9354L50.4681 42.9354L50.4681 52.9983L40.4017 52.9983L40.4017 42.9354L30.3354 42.9354L30.3354 32.8726L20.133 32.8726L20.133 22.8096L0.000308081 22.8096L0.000307201 42.9354L10.0666 42.9354L10.0666 52.9983L20.133 52.9983L20.133 63.0613L10.0666 63.0613L10.0666 73.124L0.000305881 73.124L0.000305002 93.25L20.133 93.25L20.133 83.187Z"
|
||||
fill="black"
|
||||
></path>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="200" height="116" fill="none"></rect>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
) : (
|
||||
<img alt="Vega" src={vegaWhite} className="w-[30px] lg:w-40" />
|
||||
)}
|
||||
</Link>
|
||||
<div className="h-[30px] inline-flex items-center lg:h-40 uppercase">
|
||||
<h1
|
||||
data-testid="header-title"
|
||||
className={`text-[28px] lg:text-h3 pl-8 ${
|
||||
className={`text-h3 font-alpha uppercase calt mb-2 ${
|
||||
fairground ? 'text-black' : 'text-white'
|
||||
}`}
|
||||
>
|
||||
|
@ -12,7 +12,7 @@ export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||
<div className="border-b border-white lg:grid lg:grid-rows-[auto_minmax(600px,_1fr)] lg:grid-cols-[1fr_450px]">
|
||||
<Nav />
|
||||
<main className="p-20">{children}</main>
|
||||
<aside className="hidden lg:block lg:col-start-2 lg:col-end-3 lg:row-start-1 lg: row-end-3 p-20 bg-banner bg-cover border-l border-white">
|
||||
<aside className="hidden lg:block lg:col-start-2 lg:col-end-3 lg:row-start-1 lg: row-end-3 p-20 bg-banner bg-contain border-l border-white">
|
||||
{sidebar.map((Component, i) => (
|
||||
<section className="mb-20 last:mb-0" key={i}>
|
||||
{Component}
|
||||
|
@ -1 +0,0 @@
|
||||
export * from './stateful-button';
|
@ -1,14 +0,0 @@
|
||||
import type { ButtonHTMLAttributes } from 'react';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export const StatefulButton = (
|
||||
props: ButtonHTMLAttributes<HTMLButtonElement>
|
||||
) => {
|
||||
const classProp = props.className || '';
|
||||
return (
|
||||
<Button
|
||||
{...props}
|
||||
className={`flex justify-center items-center gap-12 disabled:cursor-default ${classProp}`}
|
||||
/>
|
||||
);
|
||||
};
|
@ -4,6 +4,7 @@ import {
|
||||
Input,
|
||||
Intent,
|
||||
FormGroup,
|
||||
Lozenge,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -25,7 +26,6 @@ export const AmountInput = ({
|
||||
amount,
|
||||
setAmount,
|
||||
maximum,
|
||||
// TODO: render currency in input when https://github.com/vegaprotocol/frontend-monorepo/issues/273
|
||||
currency,
|
||||
}: {
|
||||
amount: string;
|
||||
@ -36,25 +36,31 @@ export const AmountInput = ({
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex">
|
||||
<Input
|
||||
data-testid="token-amount-input"
|
||||
className="flex-1"
|
||||
name={inputName}
|
||||
id={inputName}
|
||||
onChange={(e) => setAmount(e.target.value)}
|
||||
value={amount}
|
||||
autoComplete="off"
|
||||
type="number"
|
||||
max={maximum.toString()}
|
||||
min={0}
|
||||
step="any"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<Input
|
||||
data-testid="token-amount-input"
|
||||
name={inputName}
|
||||
id={inputName}
|
||||
onChange={(e) => setAmount(e.target.value)}
|
||||
value={amount}
|
||||
autoComplete="off"
|
||||
type="number"
|
||||
max={maximum.toString()}
|
||||
min={0}
|
||||
step="any"
|
||||
appendElement={
|
||||
<Lozenge className="text-[10px] relative top-[-2px]">
|
||||
{currency}
|
||||
</Lozenge>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{maximum && (
|
||||
<Button
|
||||
variant="inline-link"
|
||||
onClick={() => setAmount(maximum.toString())}
|
||||
data-testid="token-amount-use-maximum"
|
||||
className="flex flex-col justify-center text-ui p-8 h-28 my-0 mx-8"
|
||||
className="flex flex-col justify-center p-8 h-28 my-0 mx-8"
|
||||
>
|
||||
{t('Use maximum')}
|
||||
</Button>
|
||||
@ -69,7 +75,6 @@ export const TokenInput = ({
|
||||
perform,
|
||||
submitText,
|
||||
currency,
|
||||
|
||||
approveText,
|
||||
allowance,
|
||||
approve,
|
||||
@ -84,7 +89,6 @@ export const TokenInput = ({
|
||||
perform: () => void;
|
||||
submitText: string;
|
||||
currency: string;
|
||||
|
||||
requireApproval?: boolean;
|
||||
maximum?: BigNumber;
|
||||
minimum?: BigNumber;
|
||||
@ -128,6 +132,7 @@ export const TokenInput = ({
|
||||
new BigNumber(amount).isLessThan(minimum)
|
||||
);
|
||||
}, [amount, isApproved, maximum, requireApproval, minimum]);
|
||||
|
||||
let approveContent = null;
|
||||
|
||||
if (showApproveButton) {
|
||||
@ -158,11 +163,9 @@ export const TokenInput = ({
|
||||
}
|
||||
} else if (requireApproval) {
|
||||
approveContent = (
|
||||
<Callout
|
||||
iconName="tick"
|
||||
intent={Intent.Success}
|
||||
title={`${currency} are approved for staking`}
|
||||
/>
|
||||
<Callout iconName="tick" intent={Intent.Success}>
|
||||
<p>{`${currency} are approved for staking`}</p>
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
export * from './transaction-button';
|
@ -1,159 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { TransactionState } from '../../hooks/transaction-reducer';
|
||||
import { TxState } from '../../hooks/transaction-reducer';
|
||||
import { truncateMiddle } from '../../lib/truncate-middle';
|
||||
import { Button, Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEnvironment } from '@vegaprotocol/react-helpers';
|
||||
import { Error, HandUp, Tick } from '../icons';
|
||||
import { Loader } from '../loader';
|
||||
import { StatefulButton } from '../stateful-button';
|
||||
|
||||
interface TransactionButtonProps {
|
||||
text: string;
|
||||
transactionState: TransactionState;
|
||||
/** txHash of the transaction if already complete */
|
||||
forceTxHash: string | null;
|
||||
forceTxState?: TxState;
|
||||
disabled?: boolean;
|
||||
start: () => void;
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<div className="mb-20">{children}</div>
|
||||
);
|
||||
|
||||
const Text = ({ children }: { children: React.ReactNode }) => (
|
||||
<p className="flex justify-center items-center gap-10 m-0 py-12 px-32 leading-[1.15]">
|
||||
{children}
|
||||
</p>
|
||||
);
|
||||
|
||||
export const TransactionButton = ({
|
||||
text,
|
||||
transactionState,
|
||||
forceTxHash,
|
||||
forceTxState,
|
||||
disabled = false,
|
||||
start,
|
||||
reset,
|
||||
}: TransactionButtonProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { txState, txData } = transactionState;
|
||||
const txHash = forceTxHash || txData.hash;
|
||||
const state = forceTxState || txState;
|
||||
|
||||
if (state === TxState.Complete) {
|
||||
return (
|
||||
<Wrapper>
|
||||
<Text>
|
||||
<span className="text-vega-green">
|
||||
<Tick />
|
||||
</span>
|
||||
<span>{t('txButtonComplete')}</span>
|
||||
</Text>
|
||||
<TransactionButtonFooter txHash={txHash} />
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
// User as started transaction and we are awaiting confirmation from the users wallet
|
||||
if (state === TxState.Requested) {
|
||||
return (
|
||||
<Wrapper>
|
||||
<StatefulButton disabled={true}>
|
||||
<HandUp />
|
||||
<span>{t('txButtonActionRequired')}</span>
|
||||
</StatefulButton>
|
||||
<TransactionButtonFooter
|
||||
message={t('transactionHashPrompt')}
|
||||
txHash={txHash}
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
if (state === TxState.Pending) {
|
||||
return (
|
||||
<Wrapper>
|
||||
<StatefulButton disabled={true}>
|
||||
<Loader />
|
||||
<span>{t('txButtonAwaiting')}</span>
|
||||
</StatefulButton>
|
||||
<TransactionButtonFooter txHash={txHash} />
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
if (state === TxState.Error) {
|
||||
return (
|
||||
<Wrapper>
|
||||
<Text>
|
||||
<span className="text-intent-danger">
|
||||
<Error />
|
||||
</span>
|
||||
<span>{t('txButtonFailure')}</span>
|
||||
<Button onClick={reset} variant="inline-link">
|
||||
{t('Try again')}
|
||||
</Button>
|
||||
</Text>
|
||||
<TransactionButtonFooter txHash={txHash} />
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
// Idle
|
||||
return (
|
||||
<Wrapper>
|
||||
<StatefulButton onClick={start} disabled={disabled}>
|
||||
{text}
|
||||
</StatefulButton>
|
||||
<TransactionButtonFooter txHash={txHash} />
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
interface TransactionButtonFooterProps {
|
||||
txHash: string | null;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export const TransactionButtonFooter = ({
|
||||
txHash,
|
||||
message,
|
||||
}: TransactionButtonFooterProps) => {
|
||||
const { ETHERSCAN_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (message) {
|
||||
return (
|
||||
<div className="mt-4 mb-0 mx-0">
|
||||
<p className="m-0 py-4 pl-8 border-l border-[3px] border-intent-warning text-ui">
|
||||
<span className="relative top-2 mr-4 text-intent-warning">
|
||||
<Error />
|
||||
</span>
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (txHash) {
|
||||
return (
|
||||
<div className="transaction-button__footer">
|
||||
<p className="flex justify-between items-start m-0 text-ui">
|
||||
<span>{t('transaction')}</span>
|
||||
<Link
|
||||
href={`${ETHERSCAN_URL}/tx/${txHash}`}
|
||||
title={t('View on Etherscan')}
|
||||
>
|
||||
{truncateMiddle(txHash)}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
@ -23,13 +23,18 @@ export const TransactionComplete = ({
|
||||
intent={Intent.Success}
|
||||
title={heading || t('Complete')}
|
||||
>
|
||||
{body && <p data-testid="transaction-complete-body">{body}</p>}
|
||||
<p>
|
||||
{body && (
|
||||
<p className="mb-8" data-testid="transaction-complete-body">
|
||||
{body}
|
||||
</p>
|
||||
)}
|
||||
<p className="mb-8">
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
target="_blank"
|
||||
href={`${ETHERSCAN_URL}/tx/${hash}`}
|
||||
>
|
||||
{hash}
|
||||
{t('View transaction on Etherscan')}
|
||||
</Link>
|
||||
</p>
|
||||
{footer && <p data-testid="transaction-complete-footer">{footer}</p>}
|
||||
|
@ -20,12 +20,15 @@ export const TransactionError = ({
|
||||
|
||||
return (
|
||||
<Callout iconName="error" intent={Intent.Danger}>
|
||||
<p>{error ? error.message : t('Something went wrong')}</p>
|
||||
<p className="mb-8">
|
||||
{error ? error.message : t('Something went wrong')}
|
||||
</p>
|
||||
{hash ? (
|
||||
<p>
|
||||
<p className="mb-8">
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
href={`${ETHERSCAN_URL}/tx/${hash}`}
|
||||
target="_blank"
|
||||
>
|
||||
{hash}
|
||||
</Link>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Callout } from '@vegaprotocol/ui-toolkit';
|
||||
import { Callout, Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEnvironment } from '@vegaprotocol/react-helpers';
|
||||
@ -37,11 +37,16 @@ export const TransactionPending = ({
|
||||
return defaultTitle;
|
||||
}, [heading, remainingConfirmations, t]);
|
||||
return (
|
||||
<Callout iconName="refresh" title={title}>
|
||||
{body && <p data-testid="transaction-pending-body">{body}</p>}
|
||||
<p>
|
||||
<Callout icon={<Loader size="small" />} title={title}>
|
||||
{body && (
|
||||
<p className="mb-8" data-testid="transaction-pending-body">
|
||||
{body}
|
||||
</p>
|
||||
)}
|
||||
<p className="mb-8">
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
target="_blank"
|
||||
href={`${ETHERSCAN_URL}/tx/${hash}`}
|
||||
>
|
||||
{hash}
|
||||
|
@ -7,7 +7,7 @@ export const TransactionRequested = () => {
|
||||
<Callout
|
||||
iconName="hand-up"
|
||||
intent={Intent.Prompt}
|
||||
title={t('Awaiting action in Ethereum wallet (e.g. metamask)')}
|
||||
title={t('Awaiting action in Ethereum wallet (e.g. MetaMask)')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -85,6 +85,7 @@ export const TransactionModal = () => {
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
href={`${ETHERSCAN_URL}/tx/${transaction.tx.hash}`}
|
||||
target="_blank"
|
||||
>
|
||||
{truncateMiddle(transaction.tx.hash)}
|
||||
</Link>
|
||||
|
@ -1,31 +1,22 @@
|
||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Links } from '../../config';
|
||||
|
||||
export const DownloadWalletPrompt = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<div className="mt-8">
|
||||
<h3>{t('getWallet')}</h3>
|
||||
<p style={{ margin: 0 }}>
|
||||
<a
|
||||
className={'text-deemphasise'}
|
||||
href={Links.WALLET_GUIDE}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<p>
|
||||
<Link className="text-deemphasise" href={Links.WALLET_GUIDE}>
|
||||
{t('readGuide')}
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
<p style={{ margin: 0 }}>
|
||||
<a
|
||||
className={'text-deemphasise'}
|
||||
href={Links.WALLET_RELEASES}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<p>
|
||||
<Link className="text-deemphasise" href={Links.WALLET_RELEASES}>
|
||||
{t('downloadWallet')}
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -41,10 +41,8 @@ export const VegaWallet = () => {
|
||||
<WalletCard dark={true}>
|
||||
<WalletCardHeader dark={true}>
|
||||
<div>
|
||||
<h1 className="text-h3 px-8 uppercase">{t('vegaWallet')}</h1>
|
||||
<span className="mx-8 text-h6">
|
||||
{keypair && `(${keypair.name})`}
|
||||
</span>
|
||||
<h1 className="text-h3 uppercase">{t('vegaWallet')}</h1>
|
||||
<span className="text-h6">{keypair && `(${keypair.name})`}</span>
|
||||
</div>
|
||||
{keypair && (
|
||||
<span className="font-mono px-8">
|
||||
@ -72,6 +70,7 @@ const VegaWalletNotConnected = () => {
|
||||
})
|
||||
}
|
||||
variant="secondary"
|
||||
className="w-full"
|
||||
data-testid="connect-vega"
|
||||
>
|
||||
{t('connectVegaWalletToUseAssociated')}
|
||||
@ -182,10 +181,14 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
|
||||
))}
|
||||
<WalletCardActions>
|
||||
<Link style={{ flex: 1 }} to={Routes.GOVERNANCE}>
|
||||
<Button className="w-full">{t('governance')}</Button>
|
||||
<span className="flex items-center justify-center w-full px-28 border h-28 bg-white text-black">
|
||||
{t('governance')}
|
||||
</span>
|
||||
</Link>
|
||||
<Link style={{ flex: 1 }} to={Routes.STAKING}>
|
||||
<Button className="w-full">{t('staking')}</Button>
|
||||
<span className="flex items-center justify-center w-full px-28 border h-28 bg-white text-black">
|
||||
{t('staking')}
|
||||
</span>
|
||||
</Link>
|
||||
</WalletCardActions>
|
||||
<VegaWalletAssetList accounts={accounts} />
|
||||
|
@ -1,3 +1,4 @@
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
@ -27,15 +28,13 @@ interface WalletCardProps {
|
||||
dark?: boolean;
|
||||
}
|
||||
|
||||
export const WalletCard = ({ dark, children }: WalletCardProps) => (
|
||||
<div
|
||||
className={`text-ui border border-white ${
|
||||
dark ? 'bg-black text-white' : 'bg-white text-black'
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
export const WalletCard = ({ dark, children }: WalletCardProps) => {
|
||||
const className = classNames('text-ui border border-white', 'p-8', {
|
||||
'bg-black text-white': dark,
|
||||
'bg-white text-black': !dark,
|
||||
});
|
||||
return <div className={className}>{children}</div>;
|
||||
};
|
||||
|
||||
interface WalletCardHeaderProps {
|
||||
children: React.ReactNode;
|
||||
@ -44,7 +43,7 @@ interface WalletCardHeaderProps {
|
||||
|
||||
export const WalletCardHeader = ({ children }: WalletCardHeaderProps) => {
|
||||
return (
|
||||
<div className="flex justify-between items-center py-8">{children}</div>
|
||||
<div className="flex gap-4 justify-between items-center">{children}</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -53,7 +52,7 @@ interface WalletCardContentProps {
|
||||
}
|
||||
|
||||
export const WalletCardContent = ({ children }: WalletCardContentProps) => {
|
||||
return <div className="my-4 mx-8">{children}</div>;
|
||||
return <div className="mt-8">{children}</div>;
|
||||
};
|
||||
|
||||
export const WalletCardRow = ({
|
||||
|
@ -1,38 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export function useCopyToClipboard() {
|
||||
const [copied, setCopied] = React.useState(false);
|
||||
|
||||
// Once copied flip a boolean so we can display
|
||||
// a message to the user such as 'Copied!' which will
|
||||
// revert after 800 milliseconds
|
||||
React.useEffect(() => {
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
if (copied) {
|
||||
timeout = setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 800);
|
||||
}
|
||||
return () => clearTimeout(timeout);
|
||||
}, [copied]);
|
||||
|
||||
// Create an input we can copy text from and render it
|
||||
// off screen
|
||||
function handler(text: string) {
|
||||
const input = document.createElement('input');
|
||||
input.style.position = 'fixed';
|
||||
input.style.left = '100vw';
|
||||
input.style.opacity = '0';
|
||||
input.value = text;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(input);
|
||||
setCopied(true);
|
||||
}
|
||||
|
||||
return {
|
||||
copy: handler,
|
||||
copied,
|
||||
};
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"Home": "Home",
|
||||
"fairgroundTitle": "Fairground token",
|
||||
"pageTitleHome": "$VEGA Tokens",
|
||||
"pageTitleHome": "The $VEGA token",
|
||||
"pageTitleClaim": "Claim $VEGA tokens",
|
||||
"pageTitleAssociate": "Associate $VEGA tokens with $VEGA Key",
|
||||
"pageTitleAssociate": "Associate $VEGA tokens with Vega Key",
|
||||
"pageTitleRedemption": "Vesting",
|
||||
"pageTitleLiquidity": "Incentivised Liquidity Programme",
|
||||
"pageTitleRedemptionTranche": "Redeem from Tranche",
|
||||
@ -79,6 +79,7 @@
|
||||
"Incomplete": "Incomplete",
|
||||
"Complete": "Complete",
|
||||
"View on Etherscan (opens in a new tab)": "View on Etherscan (opens in a new tab)",
|
||||
"View transaction on Etherscan": "View transaction on Etherscan",
|
||||
"Transaction in progress": "Transaction in progress",
|
||||
"Unknown error": "Unknown error",
|
||||
"Awaiting action in Ethereum wallet (e.g. MetaMask)": "Awaiting action in Ethereum wallet (e.g. MetaMask)",
|
||||
@ -126,7 +127,6 @@
|
||||
"noVestingTokens": "You do not have any vesting $VEGA tokens. Switch to another Ethereum address to check what can be redeemed, or view <tranchesLink>all tranches</tranchesLink>",
|
||||
"ethereumKey": "Ethereum key",
|
||||
"checkingForProvider": "Checking for provider",
|
||||
"Awaiting action in wallet...": "Awaiting action in Ethereum wallet (e.g. MetaMask)",
|
||||
"In wallet": "In wallet",
|
||||
"Not staked": "Not staked",
|
||||
"viewKeys": "View keys",
|
||||
@ -149,7 +149,6 @@
|
||||
"Stake VEGA tokens": "Stake $VEGA tokens",
|
||||
"Tranche breakdown": "Tranche breakdown",
|
||||
"Across all tranches": "Across all tranches",
|
||||
"theVegaToken": "The {{symbol}} token",
|
||||
"Token Vesting": "Vesting",
|
||||
"Rewards": "Rewards",
|
||||
"Governance": "Governance",
|
||||
@ -238,7 +237,7 @@
|
||||
"VEGA Tokens": "$VEGA Tokens",
|
||||
"SLP Tokens": "SLP Tokens",
|
||||
"Connected Vega key": "Connected Vega key",
|
||||
"What Vega wallet/key is going to control your stake?": "What Vega Wallet/key is going to control your stake?",
|
||||
"What Vega key is going to control your stake?": "What Vega key is going to control your stake?",
|
||||
"Where would you like to stake from?": "Where would you like to associate from?",
|
||||
"associateNoVega": "Your connected Ethereum address does not have any $VEGA",
|
||||
"associateInfo1": "To participate in governance or to nominate a Validator you'll need to associate $VEGA tokens with a Vega wallet/key.",
|
||||
@ -322,7 +321,7 @@
|
||||
"Associate VEGA tokens": "Associate $VEGA tokens",
|
||||
"VEGA token holders can vote on proposed changes to the network and create proposals.": "$VEGA token holders can vote on proposed changes to the network and create proposals.",
|
||||
"VEGA token holders can nominate a validator node and receive staking rewards.": "$VEGA token holders can nominate a validator node and receive staking rewards.",
|
||||
"USE YOUR VEGA TOKENS": "USE YOUR $VEGA TOKENS",
|
||||
"Use your Vega tokens": "Use your Vega tokens",
|
||||
"Check your vesting VEGA tokens": "Check your vesting $VEGA tokens",
|
||||
"Tokens from this Tranche have been redeemed": "Tokens from this Tranche have been redeemed",
|
||||
"You have redeemed {{redeemedAmount}} VEGA tokens from this tranche. They are now free to transfer from your Ethereum wallet.": "You have redeemed {{redeemedAmount}} $VEGA tokens from this tranche. They are now free to transfer from your Ethereum wallet.",
|
||||
@ -450,12 +449,12 @@
|
||||
"transaction": "Transaction",
|
||||
"associated": "Associated",
|
||||
"notAssociated": "Not Associated",
|
||||
"title": "$VEGA TOKEN",
|
||||
"title": "VEGA TOKEN",
|
||||
"Use the Ethereum wallet you want to send your tokens to. You'll also need enough Ethereum to pay gas.": "Connect to the Ethereum wallet that holds your $VEGA tokens to see what can be redeemed from vesting tranches. To redeem tokens you will need some ETH to pay gas fees.\n",
|
||||
"Staked on Vega validator": "Staked $VEGA",
|
||||
"You can associate tokens while they are held in the vesting contract, when they unlock you will need to disassociate them before they can be redeemed.": "You can associate tokens while they are held in the vesting contract, when they unlock you will need to disassociate them before they can be redeemed.",
|
||||
"Nominate Stake to Validator Node": "Select a validator to nominate",
|
||||
"Vega key {{vegaKey}} can now participate in governance and Nominate a validator with it's stake.": "Vega key {{vegaKey}} can now participate in governance and nominate a validator with your associated $VEGA.",
|
||||
"successfullAssociationMessage": "Vega key {{vegaKey}} can now participate in governance and nominate a validator with your associated $VEGA.",
|
||||
"stakingStep2Text": "Your tokens need to be associated with a Vega Wallet so that you can control your stake",
|
||||
"stakingStep3": "Step 3. Select the validator you'd like to nominate",
|
||||
"stakeAddSuccessMessage": "You can cancel your nomination at any time",
|
||||
|
@ -38,6 +38,7 @@ export const Complete = ({
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
href={`${ETHERSCAN_URL}/tx/${commitTxHash}`}
|
||||
target="_blank"
|
||||
>
|
||||
{commitTxHash}
|
||||
</Link>
|
||||
@ -49,6 +50,7 @@ export const Complete = ({
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
href={`${ETHERSCAN_URL}/tx/${claimTxHash}`}
|
||||
target="_blank"
|
||||
>
|
||||
{claimTxHash}
|
||||
</Link>
|
||||
|
@ -14,8 +14,9 @@ const Contracts = () => {
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<div>{key}:</div>
|
||||
<Link
|
||||
title={t('View address on Etherscan')}
|
||||
title={t('View on Etherscan (opens in a new tab)')}
|
||||
href={`${ETHERSCAN_URL}/address/${value}`}
|
||||
target="_blank"
|
||||
>
|
||||
{value}
|
||||
</Link>
|
||||
|
@ -10,7 +10,7 @@ export const ProposalTermsJson = ({
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<section>
|
||||
<h2>{t('proposalTerms')}</h2>
|
||||
<h2 className="text-h4 text-white mb-8">{t('proposalTerms')}</h2>
|
||||
<SyntaxHighlighter data={terms} />
|
||||
</section>
|
||||
);
|
||||
|
@ -37,6 +37,7 @@ export const ProposalVotesTable = ({ proposal }: ProposalVotesTableProps) => {
|
||||
data-testid="proposal-votes-table"
|
||||
muted={true}
|
||||
numerical={true}
|
||||
headingLevel={4}
|
||||
>
|
||||
<KeyValueTableRow>
|
||||
{t('willPass')}
|
||||
|
@ -20,58 +20,67 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
return <p>{t('noProposals')}</p>;
|
||||
}
|
||||
|
||||
const renderRow = (proposal: Proposals_proposals) => {
|
||||
if (!proposal || !proposal.id) return null;
|
||||
|
||||
return (
|
||||
<li className="last:mb-0 mb-24" key={proposal.id}>
|
||||
<Link to={proposal.id} className="underline">
|
||||
<header>{getProposalName(proposal)}</header>
|
||||
</Link>
|
||||
<KeyValueTable muted={true}>
|
||||
<KeyValueTableRow>
|
||||
{t('state')}
|
||||
<span data-testid="governance-proposal-state">
|
||||
<CurrentProposalState proposal={proposal} />
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{isFuture(new Date(proposal.terms.closingDatetime))
|
||||
? t('closesOn')
|
||||
: t('closedOn')}
|
||||
|
||||
<span data-testid="governance-proposal-closingDate">
|
||||
{format(
|
||||
new Date(proposal.terms.closingDatetime),
|
||||
DATE_FORMAT_DETAILED
|
||||
)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{isFuture(new Date(proposal.terms.enactmentDatetime))
|
||||
? t('proposedEnactment')
|
||||
: t('enactedOn')}
|
||||
|
||||
<span data-testid="governance-proposal-enactmentDate">
|
||||
{format(
|
||||
new Date(proposal.terms.enactmentDatetime),
|
||||
DATE_FORMAT_DETAILED
|
||||
)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading title={t('pageTitleGovernance')} />
|
||||
<p>{t('proposedChangesToVegaNetwork')}</p>
|
||||
<p>{t('vegaTokenHoldersCanVote')}</p>
|
||||
<p>{t('requiredMajorityDescription')}</p>
|
||||
<h2>{t('proposals')}</h2>
|
||||
<ul>{proposals.map((row) => renderRow(row))}</ul>
|
||||
<p className="mb-8">{t('proposedChangesToVegaNetwork')}</p>
|
||||
<p className="mb-8">{t('vegaTokenHoldersCanVote')}</p>
|
||||
<p className="mb-8">{t('requiredMajorityDescription')}</p>
|
||||
<h2 className="text-h4 text-white">{t('proposals')}</h2>
|
||||
<ul>
|
||||
{proposals.map((proposal) => (
|
||||
<ProposalListItem proposal={proposal} />
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface ProposalListItemProps {
|
||||
proposal: Proposals_proposals;
|
||||
}
|
||||
|
||||
const ProposalListItem = ({ proposal }: ProposalListItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
if (!proposal || !proposal.id) return null;
|
||||
|
||||
return (
|
||||
<li className="last:mb-0 mb-24" key={proposal.id}>
|
||||
<Link to={proposal.id} className="underline text-white">
|
||||
<header>{getProposalName(proposal)}</header>
|
||||
</Link>
|
||||
<KeyValueTable muted={true}>
|
||||
<KeyValueTableRow>
|
||||
{t('state')}
|
||||
<span data-testid="governance-proposal-state">
|
||||
<CurrentProposalState proposal={proposal} />
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{isFuture(new Date(proposal.terms.closingDatetime))
|
||||
? t('closesOn')
|
||||
: t('closedOn')}
|
||||
|
||||
<span data-testid="governance-proposal-closingDate">
|
||||
{format(
|
||||
new Date(proposal.terms.closingDatetime),
|
||||
DATE_FORMAT_DETAILED
|
||||
)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{isFuture(new Date(proposal.terms.enactmentDatetime))
|
||||
? t('proposedEnactment')
|
||||
: t('enactedOn')}
|
||||
|
||||
<span data-testid="governance-proposal-enactmentDate">
|
||||
{format(
|
||||
new Date(proposal.terms.enactmentDatetime),
|
||||
DATE_FORMAT_DETAILED
|
||||
)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
@ -3,10 +3,8 @@ import { format } from 'date-fns';
|
||||
import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
ProposalState,
|
||||
VoteValue,
|
||||
} from '../../../../__generated__/globalTypes';
|
||||
import { ProposalState } from '../../../../__generated__/globalTypes';
|
||||
import { VoteValue } from '../../../../__generated__/globalTypes';
|
||||
import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
@ -141,9 +139,8 @@ export const VoteButtons = ({
|
||||
: 'text-intent-danger';
|
||||
return (
|
||||
<p>
|
||||
<span>{t('youVoted')}</span>{' '}
|
||||
<span className={className}>{t(`voteState_${voteState}`)}</span>
|
||||
{'. '}
|
||||
<span>{t('youVoted')}:</span>{' '}
|
||||
<span className={className}>{t(`voteState_${voteState}`)}</span>{' '}
|
||||
{voteDatetime ? (
|
||||
<span>{format(voteDatetime, DATE_FORMAT_LONG)}. </span>
|
||||
) : null}
|
||||
@ -167,17 +164,11 @@ export const VoteButtons = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
<Button
|
||||
onClick={() => submitVote(VoteValue.Yes)}
|
||||
className="w-[calc(50%_-_7px)] mt-4 mr-12"
|
||||
>
|
||||
<div className="flex gap-4">
|
||||
<Button onClick={() => submitVote(VoteValue.Yes)} className="flex-1">
|
||||
{t('voteFor')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => submitVote(VoteValue.No)}
|
||||
className="w-[calc(50%_-_7px)] mt-4"
|
||||
>
|
||||
<Button onClick={() => submitVote(VoteValue.No)} className="flex-1">
|
||||
{t('voteAgainst')}
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -44,15 +44,15 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
|
||||
|
||||
return (
|
||||
<section>
|
||||
<h3>{t('votes')}</h3>
|
||||
<p className="mb-0">
|
||||
<h3 className="text-h4 text-white mb-8">{t('votes')}</h3>
|
||||
<p className="mb-8">
|
||||
<span>
|
||||
<CurrentProposalStatus proposal={proposal} />
|
||||
</span>
|
||||
.
|
||||
{proposal.state === ProposalState.Open ? daysLeft : null}
|
||||
</p>
|
||||
<table className="w-full font-normal">
|
||||
<table className="w-full font-normal mb-12">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-vega-green w-[18%] text-left">{t('for')}</th>
|
||||
@ -86,13 +86,13 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
|
||||
{formatNumber(yesTokens, defaultDecimals)}
|
||||
</td>
|
||||
<td></td>
|
||||
<td className="text-white-60">
|
||||
<td className="text-white-60 text-right">
|
||||
{formatNumber(noTokens, defaultDecimals)}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<p className="mb-8">
|
||||
{t('participation')}
|
||||
{': '}
|
||||
{participationMet ? (
|
||||
@ -109,7 +109,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
|
||||
</p>
|
||||
{keypair ? (
|
||||
<>
|
||||
<h3>{t('yourVote')}</h3>
|
||||
<h3 className="text-h4 text-white mb-8">{t('yourVote')}</h3>
|
||||
<VoteButtonsContainer
|
||||
voteState={voteState}
|
||||
castVote={castVote}
|
||||
|
@ -8,21 +8,21 @@ export const VoteProgress = ({
|
||||
progress: BigNumber;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="w-full h-4 relative">
|
||||
<div
|
||||
data-testid="vote-progress-indicator"
|
||||
className="relative top-[10px] w-[1px] h-16 bg-white z-[1]"
|
||||
className="absolute top-[-5px] w-[1px] h-16 bg-white z-[1]"
|
||||
style={{ left: `${threshold}%` }}
|
||||
/>
|
||||
<div className="bp3-progress-bar bp3-no-stripes bg-intent-danger rounded-none h-5">
|
||||
<div className="w-full h-4">
|
||||
<div
|
||||
className="bp3-progress-meter bg-vega-green rounded-none"
|
||||
className="absolute left-0 bg-vega-green h-4"
|
||||
data-testid="vote-progress-bar"
|
||||
style={{
|
||||
width: `${progress}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -34,7 +34,7 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading title={t('theVegaToken', { symbol: '$VEGA' })} />
|
||||
<Heading title={t('pageTitleHome')} />
|
||||
<HomeSection>
|
||||
<TokenDetails
|
||||
totalSupply={appState.totalSupply}
|
||||
@ -42,7 +42,7 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
/>
|
||||
</HomeSection>
|
||||
<HomeSection>
|
||||
<h2 className="text-h4">{t('Token Vesting')}</h2>
|
||||
<h2 className="text-h4 text-white">{t('Token Vesting')}</h2>
|
||||
<p className="mb-8">
|
||||
{t(
|
||||
'The vesting contract holds VEGA tokens until they have become unlocked.'
|
||||
@ -68,7 +68,7 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
</Link>
|
||||
</HomeSection>
|
||||
<HomeSection>
|
||||
<h2 className="text-h4">{t('USE YOUR VEGA TOKENS')}</h2>
|
||||
<h2 className="text-h4 text-white">{t('Use your Vega tokens')}</h2>
|
||||
<p className="mb-8">
|
||||
{t(
|
||||
'To use your tokens on the Vega network they need to be associated with a Vega wallet/key.'
|
||||
@ -83,6 +83,7 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
<a
|
||||
data-test-id="get-vega-wallet-link"
|
||||
href={Links.WALLET_GUIDE}
|
||||
className="underline text-white"
|
||||
target="_blank"
|
||||
rel="nofollow noreferrer"
|
||||
>
|
||||
@ -90,15 +91,18 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
</a>
|
||||
</p>
|
||||
<p data-test-id="associate-vega-tokens-link-on-homepage">
|
||||
<Link to={`${Routes.STAKING}/associate`}>
|
||||
<Link
|
||||
to={`${Routes.STAKING}/associate`}
|
||||
className="underline text-white"
|
||||
>
|
||||
{t('Associate VEGA tokens')}
|
||||
</Link>
|
||||
</p>
|
||||
</HomeSection>
|
||||
<div style={{ display: 'flex', gap: 36 }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex gap-40">
|
||||
<div className="flex-1">
|
||||
<HomeSection>
|
||||
<h2 className="text-h4">{t('Staking')}</h2>
|
||||
<h2 className="text-h4 text-white">{t('Staking')}</h2>
|
||||
<p className="mb-8">
|
||||
{t(
|
||||
'VEGA token holders can nominate a validator node and receive staking rewards.'
|
||||
@ -111,9 +115,9 @@ const Home = ({ name }: RouteChildProps) => {
|
||||
</p>
|
||||
</HomeSection>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div className="flex-1">
|
||||
<HomeSection>
|
||||
<h2 className="text-h4">{t('Governance')}</h2>
|
||||
<h2 className="text-h4 text-white">{t('Governance')}</h2>
|
||||
<p className="mb-8">
|
||||
{t(
|
||||
'VEGA token holders can vote on proposed changes to the network and create proposals.'
|
||||
|
@ -43,20 +43,22 @@ export const TokenDetails = ({
|
||||
{t('Token address').toUpperCase()}
|
||||
<Link
|
||||
data-testid="token-address"
|
||||
title={t('View address on Etherscan')}
|
||||
title={t('View on Etherscan (opens in a new tab)')}
|
||||
className="font-mono"
|
||||
href={`${ETHERSCAN_URL}/address/${ADDRESSES.vegaTokenAddress}`}
|
||||
target="_blank"
|
||||
>
|
||||
{ADDRESSES.vegaTokenAddress}
|
||||
</Link>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('Vesting contract'.toUpperCase())}
|
||||
{t('Vesting contract').toUpperCase()}
|
||||
<Link
|
||||
data-testid="token-contract"
|
||||
title={t('View address on Etherscan')}
|
||||
title={t('View on Etherscan (opens in a new tab)')}
|
||||
className="font-mono"
|
||||
href={`${ETHERSCAN_URL}/address/${ADDRESSES.vestingAddress}`}
|
||||
target="_blank"
|
||||
>
|
||||
{ADDRESSES.vestingAddress}
|
||||
</Link>
|
||||
|
@ -13,9 +13,9 @@ import { Tranche0Table, TrancheTable } from '../tranche-table';
|
||||
import { VestingTable } from './vesting-table';
|
||||
|
||||
export const RedemptionInformation = () => {
|
||||
const { state, address } = useOutletContext<{
|
||||
const { state, account } = useOutletContext<{
|
||||
state: RedemptionState;
|
||||
address: string;
|
||||
account: string;
|
||||
}>();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
@ -28,7 +28,9 @@ export const RedemptionInformation = () => {
|
||||
trancheBalances,
|
||||
},
|
||||
} = useAppState();
|
||||
|
||||
const { userTranches } = state;
|
||||
|
||||
const filteredTranches = React.useMemo(
|
||||
() =>
|
||||
userTranches.filter((tr) => {
|
||||
@ -41,6 +43,7 @@ export const RedemptionInformation = () => {
|
||||
}),
|
||||
[trancheBalances, userTranches]
|
||||
);
|
||||
|
||||
const zeroTranche = React.useMemo(() => {
|
||||
const zeroTranche = trancheBalances.find((t) => t.id === 0);
|
||||
if (zeroTranche && zeroTranche.locked.isGreaterThan(0)) {
|
||||
@ -56,7 +59,9 @@ export const RedemptionInformation = () => {
|
||||
<Trans
|
||||
i18nKey="noVestingTokens"
|
||||
components={{
|
||||
tranchesLink: <Link to={Routes.TRANCHES} />,
|
||||
tranchesLink: (
|
||||
<Link className="underline text-white" to={Routes.TRANCHES} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
@ -67,25 +72,29 @@ export const RedemptionInformation = () => {
|
||||
|
||||
return (
|
||||
<section data-testid="redemption-page">
|
||||
<Callout>
|
||||
<div className="mb-12">
|
||||
<AddLockedTokenAddress />
|
||||
</Callout>
|
||||
<p className="mb-12" data-testid="redemption-description">
|
||||
</div>
|
||||
<p className="mb-24" data-testid="redemption-description">
|
||||
{t(
|
||||
'{{address}} has {{balance}} VEGA tokens in {{tranches}} tranches of the vesting contract.',
|
||||
{
|
||||
address: truncateMiddle(address),
|
||||
address: truncateMiddle(account),
|
||||
balance: formatNumber(balanceFormatted),
|
||||
tranches: filteredTranches.length,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<VestingTable
|
||||
associated={lien}
|
||||
locked={totalLockedBalance}
|
||||
vested={totalVestedBalance}
|
||||
/>
|
||||
{filteredTranches.length ? <h2>{t('Tranche breakdown')}</h2> : null}
|
||||
<div className="mb-24">
|
||||
<VestingTable
|
||||
associated={lien}
|
||||
locked={totalLockedBalance}
|
||||
vested={totalVestedBalance}
|
||||
/>
|
||||
</div>
|
||||
{filteredTranches.length ? (
|
||||
<h2 className="text-h4 text-white mb-12">{t('Tranche breakdown')}</h2>
|
||||
) : null}
|
||||
{zeroTranche && (
|
||||
<Tranche0Table
|
||||
trancheId={0}
|
||||
@ -124,8 +133,10 @@ export const RedemptionInformation = () => {
|
||||
iconName="hand-up"
|
||||
intent={Intent.Prompt}
|
||||
>
|
||||
<p>{t('Find out more about Staking.')}</p>
|
||||
<Link to="/staking">{t('Stake VEGA tokens')}</Link>
|
||||
<p className="mb-12">{t('Find out more about Staking.')}</p>
|
||||
<Link to="/staking" className="underline text-white">
|
||||
{t('Stake VEGA tokens')}
|
||||
</Link>
|
||||
</Callout>
|
||||
</section>
|
||||
);
|
||||
|
@ -4,11 +4,13 @@ import { Heading } from '../../components/heading';
|
||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
import type { RouteChildProps } from '..';
|
||||
import RedemptionRouter from './redemption';
|
||||
import { useMatch } from 'react-router-dom';
|
||||
import { Routes } from '../router-config';
|
||||
|
||||
const RedemptionIndex = ({ name }: RouteChildProps) => {
|
||||
useDocumentTitle(name);
|
||||
const { t } = useTranslation();
|
||||
const tranche = ':id';
|
||||
const tranche = useMatch(`${Routes.VESTING}/:id`);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -70,7 +70,7 @@ const RedemptionRouter = () => {
|
||||
if (!account) {
|
||||
return (
|
||||
<EthConnectPrompt>
|
||||
<p data-testid="eth-connect-prompt">
|
||||
<p data-testid="eth-connect-prompt" className="mb-8">
|
||||
{t(
|
||||
"Use the Ethereum wallet you want to send your tokens to. You'll also need enough Ethereum to pay gas."
|
||||
)}
|
||||
|
@ -74,11 +74,16 @@ export const RedeemFromTranche = () => {
|
||||
) {
|
||||
return (
|
||||
<section data-testid="redemption-page">
|
||||
<div data-testid="redemption-no-balance">
|
||||
{t(
|
||||
'You do not have any vesting VEGA tokens. Switch to another Ethereum key to check what can be redeemed.'
|
||||
)}
|
||||
</div>
|
||||
<p data-testid="redemption-no-balance">
|
||||
<Trans
|
||||
i18nKey="noVestingTokens"
|
||||
components={{
|
||||
tranchesLink: (
|
||||
<Link className="underline text-white" to={Routes.TRANCHES} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@ -92,9 +97,9 @@ export const RedeemFromTranche = () => {
|
||||
{t('Tokens from this Tranche have been redeemed')}
|
||||
</strong>
|
||||
}
|
||||
completeFooter={
|
||||
completeBody={
|
||||
<>
|
||||
<p>
|
||||
<p className="mb-8">
|
||||
{t(
|
||||
'You have redeemed {{redeemedAmount}} VEGA tokens from this tranche. They are now free to transfer from your Ethereum wallet.',
|
||||
{
|
||||
@ -102,7 +107,7 @@ export const RedeemFromTranche = () => {
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
<p className="mb-8">
|
||||
{t(
|
||||
'The VEGA token address is {{address}}, make sure you add this to your wallet to see your tokens',
|
||||
{
|
||||
@ -114,8 +119,18 @@ export const RedeemFromTranche = () => {
|
||||
<Trans
|
||||
i18nKey="Go to <stakingLink>staking</stakingLink> or <governanceLink>governance</governanceLink> to see how you can use your unlocked tokens"
|
||||
components={{
|
||||
stakingLink: <Link to={Routes.STAKING} />,
|
||||
governanceLink: <Link to={Routes.GOVERNANCE} />,
|
||||
stakingLink: (
|
||||
<Link
|
||||
className="underline text-white"
|
||||
to={Routes.STAKING}
|
||||
/>
|
||||
),
|
||||
governanceLink: (
|
||||
<Link
|
||||
className="underline text-white"
|
||||
to={Routes.GOVERNANCE}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
|
@ -112,8 +112,8 @@ export const RewardTable = ({ reward, delegations }: RewardTableProps) => {
|
||||
}, [delegations, reward.epoch]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>
|
||||
<div className="mb-24">
|
||||
<h3 className="text-h5 text-white mb-4">
|
||||
{t('Epoch')} {reward.epoch.id}
|
||||
</h3>
|
||||
<KeyValueTable>
|
||||
|
@ -6,11 +6,14 @@ export const AssociateInfo = ({ pubKey }: { pubKey: string | null }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<h2 data-testid="associate-vega-key-header">
|
||||
{t('What Vega wallet/key is going to control your stake?')}
|
||||
<h2
|
||||
className="text-h4 text-white"
|
||||
data-testid="associate-vega-key-header"
|
||||
>
|
||||
{t('What Vega key is going to control your stake?')}
|
||||
</h2>
|
||||
<ConnectedVegaKey pubKey={pubKey} />
|
||||
<h2 data-testid="associate-amount-header">
|
||||
<h2 className="text-h4 text-white" data-testid="associate-amount-header">
|
||||
{t('How much would you like to associate?')}
|
||||
</h2>
|
||||
</>
|
||||
|
@ -73,6 +73,7 @@ export const AssociatePage = ({
|
||||
setSelectedStakingMethod(params.method);
|
||||
}
|
||||
}, [params.method, zeroVega, zeroVesting]);
|
||||
|
||||
if (txState.txState !== TxState.Default) {
|
||||
return (
|
||||
<AssociateTransaction
|
||||
|
@ -1,4 +1,10 @@
|
||||
import { Button, Callout, Link, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
Button,
|
||||
Callout,
|
||||
Link,
|
||||
Intent,
|
||||
Loader,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useEnvironment } from '@vegaprotocol/react-helpers';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -15,6 +21,7 @@ import {
|
||||
} from '../../../hooks/transaction-reducer';
|
||||
import { Routes } from '../../router-config';
|
||||
import type { PartyStakeLinkings_party_stake_linkings } from './__generated__/PartyStakeLinkings';
|
||||
import { truncateMiddle } from '../../../lib/truncate-middle';
|
||||
|
||||
export const AssociateTransaction = ({
|
||||
amount,
|
||||
@ -60,22 +67,27 @@ export const AssociateTransaction = ({
|
||||
|
||||
if (derivedTxState === TxState.Pending) {
|
||||
return (
|
||||
<Callout intent={Intent.Progress} title={title}>
|
||||
<p data-testid="transaction-pending-body">
|
||||
<Callout
|
||||
icon={<Loader size="small" />}
|
||||
intent={Intent.Progress}
|
||||
title={title}
|
||||
>
|
||||
<p data-testid="transaction-pending-body" className="mb-8">
|
||||
{t('Associating {{amount}} VEGA tokens with Vega key {{vegaKey}}', {
|
||||
amount,
|
||||
vegaKey,
|
||||
vegaKey: truncateMiddle(vegaKey),
|
||||
})}
|
||||
</p>
|
||||
<p>
|
||||
<p className="mb-8">
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
href={`${ETHERSCAN_URL}/tx/${state.txData.hash}`}
|
||||
target="_blank"
|
||||
>
|
||||
{state.txData.hash}
|
||||
{t('View on Etherscan (opens in a new tab)')}
|
||||
</Link>
|
||||
</p>
|
||||
<p data-testid="transaction-pending-footer">
|
||||
<p data-testid="transaction-pending-footer" className="mb-8">
|
||||
{t('pendingAssociationText', {
|
||||
confirmations: requiredConfirmations,
|
||||
})}
|
||||
@ -87,10 +99,9 @@ export const AssociateTransaction = ({
|
||||
return (
|
||||
<TransactionCallout
|
||||
completeHeading={t('Done')}
|
||||
completeBody={t(
|
||||
'Vega key {{vegaKey}} can now participate in governance and Nominate a validator with it’s stake.',
|
||||
{ vegaKey }
|
||||
)}
|
||||
completeBody={t('successfullAssociationMessage', {
|
||||
vegaKey: truncateMiddle(vegaKey),
|
||||
})}
|
||||
completeFooter={
|
||||
<RouteLink to={Routes.STAKING}>
|
||||
<Button className="fill">
|
||||
|
@ -63,11 +63,11 @@ export const DisassociatePage = ({
|
||||
'Any Tokens that have been nominated to a node will sacrifice any Rewards they are due for the current epoch. If you do not wish to sacrifices fees you should remove stake from a node at the end of an epoch before disassocation.'
|
||||
)}
|
||||
</p>
|
||||
<h2 className="text-h4 mb-8">
|
||||
<h2 className="text-h4 text-white mb-8">
|
||||
{t('What Vega wallet are you removing Tokens from?')}
|
||||
</h2>
|
||||
<ConnectedVegaKey pubKey={vegaKey.pub} />
|
||||
<h2 className="text-h4 mb-8">
|
||||
<h2 className="text-h4 text-white mb-8">
|
||||
{t('What tokens would you like to return?')}
|
||||
</h2>
|
||||
<StakingMethodRadio
|
||||
|
@ -46,7 +46,7 @@ export const NODES_QUERY = gql`
|
||||
`;
|
||||
|
||||
const NodeListItemName = ({ children }: { children: React.ReactNode }) => (
|
||||
<span className="mr-4 underline">{children}</span>
|
||||
<span className="mr-4 underline text-white">{children}</span>
|
||||
);
|
||||
|
||||
const NodeListTr = ({ children }: { children: React.ReactNode }) => (
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Button, Callout, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button, Callout, Intent, Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAppState } from '../../contexts/app-state/app-state-context';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
@ -65,7 +65,7 @@ export const PendingStake = ({
|
||||
} else if (formState === FormState.Pending) {
|
||||
return (
|
||||
<Callout
|
||||
iconName="refresh"
|
||||
icon={<Loader size="small" />}
|
||||
title={t('removingPendingStake', { pendingAmount })}
|
||||
/>
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Callout } from '@vegaprotocol/ui-toolkit';
|
||||
import { Callout, Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Actions } from './staking-form';
|
||||
import type { StakeAction } from './staking-form';
|
||||
@ -22,7 +22,7 @@ export const StakePending = ({
|
||||
: t('stakeRemovePendingTitle', titleArgs);
|
||||
|
||||
return (
|
||||
<Callout iconName="refresh" title={title}>
|
||||
<Callout icon={<Loader size="small" />} title={title}>
|
||||
<p>{t('timeForConfirmation')}</p>
|
||||
</Callout>
|
||||
);
|
||||
|
@ -92,9 +92,11 @@ export const StakingForm = ({
|
||||
React.useEffect(() => {
|
||||
setAmount('');
|
||||
}, [action, setAmount]);
|
||||
|
||||
const { data } = useNetworkParam([
|
||||
NetworkParams.VALIDATOR_DELEGATION_MIN_AMOUNT,
|
||||
]);
|
||||
|
||||
const minTokensWithDecimals = React.useMemo(() => {
|
||||
const minTokens = new BigNumber(data && data.length === 1 ? data[0] : '');
|
||||
return addDecimal(minTokens, appState.decimals);
|
||||
@ -198,9 +200,9 @@ export const StakingForm = ({
|
||||
availableStakeToRemove.isEqualTo(0)
|
||||
) {
|
||||
if (appState.lien.isGreaterThan(0)) {
|
||||
return <span className={'text-red'}>{t('stakeNodeWrongVegaKey')}</span>;
|
||||
return <span className="text-red">{t('stakeNodeWrongVegaKey')}</span>;
|
||||
} else {
|
||||
return <span className={'text-red'}>{t('stakeNodeNone')}</span>;
|
||||
return <span className="text-red">{t('stakeNodeNone')}</span>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,9 +77,10 @@ export const StakingNode = ({ vegaKey, data }: StakingNodeProps) => {
|
||||
}, [currentEpoch, data?.party?.delegations]);
|
||||
|
||||
const unstaked = React.useMemo(() => {
|
||||
return new BigNumber(
|
||||
const value = new BigNumber(
|
||||
data?.party?.stake.currentStakeAvailableFormatted || 0
|
||||
).minus(currentDelegationAmount);
|
||||
return value.isLessThan(0) ? new BigNumber(0) : value;
|
||||
}, [
|
||||
currentDelegationAmount,
|
||||
data?.party?.stake.currentStakeAvailableFormatted,
|
||||
|
@ -30,20 +30,22 @@ export const Staking = ({ data }: { data?: StakingQueryResult }) => {
|
||||
<p className="mb-12">{t('stakingDescription3')}</p>
|
||||
<p className="mb-12">{t('stakingDescription4')}</p>
|
||||
<p className="mb-12">
|
||||
<Link href={Links.STAKING_GUIDE} target="_blank">
|
||||
<Link
|
||||
href={Links.STAKING_GUIDE}
|
||||
className="text-white underline"
|
||||
target="_blank"
|
||||
>
|
||||
{t('readMoreStaking')}
|
||||
</Link>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<BulletHeader tag="h2" style={{ marginTop: 0 }}>
|
||||
{t('stakingStep1')}
|
||||
</BulletHeader>
|
||||
<BulletHeader tag="h2">{t('stakingStep1')}</BulletHeader>
|
||||
<StakingStepConnectWallets />
|
||||
</section>
|
||||
<section>
|
||||
<BulletHeader tag="h2">{t('stakingStep2')}</BulletHeader>
|
||||
<BulletHeader tag="h2">{t('stakingStep1')}</BulletHeader>
|
||||
<StakingStepAssociate
|
||||
associated={
|
||||
new BigNumber(
|
||||
@ -73,10 +75,11 @@ export const StakingStepConnectWallets = () => {
|
||||
<p>
|
||||
{t('Connected Ethereum address')}
|
||||
<Link
|
||||
title={t('View address on Etherscan')}
|
||||
title={t('View on Etherscan (opens in a new tab)')}
|
||||
href={`${ETHERSCAN_URL}/tx/${account}`}
|
||||
target="_blank"
|
||||
>
|
||||
{account}
|
||||
{truncateMiddle(account)}
|
||||
</Link>
|
||||
</p>
|
||||
<p>
|
||||
@ -90,13 +93,16 @@ export const StakingStepConnectWallets = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<p className="mb-8">
|
||||
<Trans
|
||||
i18nKey="stakingStep1Text"
|
||||
components={{
|
||||
vegaWalletLink: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
<Link href={Links.WALLET_GUIDE} target="_blank" />
|
||||
<Link
|
||||
href={Links.WALLET_GUIDE}
|
||||
className="text-white underline"
|
||||
target="_blank"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
@ -110,7 +116,7 @@ export const StakingStepConnectWallets = () => {
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<p>
|
||||
<p className="mb-8">
|
||||
<Button
|
||||
onClick={() =>
|
||||
appDispatch({
|
||||
@ -172,18 +178,18 @@ export const StakingStepAssociate = ({
|
||||
iconName="tick"
|
||||
title={t('stakingHasAssociated', { tokens: formatNumber(associated) })}
|
||||
>
|
||||
<p>
|
||||
<RouteLink to="/staking/associate">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<RouteLink to="associate">
|
||||
<Button data-testid="associate-more-tokens-btn">
|
||||
{t('stakingAssociateMoreButton')}
|
||||
</Button>
|
||||
</RouteLink>
|
||||
</p>
|
||||
<RouteLink to="/staking/disassociate">
|
||||
<Button data-testid="disassociate-tokens-btn">
|
||||
{t('stakingDisassociateButton')}
|
||||
</Button>
|
||||
</RouteLink>
|
||||
<RouteLink to="disassociate">
|
||||
<Button data-testid="disassociate-tokens-btn">
|
||||
{t('stakingDisassociateButton')}
|
||||
</Button>
|
||||
</RouteLink>
|
||||
</div>
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
@ -59,8 +59,9 @@ export const ValidatorTable = ({
|
||||
<span>{t('ETHEREUM ADDRESS')}</span>
|
||||
<span>
|
||||
<Link
|
||||
title={t('View address on Etherscan')}
|
||||
title={t('View on Etherscan (opens in a new tab)')}
|
||||
href={`${ETHERSCAN_URL}/address/${node.ethereumAdddress}`}
|
||||
target="_blank"
|
||||
>
|
||||
{node.ethereumAdddress}
|
||||
</Link>
|
||||
|
@ -85,8 +85,9 @@ export const Tranche = () => {
|
||||
return (
|
||||
<li className="pb-4" key={i}>
|
||||
<Link
|
||||
title={t('View address on Etherscan')}
|
||||
title={t('View on Etherscan (opens in a new tab)')}
|
||||
href={`${ETHERSCAN_URL}/tx/${user.address}`}
|
||||
target="_blank"
|
||||
>
|
||||
{user.address}
|
||||
</Link>
|
||||
|
@ -22,7 +22,7 @@ const Withdraw = () => {
|
||||
return (
|
||||
<>
|
||||
<Heading title={t('withdrawPageHeading')} />
|
||||
<p>{t('withdrawPageText')}</p>
|
||||
<p className="mb-8">{t('withdrawPageText')}</p>
|
||||
<div className="mb-24">
|
||||
<VegaWalletContainer>
|
||||
{(currVegaKey) => <WithdrawContainer currVegaKey={currVegaKey} />}
|
||||
@ -140,9 +140,9 @@ export const WithdrawContainer = ({ currVegaKey }: WithdrawContainerProps) => {
|
||||
title={t('pendingWithdrawalsCalloutTitle')}
|
||||
intent={Intent.Prompt}
|
||||
>
|
||||
<p>{t('pendingWithdrawalsCalloutText')}</p>
|
||||
<p className="mb-0">
|
||||
<Link to={Routes.WITHDRAWALS}>
|
||||
<p className="mb-8">{t('pendingWithdrawalsCalloutText')}</p>
|
||||
<p>
|
||||
<Link to={Routes.WITHDRAWALS} className="underline text-white">
|
||||
{t('pendingWithdrawalsCalloutButton')}
|
||||
</Link>
|
||||
</p>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { format } from 'date-fns';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import React from 'react';
|
||||
@ -10,7 +10,6 @@ import { Heading } from '../../components/heading';
|
||||
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
||||
import { SplashLoader } from '../../components/splash-loader';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import type { VegaKeyExtended } from '@vegaprotocol/wallet';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { DATE_FORMAT_DETAILED } from '../../lib/date-formats';
|
||||
import { addDecimal } from '../../lib/decimals';
|
||||
@ -27,21 +26,13 @@ const Withdrawals = () => {
|
||||
<>
|
||||
<Heading title={t('withdrawalsTitle')} />
|
||||
<VegaWalletContainer>
|
||||
{(currVegaKey) => (
|
||||
<WithdrawPendingContainer currVegaKey={currVegaKey} />
|
||||
)}
|
||||
{(currVegaKey) => <WithdrawPendingContainer />}
|
||||
</VegaWalletContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface WithdrawPendingContainerProps {
|
||||
currVegaKey: VegaKeyExtended;
|
||||
}
|
||||
|
||||
const WithdrawPendingContainer = ({
|
||||
currVegaKey,
|
||||
}: WithdrawPendingContainerProps) => {
|
||||
const WithdrawPendingContainer = () => {
|
||||
const { t } = useTranslation();
|
||||
const { transaction, submit } = useCompleteWithdraw();
|
||||
const { data, loading, error } = useWithdrawals();
|
||||
@ -80,11 +71,11 @@ const WithdrawPendingContainer = ({
|
||||
return (
|
||||
<>
|
||||
<h2>{t('withdrawalsPreparedWarningHeading')}</h2>
|
||||
<p>{t('withdrawalsText')}</p>
|
||||
<p>{t('withdrawalsPreparedWarningText')}</p>
|
||||
<p className="mb-8">{t('withdrawalsText')}</p>
|
||||
<p className="mb-28">{t('withdrawalsPreparedWarningText')}</p>
|
||||
<ul role="list">
|
||||
{withdrawals.map((w) => (
|
||||
<li key={w.id}>
|
||||
<li key={w.id} className="mb-28">
|
||||
<Withdrawal withdrawal={w} complete={submit} />
|
||||
</li>
|
||||
))}
|
||||
@ -102,37 +93,34 @@ interface WithdrawalProps {
|
||||
export const Withdrawal = ({ withdrawal, complete }: WithdrawalProps) => {
|
||||
const { ETHERSCAN_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
let status = null;
|
||||
let footer = null;
|
||||
|
||||
const renderStatus = ({
|
||||
id,
|
||||
status,
|
||||
txHash,
|
||||
pendingOnForeignChain,
|
||||
}: Withdrawals_party_withdrawals) => {
|
||||
if (pendingOnForeignChain) {
|
||||
return t('Pending');
|
||||
if (withdrawal.pendingOnForeignChain) {
|
||||
status = t('Pending');
|
||||
footer = (
|
||||
<Button
|
||||
className="w-full"
|
||||
disabled={true}
|
||||
onClick={() => complete(withdrawal.id)}
|
||||
>
|
||||
{t('withdrawalsCompleteButton')}
|
||||
</Button>
|
||||
);
|
||||
} else if (withdrawal.status === WithdrawalStatus.Finalized) {
|
||||
if (withdrawal.txHash) {
|
||||
status = t('Complete');
|
||||
} else {
|
||||
status = t('Incomplete');
|
||||
footer = (
|
||||
<Button className="w-full" onClick={() => complete(withdrawal.id)}>
|
||||
{t('withdrawalsCompleteButton')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === WithdrawalStatus.Finalized) {
|
||||
if (txHash) {
|
||||
return t('Complete');
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{t('Incomplete')}{' '}
|
||||
<button
|
||||
className="text-white underline"
|
||||
onClick={() => complete(id)}
|
||||
>
|
||||
{t('withdrawalsCompleteButton')}
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
};
|
||||
} else {
|
||||
status = withdrawal.status;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -151,8 +139,9 @@ export const Withdrawal = ({ withdrawal, complete }: WithdrawalProps) => {
|
||||
{t('toEthereum')}
|
||||
<span>
|
||||
<Link
|
||||
title={t('View address on Etherscan')}
|
||||
title={t('View on Etherscan (opens in a new tab)')}
|
||||
href={`${ETHERSCAN_URL}/tx/${withdrawal.details?.receiverAddress}`}
|
||||
target="_blank"
|
||||
>
|
||||
{truncateMiddle(withdrawal.details?.receiverAddress ?? '')}
|
||||
</Link>
|
||||
@ -174,6 +163,7 @@ export const Withdrawal = ({ withdrawal, complete }: WithdrawalProps) => {
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
href={`${ETHERSCAN_URL}/tx/${withdrawal.txHash}`}
|
||||
target="_blank"
|
||||
>
|
||||
{truncateMiddle(withdrawal.txHash)}
|
||||
</Link>
|
||||
@ -184,9 +174,10 @@ export const Withdrawal = ({ withdrawal, complete }: WithdrawalProps) => {
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('status')}
|
||||
{renderStatus(withdrawal)}
|
||||
{status}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
{footer}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -45,7 +45,7 @@ const getIconElement = ({
|
||||
CalloutProps,
|
||||
'icon' | 'iconName' | 'iconDescription' | 'isLoading'
|
||||
>) => {
|
||||
const wrapperClassName = 'ml-8 mr-16 mt-8';
|
||||
const wrapperClassName = 'mt-4';
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
@ -64,7 +64,10 @@ const getIconElement = ({
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <div className={wrapperClassName}>{icon}</div>;
|
||||
if (icon) {
|
||||
return <div className={wrapperClassName}>{icon}</div>;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export function Callout({
|
||||
@ -85,30 +88,30 @@ export function Callout({
|
||||
});
|
||||
|
||||
const className = classNames(
|
||||
'flex gap-20',
|
||||
'border',
|
||||
'border-black',
|
||||
'dark:border-white',
|
||||
'text-body-large',
|
||||
'dark:text-white',
|
||||
'p-16',
|
||||
getIntentShadow(intent),
|
||||
{
|
||||
flex: iconElement,
|
||||
}
|
||||
getIntentShadow(intent)
|
||||
);
|
||||
const TitleTag: keyof JSX.IntrinsicElements = headingLevel
|
||||
? `h${headingLevel}`
|
||||
: 'div';
|
||||
const body = (
|
||||
<>
|
||||
{title && <TitleTag className="text-h5 mt-0 mb-8">{title}</TitleTag>}
|
||||
{title && (
|
||||
<TitleTag className="text-h5 mt-0 mb-8 last:mb-0">{title}</TitleTag>
|
||||
)}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<div data-testid="callout" className={className}>
|
||||
{iconElement}
|
||||
{iconElement ? <div className="grow">{body}</div> : body}
|
||||
<div className="grow">{body}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
|
||||
if (element) {
|
||||
return (
|
||||
<div className="inline-flex items-center relative">
|
||||
<div className="flex items-center relative">
|
||||
{hasPrepended && element}
|
||||
{input}
|
||||
{hasAppended && element}
|
||||
|
@ -10,11 +10,6 @@ const Template: Story = (args) => <Lozenge {...args}>lozenge</Lozenge>;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
export const WithDetails = Template.bind({});
|
||||
WithDetails.args = {
|
||||
details: 'details text',
|
||||
};
|
||||
|
||||
export const Highlight = Template.bind({});
|
||||
Highlight.args = {
|
||||
variant: 'highlight',
|
||||
|
@ -1,38 +1,31 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { getVariantBackground } from '../../utils/intent';
|
||||
import type { TailwindIntents } from '../../utils/intent';
|
||||
import { TailwindIntents } from '../../utils/intent';
|
||||
|
||||
interface LozengeProps {
|
||||
children: ReactNode;
|
||||
variant?: TailwindIntents;
|
||||
className?: string;
|
||||
details?: string;
|
||||
}
|
||||
|
||||
const getWrapperClasses = (className: LozengeProps['className']) => {
|
||||
return classNames('inline-flex items-center gap-4', className);
|
||||
};
|
||||
|
||||
const getLozengeClasses = (variant: LozengeProps['variant']) => {
|
||||
const getLozengeClasses = (
|
||||
variant: LozengeProps['variant'],
|
||||
className?: string
|
||||
) => {
|
||||
return classNames(
|
||||
['rounded-md', 'font-mono', 'leading-none', 'p-4'],
|
||||
getVariantBackground(variant)
|
||||
getVariantBackground(variant),
|
||||
className
|
||||
);
|
||||
};
|
||||
|
||||
export const Lozenge = ({
|
||||
children,
|
||||
variant,
|
||||
variant = TailwindIntents.Highlight,
|
||||
className,
|
||||
details,
|
||||
...props
|
||||
}: LozengeProps) => {
|
||||
return (
|
||||
<span className={getWrapperClasses(className)} {...props}>
|
||||
<span className={getLozengeClasses(variant)}>{children}</span>
|
||||
|
||||
{details && <span>{details}</span>}
|
||||
</span>
|
||||
<span className={getLozengeClasses(variant, className)}>{children}</span>
|
||||
);
|
||||
};
|
||||
|
@ -27,18 +27,18 @@ export const Web3ConnectDialog = ({
|
||||
>
|
||||
<ul data-testid="web3-connector-list">
|
||||
{connectors.map(([connector], i) => {
|
||||
const connectorName = getConnectorName(connector);
|
||||
const info = getConnectorInfo(connector);
|
||||
return (
|
||||
<li key={i} className="mb-12 last:mb-0">
|
||||
<button
|
||||
className="capitalize hover:text-vega-pink dark:hover:text-vega-yellow underline"
|
||||
data-testid={`web3-connector-${connectorName}`}
|
||||
className="hover:text-vega-pink dark:hover:text-vega-yellow underline"
|
||||
data-testid={`web3-connector-${info.name}`}
|
||||
onClick={async () => {
|
||||
await connector.activate(desiredChainId);
|
||||
setDialogOpen(false);
|
||||
}}
|
||||
>
|
||||
{connectorName}
|
||||
{info.text}
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
@ -48,8 +48,16 @@ export const Web3ConnectDialog = ({
|
||||
);
|
||||
};
|
||||
|
||||
function getConnectorName(connector: Connector) {
|
||||
if (connector instanceof MetaMask) return 'MetaMask';
|
||||
if (connector instanceof WalletConnect) return 'WalletConnect';
|
||||
return 'Unknown';
|
||||
function getConnectorInfo(connector: Connector) {
|
||||
if (connector instanceof MetaMask)
|
||||
return {
|
||||
name: 'MetaMask',
|
||||
text: t('MetaMask, Brave or other injected web wallet'),
|
||||
};
|
||||
if (connector instanceof WalletConnect)
|
||||
return {
|
||||
name: 'WalletConnect',
|
||||
text: t('WalletConnect'),
|
||||
};
|
||||
return { name: 'Unknown', text: t('Unknown') };
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ const RecipientCell = ({
|
||||
}: RecipientCellProps) => {
|
||||
return (
|
||||
<Link
|
||||
title={t('View address on Etherscan')}
|
||||
title={t('View on Etherscan (opens in a new tab)')}
|
||||
href={`${ethUrl}/address/${value}`}
|
||||
data-testid="etherscan-link"
|
||||
>
|
||||
|
Loading…
Reference in New Issue
Block a user