market page: break down components to smaller chunks for better performance (#1726)

* chore: break down components to smaller chunks for better performance

* chore: break down components to smaller chunks for better performance

* chore: break down components to smaller chunks for better performance - fix failing tests

* chore: break down components to smaller chunks for better performance - adjust token app cases

* chore: break down components to smaller chunks for better performance - small fixes

* chore: break down components to smaller chunks for better performance - small fixes

* chore: break down components to smaller chunks for better performance - small fixes

* chore: break down components to smaller chunks for better performance - small fixes

* chore: break down components to smaller chunks for better performance - add nwe store for pageTitle

* chore: break down components to smaller chunks for better performance - sm fix

* chore: break down components to smaller chunks for better performance - sm fix

* chore: break down components to smaller chunks for better performance - sm imprv

* chore: break down components to smaller chunks for better performance - change prop names

* chore: break down components to smaller chunks for better performance - fix some test

* chore: break down components to smaller chunks for better performance - change cypress url

* chore: break down components to smaller chunks for better perf - set back redundant changes

* chore: resolve conflicts

Co-authored-by: maciek <maciek@vegaprotocol.io>
This commit is contained in:
macqbat 2022-10-14 17:42:53 +02:00 committed by GitHub
parent ecb19f226b
commit 37a6217169
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 255 additions and 178 deletions

View File

@ -40,11 +40,7 @@ function App() {
<div className="max-h-full min-h-full dark:bg-lite-black dark:text-neutral-200 bg-white text-neutral-800 grid grid-rows-[min-content,1fr]"> <div className="max-h-full min-h-full dark:bg-lite-black dark:text-neutral-200 bg-white text-neutral-800 grid grid-rows-[min-content,1fr]">
<Header /> <Header />
<Main /> <Main />
<VegaConnectDialog <VegaConnectDialog connectors={Connectors} />
connectors={Connectors}
dialogOpen={vegaWalletDialog.connect}
setDialogOpen={vegaWalletDialog.setConnect}
/>
<VegaManageDialog <VegaManageDialog
dialogOpen={vegaWalletDialog.manage} dialogOpen={vegaWalletDialog.manage}
setDialogOpen={vegaWalletDialog.setManage} setDialogOpen={vegaWalletDialog.setManage}

View File

@ -1,12 +1,16 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit'; import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import Logo from './logo'; import Logo from './logo';
import { VegaWalletConnectButton } from '../vega-wallet-connect-button'; import { VegaWalletConnectButton } from '../vega-wallet-connect-button';
import LocalContext from '../../context/local-context'; import LocalContext from '../../context/local-context';
const Header = () => { const Header = () => {
const { updateVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
updateVegaWalletDialog: store.updateVegaWalletDialog,
}));
const { const {
vegaWalletDialog: { setConnect, setManage }, vegaWalletDialog: { setManage },
theme, theme,
toggleTheme, toggleTheme,
} = useContext(LocalContext); } = useContext(LocalContext);
@ -18,7 +22,7 @@ const Header = () => {
<Logo /> <Logo />
<div className="flex items-center gap-2 ml-auto relative z-10"> <div className="flex items-center gap-2 ml-auto relative z-10">
<VegaWalletConnectButton <VegaWalletConnectButton
setConnectDialog={setConnect} setConnectDialog={updateVegaWalletDialog}
setManageDialog={setManage} setManageDialog={setManage}
/> />
<ThemeSwitcher theme={theme} onToggle={toggleTheme} className="-my-4" /> <ThemeSwitcher theme={theme} onToggle={toggleTheme} className="-my-4" />

View File

@ -1,13 +1,12 @@
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import * as React from 'react'; import * as React from 'react';
import { useContext } from 'react';
import LocalContext from '../../context/local-context';
const ConnectWallet = () => { const ConnectWallet = () => {
const { const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
vegaWalletDialog: { setConnect }, openVegaWalletDialog: store.openVegaWalletDialog,
} = useContext(LocalContext); }));
return ( return (
<section <section
className="p-8 bg-white-normal dark:bg-offBlack" className="p-8 bg-white-normal dark:bg-offBlack"
@ -17,7 +16,7 @@ const ConnectWallet = () => {
{t('Please connect your Vega wallet to make a trade')} {t('Please connect your Vega wallet to make a trade')}
</h3> </h3>
<div className="mb-4"> <div className="mb-4">
<Button variant="primary" onClick={() => setConnect(true)} size="lg"> <Button variant="primary" onClick={openVegaWalletDialog} size="lg">
{t('Connect Vega wallet')} {t('Connect Vega wallet')}
</Button> </Button>
</div> </div>

View File

@ -1,9 +1,7 @@
import { createContext } from 'react'; import { createContext } from 'react';
export interface VegaWalletDialogState { export interface VegaWalletDialogState {
connect: boolean;
manage: boolean; manage: boolean;
setConnect: (isOpen: boolean) => void;
setManage: (isOpen: boolean) => void; setManage: (isOpen: boolean) => void;
} }

View File

@ -7,12 +7,9 @@ describe('local values hook', () => {
const { result } = renderHook(() => useLocalValues('light', setTheme)); const { result } = renderHook(() => useLocalValues('light', setTheme));
expect(result.current.vegaWalletDialog).toBeDefined(); expect(result.current.vegaWalletDialog).toBeDefined();
expect(result.current.vegaWalletDialog.manage).toBe(false); expect(result.current.vegaWalletDialog.manage).toBe(false);
expect(result.current.vegaWalletDialog.connect).toBe(false);
act(() => { act(() => {
result.current.vegaWalletDialog.setConnect(true);
result.current.vegaWalletDialog.setManage(true); result.current.vegaWalletDialog.setManage(true);
}); });
expect(result.current.vegaWalletDialog.manage).toBe(true); expect(result.current.vegaWalletDialog.manage).toBe(true);
expect(result.current.vegaWalletDialog.connect).toBe(true);
}); });
}); });

View File

@ -2,17 +2,16 @@ import { useMemo, useState } from 'react';
import type { LocalValues } from '../context/local-context'; import type { LocalValues } from '../context/local-context';
const useLocalValues = (theme: 'light' | 'dark', toggleTheme: () => void) => { const useLocalValues = (theme: 'light' | 'dark', toggleTheme: () => void) => {
const [connect, setConnect] = useState<boolean>(false);
const [manage, setManage] = useState<boolean>(false); const [manage, setManage] = useState<boolean>(false);
const [menuOpen, setMenuOpen] = useState(false); const [menuOpen, setMenuOpen] = useState(false);
return useMemo<LocalValues>( return useMemo<LocalValues>(
() => ({ () => ({
vegaWalletDialog: { connect, manage, setConnect, setManage }, vegaWalletDialog: { manage, setManage },
menu: { menuOpen, setMenuOpen, onToggle: () => setMenuOpen(!menuOpen) }, menu: { menuOpen, setMenuOpen, onToggle: () => setMenuOpen(!menuOpen) },
theme, theme,
toggleTheme, toggleTheme,
}), }),
[connect, manage, theme, toggleTheme, menuOpen] [manage, theme, toggleTheme, menuOpen]
); );
}; };

View File

@ -1,7 +1,7 @@
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { import {
AppStateActionType, AppStateActionType,
useAppState, useAppState,
@ -10,14 +10,18 @@ import {
export const ConnectToVega = () => { export const ConnectToVega = () => {
const { appDispatch } = useAppState(); const { appDispatch } = useAppState();
const { t } = useTranslation(); const { t } = useTranslation();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
return ( return (
<Button <Button
onClick={() => onClick={() => {
appDispatch({ appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY, type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true, isOpen: true,
}) });
} openVegaWalletDialog();
}}
data-testid="connect-to-vega-wallet-btn" data-testid="connect-to-vega-wallet-btn"
> >
{t('connectVegaWallet')} {t('connectVegaWallet')}

View File

@ -1,5 +1,5 @@
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -16,18 +16,22 @@ export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
const { appDispatch } = useAppState(); const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
if (!pubKey) { if (!pubKey) {
return ( return (
<p> <p>
<Button <Button
data-testid="connect-to-vega-wallet-btn" data-testid="connect-to-vega-wallet-btn"
onClick={() => onClick={() => {
appDispatch({ appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY, type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true, isOpen: true,
}) });
} openVegaWalletDialog();
}}
> >
{t('connectVegaWallet')} {t('connectVegaWallet')}
</Button> </Button>

View File

@ -11,8 +11,7 @@ export const VegaWalletDialogs = () => {
<> <>
<VegaConnectDialog <VegaConnectDialog
connectors={Connectors} connectors={Connectors}
dialogOpen={appState.vegaWalletOverlay} onChangeOpen={(open) =>
setDialogOpen={(open) =>
appDispatch({ appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY, type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: open, isOpen: open,

View File

@ -22,7 +22,7 @@ import {
} from '../wallet-card'; } from '../wallet-card';
import { DownloadWalletPrompt } from './download-wallet-prompt'; import { DownloadWalletPrompt } from './download-wallet-prompt';
import { usePollForDelegations } from './hooks'; import { usePollForDelegations } from './hooks';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit'; import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
export const VegaWallet = () => { export const VegaWallet = () => {
@ -69,16 +69,19 @@ export const VegaWallet = () => {
const VegaWalletNotConnected = () => { const VegaWalletNotConnected = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { appDispatch } = useAppState(); const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
return ( return (
<> <>
<Button <Button
onClick={() => onClick={() => {
appDispatch({ appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY, type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true, isOpen: true,
}) });
} openVegaWalletDialog();
}}
fill={true} fill={true}
data-testid="connect-vega" data-testid="connect-vega"
> >

View File

@ -14,7 +14,7 @@ import type {
VoteButtonsQueryVariables, VoteButtonsQueryVariables,
} from './__generated__/VoteButtonsQuery'; } from './__generated__/VoteButtonsQuery';
import { VoteState } from './use-user-vote'; import { VoteState } from './use-user-vote';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { import {
ProposalState, ProposalState,
ProposalUserAction, ProposalUserAction,
@ -84,6 +84,9 @@ export const VoteButtons = ({
const { t } = useTranslation(); const { t } = useTranslation();
const { appDispatch } = useAppState(); const { appDispatch } = useAppState();
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
const [changeVote, setChangeVote] = React.useState(false); const [changeVote, setChangeVote] = React.useState(false);
const cantVoteUI = React.useMemo(() => { const cantVoteUI = React.useMemo(() => {
@ -95,12 +98,13 @@ export const VoteButtons = ({
return ( return (
<div data-testid="connect-wallet"> <div data-testid="connect-wallet">
<ButtonLink <ButtonLink
onClick={() => onClick={() => {
appDispatch({ appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY, type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true, isOpen: true,
}) });
} openVegaWalletDialog();
}}
> >
{t('connectVegaWallet')} {t('connectVegaWallet')}
</ButtonLink>{' '} </ButtonLink>{' '}
@ -144,6 +148,7 @@ export const VoteButtons = ({
appDispatch, appDispatch,
minVoterBalance, minVoterBalance,
spamProtectionMinTokens, spamProtectionMinTokens,
openVegaWalletDialog,
]); ]);
function submitVote(vote: VoteValue) { function submitVote(vote: VoteValue) {

View File

@ -16,7 +16,7 @@ import {
} from '../../../contexts/app-state/app-state-context'; } from '../../../contexts/app-state/app-state-context';
import type { Rewards } from './__generated__/Rewards'; import type { Rewards } from './__generated__/Rewards';
import { RewardInfo } from './reward-info'; import { RewardInfo } from './reward-info';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { useNetworkParams, NetworkParams } from '@vegaprotocol/react-helpers'; import { useNetworkParams, NetworkParams } from '@vegaprotocol/react-helpers';
export const REWARDS_QUERY = gql` export const REWARDS_QUERY = gql`
@ -67,6 +67,9 @@ export const REWARDS_QUERY = gql`
export const RewardsIndex = () => { export const RewardsIndex = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { pubKey, pubKeys } = useVegaWallet(); const { pubKey, pubKeys } = useVegaWallet();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
const { appDispatch } = useAppState(); const { appDispatch } = useAppState();
const { data, loading, error } = useQuery<Rewards>(REWARDS_QUERY, { const { data, loading, error } = useQuery<Rewards>(REWARDS_QUERY, {
variables: { partyId: pubKey }, variables: { partyId: pubKey },
@ -147,12 +150,13 @@ export const RewardsIndex = () => {
<div> <div>
<Button <Button
data-testid="connect-to-vega-wallet-btn" data-testid="connect-to-vega-wallet-btn"
onClick={() => onClick={() => {
appDispatch({ appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY, type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true, isOpen: true,
}) });
} openVegaWalletDialog();
}}
> >
{t('connectVegaWallet')} {t('connectVegaWallet')}
</Button> </Button>

View File

@ -1,2 +1,2 @@
NX_VEGA_WALLET_URL=http://localhost:1789 NX_VEGA_WALLET_URL=http://localhost:1789
CYPRESS_VEGA_URL=https://api.n06.testnet.vega.xyz/graphql CYPRESS_VEGA_URL=https://api.n06.testnet.vega.xyz/graphql

View File

@ -15,9 +15,8 @@ interface NavbarProps {
} }
export const Navbar = ({ theme, toggleTheme }: NavbarProps) => { export const Navbar = ({ theme, toggleTheme }: NavbarProps) => {
const { marketId, update } = useGlobalStore((store) => ({ const { marketId } = useGlobalStore((store) => ({
marketId: store.marketId, marketId: store.marketId,
update: store.update,
})); }));
const [tradingPath, setTradingPath] = useState('/markets'); const [tradingPath, setTradingPath] = useState('/markets');
useEffect(() => { useEffect(() => {
@ -42,9 +41,7 @@ export const Navbar = ({ theme, toggleTheme }: NavbarProps) => {
</nav> </nav>
<div className="flex items-center gap-2 ml-auto"> <div className="flex items-center gap-2 ml-auto">
<ThemeSwitcher theme={theme} onToggle={toggleTheme} /> <ThemeSwitcher theme={theme} onToggle={toggleTheme} />
<VegaWalletConnectButton <VegaWalletConnectButton />
setConnectDialog={(open) => update({ connectDialog: open })}
/>
</div> </div>
</div> </div>
); );

View File

@ -1,49 +1,49 @@
import { fireEvent, render, screen } from '@testing-library/react'; import { fireEvent, render, screen } from '@testing-library/react';
import { VegaWalletContext } from '@vegaprotocol/wallet'; import { VegaWalletContext } from '@vegaprotocol/wallet';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet'; import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import type { VegaWalletConnectButtonProps } from './vega-wallet-connect-button';
import { VegaWalletConnectButton } from './vega-wallet-connect-button'; import { VegaWalletConnectButton } from './vega-wallet-connect-button';
import { truncateByChars } from '@vegaprotocol/react-helpers'; import { truncateByChars } from '@vegaprotocol/react-helpers';
let props: VegaWalletConnectButtonProps; const mockUpdateDialogOpen = jest.fn();
jest.mock('@vegaprotocol/wallet', () => ({
...jest.requireActual('@vegaprotocol/wallet'),
useVegaWalletDialogStore: () => ({
openVegaWalletDialog: mockUpdateDialogOpen,
}),
}));
beforeEach(() => { beforeEach(() => {
props = { jest.clearAllMocks();
setConnectDialog: jest.fn(),
};
}); });
const generateJsx = ( const generateJsx = (context: VegaWalletContextShape) => {
context: VegaWalletContextShape,
props: VegaWalletConnectButtonProps
) => {
return ( return (
<VegaWalletContext.Provider value={context}> <VegaWalletContext.Provider value={context}>
<VegaWalletConnectButton {...props} /> <VegaWalletConnectButton />
</VegaWalletContext.Provider> </VegaWalletContext.Provider>
); );
}; };
it('Not connected', () => { it('Not connected', () => {
render(generateJsx({ pubKey: null } as VegaWalletContextShape, props)); render(generateJsx({ pubKey: null } as VegaWalletContextShape));
const button = screen.getByRole('button'); const button = screen.getByRole('button');
expect(button).toHaveTextContent('Connect Vega wallet'); expect(button).toHaveTextContent('Connect Vega wallet');
fireEvent.click(button); fireEvent.click(button);
expect(props.setConnectDialog).toHaveBeenCalledWith(true); expect(mockUpdateDialogOpen).toHaveBeenCalled();
}); });
it('Connected', () => { it('Connected', () => {
const pubKey = { publicKey: '123456__123456', name: 'test' }; const pubKey = { publicKey: '123456__123456', name: 'test' };
render( render(
generateJsx( generateJsx({
{ pubKey: pubKey.publicKey, pubKeys: [pubKey] } as VegaWalletContextShape, pubKey: pubKey.publicKey,
props pubKeys: [pubKey],
) } as VegaWalletContextShape)
); );
const button = screen.getByRole('button'); const button = screen.getByRole('button');
expect(button).toHaveTextContent(truncateByChars(pubKey.publicKey)); expect(button).toHaveTextContent(truncateByChars(pubKey.publicKey));
fireEvent.click(button); fireEvent.click(button);
expect(props.setConnectDialog).not.toHaveBeenCalled(); expect(mockUpdateDialogOpen).not.toHaveBeenCalled();
}); });

View File

@ -10,18 +10,15 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
Icon, Icon,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import CopyToClipboard from 'react-copy-to-clipboard'; import CopyToClipboard from 'react-copy-to-clipboard';
export interface VegaWalletConnectButtonProps { export const VegaWalletConnectButton = () => {
setConnectDialog: (isOpen: boolean) => void;
}
export const VegaWalletConnectButton = ({
setConnectDialog,
}: VegaWalletConnectButtonProps) => {
const [dropdownOpen, setDropdownOpen] = useState(false); const [dropdownOpen, setDropdownOpen] = useState(false);
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
const { pubKey, pubKeys, selectPubKey, disconnect } = useVegaWallet(); const { pubKey, pubKeys, selectPubKey, disconnect } = useVegaWallet();
const isConnected = pubKey !== null; const isConnected = pubKey !== null;
@ -64,7 +61,7 @@ export const VegaWalletConnectButton = ({
return ( return (
<Button <Button
data-testid="connect-vega-wallet" data-testid="connect-vega-wallet"
onClick={() => setConnectDialog(true)} onClick={openVegaWalletDialog}
size="sm" size="sm"
> >
<span className="whitespace-nowrap">{t('Connect Vega wallet')}</span> <span className="whitespace-nowrap">{t('Connect Vega wallet')}</span>

View File

@ -1,15 +1,16 @@
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { Button, Splash } from '@vegaprotocol/ui-toolkit'; import { Button, Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { useGlobalStore } from '../../stores';
interface VegaWalletContainerProps { interface VegaWalletContainerProps {
children: ReactNode; children: ReactNode;
} }
export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => { export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
const { update } = useGlobalStore((store) => ({ update: store.update })); const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
if (!pubKey) { if (!pubKey) {
@ -20,7 +21,7 @@ export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
{t('Connect your Vega wallet')} {t('Connect your Vega wallet')}
</p> </p>
<Button <Button
onClick={() => update({ connectDialog: true })} onClick={openVegaWalletDialog}
data-testid="vega-wallet-connect" data-testid="vega-wallet-connect"
> >
{t('Connect')} {t('Connect')}

View File

@ -2,35 +2,23 @@ import type { AppProps } from 'next/app';
import Head from 'next/head'; import Head from 'next/head';
import { Navbar } from '../components/navbar'; import { Navbar } from '../components/navbar';
import { t, ThemeContext, useThemeSwitcher } from '@vegaprotocol/react-helpers'; import { t, ThemeContext, useThemeSwitcher } from '@vegaprotocol/react-helpers';
import { VegaConnectDialog, VegaWalletProvider } from '@vegaprotocol/wallet'; import { VegaWalletProvider } from '@vegaprotocol/wallet';
import { import {
EnvironmentProvider, EnvironmentProvider,
envTriggerMapping, envTriggerMapping,
useEnvironment, useEnvironment,
} from '@vegaprotocol/environment'; } from '@vegaprotocol/environment';
import { Connectors } from '../lib/vega-connectors';
import { AppLoader } from '../components/app-loader'; import { AppLoader } from '../components/app-loader';
import { RiskNoticeDialog } from '../components/risk-notice-dialog';
import './styles.css'; import './styles.css';
import { useGlobalStore } from '../stores'; import { usePageTitleStore } from '../stores';
import {
AssetDetailsDialog,
useAssetDetailsDialogStore,
} from '@vegaprotocol/assets';
import { Footer } from '../components/footer'; import { Footer } from '../components/footer';
import { useMemo } from 'react'; import { useMemo } from 'react';
import DialogsContainer from './dialogs-container';
const DEFAULT_TITLE = t('Welcome to Vega trading!'); const DEFAULT_TITLE = t('Welcome to Vega trading!');
function AppBody({ Component, pageProps }: AppProps) { const Title = () => {
const { connectDialog, update } = useGlobalStore((store) => ({ const { pageTitle } = usePageTitleStore((store) => ({
connectDialog: store.connectDialog,
update: store.update,
}));
const { isOpen, symbol, trigger, setOpen } = useAssetDetailsDialogStore();
const [theme, toggleTheme] = useThemeSwitcher();
const { pageTitle } = useGlobalStore((store) => ({
pageTitle: store.pageTitle, pageTitle: store.pageTitle,
})); }));
@ -42,32 +30,26 @@ function AppBody({ Component, pageProps }: AppProps) {
if (networkName) return `${pageTitle} [${networkName}]`; if (networkName) return `${pageTitle} [${networkName}]`;
return pageTitle; return pageTitle;
}, [pageTitle, networkName]); }, [pageTitle, networkName]);
return (
<Head>
<title>{title}</title>
</Head>
);
};
function AppBody({ Component, pageProps }: AppProps) {
const [theme, toggleTheme] = useThemeSwitcher();
return ( return (
<ThemeContext.Provider value={theme}> <ThemeContext.Provider value={theme}>
<Head> <Title />
<title>{title}</title>
</Head>
<div className="h-full relative dark:bg-black dark:text-white z-0 grid grid-rows-[min-content,1fr,min-content]"> <div className="h-full relative dark:bg-black dark:text-white z-0 grid grid-rows-[min-content,1fr,min-content]">
<AppLoader> <AppLoader>
<Navbar theme={theme} toggleTheme={toggleTheme} /> <Navbar theme={theme} toggleTheme={toggleTheme} />
<main data-testid={pageProps.page}> <main data-testid={pageProps.page}>
{/* @ts-ignore conflict between @types/react and nextjs internal types */}
<Component {...pageProps} /> <Component {...pageProps} />
</main> </main>
<Footer /> <Footer />
<VegaConnectDialog <DialogsContainer />
connectors={Connectors}
dialogOpen={connectDialog}
setDialogOpen={(open) => update({ connectDialog: open })}
/>
<AssetDetailsDialog
assetSymbol={symbol}
trigger={trigger || null}
open={isOpen}
onChange={setOpen}
/>
<RiskNoticeDialog />
</AppLoader> </AppLoader>
</div> </div>
</ThemeContext.Provider> </ThemeContext.Provider>

View File

@ -0,0 +1,25 @@
import {
AssetDetailsDialog,
useAssetDetailsDialogStore,
} from '@vegaprotocol/assets';
import { VegaConnectDialog } from '@vegaprotocol/wallet';
import { Connectors } from '../lib/vega-connectors';
import { RiskNoticeDialog } from '../components/risk-notice-dialog';
const DialogsContainer = () => {
const { isOpen, symbol, trigger, setOpen } = useAssetDetailsDialogStore();
return (
<>
<VegaConnectDialog connectors={Connectors} />
<AssetDetailsDialog
assetSymbol={symbol}
trigger={trigger || null}
open={isOpen}
onChange={setOpen}
/>
<RiskNoticeDialog />
</>
);
};
export default DialogsContainer;

View File

@ -7,7 +7,7 @@ import {
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useGlobalStore } from '../stores'; import { useGlobalStore, usePageTitleStore } from '../stores';
export function Index() { export function Index() {
const { replace } = useRouter(); const { replace } = useRouter();
@ -21,6 +21,11 @@ export function Index() {
update: store.update, update: store.update,
})); }));
const { pageTitle, updateTitle } = usePageTitleStore((store) => ({
pageTitle: store.pageTitle,
updateTitle: store.updateTitle,
}));
useEffect(() => { useEffect(() => {
update({ landingDialog: true }); update({ landingDialog: true });
@ -33,18 +38,21 @@ export function Index() {
data[0]?.decimalPlaces data[0]?.decimalPlaces
) )
: null; : null;
const pageTitle = titlefy([marketName, marketPrice]); const newPageTitle = titlefy([marketName, marketPrice]);
if (marketId) { if (marketId) {
replace(`/markets/${marketId}`); replace(`/markets/${marketId}`);
update({ marketId, pageTitle }); update({ marketId });
if (pageTitle !== newPageTitle) {
updateTitle(newPageTitle);
}
} }
// Fallback to the markets list page // Fallback to the markets list page
else { else {
replace('/markets'); replace('/markets');
} }
} }
}, [data, replace, riskNoticeDialog, update]); }, [data, replace, riskNoticeDialog, update, pageTitle, updateTitle]);
return ( return (
<AsyncRenderer data={data} loading={loading} error={error}> <AsyncRenderer data={data} loading={loading} error={error}>

View File

@ -16,7 +16,7 @@ import type {
MarketDataUpdateFieldsFragment, MarketDataUpdateFieldsFragment,
} from '@vegaprotocol/market-list'; } from '@vegaprotocol/market-list';
import { marketProvider, marketDataProvider } from '@vegaprotocol/market-list'; import { marketProvider, marketDataProvider } from '@vegaprotocol/market-list';
import { useGlobalStore } from '../../stores'; import { useGlobalStore, usePageTitleStore } from '../../stores';
import { TradeGrid, TradePanels } from './trade-grid'; import { TradeGrid, TradePanels } from './trade-grid';
import { ColumnKind, SelectMarketDialog } from '../../components/select-market'; import { ColumnKind, SelectMarketDialog } from '../../components/select-market';
@ -40,18 +40,17 @@ const MarketPage = ({
}) => { }) => {
const { query, push } = useRouter(); const { query, push } = useRouter();
const { w } = useWindowSize(); const { w } = useWindowSize();
const { const { landingDialog, riskNoticeDialog, update } = useGlobalStore(
landingDialog, (store) => ({
riskNoticeDialog, landingDialog: store.landingDialog,
update, riskNoticeDialog: store.riskNoticeDialog,
updateTitle, update: store.update,
updateMarketId, })
} = useGlobalStore((store) => ({ );
landingDialog: store.landingDialog,
riskNoticeDialog: store.riskNoticeDialog, const { pageTitle, updateTitle } = usePageTitleStore((store) => ({
update: store.update, pageTitle: store.pageTitle,
updateTitle: store.updateTitle, updateTitle: store.updateTitle,
updateMarketId: store.updateMarketId,
})); }));
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
@ -63,11 +62,11 @@ const MarketPage = ({
const onSelect = useCallback( const onSelect = useCallback(
(id: string) => { (id: string) => {
if (id && id !== marketId) { if (id && id !== marketId) {
updateMarketId(id); update({ marketId: id });
push(`/markets/${id}`); push(`/markets/${id}`);
} }
}, },
[marketId, updateMarketId, push] [marketId, update, push]
); );
const variables = useMemo( const variables = useMemo(
@ -86,20 +85,22 @@ const MarketPage = ({
skip: !marketId, skip: !marketId,
}); });
const marketName = data?.tradableInstrument.instrument.name;
const updateProvider = useCallback( const updateProvider = useCallback(
({ data: marketData }: { data: MarketData }) => { ({ data: marketData }: { data: MarketData }) => {
const marketName = data?.tradableInstrument.instrument.name;
const marketPrice = calculatePrice( const marketPrice = calculatePrice(
marketData.markPrice, marketData.markPrice,
data?.decimalPlaces data?.decimalPlaces
); );
if (marketName) { if (marketName) {
const pageTitle = titlefy([marketName, marketPrice]); const newPageTitle = titlefy([marketName, marketPrice]);
updateTitle(pageTitle); if (pageTitle !== newPageTitle) {
updateTitle(newPageTitle);
}
} }
return true; return true;
}, },
[updateTitle, data?.tradableInstrument.instrument.name, data?.decimalPlaces] [updateTitle, pageTitle, marketName, data?.decimalPlaces]
); );
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({ useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
@ -110,6 +111,16 @@ const MarketPage = ({
updateOnInit: true, updateOnInit: true,
}); });
const tradeView = useMemo(() => {
if (!data) {
return null;
}
if (w > 960) {
return <TradeGrid market={data} onSelect={onSelect} />;
}
return <TradePanels market={data} onSelect={onSelect} />;
}, [w, data, onSelect]);
if (!marketId) { if (!marketId) {
return ( return (
<Splash> <Splash>
@ -129,11 +140,7 @@ const MarketPage = ({
} }
return ( return (
<> <>
{w > 960 ? ( {tradeView}
<TradeGrid market={data} onSelect={onSelect} />
) : (
<TradePanels market={data} onSelect={onSelect} />
)}
<SelectMarketDialog <SelectMarketDialog
dialogOpen={landingDialog && !riskNoticeDialog} dialogOpen={landingDialog && !riskNoticeDialog}
setDialogOpen={(isOpen: boolean) => setDialogOpen={(isOpen: boolean) =>

View File

@ -1,16 +1,19 @@
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { MarketsContainer } from '@vegaprotocol/market-list'; import { MarketsContainer } from '@vegaprotocol/market-list';
import { useGlobalStore } from '../../stores'; import { useGlobalStore, usePageTitleStore } from '../../stores';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { titlefy } from '@vegaprotocol/react-helpers'; import { titlefy } from '@vegaprotocol/react-helpers';
const Markets = () => { const Markets = () => {
const { update } = useGlobalStore((store) => ({ update: store.update })); const { update } = useGlobalStore((store) => ({ update: store.update }));
const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle,
}));
useEffect(() => { useEffect(() => {
update({ pageTitle: titlefy(['Markets']) }); updateTitle(titlefy(['Markets']));
}, [update]); }, [updateTitle]);
const router = useRouter(); const router = useRouter();
return ( return (
<MarketsContainer <MarketsContainer
onSelect={(marketId) => { onSelect={(marketId) => {

View File

@ -1,16 +1,17 @@
import { t, titlefy } from '@vegaprotocol/react-helpers'; import { t, titlefy } from '@vegaprotocol/react-helpers';
import { Web3Container } from '@vegaprotocol/web3'; import { Web3Container } from '@vegaprotocol/web3';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useGlobalStore } from '../../../stores'; import { usePageTitleStore } from '../../../stores';
import { DepositContainer } from './deposit-container'; import { DepositContainer } from './deposit-container';
const Deposit = () => { const Deposit = () => {
const { update } = useGlobalStore((store) => ({ const { updateTitle } = usePageTitleStore((store) => ({
update: store.update, updateTitle: store.updateTitle,
})); }));
useEffect(() => { useEffect(() => {
update({ pageTitle: titlefy([t('Deposits')]) }); updateTitle(titlefy([t('Deposits')]));
}, [update]); }, [updateTitle]);
return ( return (
<Web3Container> <Web3Container>

View File

@ -10,16 +10,16 @@ import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { DepositsContainer } from './deposits-container'; import { DepositsContainer } from './deposits-container';
import { ResizableGrid } from '@vegaprotocol/ui-toolkit'; import { ResizableGrid } from '@vegaprotocol/ui-toolkit';
import { LayoutPriority } from 'allotment'; import { LayoutPriority } from 'allotment';
import { useGlobalStore } from '../../stores'; import { usePageTitleStore } from '../../stores';
import { AccountsContainer } from './accounts-container'; import { AccountsContainer } from './accounts-container';
const Portfolio = () => { const Portfolio = () => {
const { update } = useGlobalStore((store) => ({ const { updateTitle } = usePageTitleStore((store) => ({
update: store.update, updateTitle: store.updateTitle,
})); }));
useEffect(() => { useEffect(() => {
update({ pageTitle: titlefy([t('Portfolio')]) }); updateTitle(titlefy([t('Portfolio')]));
}, [update]); }, [updateTitle]);
const wrapperClasses = 'h-full max-h-full flex flex-col'; const wrapperClasses = 'h-full max-h-full flex flex-col';
const tabContentClassName = 'h-full grid grid-rows-[min-content_1fr]'; const tabContentClassName = 'h-full grid grid-rows-[min-content_1fr]';
return ( return (

View File

@ -2,33 +2,32 @@ import { LocalStorage } from '@vegaprotocol/react-helpers';
import create from 'zustand'; import create from 'zustand';
interface GlobalStore { interface GlobalStore {
connectDialog: boolean;
networkSwitcherDialog: boolean; networkSwitcherDialog: boolean;
landingDialog: boolean; landingDialog: boolean;
riskNoticeDialog: boolean; riskNoticeDialog: boolean;
marketId: string | null; marketId: string | null;
pageTitle: string | null;
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void; update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
}
interface PageTitleStore {
pageTitle: string | null;
updateTitle: (title: string) => void; updateTitle: (title: string) => void;
updateMarketId: (marketId: string) => void;
} }
export const useGlobalStore = create<GlobalStore>((set) => ({ export const useGlobalStore = create<GlobalStore>((set) => ({
connectDialog: false,
networkSwitcherDialog: false, networkSwitcherDialog: false,
landingDialog: false, landingDialog: false,
riskNoticeDialog: false, riskNoticeDialog: false,
marketId: LocalStorage.getItem('marketId') || null, marketId: LocalStorage.getItem('marketId') || null,
pageTitle: null,
update: (state) => { update: (state) => {
set(state); set(state);
if (state.marketId) { if (state.marketId) {
LocalStorage.setItem('marketId', state.marketId); LocalStorage.setItem('marketId', state.marketId);
} }
}, },
updateTitle: (title: string) => set({ pageTitle: title }), }));
updateMarketId: (marketId: string) => {
set({ marketId }); export const usePageTitleStore = create<PageTitleStore>((set) => ({
LocalStorage.setItem('marketId', marketId); pageTitle: null,
}, updateTitle: (title: string) => set({ pageTitle: title }),
})); }));

View File

@ -20,6 +20,14 @@ import { EnvironmentProvider } from '@vegaprotocol/environment';
import type { ChainIdQuery } from '@vegaprotocol/react-helpers'; import type { ChainIdQuery } from '@vegaprotocol/react-helpers';
import { ChainIdDocument } from '@vegaprotocol/react-helpers'; import { ChainIdDocument } from '@vegaprotocol/react-helpers';
const mockUpdateDialogOpen = jest.fn();
const mockCloseVegaDialog = jest.fn();
jest.mock('zustand', () => () => () => ({
updateVegaWalletDialog: mockUpdateDialogOpen,
closeVegaWalletDialog: mockCloseVegaDialog,
vegaWalletDialogOpen: true,
}));
let defaultProps: VegaConnectDialogProps; let defaultProps: VegaConnectDialogProps;
const rest = new RestConnector(); const rest = new RestConnector();
@ -29,10 +37,9 @@ const connectors = {
jsonRpc, jsonRpc,
}; };
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks();
defaultProps = { defaultProps = {
connectors, connectors,
dialogOpen: true,
setDialogOpen: jest.fn(),
}; };
}); });
@ -122,7 +129,7 @@ describe('VegaConnectDialog', () => {
expect(spy).toHaveBeenCalledWith(fields); expect(spy).toHaveBeenCalledWith(fields);
expect(defaultProps.setDialogOpen).toHaveBeenCalledWith(false); expect(mockCloseVegaDialog).toHaveBeenCalled();
}); });
it('handles failed connection', async () => { it('handles failed connection', async () => {
@ -149,7 +156,7 @@ describe('VegaConnectDialog', () => {
expect(spy).toHaveBeenCalledWith(fields); expect(spy).toHaveBeenCalledWith(fields);
expect(screen.getByTestId('form-error')).toHaveTextContent(errMessage); expect(screen.getByTestId('form-error')).toHaveTextContent(errMessage);
expect(defaultProps.setDialogOpen).not.toHaveBeenCalled(); expect(mockUpdateDialogOpen).not.toHaveBeenCalled();
// Fetch failed due to wallet not running // Fetch failed due to wallet not running
spy = jest spy = jest
@ -249,9 +256,7 @@ describe('VegaConnectDialog', () => {
}); });
it('connects with permission update', async () => { it('connects with permission update', async () => {
const mockSetDialog = jest.fn(); render(generateJSX());
render(generateJSX({ setDialogOpen: mockSetDialog }));
await selectJsonRpc(); await selectJsonRpc();
// Wallet version check // Wallet version check
@ -299,7 +304,7 @@ describe('VegaConnectDialog', () => {
await act(async () => { await act(async () => {
jest.advanceTimersByTime(CLOSE_DELAY); jest.advanceTimersByTime(CLOSE_DELAY);
}); });
expect(mockSetDialog).toHaveBeenCalledWith(false); expect(mockCloseVegaDialog).toHaveBeenCalledWith();
}); });
it('handles incompatible wallet', async () => { it('handles incompatible wallet', async () => {

View File

@ -1,3 +1,4 @@
import create from 'zustand';
import { import {
Button, Button,
Dialog, Dialog,
@ -28,15 +29,50 @@ type WalletType = 'gui' | 'cli' | 'hosted';
export interface VegaConnectDialogProps { export interface VegaConnectDialogProps {
connectors: Connectors; connectors: Connectors;
dialogOpen: boolean; onChangeOpen?: (open: boolean) => void;
setDialogOpen: (isOpen: boolean) => void; }
export const useVegaWalletDialogStore = create<VegaWalletDialogStore>(
(set) => ({
vegaWalletDialogOpen: false,
updateVegaWalletDialog: (open: boolean) =>
set({ vegaWalletDialogOpen: open }),
openVegaWalletDialog: () => set({ vegaWalletDialogOpen: true }),
closeVegaWalletDialog: () => set({ vegaWalletDialogOpen: false }),
})
);
interface VegaWalletDialogStore {
vegaWalletDialogOpen: boolean;
updateVegaWalletDialog: (open: boolean) => void;
openVegaWalletDialog: () => void;
closeVegaWalletDialog: () => void;
} }
export const VegaConnectDialog = ({ export const VegaConnectDialog = ({
connectors, connectors,
dialogOpen, onChangeOpen,
setDialogOpen,
}: VegaConnectDialogProps) => { }: VegaConnectDialogProps) => {
const {
vegaWalletDialogOpen,
closeVegaWalletDialog,
updateVegaWalletDialog,
} = useVegaWalletDialogStore((store) => ({
vegaWalletDialogOpen: store.vegaWalletDialogOpen,
updateVegaWalletDialog: onChangeOpen
? (open: boolean) => {
store.updateVegaWalletDialog(open);
onChangeOpen(open);
}
: store.updateVegaWalletDialog,
closeVegaWalletDialog: onChangeOpen
? () => {
store.closeVegaWalletDialog();
onChangeOpen(false);
}
: store.closeVegaWalletDialog,
}));
const { data, error, loading } = useChainIdQuery(); const { data, error, loading } = useChainIdQuery();
const renderContent = () => { const renderContent = () => {
@ -66,14 +102,18 @@ export const VegaConnectDialog = ({
return ( return (
<ConnectDialogContainer <ConnectDialogContainer
connectors={connectors} connectors={connectors}
closeDialog={() => setDialogOpen(false)} closeDialog={closeVegaWalletDialog}
appChainId={data.statistics.chainId} appChainId={data.statistics.chainId}
/> />
); );
}; };
return ( return (
<Dialog open={dialogOpen} size="small" onChange={setDialogOpen}> <Dialog
open={vegaWalletDialogOpen}
size="small"
onChange={updateVegaWalletDialog}
>
{renderContent()} {renderContent()}
</Dialog> </Dialog>
); );