From 75a362e5948276f08a89a2f9d16d779c04b885d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Jur=C4=8Do?= <62913177+peterjurco@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:17:33 +0100 Subject: [PATCH 1/3] Rebrand History page (#501) * Rebrand Proposals table * Add testing capabilities * Self review (fixing some colors) * Update Tag cursor * Rebrand History page * Rename Tag to ProposalTag --- src/pages/history/history.module.scss | 63 ++++++++++++++++----------- src/pages/history/history.tsx | 38 +++++++--------- 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/src/pages/history/history.module.scss b/src/pages/history/history.module.scss index 36a658c9..8a31b277 100644 --- a/src/pages/history/history.module.scss +++ b/src/pages/history/history.module.scss @@ -1,53 +1,64 @@ @import '../../styles/variables.module.scss'; +@import '../../styles/fonts.module.scss'; -.filterButton { - button { - padding: $space-sm !important; - text-decoration: none !important; +h5 { + @include font-heading-9; + color: $color-dark-blue-100; + + @media (min-width: $sm) { + @include font-heading-8; } -} -.active { - text-underline-offset: $space-xs; - text-decoration: underline; + @media (min-width: $md) { + @include font-heading-7; + } } .header { - margin-bottom: 80px; + margin-bottom: 32px; @media (min-width: $min-md) { display: flex; justify-content: space-between; + margin-bottom: 64px; } } -.treasury { - margin-top: -30px; -} - -.borderBoxHeader { +.proposalsHeader { display: flex; - align-items: center; - justify-content: space-between; - transform: translateY(-50%); + flex-direction: column; + gap: 16px; + + @media (min-width: $sm) { + flex-direction: row; + align-items: center; + justify-content: space-between; + } } .radioButtons { display: flex; align-items: center; - justify-content: flex-end; flex-wrap: wrap; - margin-left: $space-md; + gap: 16px; - & > * { - margin-right: $space-lg; + svg { + width: 16px; + height: 16px; + } - @media (max-width: $max-md) { - margin-right: $space-md; - } + label { + @include font-body-12; } - & > *:last-child { - margin-right: 0; + @media (min-width: $md) { + svg { + width: 20px; + height: 20px; + } + + label { + @include font-body-9; + } } } diff --git a/src/pages/history/history.tsx b/src/pages/history/history.tsx index b3868e9f..3581633a 100644 --- a/src/pages/history/history.tsx +++ b/src/pages/history/history.tsx @@ -5,7 +5,6 @@ import { useQueryParams } from '../../utils'; import { ProposalType } from '../../chain-data/state'; import { historyProposalsSelector, OptionalProposalType } from '../../logic/proposals/selectors'; import { useChainData } from '../../chain-data'; -import BorderedBox from '../../components/bordered-box/bordered-box'; import RadioButton from '../../components/radio-button/radio-button'; import ProposalList from '../proposal-commons/proposal-list'; import styles from './history.module.scss'; @@ -50,27 +49,22 @@ const History = () => {
- -
Past Proposals
-
- setCheckedPrimary(!checkedPrimary)}> - Primary - - setCheckedSecondary(!checkedSecondary)} - > - Secondary - -
- - } - content={} - noMobileBorders - /> +
+
Past Proposals
+
+ setCheckedPrimary(!checkedPrimary)}> + Primary + + setCheckedSecondary(!checkedSecondary)} + > + Secondary + +
+
+ ); }; From 3017fe3319f8652ad93254b6a596862ced6e79f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Jur=C4=8Do?= <62913177+peterjurco@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:22:33 +0100 Subject: [PATCH 2/3] Rebrand Unstake banner (#507) * Rebrand unstake banner * Move two line design to lower breakpoint --- public/api-icon.svg | 5 -- src/components/button/button.module.scss | 5 ++ src/components/button/button.tsx | 3 +- .../button/variants/link-gray.module.scss | 36 +++++++++++++ src/components/icons/check-circle-fill.tsx | 10 ++-- .../unstake-banner/unstake-banner.module.scss | 50 +++++++++++-------- .../unstake-banner/unstake-banner.tsx | 19 ++++--- src/utils/image-list.ts | 1 - 8 files changed, 86 insertions(+), 43 deletions(-) delete mode 100644 public/api-icon.svg create mode 100644 src/components/button/variants/link-gray.module.scss diff --git a/public/api-icon.svg b/public/api-icon.svg deleted file mode 100644 index ffe969c9..00000000 --- a/public/api-icon.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/components/button/button.module.scss b/src/components/button/button.module.scss index d9a8cd08..10b1648a 100644 --- a/src/components/button/button.module.scss +++ b/src/components/button/button.module.scss @@ -5,6 +5,7 @@ @import './variants/secondary-neutral.module.scss'; @import './variants/tertiary-color.module.scss'; @import './variants/link-blue.module.scss'; +@import './variants/link-gray.module.scss'; @import './variants/menu-link-secondary.module.scss'; @import './variants/text-blue.module.scss'; @@ -43,6 +44,10 @@ @include link-blue; } + &.link-gray { + @include link-gray; + } + &.menu-link-secondary { @include menu-link-secondary; } diff --git a/src/components/button/button.tsx b/src/components/button/button.tsx index 1d4d5883..6855e725 100644 --- a/src/components/button/button.tsx +++ b/src/components/button/button.tsx @@ -19,7 +19,8 @@ export interface Props extends BreakpointsProps { | 'text' | 'text-blue' | 'menu-link-secondary' - | 'link-blue'; + | 'link-blue' + | 'link-gray'; size?: Size; disabled?: boolean; href?: string; diff --git a/src/components/button/variants/link-gray.module.scss b/src/components/button/variants/link-gray.module.scss new file mode 100644 index 00000000..929fad52 --- /dev/null +++ b/src/components/button/variants/link-gray.module.scss @@ -0,0 +1,36 @@ +@import '../../../styles/fonts.module.scss'; + +@mixin link-gray { + color: $color-gray-700; + height: 20px; + border: none; + + &.xs { + @include font-overline-2; + } + + &.sm { + @include font-link-3; + } + + &.md { + @include font-link-2; + } + + &.lg { + height: 24px; + @include font-link-1; + } + + &:hover { + color: $color-green-800; + } + + &:active { + color: $color-green-700; + } + + &:disabled { + color: $color-gray-400; + } +} diff --git a/src/components/icons/check-circle-fill.tsx b/src/components/icons/check-circle-fill.tsx index fd65eafa..2688070d 100644 --- a/src/components/icons/check-circle-fill.tsx +++ b/src/components/icons/check-circle-fill.tsx @@ -1,10 +1,6 @@ import { ComponentProps } from 'react'; -interface Props extends ComponentProps<'svg'> { - theme?: 'dark' | 'light'; -} - -export const CheckCircleFillIcon = ({ theme = 'light', ...props }: Props) => { +export const CheckCircleFillIcon = ({ ...props }: ComponentProps<'svg'>) => { return ( @@ -18,11 +14,11 @@ export const CheckCircleFillIcon = ({ theme = 'light', ...props }: Props) => { fillRule="evenodd" clipRule="evenodd" d="M9.00033 17.3327C13.6027 17.3327 17.3337 13.6017 17.3337 8.99935C17.3337 4.39698 13.6027 0.666016 9.00033 0.666016C4.39795 0.666016 0.666992 4.39698 0.666992 8.99935C0.666992 13.6017 4.39795 17.3327 9.00033 17.3327ZM13.8415 6.82514C14.2077 6.45903 14.2077 5.86544 13.8415 5.49932C13.4754 5.1332 12.8818 5.1332 12.5157 5.49932L7.50675 10.5083L5.91303 8.91457C5.54692 8.54845 4.95333 8.54845 4.58721 8.91457C4.22109 9.28068 4.22109 9.87427 4.58721 10.2404L6.84384 12.497C7.20995 12.8631 7.80355 12.8631 8.16966 12.497L13.8415 6.82514Z" - fill={theme === 'light' ? '#4049A8' : '#FAFAFA'} + fill="currentColor" /> diff --git a/src/pages/dashboard/unstake-banner/unstake-banner.module.scss b/src/pages/dashboard/unstake-banner/unstake-banner.module.scss index 123ea9d6..6d3fd227 100644 --- a/src/pages/dashboard/unstake-banner/unstake-banner.module.scss +++ b/src/pages/dashboard/unstake-banner/unstake-banner.module.scss @@ -1,44 +1,52 @@ @import '../../../styles/variables.module.scss'; +@import '../../../styles/fonts.module.scss'; .unstakeBanner { width: 100%; display: flex; + flex-direction: column; + gap: 16px; align-items: center; justify-content: space-between; - padding: $space-lg 0; - border-top: 1px solid rgba(124, 227, 203, 0.4); - border-bottom: 1px solid rgba(124, 227, 203, 0.4); - margin-bottom: 2 * $space-xl; - - @media (max-width: $max-md) { - flex-direction: column; - - & > *:not(:last-child) { - margin-bottom: $space-lg; - } + background: linear-gradient(180deg, #fafafa 0%, #f0f4ff 100%); + border-top: 1px solid $color-dark-blue-10; + border-bottom: 1px solid $color-dark-blue-10; + margin: 32px 0; + padding: 24px 16px; + + @media (min-width: $sm) { + margin: 40px 0 72px 0; + padding: 24px; + flex-direction: row; } } .unstakeBannerWrap { display: flex; align-items: center; - margin-right: $space-lg; - @media (max-width: $max-sm) { - margin-right: 0; - margin-bottom: $space-md; + gap: 16px; + @include font-body-10; + + & > svg { + color: $color-action-success-600; + width: 28px; + height: 28px; } - & > img { - margin-right: $space-lg; + @media (min-width: $md) { + @include font-body-4; + gap: 24px; + + & > svg { + width: 36px; + height: 36px; + } } } .buttonPanel { display: flex; align-items: center; - - & > *:not(:last-child) { - margin-right: $space-xxl; - } + gap: 32px; } diff --git a/src/pages/dashboard/unstake-banner/unstake-banner.tsx b/src/pages/dashboard/unstake-banner/unstake-banner.tsx index cb4be57b..1cb8efcc 100644 --- a/src/pages/dashboard/unstake-banner/unstake-banner.tsx +++ b/src/pages/dashboard/unstake-banner/unstake-banner.tsx @@ -1,10 +1,9 @@ import { useApi3Pool } from '../../../contracts'; import { useChainData } from '../../../chain-data'; -import { images } from '../../../utils'; import Button from '../../../components/button'; -import globalStyles from '../../../styles/global-styles.module.scss'; import styles from './unstake-banner.module.scss'; import { handleTransactionError } from '../../../utils'; +import { CheckCircleFillIcon } from '../../../components/icons'; interface Props { canUnstakeAndWithdraw: boolean; @@ -36,16 +35,20 @@ const UnstakeBanner = (props: Props) => { return (
- API icon -
-

Your tokens are ready to be unstaked.

-
+ +
Your tokens are ready to be unstaked.
- -
diff --git a/src/utils/image-list.ts b/src/utils/image-list.ts index d8b599f8..0eb9aa60 100644 --- a/src/utils/image-list.ts +++ b/src/utils/image-list.ts @@ -2,7 +2,6 @@ export const images = { api3DaoLogoDarkTheme: '/api3-dao-logo-dark-theme.svg', api3DaoLogoLightTheme: '/api3-dao-logo-light-theme.svg', - apiIcon: '/api-icon.svg', arrowDropdown: '/arrow-dropdown.svg', arrowLeft: '/arrow-left.svg', arrowRight: '/arrow-right.svg', From 887c6bfd448dab1db8ca1dc3d7287dad857a2d7c Mon Sep 17 00:00:00 2001 From: Bogdan Iasinovschi Date: Wed, 4 Dec 2024 10:07:06 +0200 Subject: [PATCH 3/3] Add cookie consent logic (#508) * Add Checkbox component * Update Notice banner and align logic with Market * Add Privacy Settings modal * Fix Cypress tests * Initialize analytics and sentry * Add additional check for sentry init * Prevent default on external link click * Revert "Prevent default on external link click" This reverts commit 7c2f4a6b1365eaa95fc288b3f871ae6345c2dd63. * Stop propagation of external link in privacy modal * Omit target and rel props * Update banner text --- cypress/e2e/footer.cy.ts | 31 +++-- cypress/support/common.ts | 6 +- src/components/checkbox/checkbox.module.scss | 123 ++++++++++++++++++ src/components/checkbox/checkbox.tsx | 44 +++++++ src/components/checkbox/index.ts | 2 + .../external-link/external-link.tsx | 31 +++-- src/components/icons/check.tsx | 15 +++ src/components/icons/index.ts | 5 +- .../error-reporting-notice.module.scss | 103 ++++++--------- .../error-reporting-notice.tsx | 90 +++++++------ src/components/layout/layout.tsx | 12 +- .../layout/privacy-settings-modal/index.ts | 1 + .../privacy-settings-modal.module.scss | 80 ++++++++++++ .../privacy-settings-modal.tsx | 84 ++++++++++++ .../storage-full-notification.tsx | 10 +- src/index.tsx | 32 ++--- src/utils/analytics.ts | 24 ++++ src/utils/error-reporting.ts | 25 ++++ src/utils/generic.test.ts | 10 +- src/utils/generic.ts | 6 - src/utils/links.ts | 4 + 21 files changed, 564 insertions(+), 174 deletions(-) create mode 100644 src/components/checkbox/checkbox.module.scss create mode 100644 src/components/checkbox/checkbox.tsx create mode 100644 src/components/checkbox/index.ts create mode 100644 src/components/icons/check.tsx create mode 100644 src/components/layout/privacy-settings-modal/index.ts create mode 100644 src/components/layout/privacy-settings-modal/privacy-settings-modal.module.scss create mode 100644 src/components/layout/privacy-settings-modal/privacy-settings-modal.tsx create mode 100644 src/utils/analytics.ts create mode 100644 src/utils/error-reporting.ts create mode 100644 src/utils/links.ts diff --git a/cypress/e2e/footer.cy.ts b/cypress/e2e/footer.cy.ts index cd2c1b40..2034d986 100644 --- a/cypress/e2e/footer.cy.ts +++ b/cypress/e2e/footer.cy.ts @@ -3,20 +3,35 @@ import { HOME_PAGE } from '../support/common'; it('renders footer and error reporting', () => { cy.visit(HOME_PAGE).dataCy('error-reporting').should('exist'); - // Disallow error reporting - cy.dataCy('error-reporting').find('input').click(); - cy.findByText('Done').click(); + // Enable error report and analytics + cy.dataCy('error-reporting') + .findByRole('button', { name: /accept all/i }) + .click(); cy.dataCy('error-reporting') .should('not.exist') - .should(() => { - expect(localStorage.getItem('reportErrors')).to.equal('false'); + .then(() => { + expect(localStorage.getItem('allow-error-reporting')).to.equal('true'); + expect(localStorage.getItem('allow-analytics')).to.equal('true'); }); - // On subsequent page visit there is no notice - cy.reload().should(() => { - expect(localStorage.getItem('reportErrors')).to.equal('false'); + // On subsequent page visit the error reporting notice should not be shown + cy.reload().then(() => { + expect(cy.dataCy('error-reporting').should('not.exist')); }); + // Open the privacy settings modal from the footer and disable error reporting and analytics + cy.findByRole('button', { name: /error reporting/i }).click(); + cy.findByRole('button', { name: /manage settings/i }).click(); + cy.findByRole('checkbox', { name: /allow error reporting/i }).click(); + cy.findByRole('checkbox', { name: /allow analytics cookies/i }).click(); + cy.findByRole('button', { name: /save settings/i }).click(); + cy.dataCy('error-reporting') + .should('not.exist') + .then(() => { + expect(localStorage.getItem('allow-error-reporting')).to.equal('false'); + expect(localStorage.getItem('allow-analytics')).to.equal('false'); + }); + // Footer links should open the pages they link to in a new tab cy.findByText('Github').should('have.attr', 'target', '_blank'); }); diff --git a/cypress/support/common.ts b/cypress/support/common.ts index de3970c9..290a939a 100644 --- a/cypress/support/common.ts +++ b/cypress/support/common.ts @@ -33,8 +33,10 @@ export const abbrStr = (str: string) => { export const EPOCH_LENGTH = 7 * 60 * 60 * 24; // in seconds export const closeErrorReportingNotice = () => { - cy.dataCy('error-reporting').findByText('Done').click(); - cy.findByText('Done').should('not.exist'); + cy.dataCy('error-reporting') + .findByRole('button', { name: /accept all/i }) + .click(); + cy.dataCy('error-reporting').should('not.exist'); }; export const HOME_PAGE = 'http://localhost:3000/#/'; diff --git a/src/components/checkbox/checkbox.module.scss b/src/components/checkbox/checkbox.module.scss new file mode 100644 index 00000000..fc8ebaf2 --- /dev/null +++ b/src/components/checkbox/checkbox.module.scss @@ -0,0 +1,123 @@ +@import '../../styles/variables.module.scss'; +@import '../../styles/fonts.module.scss'; + +.checkbox { + display: flex; + align-items: flex-start; + gap: 8px; + + &, + * { + cursor: pointer; + transition: all 0.1s; + } + + .checkmark { + display: flex; + align-items: center; + justify-content: center; + min-height: 16px; + min-width: 16px; + height: 16px; + width: 16px; + background-color: $color-gray-50; + border: 1px solid $color-dark-blue-50; + border-radius: 2px; + margin-top: 2px; + + svg { + margin: auto; + width: 12px; + height: 12px; + color: $color-gray-50; + } + } + + .checkboxTextBlock { + display: flex; + flex-direction: column; + @include font-body-12; + + label { + color: $color-dark-blue-800; + } + + .description { + color: $color-gray-500; + } + } + + &[aria-checked='true'] { + .checkmark { + background-color: $color-dark-blue-400; + border-color: $color-gray-50; + } + } + + &:hover:not([aria-disabled='true']) { + .checkmark { + background-color: $color-base-light; + border-color: $color-dark-blue-400; + + svg { + color: $color-dark-blue-400; + } + } + + .checkboxTextBlock { + label { + color: $color-gray-900; + } + + .description { + color: $color-gray-600; + } + } + } + + &[aria-disabled='true'] { + pointer-events: none; + + .checkmark { + background-color: transparent; + border-color: $color-gray-200; + + svg { + color: $color-gray-200; + } + } + + .checkboxTextBlock { + label { + color: $color-gray-400; + } + + .description { + color: $color-gray-200; + } + } + } +} + +@media (min-width: $sm) { + .checkbox { + gap: 12px; + + .checkmark { + min-height: 20px; + min-width: 20px; + height: 20px; + width: 20px; + margin-top: 2px; + + svg { + width: 16px; + height: 16px; + } + } + + .checkboxTextBlock { + @include font-body-9; + } + } +} diff --git a/src/components/checkbox/checkbox.tsx b/src/components/checkbox/checkbox.tsx new file mode 100644 index 00000000..eaa2e309 --- /dev/null +++ b/src/components/checkbox/checkbox.tsx @@ -0,0 +1,44 @@ +import { KeyboardEvent, ReactNode } from 'react'; +import { CheckIcon } from '../icons'; +import styles from './checkbox.module.scss'; + +interface Props { + label?: string; + checked: boolean; + children?: ReactNode; + disabled?: boolean; + onChange: (checked: boolean) => void; +} + +const CheckBox = ({ label, checked, children, disabled, onChange }: Props) => { + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + onChange(!checked); + } + }; + + return ( + + ); +}; + +export default CheckBox; diff --git a/src/components/checkbox/index.ts b/src/components/checkbox/index.ts new file mode 100644 index 00000000..3f0e0071 --- /dev/null +++ b/src/components/checkbox/index.ts @@ -0,0 +1,2 @@ +export { default } from './checkbox'; +export * from './checkbox'; diff --git a/src/components/external-link/external-link.tsx b/src/components/external-link/external-link.tsx index 73b27ade..f9e7b281 100644 --- a/src/components/external-link/external-link.tsx +++ b/src/components/external-link/external-link.tsx @@ -1,32 +1,37 @@ -import { ReactNode, useEffect } from 'react'; +import { ComponentPropsWithoutRef, useEffect } from 'react'; -interface Props { - className?: string; +interface Props extends Omit, 'target' | 'rel'> { href: string; - children: ReactNode; } const ExternalLink = (props: Props) => { - const { className, children } = props; + const { children, href: incomingHref, ...rest } = props; - let href = props.href.trim(); - const urlRegex = /^https?:\/\//i; // Starts with https:// or http:// (case insensitive) - if (!urlRegex.test(href)) { - href = 'about:blank'; - } + const href = cleanHref(incomingHref); useEffect(() => { if (process.env.NODE_ENV === 'development' && href === 'about:blank') { // eslint-disable-next-line no-console - console.warn(`An invalid URL has been provided: "${props.href}". Only https:// or http:// URLs are allowed.`); + console.warn(`An invalid URL has been provided: "${incomingHref}". Only https:// or http:// URLs are allowed.`); } - }, [href, props.href]); + }, [href, incomingHref]); return ( - + {children} ); }; +const cleanHref = (href: string) => { + const urlRegex = /^https?:\/\//i; // Starts with https:// or http:// (case insensitive) + const trimmedHref = href.trim(); + + if (!urlRegex.test(trimmedHref)) { + return 'about:blank'; + } + + return trimmedHref; +}; + export default ExternalLink; diff --git a/src/components/icons/check.tsx b/src/components/icons/check.tsx new file mode 100644 index 00000000..8e66fe51 --- /dev/null +++ b/src/components/icons/check.tsx @@ -0,0 +1,15 @@ +import { ComponentProps } from 'react'; + +export const CheckIcon = (props: ComponentProps<'svg'>) => { + return ( + + + + ); +}; diff --git a/src/components/icons/index.ts b/src/components/icons/index.ts index c5e7cd12..8ae524e9 100644 --- a/src/components/icons/index.ts +++ b/src/components/icons/index.ts @@ -1,8 +1,9 @@ -import { CheckCircleFillIcon } from './check-circle-fill'; import { CheckboxRadioIcon } from './checkbox-radio'; +import { CheckCircleFillIcon } from './check-circle-fill'; +import { CheckIcon } from './check'; import { CloseIcon } from './close'; import { CrossIcon } from './cross'; import { HelpOutlineIcon } from './help-outline'; import { InfoCircleIcon } from './info-circle'; -export { CheckCircleFillIcon, CheckboxRadioIcon, CloseIcon, CrossIcon, HelpOutlineIcon, InfoCircleIcon }; +export { CheckCircleFillIcon, CheckboxRadioIcon, CheckIcon, CloseIcon, CrossIcon, HelpOutlineIcon, InfoCircleIcon }; diff --git a/src/components/layout/error-reporting-notice/error-reporting-notice.module.scss b/src/components/layout/error-reporting-notice/error-reporting-notice.module.scss index 8fe8b2d9..88dec47f 100644 --- a/src/components/layout/error-reporting-notice/error-reporting-notice.module.scss +++ b/src/components/layout/error-reporting-notice/error-reporting-notice.module.scss @@ -10,14 +10,9 @@ position: fixed; width: 100%; bottom: 0; - height: 221px; background: $color-dark-blue-700; align-items: center; - @media (min-width: $md) { - height: 92px; - } - &::before { content: ''; position: absolute; @@ -29,25 +24,12 @@ } } -.closeButton { - z-index: 1; - height: 24px; - align-self: flex-end; - margin: 20px 24px; - position: absolute; - color: $color-base-light; - cursor: pointer; - - &:hover { - color: $color-blue-25; - } -} - .content { padding: 64px 24px 32px 24px; display: flex; align-items: center; justify-content: space-around; + gap: 24px; flex: 1; flex-direction: column; @include font-body-15; @@ -62,62 +44,59 @@ } } -.buttons { - display: flex; +.externalLinkIcon { + margin-left: 4px; } -.checkboxWrapper { - min-width: 160px; // Make sure the checkbox and its label are on a single line - display: flex; - align-items: center; +.notice { + max-width: 550px; - label { - cursor: pointer; + a { + text-decoration: none; + @include link-blue; } +} - input { - $size: 16px; +.buttons { + display: flex; + justify-content: space-between; + width: 100%; - width: $size; - height: $size; - margin-right: $space-xs; - appearance: none; - border: none; - border-radius: 4px; - outline: none; - transition-duration: 0.3s; - background-color: $secondary-black-color; - cursor: pointer; + .manageSettingsButton { + padding-left: 0 !important; + padding-right: 0 !important; } - input:checked { - background-color: $green-color; + @media (min-width: $md) { + width: auto; + gap: 32px; } +} - input:checked::before { - content: '\2713'; - text-align: center; - color: $secondary-black-color; - display: flex; - justify-content: center; - align-items: center; - height: 100%; - } +.closeButton { + z-index: 1; + align-self: flex-end; + margin: 20px 24px; + position: absolute; + color: $color-base-light; + cursor: pointer; + padding: 0; + border: none; + background: none; - input:active { - border: 2px solid $secondary-black-color; + svg { + width: 24px; + height: 24px; } -} -.notice { - max-width: 550px; - - a { - text-decoration: none; - @include link-blue; + &:hover { + color: $color-blue-25; } -} -.externalLinkIcon { - margin-left: 4px; + @media (min-width: $md) { + svg { + width: 28px; + height: 28px; + } + } } diff --git a/src/components/layout/error-reporting-notice/error-reporting-notice.tsx b/src/components/layout/error-reporting-notice/error-reporting-notice.tsx index 756e53d8..b4065c25 100644 --- a/src/components/layout/error-reporting-notice/error-reporting-notice.tsx +++ b/src/components/layout/error-reporting-notice/error-reporting-notice.tsx @@ -1,66 +1,74 @@ -import { useRef, useState } from 'react'; -import { ERROR_REPORTING_CONSENT_KEY_NAME, images, isErrorReportingAllowed } from '../../../utils'; +import { useState } from 'react'; +import { images } from '../../../utils'; import Button from '../../button'; -import { triggerOnEnter } from '../../modal'; import styles from './error-reporting-notice.module.scss'; import ExternalLink from '../../external-link'; +import { links } from '../../../utils/links'; +import { ALLOW_ANALYTICS, ALLOW_ERROR_REPORTING, initAnalytics } from '../../../utils/analytics'; +import PrivacySettingsModal from '../privacy-settings-modal'; +import { CrossIcon } from '../../icons'; +import { initSentry } from '../../../utils/error-reporting'; interface WelcomeModalContentProps { - onClose: () => void; + onShowNotice: (showNotice: boolean) => void; } const ErrorReportingNotice = (props: WelcomeModalContentProps) => { - const { onClose } = props; - const defaultReportingValue = localStorage.getItem(ERROR_REPORTING_CONSENT_KEY_NAME); - const [errorReportingEnabled, setErrorReportingEnabled] = useState( - defaultReportingValue === null || isErrorReportingAllowed(defaultReportingValue) - ); - const onErrorReportingNoticeConfirm = () => { - localStorage.setItem(ERROR_REPORTING_CONSENT_KEY_NAME, errorReportingEnabled.toString()); - onClose(); + const { onShowNotice } = props; + + const [isModalOpen, setIsModalOpen] = useState(false); + + const handleSubmit = (allowAnalytics: boolean, allowReporting: boolean) => { + localStorage.setItem(ALLOW_ERROR_REPORTING, allowReporting.toString()); + localStorage.setItem(ALLOW_ANALYTICS, allowAnalytics.toString()); + + if (allowAnalytics) { + initAnalytics(); + (window as any).clarity?.('consent'); + } + + if (allowReporting) { + initSentry(); + } + + onShowNotice(false); + setIsModalOpen(false); }; - const errorReportingRef = useRef(null); - const toggleCheckbox = () => setErrorReportingEnabled((checked) => !checked); return ( - // NOTE: Not using focus lock, because that would prevent user from signing in <> -
+ setIsModalOpen(false)} onSubmit={handleSubmit} /> + +
In order to provide the best services for you, we collect anonymized error data through{' '} - + Sentry - . We do not gather IP address or - user agent information. + and use analytics cookies to + improve our products.
-
- - -
- +
- confirm and close icon + +
); diff --git a/src/components/layout/layout.tsx b/src/components/layout/layout.tsx index 103b9763..c9710295 100644 --- a/src/components/layout/layout.tsx +++ b/src/components/layout/layout.tsx @@ -2,12 +2,12 @@ import { ReactNode, useState } from 'react'; import { Helmet } from 'react-helmet-async'; import Navigation from '../navigation'; import Header from '../header'; -import { ERROR_REPORTING_CONSENT_KEY_NAME } from '../../utils'; import styles from './layout.module.scss'; import Button from '../button'; import ErrorReportingNotice from './error-reporting-notice'; import { DesktopMenu } from '../menu'; import ExternalLink from '../external-link'; +import { ALLOW_ANALYTICS, ALLOW_ERROR_REPORTING } from '../../utils/analytics'; type Props = { children: ReactNode; @@ -30,13 +30,13 @@ interface BaseLayoutProps { } export const BaseLayout = ({ children, subtitle }: BaseLayoutProps) => { - const [errorReportingNoticeOpen, setErrorReportingNoticeOpen] = useState( - localStorage.getItem(ERROR_REPORTING_CONSENT_KEY_NAME) === null + const [showNotice, setShowNotice] = useState( + () => localStorage.getItem(ALLOW_ERROR_REPORTING) === null || localStorage.getItem(ALLOW_ANALYTICS) === null ); const footerLinks = [ { text: 'About API3', href: 'https://api3.org/' }, - { text: 'Error Reporting', onClick: () => setErrorReportingNoticeOpen(true) }, + { text: 'Error Reporting', onClick: () => setShowNotice(true) }, { text: 'Github', href: 'https://github.com/api3dao/api3-dao-dashboard' }, ]; @@ -53,8 +53,8 @@ export const BaseLayout = ({ children, subtitle }: BaseLayoutProps) => {
{children}