From c14e57cfd5cc2837386153c551437ee351105017 Mon Sep 17 00:00:00 2001
From: macqbat
Date: Tue, 13 Dec 2022 14:31:28 +0100
Subject: [PATCH] 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
---
apps/trading-e2e/src/integration/home.cy.ts | 65 ++++++-
apps/trading/client-pages/home/home.tsx | 6 +-
apps/trading/client-pages/market/market.tsx | 35 +---
apps/trading/client-pages/markets/markets.tsx | 4 +-
apps/trading/components/constants.ts | 8 +
.../components/risk-notice-dialog/index.tsx | 1 -
.../select-market/select-market.spec.tsx | 25 +--
.../select-market/select-market.tsx | 126 +------------
.../components/welcome-dialog/index.ts | 1 +
.../welcome-dialog/proposed-markets.tsx | 79 ++++++++
.../risk-notice-dialog.spec.tsx | 49 +++--
.../risk-notice-dialog.tsx | 38 ++--
.../welcome-dialog/welcome-dialog-header.tsx | 11 ++
.../welcome-dialog/welcome-dialog.tsx | 70 ++++++++
.../welcome-landing-dialog.spec.tsx | 169 ++++++++++++++++++
.../welcome-dialog/welcome-landing-dialog.tsx | 132 ++++++++++++++
.../welcome-dialog/welcome-notice-dialog.tsx | 66 +++++++
.../components/welcome-notice/index.ts | 1 -
.../welcome-notice/welcome-notice-dialog.tsx | 146 ---------------
apps/trading/pages/dialogs-container.tsx | 6 +-
apps/trading/setup-tests.ts | 1 +
apps/trading/stores/global.ts | 6 -
libs/react-helpers/src/lib/i18n.ts | 13 +-
23 files changed, 659 insertions(+), 399 deletions(-)
delete mode 100644 apps/trading/components/risk-notice-dialog/index.tsx
create mode 100644 apps/trading/components/welcome-dialog/index.ts
create mode 100644 apps/trading/components/welcome-dialog/proposed-markets.tsx
rename apps/trading/components/{risk-notice-dialog => welcome-dialog}/risk-notice-dialog.spec.tsx (65%)
rename apps/trading/components/{risk-notice-dialog => welcome-dialog}/risk-notice-dialog.tsx (70%)
create mode 100644 apps/trading/components/welcome-dialog/welcome-dialog-header.tsx
create mode 100644 apps/trading/components/welcome-dialog/welcome-dialog.tsx
create mode 100644 apps/trading/components/welcome-dialog/welcome-landing-dialog.spec.tsx
create mode 100644 apps/trading/components/welcome-dialog/welcome-landing-dialog.tsx
create mode 100644 apps/trading/components/welcome-dialog/welcome-notice-dialog.tsx
delete mode 100644 apps/trading/components/welcome-notice/index.ts
delete mode 100644 apps/trading/components/welcome-notice/welcome-notice-dialog.tsx
diff --git a/apps/trading-e2e/src/integration/home.cy.ts b/apps/trading-e2e/src/integration/home.cy.ts
index ece141d3b..64bd703e5 100644
--- a/apps/trading-e2e/src/integration/home.cy.ts
+++ b/apps/trading-e2e/src/integration/home.cy.ts
@@ -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');
+ });
+ });
});
diff --git a/apps/trading/client-pages/home/home.tsx b/apps/trading/client-pages/home/home.tsx
index d88d4448f..694c2583b 100644
--- a/apps/trading/client-pages/home/home.tsx
+++ b/apps/trading/client-pages/home/home.tsx
@@ -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 (
diff --git a/apps/trading/client-pages/market/market.tsx b/apps/trading/client-pages/market/market.tsx
index 53f603d79..b91a6e302 100644
--- a/apps/trading/client-pages/market/market.tsx
+++ b/apps/trading/client-pages/market/market.tsx
@@ -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,
- update: store.update,
- })
- );
+ 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 ;
@@ -140,23 +129,7 @@ export const Market = ({
if (!data && !isEmpty) {
return {t('Market not found')};
}
- return (
- <>
- {tradeView}
-
- update({ landingDialog: isOpen })
- }
- onSelect={onSelect}
- onCellClick={(e, kind, value) => {
- if (value && kind === ColumnKind.Asset) {
- openAssetDetailsDialog(value, e.target as HTMLElement);
- }
- }}
- />
- >
- );
+ return <>{tradeView}>;
}}
/>
);
diff --git a/apps/trading/client-pages/markets/markets.tsx b/apps/trading/client-pages/markets/markets.tsx
index 9b4e5e2c2..2f2edd5a4 100644
--- a/apps/trading/client-pages/markets/markets.tsx
+++ b/apps/trading/client-pages/markets/markets.tsx
@@ -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]
diff --git a/apps/trading/components/constants.ts b/apps/trading/components/constants.ts
index ba9724f04..739bbb424 100644
--- a/apps/trading/components/constants.ts
+++ b/apps/trading/components/constants.ts
@@ -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).'
+);
diff --git a/apps/trading/components/risk-notice-dialog/index.tsx b/apps/trading/components/risk-notice-dialog/index.tsx
deleted file mode 100644
index aefcffdc8..000000000
--- a/apps/trading/components/risk-notice-dialog/index.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export * from './risk-notice-dialog';
diff --git a/apps/trading/components/select-market/select-market.spec.tsx b/apps/trading/components/select-market/select-market.spec.tsx
index b4756c61c..7a78c8a5b 100644
--- a/apps/trading/components/select-market/select-market.spec.tsx
+++ b/apps/trading/components/select-market/select-market.spec.tsx
@@ -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(
-
-
- ,
- { 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');
- });
});
diff --git a/apps/trading/components/select-market/select-market.tsx b/apps/trading/components/select-market/select-market.tsx
index d273b7862..ad60e7fd0 100644
--- a/apps/trading/components/select-market/select-market.tsx
+++ b/apps/trading/components/select-market/select-market.tsx
@@ -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 (
- <>
-
-
-
-
-
-
- {markets?.map((market, i) => (
-
- ))}
-
-
-
-
-
- {'Or view full market list'}
-
-
- >
- );
-};
+export type Market = MarketWithCandles & MarketWithData;
export const SelectAllMarketsTableBody = ({
markets,
@@ -265,71 +213,3 @@ const TableTitle = ({ children }: { children: ReactNode }) => {
);
};
-
-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 (
-
- );
-};
-
-interface LandingDialogContainerProps {
- onSelect: (id: string) => void;
- onCellClick: OnCellClickHandler;
-}
-
-const LandingDialogContainer = ({
- onSelect,
- onCellClick,
-}: LandingDialogContainerProps) => {
- const { data, loading, error } = useMarketList();
- if (error) {
- return (
-
-
{t('Failed to load markets')}
-
- );
- }
-
- if (loading) {
- return (
-
- );
- }
-
- return (
-
- );
-};
diff --git a/apps/trading/components/welcome-dialog/index.ts b/apps/trading/components/welcome-dialog/index.ts
new file mode 100644
index 000000000..c7ff6c38a
--- /dev/null
+++ b/apps/trading/components/welcome-dialog/index.ts
@@ -0,0 +1 @@
+export * from './welcome-dialog';
diff --git a/apps/trading/components/welcome-dialog/proposed-markets.tsx b/apps/trading/components/welcome-dialog/proposed-markets.tsx
new file mode 100644
index 000000000..16931406a
--- /dev/null
+++ b/apps/trading/components/welcome-dialog/proposed-markets.tsx
@@ -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(
+ () => (
+
+ {newMarkets.length > 0 ? (
+ <>
+
+ {t('Proposed markets')}
+
+
+ {newMarkets.map(({ displayName, id }, i) => (
+
+
{displayName}
+
-
+
+ {t('View or vote')}
+
+
+
+ ))}
+
+
+ {t('View all proposed markets')}
+
+ >
+ ) : (
+
+ {t('Propose a market')}
+
+ )}
+
+ ),
+ [newMarkets, tokenLink]
+ );
+};
diff --git a/apps/trading/components/risk-notice-dialog/risk-notice-dialog.spec.tsx b/apps/trading/components/welcome-dialog/risk-notice-dialog.spec.tsx
similarity index 65%
rename from apps/trading/components/risk-notice-dialog/risk-notice-dialog.spec.tsx
rename to apps/trading/components/welcome-dialog/risk-notice-dialog.spec.tsx
index 280fa89d7..a7976e417 100644
--- a/apps/trading/components/risk-notice-dialog/risk-notice-dialog.spec.tsx
+++ b/apps/trading/components/welcome-dialog/risk-notice-dialog.spec.tsx
@@ -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: [] }}
>
-
-
+
+
+
+ ,
+ { 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(
-
+
);
- expect(screen.queryByText('WARNING')).toBeInTheDocument();
+ expect(screen.queryByText(introText)).toBeInTheDocument();
const button = screen.getByRole('button', {
name: 'I understand, Continue',
});
fireEvent.click(button);
-
- rerender(
-
-
-
- );
-
- expect(screen.queryByText('WARNING')).not.toBeInTheDocument();
+ expect(mockOnClose).toHaveBeenCalled();
});
});
diff --git a/apps/trading/components/risk-notice-dialog/risk-notice-dialog.tsx b/apps/trading/components/welcome-dialog/risk-notice-dialog.tsx
similarity index 70%
rename from apps/trading/components/risk-notice-dialog/risk-notice-dialog.tsx
rename to apps/trading/components/welcome-dialog/risk-notice-dialog.tsx
index de3acecad..1bc65266d 100644
--- a/apps/trading/components/risk-notice-dialog/risk-notice-dialog.tsx
+++ b/apps/trading/components/welcome-dialog/risk-notice-dialog.tsx
@@ -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 });
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [update, VEGA_ENV]);
-
- const handleAcceptRisk = () => {
- update({ riskNoticeDialog: false });
+interface Props {
+ onClose: () => void;
+}
+export const RiskNoticeDialog = ({ onClose }: Props) => {
+ const handleAcceptRisk = useCallback(() => {
+ onClose();
LocalStorage.setItem(RISK_ACCEPTED_KEY, 'true');
- };
+ }, [onClose]);
return (
-
-
+ >
);
};
diff --git a/apps/trading/components/welcome-dialog/welcome-dialog-header.tsx b/apps/trading/components/welcome-dialog/welcome-dialog-header.tsx
new file mode 100644
index 000000000..de626e68e
--- /dev/null
+++ b/apps/trading/components/welcome-dialog/welcome-dialog-header.tsx
@@ -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 {header}
;
+};
diff --git a/apps/trading/components/welcome-dialog/welcome-dialog.tsx b/apps/trading/components/welcome-dialog/welcome-dialog.tsx
new file mode 100644
index 000000000..97be3da40
--- /dev/null
+++ b/apps/trading/components/welcome-dialog/welcome-dialog.tsx
@@ -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(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: ,
+ title: t('WARNING'),
+ size: 'medium',
+ onClose,
+ });
+ break;
+ case pathname === '/' && data?.length === 0:
+ setDialog({
+ content: ,
+ onClose,
+ });
+ break;
+ case pathname === '/' && (data?.length || 0) > 0:
+ setDialog({
+ content: ,
+ onClose,
+ });
+ break;
+ }
+ }, [onClose, data?.length, riskAccepted, pathname, VEGA_ENV, setDialog]);
+ return dialog ? (
+
+ ) : null;
+};
diff --git a/apps/trading/components/welcome-dialog/welcome-landing-dialog.spec.tsx b/apps/trading/components/welcome-dialog/welcome-landing-dialog.spec.tsx
new file mode 100644
index 000000000..cf1c3089d
--- /dev/null
+++ b/apps/trading/components/welcome-dialog/welcome-landing-dialog.spec.tsx
@@ -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 & { data: Partial }
+>;
+
+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(
+
+
+ ,
+ { wrapper: MockedProvider }
+ );
+ fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]);
+ expect(onClose).toHaveBeenCalled();
+ fireEvent.click(screen.getAllByTestId(`market-link-2`)[0]);
+ expect(onClose).toHaveBeenCalled();
+ });
+});
diff --git a/apps/trading/components/welcome-dialog/welcome-landing-dialog.tsx b/apps/trading/components/welcome-dialog/welcome-landing-dialog.tsx
new file mode 100644
index 000000000..cb09d0563
--- /dev/null
+++ b/apps/trading/components/welcome-dialog/welcome-landing-dialog.tsx
@@ -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(
+ (e, kind, value) => {
+ if (value && kind === ColumnKind.Asset) {
+ openAssetDetailsDialog(value, e.target as HTMLElement);
+ }
+ },
+ [openAssetDetailsDialog]
+ );
+ const showProposed = (markets?.length || 0) <= 5;
+ return (
+ <>
+
+
+ {t('Select a market to get started...')}
+
+
+
+
+
+
+ {markets?.map((market, i) => (
+
+ ))}
+
+
+
+
+ onClose()}
+ >
+ {'Or view full market list'}
+
+
+ {showProposed && }
+ >
+ );
+};
+
+interface LandingDialogContainerProps {
+ onClose: () => void;
+}
+
+export const WelcomeLandingDialog = ({
+ onClose,
+}: LandingDialogContainerProps) => {
+ const { data, loading, error } = useMarketList();
+ if (error) {
+ return (
+
+
{t('Failed to load markets')}
+
+ );
+ }
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+};
diff --git a/apps/trading/components/welcome-dialog/welcome-notice-dialog.tsx b/apps/trading/components/welcome-dialog/welcome-notice-dialog.tsx
new file mode 100644
index 000000000..73d343aa8
--- /dev/null
+++ b/apps/trading/components/welcome-dialog/welcome-notice-dialog.tsx
@@ -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 (
+ <>
+
+ {t('Welcome to Console')}
+
+
+ {t(
+ 'Vega %s is now live, but markets need to be voted for before the can be traded on. In the meantime:',
+ [networkName]
+ )}
+
+
+ {isMainnet && (
+ -
+
+ {t('Try out Console')}
+
+ {t(' on Fairground, our Testnet')}
+
+ )}
+ -
+
+ {t('View and vote for proposed markets')}
+
+
+ -
+
+ {t('Propose your own markets')}
+
+
+ -
+
+ {t('Read about the mainnet launch')}
+
+
+
+
+ >
+ );
+};
diff --git a/apps/trading/components/welcome-notice/index.ts b/apps/trading/components/welcome-notice/index.ts
deleted file mode 100644
index c0c03403f..000000000
--- a/apps/trading/components/welcome-notice/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './welcome-notice-dialog';
diff --git a/apps/trading/components/welcome-notice/welcome-notice-dialog.tsx b/apps/trading/components/welcome-notice/welcome-notice-dialog.tsx
deleted file mode 100644
index 60bfd3dd7..000000000
--- a/apps/trading/components/welcome-notice/welcome-notice-dialog.tsx
+++ /dev/null
@@ -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 && (
-
-
- {t('Proposed markets')}
-
-
- {newMarkets.map(({ displayName, id }, i) => (
-
-
{displayName}
-
-
-
- {t('View or vote')}
-
-
-
- ))}
-
-
- {t('View all proposed markets')}
-
-
- ),
- [newMarkets, tokenLink]
- );
-
- return (
-
- );
-};
-
-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]);
-};
diff --git a/apps/trading/pages/dialogs-container.tsx b/apps/trading/pages/dialogs-container.tsx
index ee5e12519..ed6329289 100644
--- a/apps/trading/pages/dialogs-container.tsx
+++ b/apps/trading/pages/dialogs-container.tsx
@@ -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}
/>
-
+
-
>
);
};
diff --git a/apps/trading/setup-tests.ts b/apps/trading/setup-tests.ts
index bcbaae582..17e7e61cb 100644
--- a/apps/trading/setup-tests.ts
+++ b/apps/trading/setup-tests.ts
@@ -1,4 +1,5 @@
import '@testing-library/jest-dom';
+import 'jest-canvas-mock';
import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;
diff --git a/apps/trading/stores/global.ts b/apps/trading/stores/global.ts
index 455eabd87..493f53efd 100644
--- a/apps/trading/stores/global.ts
+++ b/apps/trading/stores/global.ts
@@ -3,10 +3,7 @@ import create from 'zustand';
interface GlobalStore {
networkSwitcherDialog: boolean;
- landingDialog: boolean;
- riskNoticeDialog: boolean;
marketId: string | null;
- welcomeNoticeDialog: boolean;
update: (store: Partial>) => void;
}
@@ -17,10 +14,7 @@ interface PageTitleStore {
export const useGlobalStore = create((set) => ({
networkSwitcherDialog: false,
- landingDialog: false,
- riskNoticeDialog: false,
marketId: LocalStorage.getItem('marketId') || null,
- welcomeNoticeDialog: false,
update: (state) => {
set(state);
if (state.marketId) {
diff --git a/libs/react-helpers/src/lib/i18n.ts b/libs/react-helpers/src/lib/i18n.ts
index f9044ee9c..ae43536d4 100644
--- a/libs/react-helpers/src/lib/i18n.ts
+++ b/libs/react-helpers/src/lib/i18n.ts
@@ -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;
+};