feat(governance): opt in/out telemetry (#3638)
Co-authored-by: Joe <joe@vega.xyz>
This commit is contained in:
parent
00679c75f2
commit
c87c4bbc91
@ -2,6 +2,7 @@ import {
|
||||
navigateTo,
|
||||
waitForSpinner,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
createRawProposal,
|
||||
@ -50,6 +51,7 @@ describe(
|
||||
|
||||
beforeEach('visit proposals tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
|
@ -2,6 +2,7 @@
|
||||
import {
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
@ -37,6 +38,7 @@ context(
|
||||
|
||||
beforeEach('visit proposals', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
navigateTo,
|
||||
navigation,
|
||||
closeDialog,
|
||||
turnTelemetryOff,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
clickOnValidatorFromList,
|
||||
@ -85,6 +86,7 @@ context(
|
||||
|
||||
beforeEach('visit governance tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
|
@ -2,6 +2,7 @@ import {
|
||||
closeDialog,
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
@ -77,6 +78,7 @@ context(
|
||||
|
||||
beforeEach('visit governance tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
|
@ -2,6 +2,7 @@ import type { testFreeformProposal } from '../../support/common-interfaces';
|
||||
import {
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
@ -36,6 +37,7 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
|
||||
beforeEach('visit proposals tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
@ -26,6 +27,7 @@ const rewardsTimeOut = { timeout: 60000 };
|
||||
context('rewards - flow', { tags: '@slow' }, function () {
|
||||
before('set up environment to allow rewards', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.visit('/');
|
||||
waitForSpinner();
|
||||
depositAsset(vegaAssetAddress, '1000', 18);
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
waitForSpinner,
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
stakingPageAssociateTokens,
|
||||
@ -54,6 +55,7 @@ context(
|
||||
'teardown wallet & drill into a specific validator',
|
||||
function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
@ -55,6 +56,7 @@ context(
|
||||
|
||||
beforeEach('Navigate to withdrawal page', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
navigateTo(navigation.withdraw);
|
||||
|
@ -3,6 +3,7 @@
|
||||
import {
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
@ -26,6 +27,7 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
|
||||
beforeEach('visit home page', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.visit('/');
|
||||
waitForSpinner();
|
||||
cy.connectPublicKey(vegaWalletPubKey);
|
||||
|
@ -13,7 +13,6 @@ context(
|
||||
{ tags: '@regression' },
|
||||
function () {
|
||||
before('navigate to rewards page', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/');
|
||||
navigateTo(navigation.rewards);
|
||||
});
|
||||
|
@ -33,7 +33,6 @@ const stakeNumberRegex = /^\d*\.?\d*$/;
|
||||
|
||||
context('Validators Page - verify elements on page', function () {
|
||||
before('navigate to validators page', function () {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.visit('/validators');
|
||||
});
|
||||
|
||||
|
@ -35,7 +35,6 @@ context(
|
||||
{ tags: '@regression' },
|
||||
() => {
|
||||
before('visit token home page', () => {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.visit('/');
|
||||
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
|
||||
});
|
||||
|
@ -12,7 +12,6 @@ context(
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
before('navigate to withdrawals page', function () {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.visit('/');
|
||||
navigateTo(navigation.withdraw);
|
||||
});
|
||||
|
@ -90,3 +90,10 @@ export function verifyEthWalletAssociatedBalance(amount: string) {
|
||||
export function closeDialog() {
|
||||
cy.getByTestId('dialog-close').click();
|
||||
}
|
||||
|
||||
export function turnTelemetryOff() {
|
||||
// Ensuring the telemetry modal doesn't disrupt the tests
|
||||
cy.window().then((win) =>
|
||||
win.localStorage.setItem('vega_telemetry_on', 'false')
|
||||
);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import './proposal.functions.ts';
|
||||
import registerCypressGrep from '@cypress/grep';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||
import { turnTelemetryOff } from './common.functions.ts';
|
||||
registerCypressGrep();
|
||||
|
||||
// Hide fetch/XHR requests - They create a lot of noise in command log
|
||||
@ -24,6 +25,9 @@ if (!app.document.head.querySelector('[data-hide-command-log-request]')) {
|
||||
}
|
||||
|
||||
before(() => {
|
||||
cy.clearLocalStorage();
|
||||
// // Ensuring the telemetry modal doesn't disrupt the tests
|
||||
turnTelemetryOff();
|
||||
// Mock chainId fetch which happens on every page for wallet connection
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
|
@ -43,6 +43,11 @@ import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
import { WithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||
import { SplashLoader } from './components/splash-loader';
|
||||
import { ToastsManager } from './toasts-manager';
|
||||
import {
|
||||
TelemetryDialog,
|
||||
TELEMETRY_ON,
|
||||
} from './components/telemetry-dialog/telemetry-dialog';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
|
||||
const cache: InMemoryCacheConfig = {
|
||||
typePolicies: {
|
||||
@ -149,6 +154,7 @@ const Web3Container = ({
|
||||
<VegaWalletDialogs />
|
||||
<TransactionModal />
|
||||
<WithdrawalDialog />
|
||||
<TelemetryDialog />
|
||||
</>
|
||||
</BalanceManager>
|
||||
</AppLoader>
|
||||
@ -177,9 +183,10 @@ const AppContainer = () => {
|
||||
const { config, loading, error } = useEthereumConfig();
|
||||
const { VEGA_ENV, GIT_COMMIT_HASH, GIT_BRANCH, ETHEREUM_PROVIDER_URL } =
|
||||
useEnvironment();
|
||||
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
|
||||
|
||||
useEffect(() => {
|
||||
if (ENV.dsn) {
|
||||
if (ENV.dsn && telemetryOn) {
|
||||
Sentry.init({
|
||||
dsn: ENV.dsn,
|
||||
integrations: [new Integrations.BrowserTracing()],
|
||||
@ -202,8 +209,10 @@ const AppContainer = () => {
|
||||
});
|
||||
Sentry.setTag('branch', GIT_BRANCH);
|
||||
Sentry.setTag('commit', GIT_COMMIT_HASH);
|
||||
} else {
|
||||
Sentry.close();
|
||||
}
|
||||
}, [GIT_COMMIT_HASH, GIT_BRANCH, VEGA_ENV]);
|
||||
}, [GIT_COMMIT_HASH, GIT_BRANCH, VEGA_ENV, telemetryOn]);
|
||||
|
||||
return (
|
||||
<Router>
|
||||
|
@ -11,11 +11,26 @@ import {
|
||||
NavigationLink,
|
||||
NavigationList,
|
||||
NavigationTrigger,
|
||||
Icon,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { EthWallet } from '../eth-wallet';
|
||||
import { VegaWallet } from '../vega-wallet';
|
||||
import { useLocation, useMatch } from 'react-router-dom';
|
||||
import { useEffect } from 'react';
|
||||
import { useTelemetryDialog } from '../telemetry-dialog/telemetry-dialog';
|
||||
|
||||
export const SettingsLink = () => {
|
||||
const { open, isOpen, close } = useTelemetryDialog();
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => (isOpen ? close() : open())}
|
||||
aria-label="Open Telemetry Settings"
|
||||
>
|
||||
<Icon name="cog" className="w-5 h-5 mr-2" />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export const Nav = ({ theme }: Pick<NavigationProps, 'theme'>) => {
|
||||
const { t } = useTranslation();
|
||||
@ -42,7 +57,12 @@ export const Nav = ({ theme }: Pick<NavigationProps, 'theme'>) => {
|
||||
));
|
||||
|
||||
return (
|
||||
<Navigation appName="Governance" theme={theme} breakpoints={[458, 959]}>
|
||||
<Navigation
|
||||
appName="Governance"
|
||||
theme={theme}
|
||||
breakpoints={[458, 959]}
|
||||
actions={<SettingsLink />}
|
||||
>
|
||||
<NavigationList
|
||||
className="[.drawer-content_&]:border-b [.drawer-content_&]:border-b-vega-light-200 dark:[.drawer-content_&]:border-b-vega-dark-200 [.drawer-content_&]:pb-8 [.drawer-content_&]:mb-2"
|
||||
hide={[NavigationBreakpoint.Small]}
|
||||
|
@ -0,0 +1,53 @@
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useTelemetryDialog, TELEMETRY_ON } from './telemetry-dialog';
|
||||
|
||||
describe('useTelemetryDialog', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
it('should have the correct initial state based on localStorage', () => {
|
||||
localStorage.setItem(TELEMETRY_ON, 'true');
|
||||
|
||||
const { result } = renderHook(() => useTelemetryDialog());
|
||||
|
||||
expect(result.current.isOpen).toBe(false);
|
||||
expect(result.current.telemetryAccepted).toBe(true);
|
||||
});
|
||||
|
||||
it('should update localStorage and the telemetryAccepted state when setTelemetryAccepted is called', () => {
|
||||
const { result } = renderHook(() => useTelemetryDialog());
|
||||
|
||||
act(() => {
|
||||
result.current.setTelemetryAccepted(true);
|
||||
});
|
||||
|
||||
expect(result.current.telemetryAccepted).toBe(true);
|
||||
expect(localStorage.getItem(TELEMETRY_ON)).toBe('true');
|
||||
});
|
||||
|
||||
it('should update localStorage and the isOpen state when close is called', () => {
|
||||
const { result } = renderHook(() => useTelemetryDialog());
|
||||
|
||||
act(() => {
|
||||
result.current.close();
|
||||
});
|
||||
|
||||
expect(result.current.isOpen).toBe(false);
|
||||
});
|
||||
|
||||
it('should update the isOpen state when open is called', () => {
|
||||
const { result } = renderHook(() => useTelemetryDialog());
|
||||
|
||||
act(() => {
|
||||
result.current.open();
|
||||
});
|
||||
|
||||
expect(result.current.isOpen).toBe(true);
|
||||
});
|
||||
|
||||
it('should open the dialog if TELEMETRY_ON is not set', () => {
|
||||
const { result } = renderHook(() => useTelemetryDialog());
|
||||
expect(result.current.isOpen).toBe(true);
|
||||
});
|
||||
});
|
@ -0,0 +1,102 @@
|
||||
import { create } from 'zustand';
|
||||
import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { Dialog, Icon, Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
type TelemetryDialogState = {
|
||||
isOpen: boolean;
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
const useTelemetryDialogStore = create<TelemetryDialogState>((set) => ({
|
||||
isOpen: false,
|
||||
open: () => set({ isOpen: true }),
|
||||
close: () => set({ isOpen: false }),
|
||||
}));
|
||||
|
||||
export const TELEMETRY_ON = 'vega_telemetry_on';
|
||||
|
||||
export const useTelemetryDialog = () => {
|
||||
const [telemetryOn, setTelemetryOn] = useLocalStorage(TELEMETRY_ON);
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const isMainnet = VEGA_ENV === Networks.MAINNET;
|
||||
|
||||
const defaultTelemetryAccepted = isMainnet ? 'false' : 'true';
|
||||
|
||||
const { isOpen, open, close } = useTelemetryDialogStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (telemetryOn === null || telemetryOn === undefined) {
|
||||
open();
|
||||
setTelemetryOn(defaultTelemetryAccepted);
|
||||
}
|
||||
}, [defaultTelemetryAccepted, open, setTelemetryOn, telemetryOn]);
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
open: open,
|
||||
close: close,
|
||||
telemetryAccepted: telemetryOn === 'true',
|
||||
setTelemetryAccepted: (value: boolean) => {
|
||||
setTelemetryOn(value.toString());
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const TelemetryDialog = () => {
|
||||
const { t } = useTranslation();
|
||||
const { isOpen, open, close, telemetryAccepted, setTelemetryAccepted } =
|
||||
useTelemetryDialog();
|
||||
return (
|
||||
<Dialog
|
||||
title={t('ImproveVegaGovernance')}
|
||||
open={isOpen}
|
||||
onChange={(isOpen) => (isOpen ? open() : close())}
|
||||
size="small"
|
||||
>
|
||||
<div className="mt-6">{t('TelemetryModalIntro')}</div>
|
||||
<div className="flex items-center mt-6">
|
||||
<Icon name="eye-off" className="mr-6" size={6} />
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="font-semibold">{t('Anonymous')}</div>
|
||||
<div>{t('YourIdentityAnonymous')}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center mt-6">
|
||||
<Icon name="cog" className="mr-6" size={6} />
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="font-semibold">{t('Optional')}</div>
|
||||
<div>{t('OptOutOfTelemetry')}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center mt-10 gap-4">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setTelemetryAccepted(false);
|
||||
close();
|
||||
}}
|
||||
variant="default"
|
||||
data-testid="do-not-share-data-button"
|
||||
>
|
||||
{t('NoThanks')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() => {
|
||||
setTelemetryAccepted(true);
|
||||
close();
|
||||
}}
|
||||
variant="primary"
|
||||
data-testid="share-data-button"
|
||||
>
|
||||
{telemetryAccepted ? t('ContinueSharingData') : t('ShareData')}
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
@ -799,5 +799,14 @@
|
||||
"associateVegaNow": "Associate $VEGA now",
|
||||
"disconnectedNotice": "You have been disconnected. Connect your ETH wallet to the {{correctNetwork}} network to use this app.",
|
||||
"connectAVegaWalletToVote": "Connect a Vega wallet with $VEGA tokens to vote on a proposal.",
|
||||
"findOutMoreAboutHowToVote": "Find out more about how to vote on Vega"
|
||||
"findOutMoreAboutHowToVote": "Find out more about how to vote on Vega",
|
||||
"ImproveVegaGovernance": "Improve Vega governance",
|
||||
"TelemetryModalIntro": "Help us identify bugs and improve Vega Governance by sharing anonymous usage data.",
|
||||
"Anonymous": "Anonymous",
|
||||
"YourIdentityAnonymous": "Your identity is always anonymous on Vega",
|
||||
"Optional": "Optional",
|
||||
"OptOutOfTelemetry": "You can opt out any time via settings",
|
||||
"NoThanks": "No thanks",
|
||||
"ShareData": "Share data",
|
||||
"ContinueSharingData": "Continue sharing data"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user