feat(trading): add generic welcome message for non mainnet networks (#3584)

This commit is contained in:
Matthew Russell 2023-05-02 14:01:33 -07:00 committed by GitHub
parent a4f41eabb6
commit 5a73b7e769
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 126 additions and 48 deletions

View File

@ -36,7 +36,11 @@ export const Settings = () => {
/> />
</div> </div>
<Divider /> <Divider />
<TelemetryApproval isSettingsPage /> <TelemetryApproval
helpText={t(
'Help identify bugs and improve the service by sharing anonymous usage data.'
)}
/>
</RoundedWrapper> </RoundedWrapper>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
export const THROTTLE_UPDATE_TIME = 500; export const THROTTLE_UPDATE_TIME = 500;
export const RISK_ACCEPTED_KEY = 'vega-risk-accepted'; export const RISK_ACCEPTED_KEY = 'vega_risk_accepted';
export const MAINNET_WELCOME_HEADER = t( export const MAINNET_WELCOME_HEADER = t(
'Trade cash settled futures on the fully decentralised Vega network.' 'Trade cash settled futures on the fully decentralised Vega network.'
); );

View File

@ -1,9 +1,6 @@
import { BrowserRouter } from 'react-router-dom';
import { render, screen, fireEvent } from '@testing-library/react'; import { render, screen, fireEvent } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import { Networks, useEnvironment } from '@vegaprotocol/environment'; import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { RiskNoticeDialog } from './risk-notice-dialog'; import { RiskNoticeDialog } from './risk-notice-dialog';
import { WelcomeDialog } from './welcome-dialog';
jest.mock('@vegaprotocol/environment'); jest.mock('@vegaprotocol/environment');
@ -14,7 +11,6 @@ const mockEnvDefinitions = {
}; };
describe('Risk notice dialog', () => { describe('Risk notice dialog', () => {
const introText = 'Regulation may apply to use of this app';
const mockOnClose = jest.fn(); const mockOnClose = jest.fn();
afterEach(() => { afterEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
@ -22,13 +18,12 @@ describe('Risk notice dialog', () => {
it.each` it.each`
assertion | network assertion | network
${'displays'} | ${Networks.MAINNET}
${'displays'} | ${Networks.CUSTOM} ${'displays'} | ${Networks.CUSTOM}
${'displays'} | ${Networks.DEVNET} ${'displays'} | ${Networks.DEVNET}
${'displays'} | ${Networks.STAGNET3} ${'displays'} | ${Networks.STAGNET3}
${'displays'} | ${Networks.TESTNET} ${'displays'} | ${Networks.TESTNET}
`( `(
'$assertion the risk notice on $network', '$assertion a generic message on $network',
async ({ assertion, network }) => { async ({ assertion, network }) => {
// @ts-ignore ignore mock implementation // @ts-ignore ignore mock implementation
useEnvironment.mockImplementation(() => ({ useEnvironment.mockImplementation(() => ({
@ -36,27 +31,37 @@ describe('Risk notice dialog', () => {
VEGA_ENV: network, VEGA_ENV: network,
})); }));
render( render(<RiskNoticeDialog onClose={mockOnClose} network={network} />);
<MockedProvider>
<WelcomeDialog />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
expect(screen.queryByText(introText)).toBeInTheDocument(); expect(
screen.getByText(
new RegExp(
`This application for trading on Vega is connected to ${network}`
)
)
).toBeInTheDocument();
const button = screen.getByRole('button', {
name: 'Continue',
});
fireEvent.click(button);
expect(mockOnClose).toHaveBeenCalled();
} }
); );
it("doesn't display the risk notice when previously acknowledged", () => { it('displays a risk message for mainnet', () => {
const introText = 'Regulation may apply to use of this app';
const network = Networks.MAINNET;
// @ts-ignore ignore mock implementation // @ts-ignore ignore mock implementation
useEnvironment.mockImplementation(() => ({ useEnvironment.mockImplementation(() => ({
...mockEnvDefinitions, ...mockEnvDefinitions,
VEGA_ENV: Networks.MAINNET, VEGA_ENV: network,
})); }));
render(<RiskNoticeDialog onClose={mockOnClose} />); render(<RiskNoticeDialog onClose={mockOnClose} network={network} />);
expect(screen.queryByText(introText)).toBeInTheDocument(); expect(screen.getByText(introText)).toBeInTheDocument();
const button = screen.getByRole('button', { const button = screen.getByRole('button', {
name: 'I understand, Continue', name: 'I understand, Continue',

View File

@ -1,25 +1,100 @@
import { useCallback } from 'react';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { Button } from '@vegaprotocol/ui-toolkit'; import {
import { LocalStorage } from '@vegaprotocol/utils'; Button,
Link,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { RISK_ACCEPTED_KEY } from '../constants'; import { RISK_ACCEPTED_KEY } from '../constants';
import { TelemetryApproval } from './telemetry-approval'; import { TelemetryApproval } from './telemetry-approval';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { useLocalStorage } from '@vegaprotocol/react-helpers';
interface Props { interface Props {
onClose: () => void; onClose: () => void;
network: Networks;
} }
export const RiskNoticeDialog = ({ onClose }: Props) => { export const RiskNoticeDialog = ({ onClose, network }: Props) => {
const handleAcceptRisk = useCallback(() => { const [, setValue] = useLocalStorage(RISK_ACCEPTED_KEY);
onClose();
LocalStorage.setItem(RISK_ACCEPTED_KEY, 'true');
}, [onClose]);
const handleAcceptRisk = () => {
onClose();
setValue('true');
};
return (
<>
{network === Networks.MAINNET ? (
<MainnetContent />
) : (
<TestnetContent network={network} />
)}
<div className="my-4">
<TelemetryApproval
helpText={t(
'Help identify bugs and improve the service by sharing anonymous usage data. You can change this in your settings at any time.'
)}
/>
</div>
<Button onClick={handleAcceptRisk}>
{network === Networks.MAINNET
? t('I understand, Continue')
: t('Continue')}
</Button>
</>
);
};
const TestnetContent = ({ network }: { network: Networks }) => {
const { VEGA_DOCS_URL, GITHUB_FEEDBACK_URL, VEGA_WALLET_URL } =
useEnvironment();
return (
<>
<p className="mb-4">
{t(
'This application for trading on Vega is connected to %s, meaning you are free to try out trading with virtual assets and no risk.',
[network]
)}
</p>
<p className="mb-4">
{t(
'Your Vega wallet must also be connected to %s, and your Ethereum wallet must be connected to Sepolia.',
[network]
)}
</p>
{VEGA_DOCS_URL && GITHUB_FEEDBACK_URL && VEGA_WALLET_URL && (
<ul className="list-disc pl-4">
<li className="mb-1">
<Link href={VEGA_WALLET_URL} target="_blank">
<span className="underline">{t('Learn about Vega wallet')}</span>{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</Link>
</li>
<li className="mb-1">
<Link href={VEGA_DOCS_URL} target="_blank">
<span className="underline">{t('View documentation')}</span>{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</Link>
</li>
<li className="mb-1">
<Link href={GITHUB_FEEDBACK_URL} target="_blank">
<span className="underline">{t('Provide feedback')}</span>{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</Link>
</li>
</ul>
)}
</>
);
};
const MainnetContent = () => {
return ( return (
<> <>
<h4 className="text-xl mb-2 mt-4"> <h4 className="text-xl mb-2 mt-4">
{t('Regulation may apply to use of this app')} {t('Regulation may apply to use of this app')}
</h4> </h4>
<p className="text-base mb-6"> <p className="mb-6">
{t( {t(
'This decentralised application allows you to connect to and use publicly available blockchain services operated by third parties that may include trading, financial products, or other services that may be subject to legal and regulatory restrictions in your jurisdiction. This application is a front end only and does not hold any funds or provide any products or services. It is available to anyone with an internet connection via IPFS and other methods, and the ability to access it does not imply any right to use any services or that it is legal for you to do so. By using this application you accept that it is your responsibility to ensure that your use of the application and any blockchain services accessed through it is compliant with applicable laws and regulations in your jusrisdiction.' 'This decentralised application allows you to connect to and use publicly available blockchain services operated by third parties that may include trading, financial products, or other services that may be subject to legal and regulatory restrictions in your jurisdiction. This application is a front end only and does not hold any funds or provide any products or services. It is available to anyone with an internet connection via IPFS and other methods, and the ability to access it does not imply any right to use any services or that it is legal for you to do so. By using this application you accept that it is your responsibility to ensure that your use of the application and any blockchain services accessed through it is compliant with applicable laws and regulations in your jusrisdiction.'
)} )}
@ -27,15 +102,11 @@ export const RiskNoticeDialog = ({ onClose }: Props) => {
<h4 className="text-xl mb-2"> <h4 className="text-xl mb-2">
{t('Technical and financial risk of loss')} {t('Technical and financial risk of loss')}
</h4> </h4>
<p className="text-base mb-8"> <p className="mb-8">
{t( {t(
'The public blockchain services accessible via this decentralised application are operated by third parties and may carry significant risks including the potential loss of all funds that you deposit or hold with these services. Technical risks include the risk of loss in the event of the failure or compromise of the public blockchain infrastructure or smart contracts that provide any services you use. Financial risks include but are not limited to losses due to volatility, excessive leverage, low liquidity, and your own lack of understanding of the services you use. By using this decentralised application you accept that it is your responsibility to ensure that you understand any services you use and the technical and financial risks inherent in your use. Do not risk what you cannot afford to lose.' 'The public blockchain services accessible via this decentralised application are operated by third parties and may carry significant risks including the potential loss of all funds that you deposit or hold with these services. Technical risks include the risk of loss in the event of the failure or compromise of the public blockchain infrastructure or smart contracts that provide any services you use. Financial risks include but are not limited to losses due to volatility, excessive leverage, low liquidity, and your own lack of understanding of the services you use. By using this decentralised application you accept that it is your responsibility to ensure that you understand any services you use and the technical and financial risks inherent in your use. Do not risk what you cannot afford to lose.'
)} )}
</p> </p>
<Button onClick={handleAcceptRisk}>{t('I understand, Continue')}</Button>
<div className="text-base mt-8">
<TelemetryApproval />
</div>
</> </>
); );
}; };

View File

@ -3,7 +3,8 @@ import { TelemetryApproval } from './telemetry-approval';
describe('TelemetryApproval', () => { describe('TelemetryApproval', () => {
it('click on checkbox should be properly handled', () => { it('click on checkbox should be properly handled', () => {
render(<TelemetryApproval />); const helpText = 'My help text';
render(<TelemetryApproval helpText={helpText} />);
expect(screen.getByRole('checkbox')).toHaveAttribute( expect(screen.getByRole('checkbox')).toHaveAttribute(
'data-state', 'data-state',
'unchecked' 'unchecked'
@ -15,5 +16,7 @@ describe('TelemetryApproval', () => {
'data-state', 'data-state',
'checked' 'checked'
); );
expect(screen.getByText('Share usage data')).toBeInTheDocument();
expect(screen.getByText(helpText)).toBeInTheDocument();
}); });
}); });

View File

@ -2,11 +2,7 @@ import { Checkbox } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval'; import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
export const TelemetryApproval = ({ export const TelemetryApproval = ({ helpText }: { helpText: string }) => {
isSettingsPage,
}: {
isSettingsPage?: boolean;
}) => {
const [isApproved, setIsApproved] = useTelemetryApproval(); const [isApproved, setIsApproved] = useTelemetryApproval();
return ( return (
<div className="flex flex-col px-2 py-3"> <div className="flex flex-col px-2 py-3">
@ -19,13 +15,7 @@ export const TelemetryApproval = ({
/> />
</div> </div>
<div className="text-sm text-vega-light-300 dark:text-vega-dark-300 ml-6"> <div className="text-sm text-vega-light-300 dark:text-vega-dark-300 ml-6">
<span> <span>{helpText}</span>
{t(
'Help identify bugs and improve the service by sharing anonymous usage data.'
)}
{!isSettingsPage &&
' ' + t('You can change this in your settings at any time.')}
</span>
</div> </div>
</div> </div>
); );

View File

@ -9,8 +9,11 @@ import { RiskNoticeDialog } from './risk-notice-dialog';
import { WelcomeNoticeDialog } from './welcome-notice-dialog'; import { WelcomeNoticeDialog } from './welcome-notice-dialog';
import { WelcomeLandingDialog } from './welcome-landing-dialog'; import { WelcomeLandingDialog } from './welcome-landing-dialog';
import { useGlobalStore } from '../../stores'; import { useGlobalStore } from '../../stores';
import { useEnvironment } from '@vegaprotocol/environment';
import { Networks } from '@vegaprotocol/environment';
export const WelcomeDialog = () => { export const WelcomeDialog = () => {
const { VEGA_ENV } = useEnvironment();
const { pathname } = useLocation(); const { pathname } = useLocation();
let dialogContent: React.ReactNode; let dialogContent: React.ReactNode;
let title = ''; let title = '';
@ -33,8 +36,10 @@ export const WelcomeDialog = () => {
}, [update, isRiskDialogNeeded]); }, [update, isRiskDialogNeeded]);
if (isRiskDialogNeeded) { if (isRiskDialogNeeded) {
dialogContent = <RiskNoticeDialog onClose={onCloseDialog} />; dialogContent = (
title = t('WARNING'); <RiskNoticeDialog onClose={onCloseDialog} network={VEGA_ENV} />
);
title = VEGA_ENV === Networks.MAINNET ? t('WARNING') : t('Vega Console');
size = 'medium'; size = 'medium';
} else if (isWelcomeDialogNeeded && data?.length === 0) { } else if (isWelcomeDialogNeeded && data?.length === 0) {
dialogContent = <WelcomeNoticeDialog />; dialogContent = <WelcomeNoticeDialog />;