feat(2146): adjust and refactor welcome dialogs (#2384)
* feat: adjust and refactor welcome dialogs * feat: adjust and refactor welcome dialogs - add int tests * feat: adjust and refactor welcome dialogs - small fixes and imprvments * feat: adjust and refactor welcome dialogs - fix a typo * feat: adjust and refactor welcome dialogs - fix a property name * feat: adjust and refactor welcome dialogs - fix an unit test
This commit is contained in:
parent
01addaea7c
commit
c14e57cfd5
@ -86,6 +86,14 @@ describe('home', { tags: '@regression' }, () => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
cy.getByTestId('welcome-notice-proposed-markets')
|
||||
.children('div.pt-1.flex.justify-between')
|
||||
.should('have.length', 3)
|
||||
.each((item) => {
|
||||
cy.wrap(item).getByTestId('external-link').should('exist');
|
||||
});
|
||||
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(selectMarketOverlay).should('not.exist');
|
||||
|
||||
@ -96,7 +104,7 @@ describe('home', { tags: '@regression' }, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('market table should properly rendered', () => {
|
||||
describe('market table should be properly rendered', () => {
|
||||
it('redirects to a default market with the landing dialog open', () => {
|
||||
const override = {
|
||||
marketsConnection: {
|
||||
@ -161,4 +169,59 @@ describe('home', { tags: '@regression' }, () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('no proposal found', () => {
|
||||
it('there is a link to propose market', () => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasQuery(req, 'ProposalsList', {
|
||||
proposalsConnection: {
|
||||
__typename: 'ProposalsConnection',
|
||||
edges: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
cy.visit('/');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@MarketsData');
|
||||
cy.getByTestId(selectMarketOverlay)
|
||||
.get('table tr')
|
||||
.then((row) => {
|
||||
expect(row.length >= 3).to.be.true;
|
||||
});
|
||||
cy.getByTestId('external-link')
|
||||
.contains('Propose a market')
|
||||
.should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('no proposal nor markets found', () => {
|
||||
it('there are welcome text and a link to propose market', () => {
|
||||
cy.mockGQL((req) => {
|
||||
const data = {
|
||||
marketsConnection: {
|
||||
__typename: 'MarketConnection',
|
||||
edges: [],
|
||||
},
|
||||
};
|
||||
aliasQuery(req, 'Markets', data);
|
||||
aliasQuery(req, 'MarketsData', data);
|
||||
aliasQuery(req, 'ProposalsList', {
|
||||
proposalsConnection: {
|
||||
__typename: 'ProposalsConnection',
|
||||
edges: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
cy.visit('/');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@MarketsData');
|
||||
cy.getByTestId('welcome-notice-title').should(
|
||||
'contain.text',
|
||||
'Welcome to Console'
|
||||
);
|
||||
cy.getByTestId('external-link')
|
||||
.contains('Propose a market')
|
||||
.should('exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -17,8 +17,7 @@ export const Home = () => {
|
||||
const { data, error, loading } = useDataProvider({
|
||||
dataProvider: marketsWithDataProvider,
|
||||
});
|
||||
const { riskNoticeDialog, update } = useGlobalStore((store) => ({
|
||||
riskNoticeDialog: store.riskNoticeDialog,
|
||||
const { update } = useGlobalStore((store) => ({
|
||||
update: store.update,
|
||||
}));
|
||||
|
||||
@ -29,7 +28,6 @@ export const Home = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
update({ landingDialog: data.length > 0 });
|
||||
const marketId = data[0]?.id;
|
||||
const marketName = data[0]?.tradableInstrument.instrument.name;
|
||||
const marketPrice = data[0]?.data?.markPrice
|
||||
@ -50,7 +48,7 @@ export const Home = () => {
|
||||
navigate(`/markets/${EMPTY_MARKET_ID}`);
|
||||
}
|
||||
}
|
||||
}, [data, navigate, riskNoticeDialog, update, pageTitle, updateTitle]);
|
||||
}, [data, navigate, update, pageTitle, updateTitle]);
|
||||
|
||||
return (
|
||||
<AsyncRenderer data={data} loading={loading} error={error}>
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
t,
|
||||
@ -18,10 +17,8 @@ import type {
|
||||
import { marketProvider, marketDataProvider } from '@vegaprotocol/market-list';
|
||||
import { useGlobalStore, usePageTitleStore } from '../../stores';
|
||||
import { TradeGrid, TradePanels } from './trade-grid';
|
||||
import { ColumnKind, SelectMarketDialog } from '../../components/select-market';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { EMPTY_MARKET_ID } from '../../components/constants';
|
||||
import { useWelcomeNoticeDialog } from '../../components/welcome-notice';
|
||||
|
||||
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
||||
return markPrice && decimalPlaces
|
||||
@ -47,21 +44,15 @@ export const Market = ({
|
||||
const marketId = isEmpty ? undefined : params.marketId;
|
||||
|
||||
const { w } = useWindowSize();
|
||||
const { landingDialog, riskNoticeDialog, update } = useGlobalStore(
|
||||
(store) => ({
|
||||
landingDialog: store.landingDialog,
|
||||
riskNoticeDialog: store.riskNoticeDialog,
|
||||
const { update } = useGlobalStore((store) => ({
|
||||
update: store.update,
|
||||
})
|
||||
);
|
||||
}));
|
||||
|
||||
const { pageTitle, updateTitle } = usePageTitleStore((store) => ({
|
||||
pageTitle: store.pageTitle,
|
||||
updateTitle: store.updateTitle,
|
||||
}));
|
||||
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
|
||||
const onSelect = useCallback(
|
||||
(id: string) => {
|
||||
if (id && id !== marketId) {
|
||||
@ -113,8 +104,6 @@ export const Market = ({
|
||||
skip: !marketId || !data,
|
||||
});
|
||||
|
||||
useWelcomeNoticeDialog();
|
||||
|
||||
const tradeView = useMemo(() => {
|
||||
if (w > 960) {
|
||||
return <TradeGrid market={data} onSelect={onSelect} />;
|
||||
@ -140,23 +129,7 @@ export const Market = ({
|
||||
if (!data && !isEmpty) {
|
||||
return <Splash>{t('Market not found')}</Splash>;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{tradeView}
|
||||
<SelectMarketDialog
|
||||
dialogOpen={landingDialog && !riskNoticeDialog}
|
||||
setDialogOpen={(isOpen: boolean) =>
|
||||
update({ landingDialog: isOpen })
|
||||
}
|
||||
onSelect={onSelect}
|
||||
onCellClick={(e, kind, value) => {
|
||||
if (value && kind === ColumnKind.Asset) {
|
||||
openAssetDetailsDialog(value, e.target as HTMLElement);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
return <>{tradeView}</>;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -2,15 +2,13 @@ import { useCallback } from 'react';
|
||||
import { MarketsContainer } from '@vegaprotocol/market-list';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useWelcomeNoticeDialog } from '../../components/welcome-notice';
|
||||
|
||||
export const Markets = () => {
|
||||
const navigate = useNavigate();
|
||||
const { update } = useGlobalStore((store) => ({ update: store.update }));
|
||||
useWelcomeNoticeDialog();
|
||||
const handleOnSelect = useCallback(
|
||||
(marketId: string) => {
|
||||
update({ marketId, welcomeNoticeDialog: false });
|
||||
update({ marketId });
|
||||
navigate(`/markets/${marketId}`);
|
||||
},
|
||||
[update, navigate]
|
||||
|
@ -1,2 +1,10 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
export const DEBOUNCE_UPDATE_TIME = 500;
|
||||
export const EMPTY_MARKET_ID = 'empty';
|
||||
export const RISK_ACCEPTED_KEY = 'vega-risk-accepted';
|
||||
export const MAINNET_WELCOME_HEADER = t(
|
||||
'Trade cash settled futures on the fully decentralised Vega network.'
|
||||
);
|
||||
export const TESTNET_WELCOME_HEADER = t(
|
||||
'Try out trading cash settled futures on the fully decentralised Vega network (Testnet).'
|
||||
);
|
||||
|
@ -1 +0,0 @@
|
||||
export * from './risk-notice-dialog';
|
@ -1,10 +1,7 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
import {
|
||||
SelectAllMarketsTableBody,
|
||||
SelectMarketLandingTable,
|
||||
} from './select-market';
|
||||
import { SelectAllMarketsTableBody } from './select-market';
|
||||
|
||||
import type {
|
||||
MarketWithCandles,
|
||||
@ -173,24 +170,4 @@ describe('SelectMarket', () => {
|
||||
fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]);
|
||||
expect(onSelect).toHaveBeenCalledWith('1');
|
||||
});
|
||||
|
||||
it('should call onSelect callback on SelectMarketLandingTable', () => {
|
||||
const onSelect = jest.fn();
|
||||
const onCellClick = jest.fn();
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<SelectMarketLandingTable
|
||||
markets={[MARKET_A as Market, MARKET_B as Market]}
|
||||
onCellClick={onCellClick}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
</MemoryRouter>,
|
||||
{ wrapper: MockedProvider }
|
||||
);
|
||||
fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]);
|
||||
expect(onSelect).toHaveBeenCalledWith('1');
|
||||
fireEvent.click(screen.getAllByTestId(`market-link-2`)[0]);
|
||||
expect(onSelect).toHaveBeenCalledWith('2');
|
||||
});
|
||||
});
|
||||
|
@ -1,18 +1,9 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useMarketList } from '@vegaprotocol/market-list';
|
||||
import { positionsDataProvider } from '@vegaprotocol/positions';
|
||||
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
Dialog,
|
||||
ExternalLink,
|
||||
Icon,
|
||||
Intent,
|
||||
Link as UILink,
|
||||
Loader,
|
||||
Popover,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { ExternalLink, Icon, Loader, Popover } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
columnHeaders,
|
||||
columnHeadersPositionMarkets,
|
||||
@ -24,7 +15,6 @@ import {
|
||||
SelectMarketTableRow,
|
||||
SelectMarketTableRowSplash,
|
||||
} from './select-market-table';
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
import type {
|
||||
MarketWithCandles,
|
||||
@ -32,7 +22,6 @@ import type {
|
||||
} from '@vegaprotocol/market-list';
|
||||
import type { PositionFieldsFragment } from '@vegaprotocol/positions';
|
||||
import type { Column, OnCellClickHandler } from './select-market-columns';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
DApp,
|
||||
TOKEN_NEW_MARKET_PROPOSAL,
|
||||
@ -40,48 +29,7 @@ import {
|
||||
} from '@vegaprotocol/environment';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
type Market = MarketWithCandles & MarketWithData;
|
||||
|
||||
export const SelectMarketLandingTable = ({
|
||||
markets,
|
||||
onSelect,
|
||||
onCellClick,
|
||||
}: {
|
||||
markets: Market[] | null;
|
||||
onSelect: (id: string) => void;
|
||||
onCellClick: OnCellClickHandler;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="max-h-[60vh] overflow-x-auto"
|
||||
data-testid="select-market-list"
|
||||
>
|
||||
<table className="text-sm relative h-full min-w-full whitespace-nowrap">
|
||||
<thead className="sticky top-0 z-10 bg-white dark:bg-black">
|
||||
<SelectMarketTableHeader />
|
||||
</thead>
|
||||
<tbody>
|
||||
{markets?.map((market, i) => (
|
||||
<SelectMarketTableRow
|
||||
marketId={market.id}
|
||||
key={i}
|
||||
detailed={false}
|
||||
onSelect={onSelect}
|
||||
columns={columns(market, onSelect, onCellClick)}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="mt-4 text-md">
|
||||
<Link to="/markets" data-testid="view-market-list-link">
|
||||
<UILink>{'Or view full market list'} </UILink>
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export type Market = MarketWithCandles & MarketWithData;
|
||||
|
||||
export const SelectAllMarketsTableBody = ({
|
||||
markets,
|
||||
@ -265,71 +213,3 @@ const TableTitle = ({ children }: { children: ReactNode }) => {
|
||||
</thead>
|
||||
);
|
||||
};
|
||||
|
||||
export const SelectMarketDialog = ({
|
||||
dialogOpen,
|
||||
setDialogOpen,
|
||||
onSelect,
|
||||
onCellClick,
|
||||
}: {
|
||||
dialogOpen: boolean;
|
||||
setDialogOpen: (open: boolean) => void;
|
||||
title?: string;
|
||||
onSelect: (id: string) => void;
|
||||
onCellClick: OnCellClickHandler;
|
||||
}) => {
|
||||
const onSelectMarket = (id: string) => {
|
||||
onSelect(id);
|
||||
setDialogOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title={t('Select a market to get started')}
|
||||
intent={Intent.Primary}
|
||||
open={dialogOpen}
|
||||
onChange={() => setDialogOpen(false)}
|
||||
size="small"
|
||||
>
|
||||
<LandingDialogContainer
|
||||
onSelect={onSelectMarket}
|
||||
onCellClick={onCellClick}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
interface LandingDialogContainerProps {
|
||||
onSelect: (id: string) => void;
|
||||
onCellClick: OnCellClickHandler;
|
||||
}
|
||||
|
||||
const LandingDialogContainer = ({
|
||||
onSelect,
|
||||
onCellClick,
|
||||
}: LandingDialogContainerProps) => {
|
||||
const { data, loading, error } = useMarketList();
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex justify-center items-center">
|
||||
<p className="my-8">{t('Failed to load markets')}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center">
|
||||
<p className="my-8">{t('Loading...')}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectMarketLandingTable
|
||||
markets={data}
|
||||
onSelect={onSelect}
|
||||
onCellClick={onCellClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
1
apps/trading/components/welcome-dialog/index.ts
Normal file
1
apps/trading/components/welcome-dialog/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './welcome-dialog';
|
79
apps/trading/components/welcome-dialog/proposed-markets.tsx
Normal file
79
apps/trading/components/welcome-dialog/proposed-markets.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
import { useMemo } from 'react';
|
||||
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { proposalsListDataProvider } from '@vegaprotocol/governance';
|
||||
import take from 'lodash/take';
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
DApp,
|
||||
TOKEN_NEW_MARKET_PROPOSAL,
|
||||
TOKEN_PROPOSAL,
|
||||
TOKEN_PROPOSALS,
|
||||
useLinks,
|
||||
} from '@vegaprotocol/environment';
|
||||
|
||||
export const ProposedMarkets = () => {
|
||||
const variables = useMemo(() => {
|
||||
return {
|
||||
proposalType: Types.ProposalType.TYPE_NEW_MARKET,
|
||||
};
|
||||
}, []);
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: proposalsListDataProvider,
|
||||
variables,
|
||||
skipUpdates: true,
|
||||
});
|
||||
|
||||
const newMarkets = take(
|
||||
(data || []).filter((proposal) =>
|
||||
[
|
||||
Types.ProposalState.STATE_OPEN,
|
||||
Types.ProposalState.STATE_PASSED,
|
||||
Types.ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
].includes(proposal.state)
|
||||
),
|
||||
3
|
||||
).map((proposal) => ({
|
||||
id: proposal.id,
|
||||
displayName:
|
||||
proposal.terms.change.__typename === 'NewMarket' &&
|
||||
proposal.terms.change.instrument.code,
|
||||
}));
|
||||
|
||||
const tokenLink = useLinks(DApp.Token);
|
||||
return useMemo(
|
||||
() => (
|
||||
<div className="mt-7 pt-8 border-t border-neutral-700">
|
||||
{newMarkets.length > 0 ? (
|
||||
<>
|
||||
<h2 className="font-alpha uppercase text-2xl">
|
||||
{t('Proposed markets')}
|
||||
</h2>
|
||||
<dl data-testid="welcome-notice-proposed-markets" className="py-5">
|
||||
{newMarkets.map(({ displayName, id }, i) => (
|
||||
<div className="pt-1 flex justify-between" key={i}>
|
||||
<dl>{displayName}</dl>
|
||||
<dt>
|
||||
<ExternalLink
|
||||
href={tokenLink(TOKEN_PROPOSAL.replace(':id', id || ''))}
|
||||
>
|
||||
{t('View or vote')}
|
||||
</ExternalLink>
|
||||
</dt>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
<ExternalLink href={tokenLink(TOKEN_PROPOSALS)}>
|
||||
{t('View all proposed markets')}
|
||||
</ExternalLink>
|
||||
</>
|
||||
) : (
|
||||
<ExternalLink href={tokenLink(TOKEN_NEW_MARKET_PROPOSAL)}>
|
||||
{t('Propose a market')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
[newMarkets, tokenLink]
|
||||
);
|
||||
};
|
@ -1,15 +1,9 @@
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { RiskNoticeDialog } from './risk-notice-dialog';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { Networks, EnvironmentProvider } from '@vegaprotocol/environment';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
useGlobalStore.setState((state) => ({
|
||||
...state,
|
||||
riskNoticeDialog: false,
|
||||
}));
|
||||
});
|
||||
import { RiskNoticeDialog } from './risk-notice-dialog';
|
||||
import { WelcomeDialog } from './welcome-dialog';
|
||||
|
||||
const mockEnvDefinitions = {
|
||||
VEGA_CONFIG_URL: 'https://config.url',
|
||||
@ -18,6 +12,12 @@ const mockEnvDefinitions = {
|
||||
};
|
||||
|
||||
describe('Risk notice dialog', () => {
|
||||
const introText = 'Regulation may apply to use of this app';
|
||||
const mockOnClose = jest.fn();
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it.each`
|
||||
assertion | network
|
||||
${'displays'} | ${Networks.MAINNET}
|
||||
@ -33,46 +33,39 @@ describe('Risk notice dialog', () => {
|
||||
definitions={{ ...mockEnvDefinitions, VEGA_ENV: network }}
|
||||
config={{ hosts: [] }}
|
||||
>
|
||||
<RiskNoticeDialog />
|
||||
</EnvironmentProvider>
|
||||
<MockedProvider>
|
||||
<WelcomeDialog />
|
||||
</MockedProvider>
|
||||
</EnvironmentProvider>,
|
||||
{ wrapper: BrowserRouter }
|
||||
);
|
||||
|
||||
if (assertion === 'displays') {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect(screen.queryByText('WARNING')).toBeInTheDocument();
|
||||
expect(screen.queryByText(introText)).toBeInTheDocument();
|
||||
} else {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect(screen.queryByText('WARNING')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(introText)).not.toBeInTheDocument();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
it("doesn't display the risk notice when previously acknowledged", () => {
|
||||
const { rerender } = render(
|
||||
render(
|
||||
<EnvironmentProvider
|
||||
definitions={{ ...mockEnvDefinitions, VEGA_ENV: Networks.MAINNET }}
|
||||
config={{ hosts: [] }}
|
||||
>
|
||||
<RiskNoticeDialog />
|
||||
<RiskNoticeDialog onClose={mockOnClose} />
|
||||
</EnvironmentProvider>
|
||||
);
|
||||
|
||||
expect(screen.queryByText('WARNING')).toBeInTheDocument();
|
||||
expect(screen.queryByText(introText)).toBeInTheDocument();
|
||||
|
||||
const button = screen.getByRole('button', {
|
||||
name: 'I understand, Continue',
|
||||
});
|
||||
fireEvent.click(button);
|
||||
|
||||
rerender(
|
||||
<EnvironmentProvider
|
||||
definitions={{ ...mockEnvDefinitions, VEGA_ENV: Networks.MAINNET }}
|
||||
config={{ hosts: [] }}
|
||||
>
|
||||
<RiskNoticeDialog />
|
||||
</EnvironmentProvider>
|
||||
);
|
||||
|
||||
expect(screen.queryByText('WARNING')).not.toBeInTheDocument();
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -1,34 +1,20 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Dialog, Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { LocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useEnvironment, Networks } from '@vegaprotocol/environment';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { RISK_ACCEPTED_KEY } from '../constants';
|
||||
|
||||
export const RISK_ACCEPTED_KEY = 'vega-risk-accepted';
|
||||
|
||||
export const RiskNoticeDialog = () => {
|
||||
const { riskNoticeDialog, update } = useGlobalStore((store) => ({
|
||||
riskNoticeDialog: store.riskNoticeDialog,
|
||||
update: store.update,
|
||||
}));
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
|
||||
useEffect(() => {
|
||||
const isRiskAccepted = LocalStorage.getItem(RISK_ACCEPTED_KEY) === 'true';
|
||||
if (!isRiskAccepted && VEGA_ENV === Networks.MAINNET) {
|
||||
update({ riskNoticeDialog: true });
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [update, VEGA_ENV]);
|
||||
|
||||
const handleAcceptRisk = () => {
|
||||
update({ riskNoticeDialog: false });
|
||||
export const RiskNoticeDialog = ({ onClose }: Props) => {
|
||||
const handleAcceptRisk = useCallback(() => {
|
||||
onClose();
|
||||
LocalStorage.setItem(RISK_ACCEPTED_KEY, 'true');
|
||||
};
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<Dialog open={riskNoticeDialog} title={t('WARNING')} size="medium">
|
||||
<>
|
||||
<h4 className="text-xl mb-2 mt-4">
|
||||
{t('Regulation may apply to use of this app')}
|
||||
</h4>
|
||||
@ -46,6 +32,6 @@ export const RiskNoticeDialog = () => {
|
||||
)}
|
||||
</p>
|
||||
<Button onClick={handleAcceptRisk}>{t('I understand, Continue')}</Button>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import * as constants from '../constants';
|
||||
|
||||
export const WelcomeDialogHeader = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const header =
|
||||
VEGA_ENV === Networks.MAINNET
|
||||
? constants.MAINNET_WELCOME_HEADER
|
||||
: constants.TESTNET_WELCOME_HEADER;
|
||||
return <h1 className="mb-6 p-4 text-center text-2xl">{header}</h1>;
|
||||
};
|
70
apps/trading/components/welcome-dialog/welcome-dialog.tsx
Normal file
70
apps/trading/components/welcome-dialog/welcome-dialog.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
import React, { useMemo, useState, useCallback } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
t,
|
||||
useDataProvider,
|
||||
useLocalStorage,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { activeMarketsProvider } from '@vegaprotocol/market-list';
|
||||
import { useEnvironment, Networks } from '@vegaprotocol/environment';
|
||||
import * as constants from '../constants';
|
||||
import { RiskNoticeDialog } from './risk-notice-dialog';
|
||||
import { WelcomeNoticeDialog } from './welcome-notice-dialog';
|
||||
import { WelcomeLandingDialog } from './welcome-landing-dialog';
|
||||
|
||||
interface DialogConfig {
|
||||
open?: boolean;
|
||||
content: React.ReactNode;
|
||||
title?: string;
|
||||
size?: 'small' | 'medium';
|
||||
onClose: () => void;
|
||||
}
|
||||
export const WelcomeDialog = () => {
|
||||
const { pathname } = useLocation();
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const [dialog, setDialog] = useState<DialogConfig | null>(null);
|
||||
const onClose = useCallback(() => {
|
||||
setDialog(null);
|
||||
}, [setDialog]);
|
||||
const [riskAccepted] = useLocalStorage(constants.RISK_ACCEPTED_KEY);
|
||||
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: activeMarketsProvider,
|
||||
});
|
||||
|
||||
useMemo(() => {
|
||||
switch (true) {
|
||||
case riskAccepted !== 'true' && VEGA_ENV === Networks.MAINNET:
|
||||
setDialog({
|
||||
content: <RiskNoticeDialog onClose={onClose} />,
|
||||
title: t('WARNING'),
|
||||
size: 'medium',
|
||||
onClose,
|
||||
});
|
||||
break;
|
||||
case pathname === '/' && data?.length === 0:
|
||||
setDialog({
|
||||
content: <WelcomeNoticeDialog />,
|
||||
onClose,
|
||||
});
|
||||
break;
|
||||
case pathname === '/' && (data?.length || 0) > 0:
|
||||
setDialog({
|
||||
content: <WelcomeLandingDialog onClose={onClose} />,
|
||||
onClose,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}, [onClose, data?.length, riskAccepted, pathname, VEGA_ENV, setDialog]);
|
||||
return dialog ? (
|
||||
<Dialog
|
||||
open={Boolean(dialog.content)}
|
||||
title={dialog.title}
|
||||
size={dialog.size}
|
||||
onChange={dialog.onClose}
|
||||
>
|
||||
{dialog.content}
|
||||
</Dialog>
|
||||
) : null;
|
||||
};
|
@ -0,0 +1,169 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type {
|
||||
MarketWithCandles,
|
||||
MarketWithData,
|
||||
MarketData,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { SelectMarketLandingTable } from './welcome-landing-dialog';
|
||||
|
||||
type Market = MarketWithCandles & MarketWithData;
|
||||
type PartialMarket = Partial<
|
||||
Omit<Market, 'data'> & { data: Partial<MarketData> }
|
||||
>;
|
||||
|
||||
const MARKET_A: PartialMarket = {
|
||||
__typename: 'Market',
|
||||
id: '1',
|
||||
decimalPlaces: 2,
|
||||
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
id: '1',
|
||||
code: 'ABCDEF',
|
||||
name: 'ABCDEF 1-Day',
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: 'ABCDEF',
|
||||
settlementAsset: {
|
||||
__typename: 'Asset',
|
||||
id: 'asset-ABC',
|
||||
decimals: 2,
|
||||
symbol: 'ABC',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
__typename: 'InstrumentMetadata',
|
||||
tags: ['ABC', 'DEF'],
|
||||
},
|
||||
},
|
||||
},
|
||||
fees: {
|
||||
__typename: 'Fees',
|
||||
factors: {
|
||||
__typename: 'FeeFactors',
|
||||
infrastructureFee: '0.01',
|
||||
liquidityFee: '0.01',
|
||||
makerFee: '0.01',
|
||||
},
|
||||
},
|
||||
data: {
|
||||
__typename: 'MarketData',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: '1',
|
||||
},
|
||||
markPrice: '90',
|
||||
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING,
|
||||
marketState: Schema.MarketState.STATE_PENDING,
|
||||
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
|
||||
indicativeVolume: '1000',
|
||||
},
|
||||
candles: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
high: '100',
|
||||
low: '10',
|
||||
open: '10',
|
||||
close: '80',
|
||||
volume: '1000',
|
||||
periodStart: '2022-11-01T15:49:00Z',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
high: '10',
|
||||
low: '1',
|
||||
open: '1',
|
||||
close: '100',
|
||||
volume: '1000',
|
||||
periodStart: '2022-11-01T15:50:00Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const MARKET_B: PartialMarket = {
|
||||
__typename: 'Market',
|
||||
id: '2',
|
||||
decimalPlaces: 2,
|
||||
positionDecimalPlaces: 0,
|
||||
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
id: '2',
|
||||
code: 'XYZ',
|
||||
name: 'XYZ 1-Day',
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: 'XYZ',
|
||||
settlementAsset: {
|
||||
__typename: 'Asset',
|
||||
id: 'asset-XYZ',
|
||||
decimals: 2,
|
||||
symbol: 'XYZ',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
__typename: 'InstrumentMetadata',
|
||||
tags: ['XYZ'],
|
||||
},
|
||||
},
|
||||
},
|
||||
fees: {
|
||||
__typename: 'Fees',
|
||||
factors: {
|
||||
__typename: 'FeeFactors',
|
||||
infrastructureFee: '0.01',
|
||||
liquidityFee: '0.01',
|
||||
makerFee: '0.01',
|
||||
},
|
||||
},
|
||||
data: {
|
||||
__typename: 'MarketData',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: '2',
|
||||
},
|
||||
markPrice: '123.123',
|
||||
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING,
|
||||
marketState: Schema.MarketState.STATE_PENDING,
|
||||
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
|
||||
indicativeVolume: '2000',
|
||||
},
|
||||
candles: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
high: '100',
|
||||
low: '10',
|
||||
open: '10',
|
||||
close: '80',
|
||||
volume: '1000',
|
||||
periodStart: '2022-11-01T15:49:00Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('WelcomeLandingDialog', () => {
|
||||
it('should call onSelect callback on SelectMarketLandingTable', () => {
|
||||
const onClose = jest.fn();
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<SelectMarketLandingTable
|
||||
markets={[MARKET_A as Market, MARKET_B as Market]}
|
||||
onClose={onClose}
|
||||
/>
|
||||
</MemoryRouter>,
|
||||
{ wrapper: MockedProvider }
|
||||
);
|
||||
fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]);
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
fireEvent.click(screen.getAllByTestId(`market-link-2`)[0]);
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -0,0 +1,132 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useMarketList } from '@vegaprotocol/market-list';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import { Link as UILink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { Market, OnCellClickHandler } from '../select-market';
|
||||
import {
|
||||
ColumnKind,
|
||||
columns,
|
||||
SelectMarketTableHeader,
|
||||
SelectMarketTableRow,
|
||||
} from '../select-market';
|
||||
import { WelcomeDialogHeader } from './welcome-dialog-header';
|
||||
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||
import { EMPTY_MARKET_ID } from '../constants';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { ProposedMarkets } from './proposed-markets';
|
||||
|
||||
export const SelectMarketLandingTable = ({
|
||||
markets,
|
||||
onClose,
|
||||
}: {
|
||||
markets: Market[] | null;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
const isEmpty = params.marketId === EMPTY_MARKET_ID;
|
||||
const marketId = isEmpty ? undefined : params.marketId;
|
||||
|
||||
const { update } = useGlobalStore((store) => ({
|
||||
update: store.update,
|
||||
}));
|
||||
|
||||
const onSelect = useCallback(
|
||||
(id: string) => {
|
||||
if (id && id !== marketId) {
|
||||
update({ marketId: id });
|
||||
navigate(`/markets/${id}`);
|
||||
}
|
||||
},
|
||||
[marketId, update, navigate]
|
||||
);
|
||||
|
||||
const onSelectMarket = useCallback(
|
||||
(id: string) => {
|
||||
onSelect(id);
|
||||
onClose();
|
||||
},
|
||||
[onSelect, onClose]
|
||||
);
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
const onCellClick = useCallback<OnCellClickHandler>(
|
||||
(e, kind, value) => {
|
||||
if (value && kind === ColumnKind.Asset) {
|
||||
openAssetDetailsDialog(value, e.target as HTMLElement);
|
||||
}
|
||||
},
|
||||
[openAssetDetailsDialog]
|
||||
);
|
||||
const showProposed = (markets?.length || 0) <= 5;
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="max-h-[60vh] overflow-x-auto"
|
||||
data-testid="select-market-list"
|
||||
>
|
||||
<p className="text-neutral-500 dark:text-neutral-400">
|
||||
{t('Select a market to get started...')}
|
||||
</p>
|
||||
<table className="text-sm relative h-full min-w-full whitespace-nowrap">
|
||||
<thead className="sticky top-0 z-10 bg-white dark:bg-black">
|
||||
<SelectMarketTableHeader />
|
||||
</thead>
|
||||
<tbody>
|
||||
{markets?.map((market, i) => (
|
||||
<SelectMarketTableRow
|
||||
marketId={market.id}
|
||||
key={i}
|
||||
detailed={false}
|
||||
onSelect={onSelectMarket}
|
||||
columns={columns(market, onSelect, onCellClick)}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="mt-4 text-md">
|
||||
<Link
|
||||
to="/markets"
|
||||
data-testid="view-market-list-link"
|
||||
onClick={() => onClose()}
|
||||
>
|
||||
<UILink>{'Or view full market list'} </UILink>
|
||||
</Link>
|
||||
</div>
|
||||
{showProposed && <ProposedMarkets />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface LandingDialogContainerProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const WelcomeLandingDialog = ({
|
||||
onClose,
|
||||
}: LandingDialogContainerProps) => {
|
||||
const { data, loading, error } = useMarketList();
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex justify-center items-center">
|
||||
<p className="my-8">{t('Failed to load markets')}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center">
|
||||
<p className="my-8">{t('Loading...')}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WelcomeDialogHeader />
|
||||
<SelectMarketLandingTable markets={data} onClose={onClose} />
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,66 @@
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
BLOG,
|
||||
DApp,
|
||||
Networks,
|
||||
TOKEN_NEW_MARKET_PROPOSAL,
|
||||
TOKEN_PROPOSALS,
|
||||
useEnvironment,
|
||||
useLinks,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { ProposedMarkets } from './proposed-markets';
|
||||
|
||||
export const WelcomeNoticeDialog = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const tokenLink = useLinks(DApp.Token);
|
||||
const consoleFairgroundLink = useLinks(DApp.Console, Networks.TESTNET);
|
||||
const isMainnet = VEGA_ENV === Networks.MAINNET;
|
||||
const networkName = isMainnet ? 'mainnet' : 'testnet';
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1
|
||||
data-testid="welcome-notice-title"
|
||||
className="mb-6 p-4 text-center text-2xl"
|
||||
>
|
||||
{t('Welcome to Console')}
|
||||
</h1>
|
||||
<p className="leading-6 mb-7">
|
||||
{t(
|
||||
'Vega %s is now live, but markets need to be voted for before the can be traded on. In the meantime:',
|
||||
[networkName]
|
||||
)}
|
||||
</p>
|
||||
<ul className="list-[square] pl-7">
|
||||
{isMainnet && (
|
||||
<li>
|
||||
<ExternalLink target="_blank" href={consoleFairgroundLink()}>
|
||||
{t('Try out Console')}
|
||||
</ExternalLink>
|
||||
{t(' on Fairground, our Testnet')}
|
||||
</li>
|
||||
)}
|
||||
<li>
|
||||
<ExternalLink target="_blank" href={tokenLink(TOKEN_PROPOSALS)}>
|
||||
{t('View and vote for proposed markets')}
|
||||
</ExternalLink>
|
||||
</li>
|
||||
<li>
|
||||
<ExternalLink
|
||||
target="_blank"
|
||||
href={tokenLink(TOKEN_NEW_MARKET_PROPOSAL)}
|
||||
>
|
||||
{t('Propose your own markets')}
|
||||
</ExternalLink>
|
||||
</li>
|
||||
<li>
|
||||
<ExternalLink target="_blank" href={BLOG}>
|
||||
{t('Read about the mainnet launch')}
|
||||
</ExternalLink>
|
||||
</li>
|
||||
</ul>
|
||||
<ProposedMarkets />
|
||||
</>
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './welcome-notice-dialog';
|
@ -1,146 +0,0 @@
|
||||
import { Dialog, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { proposalsListDataProvider } from '@vegaprotocol/governance';
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import take from 'lodash/take';
|
||||
import { activeMarketsProvider } from '@vegaprotocol/market-list';
|
||||
import {
|
||||
BLOG,
|
||||
DApp,
|
||||
Networks,
|
||||
TOKEN_NEW_MARKET_PROPOSAL,
|
||||
TOKEN_PROPOSAL,
|
||||
TOKEN_PROPOSALS,
|
||||
useLinks,
|
||||
} from '@vegaprotocol/environment';
|
||||
|
||||
export const WelcomeNoticeDialog = () => {
|
||||
const [welcomeNoticeDialog, update] = useGlobalStore((store) => [
|
||||
store.welcomeNoticeDialog,
|
||||
store.update,
|
||||
]);
|
||||
|
||||
const onOpenChange = useCallback(
|
||||
(isOpen: boolean) => {
|
||||
update({ welcomeNoticeDialog: isOpen });
|
||||
},
|
||||
[update]
|
||||
);
|
||||
|
||||
const variables = useMemo(() => {
|
||||
return {
|
||||
proposalType: Types.ProposalType.TYPE_NEW_MARKET,
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: proposalsListDataProvider,
|
||||
variables,
|
||||
skipUpdates: true,
|
||||
});
|
||||
|
||||
const newMarkets = take(
|
||||
(data || []).filter((proposal) =>
|
||||
[
|
||||
Types.ProposalState.STATE_OPEN,
|
||||
Types.ProposalState.STATE_PASSED,
|
||||
Types.ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
].includes(proposal.state)
|
||||
),
|
||||
3
|
||||
).map((proposal) => ({
|
||||
id: proposal.id,
|
||||
displayName:
|
||||
proposal.terms.change.__typename === 'NewMarket' &&
|
||||
proposal.terms.change.instrument.code,
|
||||
}));
|
||||
|
||||
const tokenLink = useLinks(DApp.Token);
|
||||
const consoleFairgroundLink = useLinks(DApp.Console, Networks.TESTNET);
|
||||
|
||||
const proposedMarkets = useMemo(
|
||||
() =>
|
||||
newMarkets.length > 0 && (
|
||||
<div className="mt-7 pt-8 border-t border-neutral-700">
|
||||
<h2 className="font-alpha uppercase text-2xl">
|
||||
{t('Proposed markets')}
|
||||
</h2>
|
||||
<dl data-testid="welcome-notice-proposed-markets" className="py-5">
|
||||
{newMarkets.map(({ displayName, id }, i) => (
|
||||
<div className="pt-1 flex justify-between" key={i}>
|
||||
<dl>{displayName}</dl>
|
||||
<dt>
|
||||
<ExternalLink
|
||||
href={tokenLink(TOKEN_PROPOSAL.replace(':id', id || ''))}
|
||||
>
|
||||
{t('View or vote')}
|
||||
</ExternalLink>
|
||||
</dt>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
<ExternalLink href={tokenLink(TOKEN_PROPOSALS)}>
|
||||
{t('View all proposed markets')}
|
||||
</ExternalLink>
|
||||
</div>
|
||||
),
|
||||
[newMarkets, tokenLink]
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog open={welcomeNoticeDialog} onChange={onOpenChange}>
|
||||
<h1
|
||||
data-testid="welcome-notice-title"
|
||||
className="font-alpha uppercase text-4xl mb-7 mt-5"
|
||||
>
|
||||
{t('Welcome to Console')}
|
||||
</h1>
|
||||
<p className="leading-6 mb-7">
|
||||
{t(
|
||||
'Vega mainnet is now live, but markets need to be voted for before the can be traded on. In the meantime:'
|
||||
)}
|
||||
</p>
|
||||
<ul className="list-[square] pl-7">
|
||||
<li>
|
||||
<ExternalLink target="_blank" href={consoleFairgroundLink()}>
|
||||
{t('Try out Console')}
|
||||
</ExternalLink>
|
||||
{t(' on Fairground, our Testnet')}
|
||||
</li>
|
||||
<li>
|
||||
<ExternalLink target="_blank" href={tokenLink(TOKEN_PROPOSALS)}>
|
||||
{t('View and vote for proposed markets')}
|
||||
</ExternalLink>
|
||||
</li>
|
||||
<li>
|
||||
<ExternalLink
|
||||
target="_blank"
|
||||
href={tokenLink(TOKEN_NEW_MARKET_PROPOSAL)}
|
||||
>
|
||||
{t('Propose your own markets')}
|
||||
</ExternalLink>
|
||||
</li>
|
||||
<li>
|
||||
<ExternalLink target="_blank" href={BLOG}>
|
||||
{t('Read about the mainnet launch')}
|
||||
</ExternalLink>
|
||||
</li>
|
||||
</ul>
|
||||
{proposedMarkets}
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export const useWelcomeNoticeDialog = () => {
|
||||
const { update } = useGlobalStore((store) => ({ update: store.update }));
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: activeMarketsProvider,
|
||||
});
|
||||
useEffect(() => {
|
||||
if (data?.length === 0) {
|
||||
update({ welcomeNoticeDialog: true });
|
||||
}
|
||||
}, [data, update]);
|
||||
};
|
@ -4,11 +4,10 @@ import {
|
||||
} from '@vegaprotocol/assets';
|
||||
import { VegaConnectDialog } from '@vegaprotocol/wallet';
|
||||
import { Connectors } from '../lib/vega-connectors';
|
||||
import { RiskNoticeDialog } from '../components/risk-notice-dialog';
|
||||
import { WithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||
import { DepositDialog } from '@vegaprotocol/deposits';
|
||||
import { Web3ConnectUncontrolledDialog } from '@vegaprotocol/web3';
|
||||
import { WelcomeNoticeDialog } from '../components/welcome-notice';
|
||||
import { WelcomeDialog } from '../components/welcome-dialog';
|
||||
|
||||
const DialogsContainer = () => {
|
||||
const { isOpen, id, trigger, setOpen } = useAssetDetailsDialogStore();
|
||||
@ -21,11 +20,10 @@ const DialogsContainer = () => {
|
||||
open={isOpen}
|
||||
onChange={setOpen}
|
||||
/>
|
||||
<RiskNoticeDialog />
|
||||
<WelcomeDialog />
|
||||
<DepositDialog />
|
||||
<Web3ConnectUncontrolledDialog />
|
||||
<WithdrawalDialog />
|
||||
<WelcomeNoticeDialog />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import 'jest-canvas-mock';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
|
||||
global.ResizeObserver = ResizeObserver;
|
||||
|
@ -3,10 +3,7 @@ import create from 'zustand';
|
||||
|
||||
interface GlobalStore {
|
||||
networkSwitcherDialog: boolean;
|
||||
landingDialog: boolean;
|
||||
riskNoticeDialog: boolean;
|
||||
marketId: string | null;
|
||||
welcomeNoticeDialog: boolean;
|
||||
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
||||
}
|
||||
|
||||
@ -17,10 +14,7 @@ interface PageTitleStore {
|
||||
|
||||
export const useGlobalStore = create<GlobalStore>((set) => ({
|
||||
networkSwitcherDialog: false,
|
||||
landingDialog: false,
|
||||
riskNoticeDialog: false,
|
||||
marketId: LocalStorage.getItem('marketId') || null,
|
||||
welcomeNoticeDialog: false,
|
||||
update: (state) => {
|
||||
set(state);
|
||||
if (state.marketId) {
|
||||
|
@ -6,4 +6,15 @@
|
||||
* @param str A
|
||||
* @returns str A
|
||||
*/
|
||||
export const t = (str: string) => str;
|
||||
export const t = (str: string, replacements?: string | string[]) => {
|
||||
if (replacements) {
|
||||
let i = 0;
|
||||
return str.replace(/%s/g, () => {
|
||||
return (
|
||||
(Array.isArray(replacements) ? replacements : [replacements])[i++] ||
|
||||
'%s'
|
||||
);
|
||||
});
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user