Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c17095ee1 | ||
|
|
2d4be5fcb3 | ||
|
|
835f2b793f | ||
|
|
d50b988b4a | ||
|
|
a6aec899c8 | ||
|
|
8b19572dc9 | ||
|
|
80399d04f7 | ||
|
|
8322fc7edd | ||
|
|
46752816ec | ||
|
|
f99f78780c | ||
|
|
f7037bca80 | ||
|
|
ba39720f05 | ||
|
|
6d35f2b39d | ||
|
|
78afecc2e5 | ||
|
|
3de5b07495 | ||
|
|
e944d3e37c | ||
|
|
5596392835 | ||
|
|
5c57106c04 | ||
|
|
2fc688f477 |
@@ -155,10 +155,10 @@ jobs:
|
||||
- name: Sanity check docker image
|
||||
run: |
|
||||
echo "Check ipfs-hash"
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash > ${{ matrix.app }}-ipfs-hash
|
||||
docker run --rm --entrypoint /bin/sh ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local -c 'cat /ipfs-hash'
|
||||
docker run --rm --entrypoint /bin/sh ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local -c 'cat /ipfs-hash' > ${{ matrix.app }}-ipfs-hash
|
||||
echo "List html directory"
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree /usr/share/nginx/html'
|
||||
docker run --rm --entrypoint /bin/sh ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local -c 'apk add --update tree; tree /usr/share/nginx/html'
|
||||
|
||||
- name: Publish dist as docker image (ghcr)
|
||||
uses: docker/build-push-action@v3
|
||||
|
||||
@@ -212,7 +212,7 @@ context(
|
||||
closeStakingDialog();
|
||||
navigateTo(navigation.validators);
|
||||
cy.get(`[row-id="${0}"]`)
|
||||
.eq(1)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.should('have.text', '3,002.00')
|
||||
@@ -222,7 +222,7 @@ context(
|
||||
.and('be.visible');
|
||||
});
|
||||
cy.get(`[row-id="${1}"]`)
|
||||
.eq(1)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.scrollIntoView()
|
||||
|
||||
+10
-4
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useEffect, useState, useCallback } from 'react';
|
||||
import { useMemo, useEffect, useState, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
@@ -86,12 +86,18 @@ export const EpochIndividualRewards = ({
|
||||
[epochId, page, refetch, delegationsPagination, pubKey]
|
||||
);
|
||||
|
||||
const prevEpochIdRef = useRef<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// when the epoch changes, we want to refetch the data to update the current page
|
||||
if (data) {
|
||||
if (prevEpochIdRef.current === null) {
|
||||
prevEpochIdRef.current = epochId;
|
||||
} else if (epochId !== prevEpochIdRef.current) {
|
||||
// When the epoch changes, we want to refetch the data to update the current page
|
||||
refetchData();
|
||||
}
|
||||
}, [epochId, data, refetchData]);
|
||||
|
||||
prevEpochIdRef.current = epochId;
|
||||
}, [epochId, refetchData]);
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
|
||||
@@ -104,7 +104,7 @@ describe('deposit actions', { tags: '@smoke' }, () => {
|
||||
cy.visit('/#/markets/market-1');
|
||||
});
|
||||
|
||||
it('Deposit to trade is visible', () => {
|
||||
it.skip('Deposit to trade is visible', () => {
|
||||
cy.getByTestId('Collateral').click();
|
||||
cy.get('[row-id="asset-id"]').contains('tEURO').should('be.visible');
|
||||
cy.contains('[data-testid="deposit"]', 'Deposit').should('be.visible');
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
import { MarketTradingModeMapping } from '@vegaprotocol/types';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import compact from 'lodash/compact';
|
||||
|
||||
const accordionContent = 'accordion-content';
|
||||
const blockExplorerLink = 'block-explorer-link';
|
||||
const dialogClose = 'dialog-close';
|
||||
const dialogContent = 'dialog-content';
|
||||
const externalLink = 'external-link';
|
||||
const githubLink = 'github-link';
|
||||
const liquidityLink = 'view-liquidity-link';
|
||||
const marketInfoBtn = 'Info';
|
||||
const marketTitle = 'accordion-title';
|
||||
const providerName = 'provider-name';
|
||||
const row = 'key-value-table-row';
|
||||
const verifiedProofs = 'verified-proofs';
|
||||
|
||||
describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
});
|
||||
|
||||
before(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockTradingPage(MarketState.STATE_ACTIVE);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
cy.wait('@MarketInfo');
|
||||
});
|
||||
|
||||
it('current fees displayed', () => {
|
||||
// 6002-MDET-101
|
||||
cy.getByTestId(marketTitle).contains('Current fees').click();
|
||||
validateMarketDataRow(0, 'Maker Fee', '0.02%');
|
||||
validateMarketDataRow(1, 'Infrastructure Fee', '0.05%');
|
||||
validateMarketDataRow(2, 'Liquidity Fee', '1.00%');
|
||||
validateMarketDataRow(3, 'Total Fees', '1.07%');
|
||||
});
|
||||
|
||||
it('market price', () => {
|
||||
// 6002-MDET-102
|
||||
cy.getByTestId(marketTitle).contains('Market price').click();
|
||||
validateMarketDataRow(0, 'Mark Price', '46,126.90058');
|
||||
validateMarketDataRow(1, 'Best Bid Price', '44,126.90058 ');
|
||||
validateMarketDataRow(2, 'Best Offer Price', '48,126.90058 ');
|
||||
validateMarketDataRow(3, 'Quote Unit', 'BTC');
|
||||
});
|
||||
|
||||
it('market volume displayed', () => {
|
||||
// 6002-MDET-103
|
||||
cy.getByTestId(marketTitle).contains('Market volume').click();
|
||||
validateMarketDataRow(1, 'Open Interest', '-');
|
||||
validateMarketDataRow(2, 'Best Bid Volume', '1');
|
||||
validateMarketDataRow(3, 'Best Offer Volume', '3');
|
||||
validateMarketDataRow(4, 'Best Static Bid Volume', '2');
|
||||
validateMarketDataRow(5, 'Best Static Offer Volume', '4');
|
||||
});
|
||||
|
||||
it('insurance pool displayed', () => {
|
||||
// 6002-MDET-104
|
||||
cy.getByTestId(marketTitle).contains('Insurance pool').click();
|
||||
validateMarketDataRow(0, 'Balance', '0');
|
||||
});
|
||||
|
||||
it('key details displayed', () => {
|
||||
// 6002-MDET-201
|
||||
cy.getByTestId(marketTitle).contains('Key details').click();
|
||||
|
||||
const rows: [string, string][] = compact([
|
||||
['Name', 'BTCUSD Monthly (30 Jun 2022)'],
|
||||
['Market ID', 'market-0'],
|
||||
Cypress.env('NX_SUCCESSOR_MARKETS') && ['Parent Market ID', 'PARENT-A'],
|
||||
Cypress.env('NX_SUCCESSOR_MARKETS') && [
|
||||
'Insurance Pool Fraction',
|
||||
'0.75',
|
||||
],
|
||||
['Trading Mode', MarketTradingModeMapping.TRADING_MODE_CONTINUOUS],
|
||||
['Market Decimal Places', '5'],
|
||||
['Position Decimal Places', '0'],
|
||||
['Settlement Asset Decimal Places', '5'],
|
||||
]);
|
||||
|
||||
for (const rowNumber in rows) {
|
||||
const [name, value] = rows[rowNumber];
|
||||
validateMarketDataRow(Number(rowNumber), name, value);
|
||||
}
|
||||
});
|
||||
|
||||
it('instrument displayed', () => {
|
||||
// 6002-MDET-202
|
||||
cy.getByTestId(marketTitle).contains('Instrument').click();
|
||||
|
||||
validateMarketDataRow(0, 'Market Name', 'BTCUSD Monthly (30 Jun 2022)');
|
||||
validateMarketDataRow(1, 'Code', 'BTCUSD.MF21');
|
||||
validateMarketDataRow(2, 'Product Type', 'Future');
|
||||
validateMarketDataRow(3, 'Quote Name', 'BTC');
|
||||
});
|
||||
|
||||
it('oracle displayed', () => {
|
||||
// 6002-MDET-203
|
||||
cy.getByTestId(marketTitle).contains('Oracle').click();
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId(providerName)
|
||||
.and('contain', 'Another oracle');
|
||||
|
||||
cy.getByTestId(providerName).should('be.visible').click();
|
||||
cy.getByTestId(dialogContent)
|
||||
.eq(1)
|
||||
.within(() => {
|
||||
cy.getByTestId(blockExplorerLink).contains('Block explorer');
|
||||
cy.getByTestId(githubLink).contains('Oracle repository');
|
||||
});
|
||||
cy.getByTestId(dialogClose).click();
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId(verifiedProofs)
|
||||
.and('contain', '1');
|
||||
});
|
||||
|
||||
it('settlement asset displayed', () => {
|
||||
// 6002-MDET-206
|
||||
cy.getByTestId(marketTitle).contains('Settlement asset').click();
|
||||
cy.window().then((win) => {
|
||||
cy.stub(win, 'prompt').returns('DISABLED WINDOW PROMPT');
|
||||
});
|
||||
validateMarketDataRow(0, 'ID', 'asset-id');
|
||||
validateMarketDataRow(1, 'Type', 'ERC20');
|
||||
validateMarketDataRow(2, 'Name', 'Euro');
|
||||
validateMarketDataRow(3, 'Symbol', 'tEURO');
|
||||
validateMarketDataRow(4, 'Decimals', '5');
|
||||
validateMarketDataRow(5, 'Quantum', '1');
|
||||
validateMarketDataRow(6, 'Status', 'Enabled');
|
||||
validateMarketDataRow(7, 'Contract address', '0x0158…78a4');
|
||||
validateMarketDataRow(8, 'Withdrawal threshold', '0.0005');
|
||||
validateMarketDataRow(9, 'Lifetime limit', '1,230');
|
||||
validateMarketDataRow(10, 'Infrastructure fee account balance', '0.00001');
|
||||
validateMarketDataRow(11, 'Global reward pool account balance', '0.00002');
|
||||
});
|
||||
|
||||
it('metadata displayed', () => {
|
||||
// 6002-MDET-207
|
||||
cy.getByTestId(marketTitle).contains('Metadata').click();
|
||||
|
||||
validateMarketDataRow(0, 'Formerly', '076BB86A5AA41E3E');
|
||||
validateMarketDataRow(1, 'Base', 'BTC');
|
||||
validateMarketDataRow(2, 'Quote', 'USD');
|
||||
validateMarketDataRow(3, 'Class', 'fx/crypto');
|
||||
validateMarketDataRow(4, 'Sector', 'crypto');
|
||||
});
|
||||
|
||||
it('risk model displayed', () => {
|
||||
// 6002-MDET-208
|
||||
cy.getByTestId(marketTitle).contains('Risk model').click();
|
||||
validateMarketDataRow(0, 'Tau', '0.0001140771161');
|
||||
validateMarketDataRow(1, 'Risk Aversion Parameter', '0.01');
|
||||
});
|
||||
|
||||
it('risk parameters displayed', () => {
|
||||
// 6002-MDET-209
|
||||
cy.getByTestId(marketTitle).contains('Risk parameters').click();
|
||||
validateMarketDataRow(0, 'R', '0.016');
|
||||
validateMarketDataRow(1, 'Sigma', '0.3');
|
||||
});
|
||||
|
||||
it('risk factors displayed', () => {
|
||||
// 6002-MDET-210
|
||||
cy.getByTestId(marketTitle).contains('Risk factors').click();
|
||||
|
||||
validateMarketDataRow(0, 'Short', '0.008571790367285281');
|
||||
validateMarketDataRow(1, 'Long', '0.008508132993273576');
|
||||
});
|
||||
|
||||
it('price monitoring bounds displayed', () => {
|
||||
// 6002-MDET-211
|
||||
cy.getByTestId(marketTitle).contains('Price monitoring bounds 1').click();
|
||||
cy.get('p.col-span-1').contains('99.99999% probability price bounds');
|
||||
cy.get('p.col-span-1').contains('Within 43,200 seconds');
|
||||
validateMarketDataRow(0, 'Highest Price', '7.97323 ');
|
||||
validateMarketDataRow(1, 'Lowest Price', '6.54701 ');
|
||||
});
|
||||
|
||||
it('liquidity monitoring parameters displayed', () => {
|
||||
// 6002-MDET-212
|
||||
cy.getByTestId(marketTitle)
|
||||
.contains('Liquidity monitoring parameters')
|
||||
.click();
|
||||
|
||||
validateMarketDataRow(0, 'Triggering Ratio', '0.7');
|
||||
validateMarketDataRow(1, 'Time Window', '3,600');
|
||||
validateMarketDataRow(2, 'Scaling Factor', '10');
|
||||
});
|
||||
|
||||
it('liquidity displayed', () => {
|
||||
// 6002-MDET-213
|
||||
cy.getByTestId(marketTitle)
|
||||
.contains(/Liquidity(?! m)/)
|
||||
.click();
|
||||
|
||||
validateMarketDataRow(0, 'Target Stake', '10.00 tBTC');
|
||||
validateMarketDataRow(1, 'Supplied Stake', '0.01 tBTC');
|
||||
validateMarketDataRow(2, 'Market Value Proxy', '20.00 tBTC');
|
||||
cy.getByTestId(liquidityLink).should(
|
||||
'have.text',
|
||||
'View liquidity provision table'
|
||||
);
|
||||
});
|
||||
|
||||
it('liquidity price range displayed', () => {
|
||||
// 6002-MDET-214
|
||||
cy.getByTestId(marketTitle).contains('Liquidity price range').click();
|
||||
|
||||
validateMarketDataRow(0, 'Liquidity Price Range', '2.00% of mid price');
|
||||
validateMarketDataRow(1, 'Lowest Price', '45,204.362 BTC');
|
||||
validateMarketDataRow(2, 'Highest Price', '47,049.438 BTC');
|
||||
});
|
||||
|
||||
it('proposal displayed', () => {
|
||||
// 6002-MDET-301
|
||||
cy.getByTestId(marketTitle).contains('Proposal').click();
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.first()
|
||||
.should('have.text', 'View governance proposal')
|
||||
.and('have.attr', 'href')
|
||||
.and('contain', '/proposals/market-0');
|
||||
cy.getByTestId(accordionContent)
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.eq(1)
|
||||
.should('have.text', 'Propose a change to market')
|
||||
.and('have.attr', 'href')
|
||||
.and('contain', '/proposals/propose/update-market');
|
||||
});
|
||||
|
||||
afterEach('close toggle', () => {
|
||||
cy.get('[data-state="open"]').then((tab) => {
|
||||
if (tab) tab.find('button').trigger('click');
|
||||
});
|
||||
});
|
||||
|
||||
function validateMarketDataRow(
|
||||
rowNumber: number,
|
||||
name: string,
|
||||
value: string
|
||||
) {
|
||||
cy.getByTestId(row)
|
||||
.eq(rowNumber)
|
||||
.within(() => {
|
||||
cy.get('dt').should('contain.text', name);
|
||||
cy.get('dd').should('contain.text', value);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -10,10 +10,10 @@ const dialogContent = 'dialog-content';
|
||||
describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
// Using portfolio page as it requires vega wallet connection
|
||||
cy.visit('/#/portfolio');
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||
});
|
||||
|
||||
|
||||
+2
-1
@@ -16,13 +16,14 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
@@ -17,10 +17,6 @@ NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-ma
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
|
||||
# TAG name of the current app version - TODO: bump to the latest upon release
|
||||
NX_APP_VERSION=v0.21.1-core-0.72.14
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
|
||||
@@ -1,18 +1,58 @@
|
||||
import { DepositContainer } from '@vegaprotocol/deposits';
|
||||
import { GetStarted } from '../../components/welcome-dialog';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Intent, TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { GetStartedCheckList } from '../../components/welcome-dialog';
|
||||
import {
|
||||
useGetOnboardingStep,
|
||||
useOnboardingStore,
|
||||
OnboardingStep,
|
||||
} from '../../components/welcome-dialog/use-get-onboarding-step';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export const Deposit = () => {
|
||||
return (
|
||||
<div className="py-16 px-8 flex w-full justify-center">
|
||||
<div className="lg:min-w-[700px] min-w-[300px] max-w-[700px]">
|
||||
<h1 className="text-4xl xl:text-5xl uppercase font-alpha calt">
|
||||
{t('Deposit')}
|
||||
</h1>
|
||||
<div className="mt-10">
|
||||
<DepositContainer />
|
||||
<GetStarted />
|
||||
</div>
|
||||
<div className="max-w-[600px] px-4 py-8 mx-auto lg:px-8">
|
||||
<h1 className="mb-6 text-4xl uppercase xl:text-5xl font-alpha calt">
|
||||
{t('Deposit')}
|
||||
</h1>
|
||||
<div className="flex flex-col gap-6">
|
||||
<DepositContainer />
|
||||
<DepositGetStarted />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DepositGetStarted = () => {
|
||||
const onboardingDismissed = useOnboardingStore((store) => store.dismissed);
|
||||
const dismiss = useOnboardingStore((store) => store.dismiss);
|
||||
const step = useGetOnboardingStep();
|
||||
const wrapperClasses = classNames(
|
||||
'flex flex-col py-4 px-6 gap-4 rounded',
|
||||
'bg-vega-blue-300 dark:bg-vega-blue-700',
|
||||
'border border-vega-blue-350 dark:border-vega-blue-650'
|
||||
);
|
||||
|
||||
// Dont show unless still onboarding
|
||||
if (onboardingDismissed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pt-6 border-t border-default">
|
||||
<div className={wrapperClasses}>
|
||||
<h3 className="text-lg">{t('Get started')}</h3>
|
||||
<GetStartedCheckList />
|
||||
{step > OnboardingStep.ONBOARDING_DEPOSIT_STEP && (
|
||||
<TradingAnchorButton
|
||||
href={Links[Routes.HOME]()}
|
||||
onClick={() => dismiss()}
|
||||
intent={Intent.Info}
|
||||
>
|
||||
{t('Start trading')}
|
||||
</TradingAnchorButton>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { TradePanels } from './trade-panels';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
||||
return markPrice && decimalPlaces
|
||||
@@ -58,7 +59,9 @@ const TitleUpdater = ({
|
||||
export const MarketPage = () => {
|
||||
const { marketId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { init, view, setView } = useSidebar();
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const { setViews, getView } = useSidebar();
|
||||
const view = getView(currentRouteId);
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
@@ -69,15 +72,14 @@ export const MarketPage = () => {
|
||||
useEffect(() => {
|
||||
if (data?.id && data.id !== lastMarketId) {
|
||||
update({ marketId: data.id });
|
||||
// make sidebar open on market id change
|
||||
setView({ type: ViewType.Order });
|
||||
}
|
||||
}, [update, lastMarketId, data?.id]);
|
||||
|
||||
// make sidebar open on deal ticket by default
|
||||
if (view === null) {
|
||||
setView({ type: ViewType.Order });
|
||||
useEffect(() => {
|
||||
if (largeScreen && view === undefined) {
|
||||
setViews({ type: ViewType.Order }, currentRouteId);
|
||||
}
|
||||
}, [update, lastMarketId, data?.id, setView, init, view]);
|
||||
}, [setViews, view, currentRouteId, largeScreen]);
|
||||
|
||||
const tradeView = useMemo(() => {
|
||||
if (largeScreen) {
|
||||
|
||||
@@ -56,6 +56,7 @@ const MainGrid = memo(
|
||||
<Tabs storageKey="console-trade-grid-main-left">
|
||||
<Tab
|
||||
id="chart"
|
||||
overflowHidden
|
||||
name={t('Chart')}
|
||||
menu={<TradingViews.candles.menu />}
|
||||
>
|
||||
@@ -72,7 +73,7 @@ const MainGrid = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
minSize={200}
|
||||
preferredSize={sizesMiddle[1] || 300}
|
||||
preferredSize={sizesMiddle[1] || 275}
|
||||
>
|
||||
<TradeGridChild>
|
||||
<Tabs storageKey="console-trade-grid-main-right">
|
||||
@@ -180,7 +181,7 @@ const TradeGridChild = ({ children }: TradeGridChildProps) => {
|
||||
{({ width, height }) => (
|
||||
<div
|
||||
style={{ width, height }}
|
||||
className="border border-default rounded-sm"
|
||||
className="border rounded-sm border-default"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -23,6 +23,7 @@ import { ViewType, useSidebar } from '../../components/sidebar';
|
||||
import { AccountsMenu } from '../../components/accounts-menu';
|
||||
import { DepositsMenu } from '../../components/deposits-menu';
|
||||
import { WithdrawalsMenu } from '../../components/withdrawals-menu';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
const WithdrawalsIndicator = () => {
|
||||
const { ready } = useIncompleteWithdrawals();
|
||||
@@ -37,7 +38,10 @@ const WithdrawalsIndicator = () => {
|
||||
};
|
||||
|
||||
export const Portfolio = () => {
|
||||
const { init, view, setView } = useSidebar();
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const { getView, setViews } = useSidebar();
|
||||
const view = getView(currentRouteId);
|
||||
|
||||
const { updateTitle } = usePageTitleStore((store) => ({
|
||||
updateTitle: store.updateTitle,
|
||||
}));
|
||||
@@ -48,10 +52,10 @@ export const Portfolio = () => {
|
||||
|
||||
// Make transfer sidebar open by default
|
||||
useEffect(() => {
|
||||
if (init && view === null) {
|
||||
setView({ type: ViewType.Transfer });
|
||||
if (view === undefined) {
|
||||
setViews({ type: ViewType.Transfer }, currentRouteId);
|
||||
}
|
||||
}, [init, view, setView]);
|
||||
}, [view, setViews, currentRouteId]);
|
||||
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
|
||||
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col';
|
||||
|
||||
@@ -12,6 +12,7 @@ import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const AccountsContainer = ({
|
||||
pinnedAsset,
|
||||
@@ -21,7 +22,8 @@ export const AccountsContainer = ({
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
|
||||
const gridStore = useAccountStore((store) => store.gridStore);
|
||||
const updateGridStore = useAccountStore((store) => store.updateGridStore);
|
||||
@@ -49,13 +51,13 @@ export const AccountsContainer = ({
|
||||
partyId={pubKey}
|
||||
onClickAsset={onClickAsset}
|
||||
onClickWithdraw={(assetId) => {
|
||||
setView({ type: ViewType.Withdraw, assetId });
|
||||
setViews({ type: ViewType.Withdraw, assetId }, currentRouteId);
|
||||
}}
|
||||
onClickDeposit={(assetId) => {
|
||||
setView({ type: ViewType.Deposit, assetId });
|
||||
setViews({ type: ViewType.Deposit, assetId }, currentRouteId);
|
||||
}}
|
||||
onClickTransfer={(assetId) => {
|
||||
setView({ type: ViewType.Transfer, assetId });
|
||||
setViews({ type: ViewType.Transfer, assetId }, currentRouteId);
|
||||
}}
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const AccountsMenu = () => {
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TradingButton
|
||||
size="extra-small"
|
||||
data-testid="open-transfer"
|
||||
onClick={() => setView({ type: ViewType.Transfer })}
|
||||
onClick={() => setViews({ type: ViewType.Transfer }, currentRouteId)}
|
||||
>
|
||||
{t('Transfer')}
|
||||
</TradingButton>
|
||||
<TradingButton
|
||||
size="extra-small"
|
||||
onClick={() => setView({ type: ViewType.Deposit })}
|
||||
onClick={() => setViews({ type: ViewType.Deposit }, currentRouteId)}
|
||||
>
|
||||
{t('Deposit')}
|
||||
</TradingButton>
|
||||
|
||||
@@ -9,5 +9,11 @@ export const AnnouncementBanner = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Banner app="console" configUrl={ANNOUNCEMENTS_CONFIG_URL} />;
|
||||
return (
|
||||
<Banner
|
||||
app="console"
|
||||
configUrl={ANNOUNCEMENTS_CONFIG_URL}
|
||||
background="url('/banner-bg.jpg')"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export * from './announcement-banner';
|
||||
export * from './upgrade-banner';
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { gt, prerelease } from 'semver';
|
||||
import {
|
||||
ReleasesFeed,
|
||||
useEnvironment,
|
||||
useReleases,
|
||||
Networks,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
CopyWithTooltip,
|
||||
ExternalLink,
|
||||
Intent,
|
||||
NotificationBanner,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
// v0.20.12-core-0.71.4 -> v0.20.12
|
||||
// we need to strip the "core" suffix in order to determine whether a release
|
||||
// is a pre-release (candidate); example: v.0.21.0-beta.1-core-0.71.4
|
||||
const parseTagName = (tagName: string) => tagName.replace(/-core-[\d.]+$/i, '');
|
||||
|
||||
type UpgradeBannerProps = {
|
||||
showVersionChange: boolean;
|
||||
};
|
||||
export const UpgradeBanner = ({ showVersionChange }: UpgradeBannerProps) => {
|
||||
const [visible, setVisible] = useState(true);
|
||||
const { data } = useReleases(ReleasesFeed.FrontEnd);
|
||||
const { APP_VERSION, VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
|
||||
|
||||
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
|
||||
|
||||
const newest = useMemo(() => {
|
||||
if (!APP_VERSION || !data) return undefined;
|
||||
const newer = data.filter((r) => gt(r.tagName, APP_VERSION));
|
||||
const valid =
|
||||
// filter pre-releases on mainnet
|
||||
VEGA_ENV === Networks.MAINNET
|
||||
? newer?.filter((r) => !prerelease(parseTagName(r.tagName)))
|
||||
: newer;
|
||||
return valid.sort((a, b) => (gt(a.tagName, b.tagName) ? -1 : 1))[0];
|
||||
}, [APP_VERSION, VEGA_ENV, data]);
|
||||
|
||||
if (!visible || !newest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<NotificationBanner
|
||||
intent={Intent.Warning}
|
||||
onClose={() => {
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="uppercase mb-1">
|
||||
<ExternalLink href={CANONICAL_URL}>
|
||||
{t('Upgrade to the latest version of Console')}
|
||||
</ExternalLink>
|
||||
</div>
|
||||
<div data-testid="bookmark-message">
|
||||
<a
|
||||
className="underline"
|
||||
href={newest.htmlUrl}
|
||||
rel="noreferrer nofollow noopener"
|
||||
target="_blank"
|
||||
>
|
||||
{t("View what's changed")}
|
||||
</a>{' '}
|
||||
{t(' or bookmark')}{' '}
|
||||
<a className="underline" href={CANONICAL_URL}>
|
||||
{t('console.vega.xyz')}
|
||||
</a>{' '}
|
||||
<CopyWithTooltip text={CANONICAL_URL}>
|
||||
<button title={t('Copy %s', CANONICAL_URL)}>
|
||||
<span className="sr-only">{t('Copy %s', CANONICAL_URL)}</span>
|
||||
<VegaIcon size={14} name={VegaIconNames.COPY} />
|
||||
</button>
|
||||
</CopyWithTooltip>{' '}
|
||||
{'to always see the latest version.'}
|
||||
</div>
|
||||
</NotificationBanner>
|
||||
);
|
||||
};
|
||||
@@ -1,14 +1,16 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const DepositsMenu = () => {
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
|
||||
return (
|
||||
<TradingButton
|
||||
size="extra-small"
|
||||
onClick={() => setView({ type: ViewType.Deposit })}
|
||||
onClick={() => setViews({ type: ViewType.Deposit }, currentRouteId)}
|
||||
data-testid="deposit-button"
|
||||
>
|
||||
{t('Deposit')}
|
||||
|
||||
@@ -4,16 +4,19 @@ import classNames from 'classnames';
|
||||
import { Routes as AppRoutes } from '../../pages/client-router';
|
||||
import { MarketHeader } from '../market-header';
|
||||
import { LiquidityHeader } from '../liquidity-header';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const LayoutWithSidebar = () => {
|
||||
const sidebarView = useSidebar((store) => store.view);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const views = useSidebar((store) => store.views);
|
||||
const sidebarView = views[currentRouteId] || null;
|
||||
const sidebarOpen = sidebarView !== null;
|
||||
|
||||
const gridClasses = classNames(
|
||||
'h-full relative z-0 grid',
|
||||
'grid-rows-[min-content_1fr_40px]',
|
||||
'lg:grid-rows-[min-content_1fr]',
|
||||
'lg:grid-cols-[1fr_350px_40px]'
|
||||
'lg:grid-cols-[1fr_280px_40px]',
|
||||
'xxxl:grid-cols-[1fr_320px_40px]'
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { OrderbookManager } from '@vegaprotocol/market-depth';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useDealTicketFormValues } from '@vegaprotocol/deal-ticket';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const OrderbookContainer = ({ marketId }: { marketId: string }) => {
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const update = useDealTicketFormValues((state) => state.updateAll);
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
return (
|
||||
<OrderbookManager
|
||||
marketId={marketId}
|
||||
onClick={(values) => {
|
||||
update(marketId, values);
|
||||
setView({ type: ViewType.Order });
|
||||
setViews({ type: ViewType.Order }, currentRouteId);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { Routes as AppRoutes } from '../../pages/client-router';
|
||||
|
||||
jest.mock('../node-health', () => ({
|
||||
NodeHealthContainer: () => <span data-testid="node-health" />,
|
||||
@@ -115,7 +116,11 @@ describe('SidebarContent', () => {
|
||||
<VegaWalletContext.Provider value={walletContext}>
|
||||
<MemoryRouter initialEntries={['/markets/ABC']}>
|
||||
<Routes>
|
||||
<Route path="/markets/:marketId" element={<SidebarContent />} />
|
||||
<Route
|
||||
path="/markets/:marketId"
|
||||
id={AppRoutes.MARKET}
|
||||
element={<SidebarContent />}
|
||||
/>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</VegaWalletContext.Provider>
|
||||
@@ -124,13 +129,17 @@ describe('SidebarContent', () => {
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
|
||||
act(() => {
|
||||
useSidebar.setState({ view: { type: ViewType.Transfer } });
|
||||
useSidebar.setState({
|
||||
views: { [AppRoutes.MARKET]: { type: ViewType.Transfer } },
|
||||
});
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('transfer')).toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
useSidebar.setState({ view: { type: ViewType.Deposit } });
|
||||
useSidebar.setState({
|
||||
views: { [AppRoutes.MARKET]: { type: ViewType.Deposit } },
|
||||
});
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('deposit')).toBeInTheDocument();
|
||||
@@ -141,26 +150,36 @@ describe('SidebarContent', () => {
|
||||
<VegaWalletContext.Provider value={walletContext}>
|
||||
<MemoryRouter initialEntries={['/portfolio']}>
|
||||
<Routes>
|
||||
<Route path="/portfolio" element={<SidebarContent />} />
|
||||
<Route
|
||||
path="/portfolio"
|
||||
id={AppRoutes.PORTFOLIO}
|
||||
element={<SidebarContent />}
|
||||
/>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</VegaWalletContext.Provider>
|
||||
);
|
||||
|
||||
act(() => {
|
||||
useSidebar.setState({ view: { type: ViewType.Order } });
|
||||
useSidebar.setState({
|
||||
views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Order } },
|
||||
});
|
||||
});
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
|
||||
act(() => {
|
||||
useSidebar.setState({ view: { type: ViewType.Settings } });
|
||||
useSidebar.setState({
|
||||
views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Settings } },
|
||||
});
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('settings')).toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
useSidebar.setState({ view: { type: ViewType.Info } });
|
||||
useSidebar.setState({
|
||||
views: { [AppRoutes.PORTFOLIO]: { type: ViewType.Info } },
|
||||
});
|
||||
});
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
@@ -178,6 +197,7 @@ describe('SidebarButton', () => {
|
||||
tooltip="INFO"
|
||||
onClick={onClick}
|
||||
view={view}
|
||||
routeId="current-route-id"
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ import { Settings } from '../settings';
|
||||
import { Tooltip } from '../../components/tooltip';
|
||||
import { WithdrawContainer } from '../withdraw-container';
|
||||
import { Routes as AppRoutes } from '../../pages/client-router';
|
||||
import { GetStarted } from '../welcome-dialog';
|
||||
import { useVegaWallet, useViewAsDialog } from '@vegaprotocol/wallet';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
import { GetStarted } from '../welcome-dialog';
|
||||
|
||||
export enum ViewType {
|
||||
Order = 'Order',
|
||||
@@ -51,27 +52,31 @@ type SidebarView =
|
||||
};
|
||||
|
||||
export const Sidebar = () => {
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1';
|
||||
const setViewAsDialogOpen = useViewAsDialog((state) => state.setOpen);
|
||||
const { pubKeys } = useVegaWallet();
|
||||
return (
|
||||
<div className="flex lg:flex-col gap-2 h-full p-1" data-testid="sidebar">
|
||||
<div className="flex h-full p-1 lg:flex-col gap-2" data-testid="sidebar">
|
||||
<nav className={navClasses}>
|
||||
{/* sidebar options that always show */}
|
||||
<SidebarButton
|
||||
view={ViewType.Deposit}
|
||||
icon={VegaIconNames.DEPOSIT}
|
||||
tooltip={t('Deposit')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
<SidebarButton
|
||||
view={ViewType.Withdraw}
|
||||
icon={VegaIconNames.WITHDRAW}
|
||||
tooltip={t('Withdraw')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
<SidebarButton
|
||||
view={ViewType.Transfer}
|
||||
icon={VegaIconNames.TRANSFER}
|
||||
tooltip={t('Transfer')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
{/* buttons for specific routes */}
|
||||
<Routes>
|
||||
@@ -94,11 +99,13 @@ export const Sidebar = () => {
|
||||
view={ViewType.Order}
|
||||
icon={VegaIconNames.TICKET}
|
||||
tooltip={t('Order')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
<SidebarButton
|
||||
view={ViewType.Info}
|
||||
icon={VegaIconNames.BREAKDOWN}
|
||||
tooltip={t('Market specification')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
@@ -114,12 +121,13 @@ export const Sidebar = () => {
|
||||
icon={VegaIconNames.EYE}
|
||||
tooltip={t('View as party')}
|
||||
disabled={Boolean(pubKeys)}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
|
||||
<SidebarButton
|
||||
view={ViewType.Settings}
|
||||
icon={VegaIconNames.COG}
|
||||
tooltip={t('Settings')}
|
||||
routeId={currentRouteId}
|
||||
/>
|
||||
<NodeHealthContainer />
|
||||
</nav>
|
||||
@@ -133,23 +141,25 @@ export const SidebarButton = ({
|
||||
tooltip,
|
||||
disabled = false,
|
||||
onClick,
|
||||
routeId,
|
||||
}: {
|
||||
view?: ViewType;
|
||||
icon: VegaIconNames;
|
||||
tooltip: string;
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
routeId: string;
|
||||
}) => {
|
||||
const { currView, setView } = useSidebar((store) => ({
|
||||
currView: store.view,
|
||||
setView: store.setView,
|
||||
const { setViews, getView } = useSidebar((store) => ({
|
||||
setViews: store.setViews,
|
||||
getView: store.getView,
|
||||
}));
|
||||
|
||||
const currView = getView(routeId);
|
||||
const onSelect = (view: SidebarView['type']) => {
|
||||
if (view === currView?.type) {
|
||||
setView(null);
|
||||
setViews(null, routeId);
|
||||
} else {
|
||||
setView({ type: view });
|
||||
setViews({ type: view }, routeId);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -187,7 +197,7 @@ export const SidebarButton = ({
|
||||
const SidebarDivider = () => {
|
||||
return (
|
||||
<div
|
||||
className="bg-vega-clight-600 dark:bg-vega-cdark-600 w-px h-4 lg:w-4 lg:h-px"
|
||||
className="w-px h-4 bg-vega-clight-600 dark:bg-vega-cdark-600 lg:w-4 lg:h-px"
|
||||
role="separator"
|
||||
/>
|
||||
);
|
||||
@@ -195,8 +205,10 @@ const SidebarDivider = () => {
|
||||
|
||||
export const SidebarContent = () => {
|
||||
const params = useParams();
|
||||
const { view, setView } = useSidebar();
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
|
||||
const { setViews, getView } = useSidebar();
|
||||
const view = getView(currentRouteId);
|
||||
if (!view) return null;
|
||||
|
||||
if (view.type === ViewType.Order) {
|
||||
@@ -206,7 +218,7 @@ export const SidebarContent = () => {
|
||||
<DealTicketContainer
|
||||
marketId={params.marketId}
|
||||
onDeposit={(assetId) =>
|
||||
setView({ type: ViewType.Deposit, assetId })
|
||||
setViews({ type: ViewType.Deposit, assetId }, currentRouteId)
|
||||
}
|
||||
/>
|
||||
<GetStarted />
|
||||
@@ -233,7 +245,6 @@ export const SidebarContent = () => {
|
||||
return (
|
||||
<ContentWrapper title={t('Deposit')}>
|
||||
<DepositContainer assetId={view.assetId} />
|
||||
<GetStarted />
|
||||
</ContentWrapper>
|
||||
);
|
||||
}
|
||||
@@ -242,7 +253,6 @@ export const SidebarContent = () => {
|
||||
return (
|
||||
<ContentWrapper title={t('Withdraw')}>
|
||||
<WithdrawContainer assetId={view.assetId} />
|
||||
<GetStarted />
|
||||
</ContentWrapper>
|
||||
);
|
||||
}
|
||||
@@ -251,7 +261,6 @@ export const SidebarContent = () => {
|
||||
return (
|
||||
<ContentWrapper title={t('Transfer')}>
|
||||
<TransferContainer assetId={view.assetId} />
|
||||
<GetStarted />
|
||||
</ContentWrapper>
|
||||
);
|
||||
}
|
||||
@@ -276,7 +285,7 @@ const ContentWrapper = ({
|
||||
}) => {
|
||||
return (
|
||||
<TinyScroll
|
||||
className="h-full overflow-auto py-4 pl-3 pr-4"
|
||||
className="h-full py-4 pl-3 pr-4 overflow-auto"
|
||||
// panes have p-1, since sidebar is on the right make pl less to account for additional pane space
|
||||
data-testid="sidebar-content"
|
||||
>
|
||||
@@ -288,25 +297,21 @@ const ContentWrapper = ({
|
||||
|
||||
/** If rendered will close sidebar */
|
||||
const CloseSidebar = () => {
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
useEffect(() => {
|
||||
setView(null);
|
||||
}, [setView]);
|
||||
setViews(null, currentRouteId);
|
||||
}, [setViews, currentRouteId]);
|
||||
return null;
|
||||
};
|
||||
|
||||
export const useSidebar = create<{
|
||||
init: boolean;
|
||||
view: SidebarView | null;
|
||||
setView: (view: SidebarView | null) => void;
|
||||
}>()((set) => ({
|
||||
init: true,
|
||||
view: null,
|
||||
setView: (x) =>
|
||||
set(() => {
|
||||
if (x == null) {
|
||||
return { view: null, init: false };
|
||||
}
|
||||
return { view: x, init: false };
|
||||
}),
|
||||
views: { [key: string]: SidebarView | null };
|
||||
setViews: (view: SidebarView | null, routeId: string) => void;
|
||||
getView: (routeId: string) => SidebarView | null | undefined;
|
||||
}>()((set, get) => ({
|
||||
views: {},
|
||||
setViews: (x, routeId) =>
|
||||
set(({ views }) => ({ views: { ...views, [routeId]: x } })),
|
||||
getView: (routeId) => get().views[routeId],
|
||||
}));
|
||||
|
||||
@@ -11,6 +11,10 @@ jest.mock('@vegaprotocol/wallet', () => ({
|
||||
useVegaWalletDialogStore: () => mockUpdateDialogOpen,
|
||||
}));
|
||||
|
||||
jest.mock('../../lib/hooks/use-get-current-route-id', () => ({
|
||||
useGetCurrentRouteId: jest.fn().mockReturnValue('current-route-id'),
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
@@ -22,13 +22,15 @@ import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useCopyTimeout } from '@vegaprotocol/react-helpers';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import classNames from 'classnames';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const VegaWalletConnectButton = () => {
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
const {
|
||||
pubKey,
|
||||
pubKeys,
|
||||
@@ -95,7 +97,7 @@ export const VegaWalletConnectButton = () => {
|
||||
<TradingDropdownItem
|
||||
data-testid="wallet-transfer"
|
||||
onClick={() => {
|
||||
setView({ type: ViewType.Transfer });
|
||||
setViews({ type: ViewType.Transfer }, currentRouteId);
|
||||
setDropdownOpen(false);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useVegaWallet, type PubKey } from '@vegaprotocol/wallet';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const VegaWalletMenu = ({
|
||||
setMenu,
|
||||
@@ -17,7 +18,8 @@ export const VegaWalletMenu = ({
|
||||
setMenu: (open: 'nav' | 'wallet' | null) => void;
|
||||
}) => {
|
||||
const { pubKey, pubKeys, selectPubKey, disconnect } = useVegaWallet();
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
|
||||
const activeKey = useMemo(() => {
|
||||
return pubKeys?.find((pk) => pk.publicKey === pubKey);
|
||||
@@ -46,7 +48,7 @@ export const VegaWalletMenu = ({
|
||||
<div className="flex flex-col gap-2 m-4">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setView({ type: ViewType.Transfer });
|
||||
setViews({ type: ViewType.Transfer }, currentRouteId);
|
||||
setMenu(null);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -75,7 +75,7 @@ describe('GetStarted', () => {
|
||||
</MemoryRouter>
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(screen.getByRole('button', { name: 'Deposit' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: 'Deposit' })).toBeInTheDocument();
|
||||
|
||||
mockStep = 4;
|
||||
rerender(
|
||||
@@ -87,10 +87,10 @@ describe('GetStarted', () => {
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(
|
||||
screen.getByRole('button', { name: 'Ready to trade' })
|
||||
screen.getByRole('link', { name: 'Ready to trade' })
|
||||
).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Ready to trade' }));
|
||||
fireEvent.click(screen.getByRole('link', { name: 'Ready to trade' }));
|
||||
|
||||
mockStep = 5;
|
||||
rerender(
|
||||
|
||||
@@ -3,13 +3,13 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
ExternalLink,
|
||||
Intent,
|
||||
TradingAnchorButton,
|
||||
TradingButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
OnboardingStep,
|
||||
useGetOnboardingStep,
|
||||
@@ -24,56 +24,90 @@ interface Props {
|
||||
}
|
||||
|
||||
const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
|
||||
const navigate = useNavigate();
|
||||
const dismiss = useOnboardingStore((store) => store.dismiss);
|
||||
const setDialogOpen = useOnboardingStore((store) => store.setDialogOpen);
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
const link = marketId ? Links[Routes.MARKET](marketId) : Links[Routes.HOME]();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
let buttonText = t('Get started');
|
||||
let onClickHandle = () => {
|
||||
openVegaWalletDialog();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
|
||||
const buttonProps = {
|
||||
size: 'small' as const,
|
||||
'data-testid': 'get-started-button',
|
||||
intent: Intent.Info,
|
||||
};
|
||||
|
||||
if (step <= OnboardingStep.ONBOARDING_CONNECT_STEP) {
|
||||
buttonText = t('Connect');
|
||||
return (
|
||||
<TradingButton {...buttonProps} onClick={() => openVegaWalletDialog()}>
|
||||
{t('Connect')}
|
||||
</TradingButton>
|
||||
);
|
||||
} else if (step === OnboardingStep.ONBOARDING_DEPOSIT_STEP) {
|
||||
buttonText = t('Deposit');
|
||||
onClickHandle = () => {
|
||||
navigate(link);
|
||||
setView({ type: ViewType.Deposit });
|
||||
setDialogOpen(false);
|
||||
};
|
||||
return (
|
||||
<TradingAnchorButton
|
||||
{...buttonProps}
|
||||
href={Links[Routes.DEPOSIT]()}
|
||||
onClick={() => setDialogOpen(false)}
|
||||
>
|
||||
{t('Deposit')}
|
||||
</TradingAnchorButton>
|
||||
);
|
||||
} else if (step >= OnboardingStep.ONBOARDING_ORDER_STEP) {
|
||||
buttonText = t('Ready to trade');
|
||||
onClickHandle = () => {
|
||||
navigate(link);
|
||||
setView({ type: ViewType.Order });
|
||||
dismiss();
|
||||
};
|
||||
return (
|
||||
<TradingAnchorButton
|
||||
{...buttonProps}
|
||||
href={marketId ? Links[Routes.MARKET](marketId) : Links[Routes.HOME]()}
|
||||
onClick={() => {
|
||||
setViews({ type: ViewType.Order }, Routes.MARKET);
|
||||
dismiss();
|
||||
}}
|
||||
>
|
||||
{t('Ready to trade')}
|
||||
</TradingAnchorButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TradingButton
|
||||
onClick={onClickHandle}
|
||||
size="small"
|
||||
data-testid="get-started-button"
|
||||
intent={Intent.Info}
|
||||
>
|
||||
{buttonText}
|
||||
<TradingButton {...buttonProps} onClick={() => openVegaWalletDialog()}>
|
||||
{t('Get started')}
|
||||
</TradingButton>
|
||||
);
|
||||
};
|
||||
|
||||
export const GetStartedCheckList = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const currentStep = useGetOnboardingStep();
|
||||
return (
|
||||
<ul className="list-none">
|
||||
<Step
|
||||
step={1}
|
||||
text={t('Connect')}
|
||||
complete={Boolean(
|
||||
currentStep > OnboardingStep.ONBOARDING_CONNECT_STEP || pubKey
|
||||
)}
|
||||
/>
|
||||
<Step
|
||||
step={2}
|
||||
text={t('Deposit funds')}
|
||||
complete={currentStep > OnboardingStep.ONBOARDING_DEPOSIT_STEP}
|
||||
/>
|
||||
<Step
|
||||
step={3}
|
||||
text={t('Open a position')}
|
||||
complete={currentStep > OnboardingStep.ONBOARDING_ORDER_STEP}
|
||||
/>
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export const GetStarted = ({ lead }: Props) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
|
||||
const currentStep = useGetOnboardingStep();
|
||||
const dismissed = useOnboardingStore((store) => store.dismissed);
|
||||
|
||||
@@ -90,25 +124,7 @@ export const GetStarted = ({ lead }: Props) => {
|
||||
{lead && <h2>{lead}</h2>}
|
||||
<h3 className="text-lg">{t('Get started')}</h3>
|
||||
<div>
|
||||
<ul className="list-none">
|
||||
<Step
|
||||
step={1}
|
||||
text={t('Connect')}
|
||||
complete={Boolean(
|
||||
currentStep > OnboardingStep.ONBOARDING_CONNECT_STEP || pubKey
|
||||
)}
|
||||
/>
|
||||
<Step
|
||||
step={2}
|
||||
text={t('Deposit funds')}
|
||||
complete={currentStep > OnboardingStep.ONBOARDING_DEPOSIT_STEP}
|
||||
/>
|
||||
<Step
|
||||
step={3}
|
||||
text={t('Open a position')}
|
||||
complete={currentStep > OnboardingStep.ONBOARDING_ORDER_STEP}
|
||||
/>
|
||||
</ul>
|
||||
<GetStartedCheckList />
|
||||
</div>
|
||||
<div>
|
||||
<GetStartedButton step={currentStep} />
|
||||
@@ -116,7 +132,7 @@ export const GetStarted = ({ lead }: Props) => {
|
||||
{VEGA_ENV === Networks.MAINNET && (
|
||||
<p className="text-sm">
|
||||
{t('Experiment for free with virtual assets on')}{' '}
|
||||
<ExternalLink href={CANONICAL_URL}>
|
||||
<ExternalLink href={VEGA_NETWORKS.TESTNET}>
|
||||
{t('Fairground Testnet')}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
@@ -124,7 +140,7 @@ export const GetStarted = ({ lead }: Props) => {
|
||||
{VEGA_ENV === Networks.TESTNET && (
|
||||
<p className="text-sm">
|
||||
{t('Ready to trade with real funds?')}{' '}
|
||||
<ExternalLink href={CANONICAL_URL}>
|
||||
<ExternalLink href={VEGA_NETWORKS.MAINNET}>
|
||||
{t('Switch to Mainnet')}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
|
||||
export const WithdrawalsMenu = () => {
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
return (
|
||||
<TradingButton
|
||||
size="extra-small"
|
||||
onClick={() => setView({ type: ViewType.Withdraw })}
|
||||
onClick={() => setViews({ type: ViewType.Withdraw }, currentRouteId)}
|
||||
data-testid="withdraw-dialog-button"
|
||||
>
|
||||
{t('Make withdrawal')}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { routerConfig } from '../../pages/client-router';
|
||||
import { matchRoutes, useLocation } from 'react-router-dom';
|
||||
|
||||
export const useGetCurrentRouteId = () => {
|
||||
const location = useLocation();
|
||||
const currentRoute = matchRoutes(routerConfig, location);
|
||||
const lastRoute = currentRoute?.pop();
|
||||
if (lastRoute) {
|
||||
const {
|
||||
route: { id },
|
||||
} = lastRoute;
|
||||
return id || '';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
@@ -38,7 +38,7 @@ import { AppLoader, DynamicLoader } from '../components/app-loader';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { activeOrdersProvider } from '@vegaprotocol/orders';
|
||||
import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval';
|
||||
import { AnnouncementBanner, UpgradeBanner } from '../components/banner';
|
||||
import { AnnouncementBanner } from '../components/banner';
|
||||
import { Navbar } from '../components/navbar';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
@@ -115,7 +115,6 @@ function AppBody({ Component }: AppProps) {
|
||||
/>
|
||||
<ProtocolUpgradeInProgressNotification />
|
||||
<ViewingBanner />
|
||||
<UpgradeBanner showVersionChange={true} />
|
||||
</div>
|
||||
<div data-testid={`pathname-${location.pathname}`}>
|
||||
<Component />
|
||||
|
||||
@@ -4,33 +4,24 @@ export default function Document() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
{/*
|
||||
{/*
|
||||
meta tags
|
||||
- next advised against using _document for this, so they exist in our
|
||||
- next advised against using _document for this, so they exist in our
|
||||
- single page index.page.tsx
|
||||
*/}
|
||||
|
||||
{/* icons */}
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
content="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
|
||||
{/* fonts */}
|
||||
{/* preload fonts */}
|
||||
<link
|
||||
rel="preload"
|
||||
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
|
||||
href="/AlphaLyrae-Medium.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
/>
|
||||
|
||||
{/* styles */}
|
||||
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
|
||||
{/* icons */}
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="apple-touch-icon" content="/favicon.ico" />
|
||||
|
||||
{/* eslint-disable-next-line @next/next/no-css-tags */}
|
||||
<link rel="stylesheet" href="/preloader.css" media="all" />
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ export const Links: ConsoleLinks = {
|
||||
[Routes.DEPOSIT]: () => Routes.DEPOSIT,
|
||||
};
|
||||
|
||||
const routerConfig: RouteObject[] = [
|
||||
export const routerConfig: RouteObject[] = [
|
||||
{
|
||||
path: '/*',
|
||||
element: <LayoutWithSidebar />,
|
||||
@@ -68,6 +68,7 @@ const routerConfig: RouteObject[] = [
|
||||
{
|
||||
index: true,
|
||||
element: <LazyHome />,
|
||||
id: Routes.HOME,
|
||||
},
|
||||
{
|
||||
path: 'markets',
|
||||
@@ -76,16 +77,19 @@ const routerConfig: RouteObject[] = [
|
||||
{
|
||||
path: 'all',
|
||||
element: <LazyMarkets />,
|
||||
id: Routes.MARKETS,
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <LazyMarket />,
|
||||
id: Routes.MARKET,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'portfolio',
|
||||
element: <LazyPortfolio />,
|
||||
id: Routes.PORTFOLIO,
|
||||
},
|
||||
{
|
||||
path: 'liquidity',
|
||||
@@ -94,6 +98,7 @@ const routerConfig: RouteObject[] = [
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <LazyLiquidity />,
|
||||
id: Routes.LIQUIDITY,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -19,17 +19,11 @@ export default function Index() {
|
||||
<meta name="og:url" content="https://console.vega.xyz/" />
|
||||
<meta name="og:title" content="Vega Protocol - Console" />
|
||||
<meta name="og:site_name" content="Vega Protocol - Console" />
|
||||
<meta name="og:image" content="https://static.vega.xyz/favicon.ico" />
|
||||
<meta
|
||||
name="twitter:card"
|
||||
content="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<meta name="og:image" content="./favicon.ico" />
|
||||
<meta name="twitter:card" content="./favicon.ico" />
|
||||
<meta name="twitter:title" content="Vega Protocol - Console" />
|
||||
<meta name="twitter:description" content="Vega Protocol - Console" />
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<meta name="twitter:image" content="./favicon.ico" />
|
||||
<meta name="twitter:image:alt" content="VEGA logo" />
|
||||
<meta name="twitter:site" content="@vegaprotocol" />
|
||||
</Head>
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
@import 'ag-grid-community/styles/ag-grid.css';
|
||||
@import 'ag-grid-community/styles/ag-theme-balham.css';
|
||||
|
||||
/** Load AlphaLyrae font */
|
||||
@font-face {
|
||||
font-family: AlphaLyrae;
|
||||
src: url('/AlphaLyrae-Medium.woff2') format('woff2'),
|
||||
url('/AlphaLyrae-Medium.woff') format('woff');
|
||||
}
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -83,8 +90,7 @@ html [data-theme='light'] {
|
||||
--pennant-color-volume-sell: theme(colors.market.red.DEFAULT);
|
||||
|
||||
/* reduce space between candles */
|
||||
--pennant-candlestick-inner-padding: 0.175;
|
||||
--pennant-candlestick-stroke-width: 0.5;
|
||||
--pennant-candlestick-inner-padding: 0.25;
|
||||
}
|
||||
|
||||
html [data-theme='light'] {
|
||||
@@ -162,11 +168,22 @@ html [data-theme='dark'] {
|
||||
@apply font-normal font-alpha;
|
||||
}
|
||||
|
||||
/* Light variables */
|
||||
.ag-theme-balham {
|
||||
.ag-theme-balham,
|
||||
.ag-theme-balham-dark {
|
||||
--ag-grid-size: 2px; /* Used for compactness */
|
||||
--ag-row-height: 36px;
|
||||
--ag-header-height: 36px;
|
||||
--ag-header-height: 28px;
|
||||
}
|
||||
|
||||
@media (min-width: theme(screens.xxl)) {
|
||||
.ag-theme-balham,
|
||||
.ag-theme-balham-dark {
|
||||
--ag-header-height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Light variables */
|
||||
.ag-theme-balham {
|
||||
--ag-background-color: theme(colors.white);
|
||||
--ag-border-color: theme(colors.vega.clight.600);
|
||||
--ag-header-background-color: theme(colors.vega.clight.700);
|
||||
@@ -179,9 +196,6 @@ html [data-theme='dark'] {
|
||||
|
||||
/* Dark variables */
|
||||
.ag-theme-balham-dark {
|
||||
--ag-grid-size: 2px; /* Used for compactness */
|
||||
--ag-row-height: 36px;
|
||||
--ag-header-height: 36px;
|
||||
--ag-background-color: theme(colors.vega.cdark.900);
|
||||
--ag-border-color: theme(colors.vega.cdark.600);
|
||||
--ag-header-background-color: theme(colors.vega.cdark.700);
|
||||
@@ -191,6 +205,7 @@ html [data-theme='dark'] {
|
||||
--ag-row-hover-color: theme(colors.vega.cdark.800);
|
||||
--ag-modal-overlay-background-color: rgb(9 11 16 / 50%);
|
||||
}
|
||||
|
||||
.ag-theme-balham-dark .ag-row.no-hover,
|
||||
.ag-theme-balham-dark .ag-row.no-hover:hover,
|
||||
.ag-theme-balham .ag-row.no-hover,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
@@ -14,6 +14,7 @@ import {
|
||||
export type AnnouncementBannerProps = {
|
||||
app: AppNameType;
|
||||
configUrl: string;
|
||||
background?: string;
|
||||
};
|
||||
|
||||
// run only if below the allowed maximum delay ~24.8 days (https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value)
|
||||
@@ -36,6 +37,7 @@ const doesEndInTheFuture = (now: Date, data: Announcement) => {
|
||||
export const AnnouncementBanner = ({
|
||||
app,
|
||||
configUrl,
|
||||
background,
|
||||
}: AnnouncementBannerProps) => {
|
||||
const [isVisible, setVisible] = useState(false);
|
||||
const { data, reload } = useAnnouncement(app, configUrl);
|
||||
@@ -79,10 +81,10 @@ export const AnnouncementBanner = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Banner className="relative px-10">
|
||||
<Banner className="relative px-10" background={background}>
|
||||
<div
|
||||
data-testid="app-announcement"
|
||||
className="relative font-alpha flex gap-2 justify-center text-center text-lg text-white"
|
||||
className="relative flex justify-center text-lg text-center text-white font-alpha gap-2"
|
||||
>
|
||||
<span>{data.text}</span>{' '}
|
||||
{data.urlText && data.url && (
|
||||
@@ -90,7 +92,7 @@ export const AnnouncementBanner = ({
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
className="absolute right-0 top-0 p-4 w-10 h-full flex items-center justify-center text-white"
|
||||
className="absolute top-0 right-0 flex items-center justify-center w-10 h-full p-4 text-white"
|
||||
data-testid="app-announcement-close"
|
||||
onClick={() => {
|
||||
setVisible(false);
|
||||
|
||||
@@ -8,13 +8,16 @@ import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useCandlesChartSettings } from './use-candles-chart-settings';
|
||||
import {
|
||||
STUDY_SIZE,
|
||||
useCandlesChartSettings,
|
||||
} from './use-candles-chart-settings';
|
||||
|
||||
export type CandlesChartContainerProps = {
|
||||
marketId: string;
|
||||
};
|
||||
|
||||
const CANDLES_TO_WIDTH_FACTOR = 0.15;
|
||||
const CANDLES_TO_WIDTH_FACTOR = 0.2;
|
||||
|
||||
export const CandlesChartContainer = ({
|
||||
marketId,
|
||||
@@ -49,33 +52,34 @@ export const CandlesChartContainer = ({
|
||||
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<div style={{ width, height }}>
|
||||
<CandlestickChart
|
||||
dataSource={dataSource}
|
||||
options={{
|
||||
chartType,
|
||||
overlays,
|
||||
studies,
|
||||
notEnoughDataText: (
|
||||
<span className="text-xs text-center">{t('No data')}</span>
|
||||
),
|
||||
initialNumCandlesToDisplay: Math.floor(
|
||||
width * CANDLES_TO_WIDTH_FACTOR
|
||||
),
|
||||
studySize: 150, // default size
|
||||
studySizes,
|
||||
}}
|
||||
interval={interval}
|
||||
theme={theme}
|
||||
onOptionsChanged={(options) => {
|
||||
setStudies(options.studies);
|
||||
setOverlays(options.overlays);
|
||||
}}
|
||||
onPaneChanged={handlePaneChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{({ width, height }) => {
|
||||
const candlesCount = Math.floor(width * CANDLES_TO_WIDTH_FACTOR);
|
||||
return (
|
||||
<div style={{ width, height }}>
|
||||
<CandlestickChart
|
||||
dataSource={dataSource}
|
||||
options={{
|
||||
chartType,
|
||||
overlays,
|
||||
studies,
|
||||
notEnoughDataText: (
|
||||
<span className="text-xs text-center">{t('No data')}</span>
|
||||
),
|
||||
initialNumCandlesToDisplay: candlesCount,
|
||||
studySize: STUDY_SIZE,
|
||||
studySizes,
|
||||
}}
|
||||
interval={interval}
|
||||
theme={theme}
|
||||
onOptionsChanged={(options) => {
|
||||
setStudies(options.studies);
|
||||
setOverlays(options.overlays);
|
||||
}}
|
||||
onPaneChanged={handlePaneChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</AutoSizer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ interface StoredSettings {
|
||||
studySizes: StudySizes;
|
||||
}
|
||||
|
||||
export const STUDY_SIZE = 100;
|
||||
export const STUDY_SIZE = 90;
|
||||
const STUDY_ORDER: Study[] = [
|
||||
Study.FORCE_INDEX,
|
||||
Study.RELATIVE_STRENGTH_INDEX,
|
||||
|
||||
@@ -26,10 +26,7 @@ import {
|
||||
Pill,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import {
|
||||
useEstimatePositionQuery,
|
||||
useOpenVolume,
|
||||
} from '@vegaprotocol/positions';
|
||||
import { useOpenVolume } from '@vegaprotocol/positions';
|
||||
import {
|
||||
toBigNum,
|
||||
removeDecimal,
|
||||
@@ -63,13 +60,14 @@ import {
|
||||
useAccountBalance,
|
||||
} from '@vegaprotocol/accounts';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import type { OrderFormValues } from '../../hooks';
|
||||
import {
|
||||
DealTicketType,
|
||||
dealTicketTypeToOrderType,
|
||||
isStopOrderType,
|
||||
} from '../../hooks/use-form-values';
|
||||
import type { OrderFormValues } from '../../hooks/use-form-values';
|
||||
import { useDealTicketFormValues } from '../../hooks/use-form-values';
|
||||
useDealTicketFormValues,
|
||||
usePositionEstimate,
|
||||
} from '../../hooks';
|
||||
import { DealTicketSizeIceberg } from './deal-ticket-size-iceberg';
|
||||
import noop from 'lodash/noop';
|
||||
import { isNonPersistentOrder } from '../../utils/time-in-force-persistance';
|
||||
@@ -253,16 +251,14 @@ export const DealTicket = ({
|
||||
side: normalizedOrder.side,
|
||||
});
|
||||
}
|
||||
const { data: positionEstimate } = useEstimatePositionQuery({
|
||||
variables: {
|
||||
marketId: market.id,
|
||||
openVolume,
|
||||
orders,
|
||||
collateralAvailable:
|
||||
marginAccountBalance || generalAccountBalance ? balance : undefined,
|
||||
},
|
||||
|
||||
const positionEstimate = usePositionEstimate({
|
||||
marketId: market.id,
|
||||
openVolume,
|
||||
orders,
|
||||
collateralAvailable:
|
||||
marginAccountBalance || generalAccountBalance ? balance : undefined,
|
||||
skip: !normalizedOrder,
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
|
||||
const assetSymbol =
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './__generated__/EstimateOrder';
|
||||
export * from './use-estimate-fees';
|
||||
export * from './use-form-values';
|
||||
export * from './use-position-estimate';
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { usePositionEstimate } from './use-position-estimate';
|
||||
import * as positionsModule from '@vegaprotocol/positions';
|
||||
import type {
|
||||
EstimatePositionQuery,
|
||||
EstimatePositionQueryVariables,
|
||||
} from '@vegaprotocol/positions';
|
||||
import type { QueryResult } from '@apollo/client';
|
||||
|
||||
let mockData: object | undefined = {};
|
||||
|
||||
describe('usePositionEstimate', () => {
|
||||
const args = {
|
||||
marketId: 'marketId',
|
||||
openVolume: '10',
|
||||
orders: [],
|
||||
collateralAvailable: '200',
|
||||
skip: false,
|
||||
};
|
||||
it('should return proper data', () => {
|
||||
jest
|
||||
.spyOn(positionsModule, 'useEstimatePositionQuery')
|
||||
.mockReturnValue({ data: mockData } as unknown as QueryResult<
|
||||
EstimatePositionQuery,
|
||||
EstimatePositionQueryVariables
|
||||
>);
|
||||
const { result, rerender } = renderHook(() => usePositionEstimate(args));
|
||||
expect(result.current).toEqual(mockData);
|
||||
mockData = undefined;
|
||||
rerender(true);
|
||||
expect(result.current).toEqual({});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import type {
|
||||
EstimatePositionQueryVariables,
|
||||
EstimatePositionQuery,
|
||||
} from '@vegaprotocol/positions';
|
||||
import { useEstimatePositionQuery } from '@vegaprotocol/positions';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
interface PositionEstimateProps extends EstimatePositionQueryVariables {
|
||||
skip: boolean;
|
||||
}
|
||||
|
||||
export const usePositionEstimate = ({
|
||||
marketId,
|
||||
openVolume,
|
||||
orders,
|
||||
collateralAvailable,
|
||||
skip,
|
||||
}: PositionEstimateProps) => {
|
||||
const [estimates, setEstimates] = useState<EstimatePositionQuery | undefined>(
|
||||
undefined
|
||||
);
|
||||
const { data } = useEstimatePositionQuery({
|
||||
variables: {
|
||||
marketId,
|
||||
openVolume,
|
||||
orders,
|
||||
collateralAvailable,
|
||||
},
|
||||
skip,
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setEstimates(data);
|
||||
}
|
||||
}, [data]);
|
||||
return estimates;
|
||||
};
|
||||
@@ -658,7 +658,7 @@ export const LiquidityMonitoringParametersInfoPanel = ({
|
||||
parentMarket.liquidityMonitoringParameters.targetStakeParameters
|
||||
.scalingFactor,
|
||||
}
|
||||
: {};
|
||||
: undefined;
|
||||
|
||||
return <MarketInfoTable data={marketData} parentData={parentMarketData} />;
|
||||
};
|
||||
|
||||
@@ -4,14 +4,27 @@ import type { ReactNode } from 'react';
|
||||
export interface BannerProps {
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
background?: string;
|
||||
}
|
||||
|
||||
export const AnnouncementBanner = ({ className, children }: BannerProps) => {
|
||||
const bannerClasses = classnames(
|
||||
"bg-[url('https://static.vega.xyz/assets/img/banner-bg.jpg')] bg-cover bg-center bg-no-repeat",
|
||||
'p-4',
|
||||
className
|
||||
);
|
||||
export const AnnouncementBanner = ({
|
||||
className,
|
||||
children,
|
||||
background = 'url("https://static.vega.xyz/assets/img/banner-bg.jpg")',
|
||||
}: BannerProps) => {
|
||||
const bannerClasses = classnames('p-4', className);
|
||||
|
||||
return <div className={bannerClasses}>{children}</div>;
|
||||
return (
|
||||
<div
|
||||
className={bannerClasses}
|
||||
style={{
|
||||
background,
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'center',
|
||||
backgroundSize: 'cover',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -113,7 +113,9 @@ export const Tabs = ({
|
||||
return (
|
||||
<TabsPrimitive.Content
|
||||
value={child.props.id}
|
||||
className="h-full"
|
||||
className={classNames('h-full', {
|
||||
'overflow-hidden': child.props.overflowHidden,
|
||||
})}
|
||||
data-testid={`tab-${child.props.id}`}
|
||||
>
|
||||
{child.props.children}
|
||||
@@ -131,6 +133,7 @@ interface TabProps {
|
||||
name: string;
|
||||
indicator?: ReactNode;
|
||||
hidden?: boolean;
|
||||
overflowHidden?: boolean;
|
||||
menu?: ReactNode;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ const requestedTransactionUpdate = {
|
||||
status: EthTxStatus.Requested,
|
||||
error: null,
|
||||
confirmations: 0,
|
||||
notify: true,
|
||||
};
|
||||
|
||||
const mockDepositAsset = jest.fn();
|
||||
@@ -57,6 +58,7 @@ const createTransaction = (
|
||||
dialogOpen: false,
|
||||
txHash: null,
|
||||
receipt: null,
|
||||
notify: true,
|
||||
...transaction,
|
||||
});
|
||||
|
||||
@@ -158,6 +160,7 @@ describe('useVegaTransactionManager', () => {
|
||||
expect(update.mock.calls[1][1]).toEqual({
|
||||
status: EthTxStatus.Pending,
|
||||
txHash,
|
||||
notify: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -197,6 +200,7 @@ describe('useVegaTransactionManager', () => {
|
||||
expect(update.mock.calls[3][1]).toEqual({
|
||||
status: EthTxStatus.Confirmed,
|
||||
receipt,
|
||||
notify: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ export const useEthTransactionManager = () => {
|
||||
status: EthTxStatus.Requested,
|
||||
error: null,
|
||||
confirmations: 0,
|
||||
notify: true,
|
||||
});
|
||||
const {
|
||||
contract,
|
||||
@@ -48,6 +49,7 @@ export const useEthTransactionManager = () => {
|
||||
update(transaction.id, {
|
||||
status: EthTxStatus.Error,
|
||||
error: err as EthereumError,
|
||||
notify: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -61,6 +63,7 @@ export const useEthTransactionManager = () => {
|
||||
update(transaction.id, {
|
||||
status: EthTxStatus.Pending,
|
||||
txHash: tx.hash,
|
||||
notify: true,
|
||||
});
|
||||
|
||||
for (let i = 1; i <= requiredConfirmations; i++) {
|
||||
@@ -77,19 +80,31 @@ export const useEthTransactionManager = () => {
|
||||
}
|
||||
|
||||
if (requiresConfirmation) {
|
||||
update(transaction.id, { status: EthTxStatus.Complete, receipt });
|
||||
update(transaction.id, {
|
||||
status: EthTxStatus.Complete,
|
||||
receipt,
|
||||
});
|
||||
} else {
|
||||
update(transaction.id, { status: EthTxStatus.Confirmed, receipt });
|
||||
update(transaction.id, {
|
||||
status: EthTxStatus.Confirmed,
|
||||
receipt,
|
||||
notify: true,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof Error || isEthereumError(err)) {
|
||||
if (!isExpectedEthereumError(err)) {
|
||||
update(transaction.id, { status: EthTxStatus.Error, error: err });
|
||||
update(transaction.id, {
|
||||
status: EthTxStatus.Error,
|
||||
error: err,
|
||||
notify: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
update(transaction.id, {
|
||||
status: EthTxStatus.Error,
|
||||
error: new Error('Something went wrong'),
|
||||
notify: true,
|
||||
});
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -27,7 +27,13 @@ export interface EthStoredTxState extends EthTxState {
|
||||
methodName: ContractMethod;
|
||||
args: string[];
|
||||
requiredConfirmations: number;
|
||||
requiresConfirmation: boolean; // whether or not the tx needs external confirmation (IE from a subscription even)
|
||||
// whether or not the tx needs external confirmation (IE from a subscription even)
|
||||
requiresConfirmation: boolean;
|
||||
// whether or not to notify via toast
|
||||
// true = force open toast
|
||||
// false = force close toast
|
||||
// undefined = leave alone
|
||||
notify: boolean | undefined;
|
||||
assetId?: string;
|
||||
deposit?: DepositBusEventFieldsFragment;
|
||||
withdrawal?: WithdrawalBusEventFieldsFragment;
|
||||
@@ -49,7 +55,7 @@ export interface EthTransactionStore {
|
||||
update?: Partial<
|
||||
Pick<
|
||||
EthStoredTxState,
|
||||
'status' | 'error' | 'receipt' | 'confirmations' | 'txHash'
|
||||
'status' | 'error' | 'receipt' | 'confirmations' | 'txHash' | 'notify'
|
||||
>
|
||||
>
|
||||
) => void;
|
||||
@@ -89,6 +95,7 @@ export const useEthTransactionStore = create<EthTransactionStore>()(
|
||||
requiresConfirmation,
|
||||
assetId,
|
||||
withdrawal,
|
||||
notify: true,
|
||||
};
|
||||
set({ transactions: transactions.concat(transaction) });
|
||||
return transaction.id;
|
||||
@@ -135,6 +142,7 @@ export const useEthTransactionStore = create<EthTransactionStore>()(
|
||||
transaction.deposit = deposit;
|
||||
transaction.dialogOpen = true;
|
||||
transaction.updatedAt = new Date();
|
||||
transaction.notify = true;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { useAssetsDataProvider } from '@vegaprotocol/assets';
|
||||
import { EtherscanLink } from '@vegaprotocol/environment';
|
||||
import { formatNumber, toBigNum } from '@vegaprotocol/utils';
|
||||
@@ -169,11 +170,13 @@ export const useEthereumTransactionToasts = () => {
|
||||
]);
|
||||
|
||||
const dismissTx = useEthTransactionStore((state) => state.dismiss);
|
||||
const updateTx = useEthTransactionStore((state) => state.update);
|
||||
|
||||
const onClose = useCallback(
|
||||
(tx: EthStoredTxState) => () => {
|
||||
dismissTx(tx.id);
|
||||
removeToast(`eth-${tx.id}`);
|
||||
updateTx(tx.id, { notify: false });
|
||||
// closes related "Funds released" toast after successful withdrawal
|
||||
if (
|
||||
isWithdrawTransaction(tx) &&
|
||||
@@ -183,7 +186,7 @@ export const useEthereumTransactionToasts = () => {
|
||||
closeToastBy({ withdrawalId: tx.withdrawal.id });
|
||||
}
|
||||
},
|
||||
[closeToastBy, dismissTx, removeToast]
|
||||
[closeToastBy, dismissTx, removeToast, updateTx]
|
||||
);
|
||||
|
||||
const fromEthTransaction = useCallback(
|
||||
@@ -213,17 +216,25 @@ export const useEthereumTransactionToasts = () => {
|
||||
loader: [EthTxStatus.Pending, EthTxStatus.Complete].includes(tx.status),
|
||||
content,
|
||||
closeAfter,
|
||||
hidden: !tx.notify,
|
||||
};
|
||||
},
|
||||
[onClose]
|
||||
);
|
||||
|
||||
useEthTransactionStore.subscribe(
|
||||
(state) => compact(state.transactions.filter((tx) => tx?.dialogOpen)),
|
||||
(txs) => {
|
||||
txs.forEach((tx) => {
|
||||
setToast(fromEthTransaction(tx));
|
||||
});
|
||||
}
|
||||
);
|
||||
// Only register a subscription once
|
||||
useEffect(() => {
|
||||
const unsubscribe = useEthTransactionStore.subscribe(
|
||||
(state) => compact(state.transactions.filter((tx) => tx?.dialogOpen)),
|
||||
(txs) => {
|
||||
txs.forEach((tx) => {
|
||||
setToast(fromEthTransaction(tx));
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [fromEthTransaction, setToast]);
|
||||
};
|
||||
|
||||
@@ -87,7 +87,6 @@
|
||||
"recharts": "^2.1.2",
|
||||
"recursive-key-filter": "^1.0.2",
|
||||
"regenerator-runtime": "0.13.7",
|
||||
"semver": "^7.5.1",
|
||||
"toml": "^3.0.0",
|
||||
"tslib": "^2.3.0",
|
||||
"uuid": "^8.3.2",
|
||||
|
||||
@@ -22591,13 +22591,6 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.5.1:
|
||||
version "7.5.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec"
|
||||
integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^7.5.3:
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
||||
|
||||
Reference in New Issue
Block a user