chore(trading): 4349 delayed telemetry opt in (#4642)
This commit is contained in:
parent
105a758e8d
commit
6523490d96
@ -24,8 +24,8 @@ export const Settings = () => {
|
|||||||
>
|
>
|
||||||
<Switch
|
<Switch
|
||||||
name="settings-telemetry-switch"
|
name="settings-telemetry-switch"
|
||||||
onCheckedChange={(isOn) => setIsApproved(isOn)}
|
onCheckedChange={(isOn) => setIsApproved(isOn ? 'true' : 'false')}
|
||||||
checked={isApproved}
|
checked={isApproved === 'true'}
|
||||||
/>
|
/>
|
||||||
</SettingsGroup>
|
</SettingsGroup>
|
||||||
<SettingsGroup label={t('Toast location')}>
|
<SettingsGroup label={t('Toast location')}>
|
||||||
|
@ -23,6 +23,7 @@ import { Links, Routes } from '../../pages/client-router';
|
|||||||
import { useGlobalStore } from '../../stores';
|
import { useGlobalStore } from '../../stores';
|
||||||
import { useSidebar, ViewType } from '../sidebar';
|
import { useSidebar, ViewType } from '../sidebar';
|
||||||
import * as constants from '../constants';
|
import * as constants from '../constants';
|
||||||
|
import { useOnboardingStore } from './welcome-dialog';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
lead?: string;
|
lead?: string;
|
||||||
@ -35,7 +36,7 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
|
|||||||
constants.ONBOARDING_VIEWED_KEY
|
constants.ONBOARDING_VIEWED_KEY
|
||||||
);
|
);
|
||||||
|
|
||||||
const update = useGlobalStore((store) => store.update);
|
const dismiss = useOnboardingStore((store) => store.dismiss);
|
||||||
const marketId = useGlobalStore((store) => store.marketId);
|
const marketId = useGlobalStore((store) => store.marketId);
|
||||||
const link = marketId ? Links[Routes.MARKET](marketId) : Links[Routes.HOME]();
|
const link = marketId ? Links[Routes.MARKET](marketId) : Links[Routes.HOME]();
|
||||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||||
@ -61,7 +62,7 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
|
|||||||
onClickHandle = () => {
|
onClickHandle = () => {
|
||||||
navigate(link);
|
navigate(link);
|
||||||
setView({ type: ViewType.Deposit });
|
setView({ type: ViewType.Deposit });
|
||||||
update({ onBoardingDismissed: true });
|
dismiss();
|
||||||
};
|
};
|
||||||
} else if (step === OnboardingStep.ONBOARDING_ORDER_STEP) {
|
} else if (step === OnboardingStep.ONBOARDING_ORDER_STEP) {
|
||||||
buttonText = t('Dismiss');
|
buttonText = t('Dismiss');
|
||||||
|
@ -2,29 +2,35 @@ import { render, screen } from '@testing-library/react';
|
|||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { TelemetryApproval } from './telemetry-approval';
|
import { TelemetryApproval } from './telemetry-approval';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/logger', () => ({
|
|
||||||
SentryInit: () => undefined,
|
|
||||||
SentryClose: () => undefined,
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
|
||||||
useEnvironment: () => ({ VEGA_ENV: 'test', SENTRY_DSN: 'sentry-dsn' }),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('TelemetryApproval', () => {
|
describe('TelemetryApproval', () => {
|
||||||
it('click on checkbox should be properly handled', async () => {
|
it('click on buttons should be properly handled', async () => {
|
||||||
const helpText = 'My help text';
|
const mockSetTelemetryValue = jest.fn();
|
||||||
render(<TelemetryApproval helpText={helpText} />);
|
render(
|
||||||
expect(screen.getByRole('checkbox')).toHaveAttribute(
|
<TelemetryApproval
|
||||||
'data-state',
|
telemetryValue="false"
|
||||||
'unchecked'
|
setTelemetryValue={mockSetTelemetryValue}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
await userEvent.click(screen.getByRole('checkbox'));
|
expect(
|
||||||
expect(screen.getByRole('checkbox')).toHaveAttribute(
|
screen.getByRole('button', { name: 'No thanks' })
|
||||||
'data-state',
|
).toBeInTheDocument();
|
||||||
'checked'
|
await userEvent.click(screen.getByRole('button', { name: 'No thanks' }));
|
||||||
|
expect(mockSetTelemetryValue).toHaveBeenCalledWith('false');
|
||||||
|
expect(screen.getByText('Share data')).toBeInTheDocument();
|
||||||
|
await userEvent.click(screen.getByText('Share data'));
|
||||||
|
expect(mockSetTelemetryValue).toHaveBeenCalledWith('true');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('confirm button should have proper text', async () => {
|
||||||
|
const mockSetTelemetryValue = jest.fn();
|
||||||
|
render(
|
||||||
|
<TelemetryApproval
|
||||||
|
telemetryValue="true"
|
||||||
|
setTelemetryValue={mockSetTelemetryValue}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
expect(screen.getByText('Share usage data')).toBeInTheDocument();
|
expect(screen.getByText('Continue sharing data')).toBeInTheDocument();
|
||||||
expect(screen.getByText(helpText)).toBeInTheDocument();
|
await userEvent.click(screen.getByText('Continue sharing data'));
|
||||||
|
expect(mockSetTelemetryValue).toHaveBeenCalledWith('true');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,21 +1,66 @@
|
|||||||
import { TradingCheckbox } from '@vegaprotocol/ui-toolkit';
|
import {
|
||||||
|
Intent,
|
||||||
|
TradingButton,
|
||||||
|
VegaIcon,
|
||||||
|
VegaIconNames,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
|
|
||||||
|
|
||||||
export const TelemetryApproval = ({ helpText }: { helpText: string }) => {
|
interface Props {
|
||||||
const [isApproved, setIsApproved] = useTelemetryApproval();
|
telemetryValue: string;
|
||||||
|
setTelemetryValue: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TelemetryApproval = ({
|
||||||
|
telemetryValue,
|
||||||
|
setTelemetryValue,
|
||||||
|
}: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col py-3">
|
<div className="flex flex-col py-3">
|
||||||
<div className="mr-4" role="form">
|
<div className="mr-4" role="form">
|
||||||
<TradingCheckbox
|
<div className="mt-2">
|
||||||
label={<span className="text-lg pl-1">{t('Share usage data')}</span>}
|
{t(
|
||||||
checked={isApproved}
|
'Help us identify bugs and improve Vega Governance by sharing anonymous usage data.'
|
||||||
name="telemetry-approval"
|
)}
|
||||||
onCheckedChange={() => setIsApproved(!isApproved)}
|
</div>
|
||||||
/>
|
<div className="flex items-center mt-2">
|
||||||
</div>
|
<VegaIcon name={VegaIconNames.EYE_OFF} size={24} />
|
||||||
<div className="text-sm text-vega-light-300 dark:text-vega-dark-300 ml-6">
|
<div className="flex flex-col gap-1 ml-6">
|
||||||
<span>{helpText}</span>
|
<div className="font-semibold">{t('Anonymous')}</div>
|
||||||
|
<div>{t('Your identity is always anonymous on Vega')}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center mt-2">
|
||||||
|
<VegaIcon name={VegaIconNames.COG} size={24} />
|
||||||
|
<div className="flex flex-col gap-1 ml-6">
|
||||||
|
<div className="font-semibold">{t('Optional')}</div>
|
||||||
|
<div>{t('You can opt out any time via settings')}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col justify-around items-center mt-10 gap-4 w-full px-4">
|
||||||
|
<TradingButton
|
||||||
|
onClick={() => setTelemetryValue('false')}
|
||||||
|
size="small"
|
||||||
|
intent={Intent.None}
|
||||||
|
data-testid="do-not-share-data-button"
|
||||||
|
fill
|
||||||
|
>
|
||||||
|
{t('No thanks')}
|
||||||
|
</TradingButton>
|
||||||
|
|
||||||
|
<TradingButton
|
||||||
|
onClick={() => setTelemetryValue('true')}
|
||||||
|
intent={Intent.Info}
|
||||||
|
data-testid="share-data-button"
|
||||||
|
size="small"
|
||||||
|
fill
|
||||||
|
>
|
||||||
|
{telemetryValue === 'true'
|
||||||
|
? t('Continue sharing data')
|
||||||
|
: t('Share data')}
|
||||||
|
</TradingButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -5,17 +5,17 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { Links, Routes } from '../../pages/client-router';
|
import { Links, Routes } from '../../pages/client-router';
|
||||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { useGlobalStore } from '../../stores';
|
import { useOnboardingStore } from './welcome-dialog';
|
||||||
|
|
||||||
export const WelcomeDialogContent = () => {
|
export const WelcomeDialogContent = () => {
|
||||||
const { VEGA_ENV } = useEnvironment();
|
const { VEGA_ENV } = useEnvironment();
|
||||||
|
|
||||||
const update = useGlobalStore((store) => store.update);
|
const dismiss = useOnboardingStore((store) => store.dismiss);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const browseMarkets = () => {
|
const browseMarkets = () => {
|
||||||
const link = Links[Routes.MARKETS]();
|
const link = Links[Routes.MARKETS]();
|
||||||
navigate(link);
|
navigate(link);
|
||||||
update({ onBoardingDismissed: true });
|
dismiss();
|
||||||
};
|
};
|
||||||
const lead =
|
const lead =
|
||||||
VEGA_ENV === Networks.MAINNET
|
VEGA_ENV === Networks.MAINNET
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
import type { Toast } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { Dialog, Intent, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
import { WelcomeDialogContent } from './welcome-dialog-content';
|
import { WelcomeDialogContent } from './welcome-dialog-content';
|
||||||
@ -12,15 +14,41 @@ import {
|
|||||||
OnboardingStep,
|
OnboardingStep,
|
||||||
} from './use-get-onboarding-step';
|
} from './use-get-onboarding-step';
|
||||||
import * as constants from '../constants';
|
import * as constants from '../constants';
|
||||||
|
import { TelemetryApproval } from './telemetry-approval';
|
||||||
|
import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
const ONBOARDING_STORAGE_KEY = 'vega_onboarding_dismiss_store';
|
||||||
|
export const useOnboardingStore = create<{
|
||||||
|
dismissed: boolean;
|
||||||
|
dismiss: () => void;
|
||||||
|
}>()(
|
||||||
|
persist(
|
||||||
|
(set) => ({
|
||||||
|
dismissed: false,
|
||||||
|
dismiss: () => set(() => ({ dismissed: true })),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: ONBOARDING_STORAGE_KEY,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const TELEMETRY_APPROVAL_TOAST_ID = 'telemetry_tost_id';
|
||||||
export const WelcomeDialog = () => {
|
export const WelcomeDialog = () => {
|
||||||
const { VEGA_ENV } = useEnvironment();
|
const { VEGA_ENV } = useEnvironment();
|
||||||
const [onBoardingViewed] = useLocalStorage(constants.ONBOARDING_VIEWED_KEY);
|
|
||||||
const update = useGlobalStore((store) => store.update);
|
|
||||||
const dismissed = useGlobalStore((store) => store.onBoardingDismissed);
|
|
||||||
const currentStep = useGetOnboardingStep();
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [telemetryValue, setTelemetryValue, isTelemetryNeeded, closeTelemetry] =
|
||||||
|
useTelemetryApproval();
|
||||||
|
const [onBoardingViewed] = useLocalStorage(constants.ONBOARDING_VIEWED_KEY);
|
||||||
|
const dismiss = useOnboardingStore((store) => store.dismiss);
|
||||||
|
const dismissed = useOnboardingStore((store) => store.dismissed);
|
||||||
|
const currentStep = useGetOnboardingStep();
|
||||||
|
const isTelemetryPopupNeeded =
|
||||||
|
isTelemetryNeeded &&
|
||||||
|
(onBoardingViewed === 'true' ||
|
||||||
|
currentStep > OnboardingStep.ONBOARDING_ORDER_STEP);
|
||||||
|
|
||||||
const isOnboardingDialogNeeded =
|
const isOnboardingDialogNeeded =
|
||||||
onBoardingViewed !== 'true' &&
|
onBoardingViewed !== 'true' &&
|
||||||
currentStep &&
|
currentStep &&
|
||||||
@ -29,12 +57,58 @@ export const WelcomeDialog = () => {
|
|||||||
const marketId = useGlobalStore((store) => store.marketId);
|
const marketId = useGlobalStore((store) => store.marketId);
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
const link = marketId
|
if (isTelemetryPopupNeeded) {
|
||||||
? Links[Routes.MARKET](marketId)
|
closeTelemetry();
|
||||||
: Links[Routes.HOME]();
|
} else {
|
||||||
navigate(link);
|
const link = marketId
|
||||||
update({ onBoardingDismissed: true });
|
? Links[Routes.MARKET](marketId)
|
||||||
|
: Links[Routes.HOME]();
|
||||||
|
navigate(link);
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [setToast, hasToast, removeToast] = useToasts((store) => [
|
||||||
|
store.setToast,
|
||||||
|
store.hasToast,
|
||||||
|
store.remove,
|
||||||
|
]);
|
||||||
|
const onApprovalClose = useCallback(() => {
|
||||||
|
closeTelemetry();
|
||||||
|
removeToast(TELEMETRY_APPROVAL_TOAST_ID);
|
||||||
|
}, [removeToast, closeTelemetry]);
|
||||||
|
|
||||||
|
const setTelemetryApprovalAndClose = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
setTelemetryValue(value);
|
||||||
|
onApprovalClose();
|
||||||
|
},
|
||||||
|
[setTelemetryValue, onApprovalClose]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isTelemetryPopupNeeded) {
|
||||||
|
const toast: Toast = {
|
||||||
|
id: TELEMETRY_APPROVAL_TOAST_ID,
|
||||||
|
intent: Intent.Primary,
|
||||||
|
content: (
|
||||||
|
<>
|
||||||
|
<h3 className="text-sm uppercase mb-1">
|
||||||
|
{t('Improve vega console')}
|
||||||
|
</h3>
|
||||||
|
<TelemetryApproval
|
||||||
|
telemetryValue={telemetryValue}
|
||||||
|
setTelemetryValue={setTelemetryApprovalAndClose}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
onClose: onApprovalClose,
|
||||||
|
};
|
||||||
|
if (!hasToast(TELEMETRY_APPROVAL_TOAST_ID)) {
|
||||||
|
setToast(toast);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const title = (
|
const title = (
|
||||||
<span className="font-alpha calt" data-testid="welcome-title">
|
<span className="font-alpha calt" data-testid="welcome-title">
|
||||||
{t('Console')}{' '}
|
{t('Console')}{' '}
|
||||||
|
@ -1,19 +1,40 @@
|
|||||||
import { renderHook, act, waitFor } from '@testing-library/react';
|
import { renderHook, act, waitFor } from '@testing-library/react';
|
||||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
import { SentryInit, SentryClose } from '@vegaprotocol/logger';
|
import { SentryInit, SentryClose } from '@vegaprotocol/logger';
|
||||||
import { STORAGE_KEY, useTelemetryApproval } from './use-telemetry-approval';
|
import {
|
||||||
|
STORAGE_KEY,
|
||||||
|
STORAGE_SECOND_KEY,
|
||||||
|
useTelemetryApproval,
|
||||||
|
} from './use-telemetry-approval';
|
||||||
|
import { Networks } from '@vegaprotocol/environment';
|
||||||
|
|
||||||
const mockSetValue = jest.fn();
|
const mockSetValue = jest.fn();
|
||||||
const mockRemoveValue = jest.fn();
|
let mockStorageHookApprovalResult: [string | null, jest.Mock] = [
|
||||||
|
null,
|
||||||
|
mockSetValue,
|
||||||
|
];
|
||||||
|
const mockSetSecondValue = jest.fn();
|
||||||
|
let mockStorageHookViewedResult: [string | null, jest.Mock] = [
|
||||||
|
null,
|
||||||
|
mockSetSecondValue,
|
||||||
|
];
|
||||||
jest.mock('@vegaprotocol/logger');
|
jest.mock('@vegaprotocol/logger');
|
||||||
jest.mock('@vegaprotocol/react-helpers', () => ({
|
jest.mock('@vegaprotocol/react-helpers', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/react-helpers'),
|
...jest.requireActual('@vegaprotocol/react-helpers'),
|
||||||
useLocalStorage: jest
|
useLocalStorage: jest.fn((key: string) => {
|
||||||
.fn()
|
if (key === 'vega_telemetry_approval') {
|
||||||
.mockImplementation(() => [false, mockSetValue, mockRemoveValue]),
|
return mockStorageHookApprovalResult;
|
||||||
|
}
|
||||||
|
return mockStorageHookViewedResult;
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
let mockVegaEnv = 'test';
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
useEnvironment: () => ({ VEGA_ENV: 'test', SENTRY_DSN: 'sentry-dsn' }),
|
...jest.requireActual('@vegaprotocol/environment'),
|
||||||
|
useEnvironment: jest.fn(() => ({
|
||||||
|
VEGA_ENV: mockVegaEnv,
|
||||||
|
SENTRY_DSN: 'sentry-dsn',
|
||||||
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('useTelemetryApproval', () => {
|
describe('useTelemetryApproval', () => {
|
||||||
@ -21,32 +42,71 @@ describe('useTelemetryApproval', () => {
|
|||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hook should return proper array', () => {
|
it('when empty hook should return proper array', () => {
|
||||||
const { result } = renderHook(() => useTelemetryApproval());
|
const { result } = renderHook(() => useTelemetryApproval());
|
||||||
expect(result.current[0]).toEqual(false);
|
expect(result.current[0]).toEqual('');
|
||||||
expect(result.current[1]).toEqual(expect.any(Function));
|
expect(result.current[1]).toEqual(expect.any(Function));
|
||||||
|
expect(result.current[2]).toEqual(true);
|
||||||
|
expect(result.current[3]).toEqual(expect.any(Function));
|
||||||
expect(useLocalStorage).toHaveBeenCalledWith(STORAGE_KEY);
|
expect(useLocalStorage).toHaveBeenCalledWith(STORAGE_KEY);
|
||||||
|
expect(useLocalStorage).toHaveBeenCalledWith(STORAGE_SECOND_KEY);
|
||||||
|
expect(mockSetValue).toHaveBeenCalledWith('true');
|
||||||
|
expect(mockSetSecondValue).not.toHaveBeenCalledWith('true');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when approval not empty but viewed is empty should return proper array', () => {
|
||||||
|
mockStorageHookApprovalResult = ['false', mockSetValue];
|
||||||
|
const { result } = renderHook(() => useTelemetryApproval());
|
||||||
|
expect(result.current[0]).toEqual('false');
|
||||||
|
expect(result.current[1]).toEqual(expect.any(Function));
|
||||||
|
expect(result.current[2]).toEqual(true);
|
||||||
|
expect(result.current[3]).toEqual(expect.any(Function));
|
||||||
|
expect(useLocalStorage).toHaveBeenCalledWith(STORAGE_KEY);
|
||||||
|
expect(useLocalStorage).toHaveBeenCalledWith(STORAGE_SECOND_KEY);
|
||||||
|
expect(mockSetValue).not.toHaveBeenCalled();
|
||||||
|
expect(mockSetSecondValue).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when NOT empty hook should return proper array', () => {
|
||||||
|
mockStorageHookApprovalResult = ['false', mockSetValue];
|
||||||
|
mockStorageHookViewedResult = ['true', mockSetSecondValue];
|
||||||
|
const { result } = renderHook(() => useTelemetryApproval());
|
||||||
|
expect(result.current[0]).toEqual('false');
|
||||||
|
expect(result.current[1]).toEqual(expect.any(Function));
|
||||||
|
expect(result.current[2]).toEqual(false);
|
||||||
|
expect(result.current[3]).toEqual(expect.any(Function));
|
||||||
|
expect(useLocalStorage).toHaveBeenCalledWith(STORAGE_KEY);
|
||||||
|
expect(mockSetValue).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('on mainnet hook should init properly', () => {
|
||||||
|
mockStorageHookApprovalResult = [null, mockSetValue];
|
||||||
|
mockVegaEnv = Networks.MAINNET;
|
||||||
|
renderHook(() => useTelemetryApproval());
|
||||||
|
expect(mockSetValue).toHaveBeenCalledWith('false');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hook should init stuff properly', async () => {
|
it('hook should init stuff properly', async () => {
|
||||||
const { result } = renderHook(() => useTelemetryApproval());
|
const { result } = renderHook(() => useTelemetryApproval());
|
||||||
await act(() => {
|
await act(() => {
|
||||||
result.current[1](true);
|
result.current[1]('true');
|
||||||
});
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(SentryInit).toHaveBeenCalled();
|
expect(SentryInit).toHaveBeenCalled();
|
||||||
expect(mockSetValue).toHaveBeenCalledWith('1');
|
expect(mockSetValue).toHaveBeenCalledWith('true');
|
||||||
|
expect(mockSetSecondValue).toHaveBeenCalledWith('true');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hook should close stuff properly', async () => {
|
it('hook should close stuff properly', async () => {
|
||||||
const { result } = renderHook(() => useTelemetryApproval());
|
const { result } = renderHook(() => useTelemetryApproval());
|
||||||
await act(() => {
|
await act(() => {
|
||||||
result.current[1](false);
|
result.current[1]('false');
|
||||||
});
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(SentryClose).toHaveBeenCalled();
|
expect(SentryClose).toHaveBeenCalled();
|
||||||
expect(mockRemoveValue).toHaveBeenCalledWith();
|
expect(mockSetValue).toHaveBeenCalledWith('false');
|
||||||
|
expect(mockSetSecondValue).toHaveBeenCalledWith('true');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,25 +1,51 @@
|
|||||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
import { useCallback } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { SentryInit, SentryClose } from '@vegaprotocol/logger';
|
import { SentryInit, SentryClose } from '@vegaprotocol/logger';
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||||
|
|
||||||
export const STORAGE_KEY = 'vega_telemetry_approval';
|
export const STORAGE_KEY = 'vega_telemetry_approval';
|
||||||
|
export const STORAGE_SECOND_KEY = 'vega_telemetry_viewed';
|
||||||
|
|
||||||
export const useTelemetryApproval = (): [
|
export const useTelemetryApproval = (): [
|
||||||
value: boolean,
|
value: string,
|
||||||
setValue: (value: boolean) => void
|
setValue: (value: string) => void,
|
||||||
|
shouldOpen: boolean,
|
||||||
|
close: () => void
|
||||||
] => {
|
] => {
|
||||||
const { VEGA_ENV, SENTRY_DSN } = useEnvironment();
|
const { VEGA_ENV, SENTRY_DSN } = useEnvironment();
|
||||||
const [value, setValue, removeValue] = useLocalStorage(STORAGE_KEY);
|
const defaultTelemetryValue =
|
||||||
const setApprove = useCallback(
|
VEGA_ENV === Networks.MAINNET ? 'false' : 'true';
|
||||||
(value: boolean) => {
|
const [value, setValue] = useLocalStorage(STORAGE_KEY);
|
||||||
if (value && SENTRY_DSN) {
|
const [viewedValue, setViewedValue] = useLocalStorage(STORAGE_SECOND_KEY);
|
||||||
|
const [shouldOpen, setShouldOpen] = useState(!value || !viewedValue);
|
||||||
|
const close = useCallback(() => {
|
||||||
|
setShouldOpen(false);
|
||||||
|
setViewedValue('true');
|
||||||
|
}, [setViewedValue]);
|
||||||
|
const manageValue = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
if (value === 'true' && SENTRY_DSN) {
|
||||||
SentryInit(SENTRY_DSN, VEGA_ENV);
|
SentryInit(SENTRY_DSN, VEGA_ENV);
|
||||||
return setValue('1');
|
return setValue('true');
|
||||||
}
|
}
|
||||||
SentryClose();
|
SentryClose();
|
||||||
removeValue();
|
setValue('false');
|
||||||
},
|
},
|
||||||
[setValue, removeValue, SENTRY_DSN, VEGA_ENV]
|
[setValue, SENTRY_DSN, VEGA_ENV]
|
||||||
);
|
);
|
||||||
return [Boolean(value), setApprove];
|
const setTelemetryValue = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
setShouldOpen(false);
|
||||||
|
setViewedValue('true');
|
||||||
|
manageValue(value);
|
||||||
|
},
|
||||||
|
[manageValue, setViewedValue]
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!value) {
|
||||||
|
manageValue(defaultTelemetryValue);
|
||||||
|
}
|
||||||
|
}, [value, manageValue, defaultTelemetryValue]);
|
||||||
|
|
||||||
|
return [value || '', setTelemetryValue, shouldOpen, close];
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,6 @@ import produce from 'immer';
|
|||||||
|
|
||||||
interface GlobalStore {
|
interface GlobalStore {
|
||||||
marketId: string | null;
|
marketId: string | null;
|
||||||
onBoardingDismissed: boolean;
|
|
||||||
eagerConnecting: boolean;
|
eagerConnecting: boolean;
|
||||||
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
||||||
}
|
}
|
||||||
@ -16,7 +15,6 @@ interface PageTitleStore {
|
|||||||
|
|
||||||
export const useGlobalStore = create<GlobalStore>()((set) => ({
|
export const useGlobalStore = create<GlobalStore>()((set) => ({
|
||||||
marketId: LocalStorage.getItem('marketId') || null,
|
marketId: LocalStorage.getItem('marketId') || null,
|
||||||
onBoardingDismissed: false,
|
|
||||||
eagerConnecting: false,
|
eagerConnecting: false,
|
||||||
update: (newState) => {
|
update: (newState) => {
|
||||||
set(
|
set(
|
||||||
|
@ -65,6 +65,8 @@ export function addSetVegaWallet() {
|
|||||||
Cypress.Commands.add('setVegaWallet', () => {
|
Cypress.Commands.add('setVegaWallet', () => {
|
||||||
cy.window().then((win) => {
|
cy.window().then((win) => {
|
||||||
win.localStorage.setItem('vega_onboarding_viewed', 'true');
|
win.localStorage.setItem('vega_onboarding_viewed', 'true');
|
||||||
|
win.localStorage.setItem('vega_telemetry_approval', 'false');
|
||||||
|
win.localStorage.setItem('vega_telemetry_viewed', 'true');
|
||||||
win.localStorage.setItem(
|
win.localStorage.setItem(
|
||||||
'vega_wallet_config',
|
'vega_wallet_config',
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@ -81,6 +83,8 @@ export function addSetOnBoardingViewed() {
|
|||||||
Cypress.Commands.add('setOnBoardingViewed', () => {
|
Cypress.Commands.add('setOnBoardingViewed', () => {
|
||||||
cy.window().then((win) => {
|
cy.window().then((win) => {
|
||||||
win.localStorage.setItem('vega_onboarding_viewed', 'true');
|
win.localStorage.setItem('vega_onboarding_viewed', 'true');
|
||||||
|
win.localStorage.setItem('vega_telemetry_approval', 'false');
|
||||||
|
win.localStorage.setItem('vega_telemetry_viewed', 'true');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
export const IconEyeOff = ({ size = 16 }: { size: number }) => {
|
||||||
|
return (
|
||||||
|
<svg width={size} height={size} viewBox="0 0 16 16">
|
||||||
|
<path d="M16 7.97v-.02-.01-.02-.02a.672.672 0 00-.17-.36c-.49-.63-1.07-1.2-1.65-1.72l-3.16 2.26a2.978 2.978 0 01-2.98 2.9c-.31 0-.6-.06-.88-.15L5.09 12.3c.44.19.9.36 1.37.47.97.23 1.94.24 2.92.05.88-.17 1.74-.54 2.53-.98 1.25-.7 2.39-1.67 3.38-2.75.18-.2.37-.41.53-.62.09-.1.15-.22.17-.36v-.02-.02-.01-.02-.03c.01-.02.01-.03.01-.04zm-.43-4.17c.25-.18.43-.46.43-.8 0-.55-.45-1-1-1-.22 0-.41.08-.57.2l-.01-.01-2.67 1.91c-.69-.38-1.41-.69-2.17-.87a6.8 6.8 0 00-2.91-.05c-.88.18-1.74.54-2.53.99-1.25.7-2.39 1.67-3.38 2.75-.18.2-.37.41-.53.62-.23.29-.23.63-.01.92.51.66 1.11 1.25 1.73 1.79.18.16.38.29.56.44l-2.09 1.5.01.01c-.25.18-.43.46-.43.8 0 .55.45 1 1 1 .22 0 .41-.08.57-.2l.01.01 14-10-.01-.01zm-10.41 5a3.03 3.03 0 01-.11-.8 2.99 2.99 0 012.99-2.98c.62 0 1.19.21 1.66.53L5.16 8.8z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
@ -15,6 +15,7 @@ import { IconDeposit } from './svg-icons/icon-deposit';
|
|||||||
import { IconEdit } from './svg-icons/icon-edit';
|
import { IconEdit } from './svg-icons/icon-edit';
|
||||||
import { IconExclaimationMark } from './svg-icons/icon-exclaimation-mark';
|
import { IconExclaimationMark } from './svg-icons/icon-exclaimation-mark';
|
||||||
import { IconEye } from './svg-icons/icon-eye';
|
import { IconEye } from './svg-icons/icon-eye';
|
||||||
|
import { IconEyeOff } from './svg-icons/icon-eye-off';
|
||||||
import { IconForum } from './svg-icons/icon-forum';
|
import { IconForum } from './svg-icons/icon-forum';
|
||||||
import { IconGlobe } from './svg-icons/icon-globe';
|
import { IconGlobe } from './svg-icons/icon-globe';
|
||||||
import { IconInfo } from './svg-icons/icon-info';
|
import { IconInfo } from './svg-icons/icon-info';
|
||||||
@ -55,6 +56,7 @@ export enum VegaIconNames {
|
|||||||
EDIT = 'edit',
|
EDIT = 'edit',
|
||||||
EXCLAIMATION_MARK = 'exclaimation-mark',
|
EXCLAIMATION_MARK = 'exclaimation-mark',
|
||||||
EYE = 'eye',
|
EYE = 'eye',
|
||||||
|
EYE_OFF = 'eye-off',
|
||||||
FORUM = 'forum',
|
FORUM = 'forum',
|
||||||
GLOBE = 'globe',
|
GLOBE = 'globe',
|
||||||
INFO = 'info',
|
INFO = 'info',
|
||||||
@ -90,6 +92,7 @@ export const VegaIconNameMap: Record<
|
|||||||
'chevron-down': IconChevronDown,
|
'chevron-down': IconChevronDown,
|
||||||
'chevron-left': IconChevronLeft,
|
'chevron-left': IconChevronLeft,
|
||||||
'chevron-up': IconChevronUp,
|
'chevron-up': IconChevronUp,
|
||||||
|
'eye-off': IconEyeOff,
|
||||||
'exclaimation-mark': IconExclaimationMark,
|
'exclaimation-mark': IconExclaimationMark,
|
||||||
'open-external': IconOpenExternal,
|
'open-external': IconOpenExternal,
|
||||||
'question-mark': IconQuestionMark,
|
'question-mark': IconQuestionMark,
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
- **Must** There is a link to try out trading on Fairground when I'm on Mainnet (<a name="0007-FUGS-008" href="#0007-FUGS-008">0007-FUGS-008</a>)
|
- **Must** There is a link to try out trading on Fairground when I'm on Mainnet (<a name="0007-FUGS-008" href="#0007-FUGS-008">0007-FUGS-008</a>)
|
||||||
- **Must** There is a link to trade with real funds on Mainnet when I am on Fairground (<a name="0007-FUGS-010" href="#0007-FUGS-010">0007-FUGS-010</a>)
|
- **Must** There is a link to trade with real funds on Mainnet when I am on Fairground (<a name="0007-FUGS-010" href="#0007-FUGS-010">0007-FUGS-010</a>)
|
||||||
- **Must** When I am on the Fairground version, I can see a warning / call out that this is Fairground meaning I can try out with virtual assets at no risk (<a name="0007-FUGS-011" href="#0007-FUGS-011">0007-FUGS-011</a>)
|
- **Must** When I am on the Fairground version, I can see a warning / call out that this is Fairground meaning I can try out with virtual assets at no risk (<a name="0007-FUGS-011" href="#0007-FUGS-011">0007-FUGS-011</a>)
|
||||||
- If I dismiss the popup, I **must** not see it unless I NOT accomplish full "onboarding"
|
- If I dismiss the popup, I **must** not see it anymore (<a name="0007-FUGS-018" href="#0007-FUGS-018">0007-FUGS-018</a>)
|
||||||
- If I dismiss the popup, I land on the default market (<a name="0007-FUGS-012" href="#0007-FUGS-012">0007-FUGS-012</a>)
|
- If I dismiss the popup, I land on the default market (<a name="0007-FUGS-012" href="#0007-FUGS-012">0007-FUGS-012</a>)
|
||||||
|
|
||||||
## When the popup has been dismissed:
|
## When the popup has been dismissed:
|
||||||
@ -33,3 +33,7 @@
|
|||||||
- **Must** We've replaced "connect wallet" in the top right with "get started" (<a name="0007-FUGS-015" href="#0007-FUGS-015">0007-FUGS-015</a>)
|
- **Must** We've replaced "connect wallet" in the top right with "get started" (<a name="0007-FUGS-015" href="#0007-FUGS-015">0007-FUGS-015</a>)
|
||||||
- **Must** When I press the get started CTA, I see the wallet connect popup (<a name="0007-FUGS-016" href="#0007-FUGS-016">0007-FUGS-016</a>)
|
- **Must** When I press the get started CTA, I see the wallet connect popup (<a name="0007-FUGS-016" href="#0007-FUGS-016">0007-FUGS-016</a>)
|
||||||
- **Must** If I have a wallet installed already I don't see this quick start onboarding, and instead call(s) to action in Console revert to connect wallet, not "get started" (button in nav header) (<a name="0007-FUGS-017" href="#0007-FUGS-017">0007-FUGS-017</a>)
|
- **Must** If I have a wallet installed already I don't see this quick start onboarding, and instead call(s) to action in Console revert to connect wallet, not "get started" (button in nav header) (<a name="0007-FUGS-017" href="#0007-FUGS-017">0007-FUGS-017</a>)
|
||||||
|
|
||||||
|
## When onboarding process has been accomplished:
|
||||||
|
|
||||||
|
- I can see telemetry approval toast: on environment other than mainnet telemetry is enabled by default (<a name="0007-FUGS-019" href="#0007-FUGS-019">0007-FUGS-019</a>)
|
||||||
|
Loading…
Reference in New Issue
Block a user