Add Cookie Manager and Add CCA Device Tracking (#284)

* Add Coinbase cookie banner to Base Web.

* Base Web - Add cookie policy page and cookie manager. Init CCA with device ID cookie.

* Base Docs - Add cookie policy page and cookie manager. Init CCA with device ID cookie.

* Add custom CookieBanner theme to improve contrast.

* Ignore eslint rule affecting useCookie from @coinbase/cookie-manager.

* Explicitly set base.org domain for cookie.
This commit is contained in:
jacob-moore-cb
2024-01-29 14:25:23 -08:00
committed by GitHub
parent 71829a82a9
commit 15a66ea3d4
20 changed files with 1099 additions and 45 deletions

View File

@@ -0,0 +1,95 @@
---
title: Cookie Policy
slug: /cookie-policy
description: Policy explaining how Base uses cookies on our website.
---
# Base Cookie Policy
Last updated: January 17, 2024
---
import CookieManager from '../src/components/CookieManager';
This Cookie Policy explains how Base (referred to here as **"we"**, **"us"** and **"our"**)
uses cookies and similar technologies when you visit [base.org](https://base.org) (the
**"Site"**) and/or when you explore and use Base through the Base protocol or any other
applications, tools, and features we operate (collectively referred to as the **"Services"**).
It explains what these technologies are and why we use them, as well as your rights to
control our use of them.
In some cases, we may use cookies and similar technologies to collect personal information,
or information that becomes personal information if we combine it with other information.
In such cases, the [Base Privacy Policy](/privacy-policy) will apply
in addition to this Cookie Policy.
## 1. What Are Cookies?
Browser cookies are text files with small pieces of data downloaded onto your computer or
mobile device. Browser cookies and other similar technologies enable websites and apps to
store information or facilitate access to information stored on your device to enable
certain features and distinguish you from other visitors. These technologies are used by
most website and app providers to let users navigate between pages efficiently, ensure
security of the webpage or application, understand how their websites are used, remember
user preferences and generally improve the user experience. More information on cookies
and their use can be found at [aboutcookies.org](https://www.aboutcookies.org) or
[allaboutcookies.org](https://www.allaboutcookies.org). Cookies set by the website operator
are called **"first party cookies"** and Cookies set by parties other than the website
operator are called **"third party cookies"**. You should check the third-party's website
for more information on how they use cookies.
## 2. What Do We Use Cookies For?
When you access our Site and Services, we, or companies we work with, may place cookies
(collectively called **"cookies"** in this Cookie Policy) and similar technologies (such as
web beacons, software development kits (**"SDKs"**), APIs, tags and local storage) on your
computer or other device for the following purposes:
### Strictly Necessary purposes
Strictly Necessary cookies are essential for our Services to function and therefore
cannot be switched off. They are usually only set in response to actions made by you
which amount to a request for services, such as setting your privacy preferences,
logging in, or filling in forms. These also include cookies we may rely on for security
purposes, such as to prevent unauthorised access attempts. You can set your browser to
block or alert you about these cookies at any time, but some features of our Services
may not work.
### Performance purposes
We use these cookies to count visits and traffic sources so we can measure and improve
the performance of our Services. These cookies help us to know which pages are the most
and least popular and see how visitors move around the Site, and to resolve any errors
that occur on the Services quickly to provide you with a better experience. For example,
our first party base_device_id cookie is used to provide analytics on how our Site and
Services perform. This cookie lasts for 3 months.
## 3. How to Manage Cookies, Similar Technologies and Targeted Online Mobile Advertising
You have the right to decide whether to accept or reject cookies (except strictly necessary
cookies). You can enable or disable categories of cookies by visiting our <CookieManager />.
This includes both first party and third party cookies. You can use the browser with which you
are viewing this website to enable, disable or delete cookies. To do this, follow the instructions
provided by your browser (usually located within the "Help", "Tools" or "Edit" settings). However,
please note, if you set your browser to disable cookies, you may not be able to access secure areas
of our Services. Also, if you disable cookies, other parts of our Services may not function properly.
There are also additional tools available to manage third party cookies. You can visit the DAA's
opt-out portal available at [optout.aboutads.info](http://optout.aboutads.info/), the DAA of Canada's
opt-out portal available at [youradchoices.ca/en/tools](https://youradchoices.ca/en/tools), or visit
the NAI's opt-out portal available at [optout.networkadvertising.org](http://optout.networkadvertising.org/?c=1).
Residents of the European Union may opt-out of online behavioural advertising served by the European
Interactive Digital Advertising Alliance&apos;s participating member organizations by visiting
[youronlinechoices.eu](https://www.youronlinechoices.eu/) or through your mobile device settings,
where available and the DAA&apos;s AppChoices mobile application opt-out offering here:
[youradchoices.com/appchoices](https://youradchoices.com/appchoices).
If you have any questions about our use of cookies or other technologies, please submit
your request to privacy@base.org.
## 4. Will This Cookie Policy Be Updated?
We may update this Cookie Policy from time to time to reflect, for example, changes to
the cookies we use or for other operational, legal or regulatory reasons. You can also
revisit this page if you wish to keep yourself informed.

View File

@@ -16,6 +16,8 @@
"typecheck": "tsc"
},
"dependencies": {
"@coinbase/cookie-banner": "^1.0.3",
"@coinbase/cookie-manager": "^1.1.1",
"@docusaurus/core": "2.4.1",
"@docusaurus/preset-classic": "2.4.1",
"@mdx-js/react": "^1.6.22",

View File

@@ -274,5 +274,6 @@ module.exports = {
},
['terms-of-service'],
['privacy-policy'],
['cookie-policy'],
],
};

View File

@@ -0,0 +1,21 @@
import React, { useState, useCallback } from 'react';
import { CookiePreferencesModal } from '@coinbase/cookie-banner';
import styles from './styles.module.css';
export default function CookieManager() {
const [isOpen, setIsOpen] = useState(false);
const handleOpenModal = useCallback(() => setIsOpen(true), []);
const handleCloseModal = useCallback(() => setIsOpen(false), []);
return (
<>
<button type="button" className={styles.cookieManagerButton} onClick={handleOpenModal}>
Cookie Preference Manager
</button>
{isOpen && <CookiePreferencesModal isOpen={isOpen} onClose={handleCloseModal} />}
</>
);
}

View File

@@ -0,0 +1,18 @@
.cookieManagerButton {
color: #3773f5;
background-color: transparent;
font-size: 1rem;
padding: 0;
appearance: none;
cursor: pointer;
display: inline-block;
}
[data-theme='dark'] .cookieManagerButton {
color: #3773f5;
}
[data-theme='light'] .cookieManagerButton {
color: #0052ff;
}
.cookieManagerButton:hover {
text-decoration: underline;
}

View File

@@ -1,11 +1,10 @@
type IconProps = {
name: 'thumbs-up' | 'thumbs-up-filled' | 'thumbs-down' | 'thumbs-down-filled' | 'caret-down';
color?: 'white' | 'black';
width?: string;
height?: string;
};
export default function Icon({ name, color = 'white', width = '24', height = '24' }: IconProps) {
export default function Icon({ name, width = '24', height = '24' }: IconProps) {
if (name === 'thumbs-up') {
return (
<svg

View File

@@ -1,13 +1,16 @@
/* eslint-disable */
import React, { useState, useEffect, useCallback, useRef } from 'react';
import {
connectorsForWallets,
getDefaultWallets,
RainbowKitProvider,
} from '@rainbow-me/rainbowkit';
import { useEffect, useState } from 'react';
import { configureChains, createConfig, WagmiConfig } from 'wagmi';
import { baseGoerli, baseSepolia } from 'wagmi/chains';
import { jsonRpcProvider } from 'wagmi/providers/jsonRpc';
import { Provider as CookieManagerProvider, Region } from '@coinbase/cookie-manager';
import { cookieManagerConfig } from '../utils/cookieManagerConfig';
import { CookieBanner } from '@coinbase/cookie-banner';
export const { chains, publicClient } = configureChains(
[baseGoerli, baseSepolia],
@@ -32,12 +35,101 @@ const wagmiConfig = createConfig({
publicClient,
});
const cookieBannerTheme = {
colors: {
primary: '#1652F0',
positive: '#05B169',
negative: '#DF5F67',
warning: '#F4C622',
background: '#FFFFFF',
backgroundMuted: '#EEF0F3',
onBackground: '#050F1A',
onBackgroundMuted: '#0A0B0D',
onPrimary: '#FFFFFF',
overlay: 'rgba(17,52,83,0.6)',
},
border: {
border: '1px solid #D8D8D8',
borderRadius: '4px',
},
fontSize: {
sm: '14px',
md: '16px',
},
fontWeight: {
regular: '400',
bold: '500',
},
size: {
xs: '8px',
sm: '16px',
md: '24px',
lg: '32px',
},
breakpoints: {
phone: 560,
desktop: 992,
tablet: 768,
},
zIndex: {
high: 2,
overlay: 1000,
},
};
export default function Root({ children }) {
const [mounted, setMounted] = useState(false);
// Cookie Consent Manager Provider Configuration
const trackingPreference = useRef();
const setTrackingPreference = useCallback((newPreference) => {
const priorConsent = trackingPreference.current?.consent;
trackingPreference.current = newPreference;
if (!priorConsent) {
// The first time the modal appears, this function is called with nothing present in
// trackingPreference.current. To avoid an infinite refresh loop, we return early on
// the first call.
return;
}
const newConsent = newPreference.consent;
// Check if the preferences have changed.
const diff = [
...priorConsent.filter((elem) => !newConsent.includes(elem)),
...newConsent.filter((elem) => !priorConsent.includes(elem)),
];
// Reload if the preferences have changed.
if (diff.length > 0) {
window.location.reload();
}
}, []);
const handleLogError = useCallback((err) => console.error(err), []);
useEffect(() => setMounted(true), []);
if (!mounted) return null;
return (
<WagmiConfig config={wagmiConfig}>
<RainbowKitProvider chains={chains}>{children}</RainbowKitProvider>
<RainbowKitProvider chains={chains}>
<CookieManagerProvider
projectName="base_docs"
locale="en"
region={Region.DEFAULT}
log={console.log}
onError={handleLogError}
onPreferenceChange={setTrackingPreference}
config={cookieManagerConfig}
>
{children}
<CookieBanner companyName="Base" link="/cookie-policy" theme={cookieBannerTheme} />
</CookieManagerProvider>
</RainbowKitProvider>
</WagmiConfig>
);
}

View File

@@ -0,0 +1,42 @@
/*
Coinbase Client Analytics Lite is currently loaded via a Docusaurus script tag configured in docusaurus.config.js
and initialized via a Docusaurus client module in ./initCCA.ts. @coinbase/cookie-manager hooks cannot be used in
a Docusaurus client module, so we are temporarily using the below cookie management functions. This implementation
will be adjusted once CCA becomes open source, as we'll then be able to cease use of the CCA Lite script and move
the CCA implementation out of a client module so @coinbase/cookie-manager hooks can be used.
*/
export function setCookie(cookieName: string, cookieValue: string, expiryDays: number) {
const d = new Date();
d.setTime(d.getTime() + expiryDays * 24 * 60 * 60 * 1000);
let expires = 'expires=' + d.toUTCString();
document.cookie = cookieName + '=' + cookieValue + ';' + expires + ';path=/';
}
export function getCookie(cookieName: string): string | undefined {
let name = cookieName + '=';
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(';');
for (const el of ca) {
let c = el;
while (c.startsWith(' ')) {
c = c.substring(1);
}
if (c.startsWith(name)) {
return c.substring(name.length, c.length);
}
}
return undefined;
}
export function deserializeCookie(cookie: string): string | undefined {
let parsedCookie: string | undefined;
try {
parsedCookie = JSON.parse(cookie) as string;
} catch (e) {
parsedCookie = cookie;
}
return parsedCookie;
}

View File

@@ -0,0 +1,42 @@
import { Framework, Region, TrackerType, TrackingCategory } from '@coinbase/cookie-manager';
export const cookieManagerConfig = {
categories: [
{
id: TrackingCategory.NECESSARY,
expiry: 365,
required: true,
trackers: [
{
id: 'ip_country',
type: TrackerType.COOKIE,
expiry: 10,
},
{
id: 'locale',
type: TrackerType.COOKIE,
},
],
},
{
id: TrackingCategory.PERFORMANCE,
expiry: 365,
trackers: [
{
id: 'base_device_id',
type: TrackerType.COOKIE,
},
],
},
],
geolocationRules: [
{
region: Region.DEFAULT,
framework: Framework.OPT_OUT,
},
{
region: Region.EU,
framework: Framework.OPT_IN,
},
],
};

View File

@@ -4,28 +4,46 @@
// @ts-nocheck
const docusaurusConfig = require('@generated/docusaurus.config');
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
import { setCookie, getCookie, deserializeCookie } from './cookieManagement';
import { TrackingPreference } from '@coinbase/cookie-manager';
const { customFields } = docusaurusConfig.default;
const isDevelopment = customFields.nodeEnv === 'development';
// Initialize Client Analytics
const initCCA = () => {
if (ExecutionEnvironment.canUseDOM && window.ClientAnalytics) {
const { init, identify, PlatformName } = window.ClientAnalytics;
if (ExecutionEnvironment.canUseDOM) {
let deviceId: string | undefined = deserializeCookie(getCookie('base_device_id'));
init({
isProd: !isDevelopment,
amplitudeApiKey: isDevelopment
? 'ca92bbcb548f7ec4b8ebe9194b8eda81'
: '2b38c7ac93c0dccc83ebf9acc5107413',
platform: PlatformName.web,
projectName: 'base_docs',
showDebugLogging: isDevelopment,
version: '1.0.0',
apiEndpoint: 'https://cca-lite.coinbase.com',
});
const trackingPreference: TrackingPreference | undefined = deserializeCookie(
getCookie('cm_default_preferences'),
);
const trackingAllowed = trackingPreference?.consent.includes('performance');
identify({ deviceId: 'base_docs_device_id' });
if (!trackingAllowed) {
deviceId = 'base_docs_device_id';
} else if (!deviceId) {
deviceId = crypto?.randomUUID();
setCookie('base_device_id', deviceId, 365);
}
if (window.ClientAnalytics) {
const { init, identify, PlatformName } = window.ClientAnalytics;
init({
isProd: !isDevelopment,
amplitudeApiKey: isDevelopment
? 'ca92bbcb548f7ec4b8ebe9194b8eda81'
: '2b38c7ac93c0dccc83ebf9acc5107413',
platform: PlatformName.web,
projectName: 'base_docs',
showDebugLogging: isDevelopment,
version: '1.0.0',
apiEndpoint: 'https://cca-lite.coinbase.com',
});
identify({ deviceId: deviceId });
}
}
};

View File

@@ -10,6 +10,8 @@
"update-contributors": "node ./scripts/updateContributors.js && npx prettier ./src/components/CoreContributors/CoreContributors.json -w"
},
"dependencies": {
"@coinbase/cookie-banner": "^1.0.3",
"@coinbase/cookie-manager": "^1.1.1",
"@heroicons/react": "^2.0.18",
"@next/font": "^13.1.5",
"ethers": "5.7.2",
@@ -18,7 +20,8 @@
"react": "^18.2.0",
"react-blockies": "^1.4.1",
"react-dom": "^18.2.0",
"react-intl": "^6.2.1"
"react-intl": "^6.2.1",
"uuidv4": "^6.2.13"
},
"devDependencies": {
"@types/node": "18.11.18",

View File

@@ -1,13 +1,19 @@
import './global.css';
import Script from 'next/script';
import { useRouter } from 'next/router';
import { useCallback } from 'react';
import initCCA from '../src/utils/initCCA';
import { MotionConfig } from 'framer-motion';
import { useState, useEffect, useCallback, useRef } from 'react';
import App, { AppContext, AppProps } from 'next/app';
import { MotionConfig } from 'framer-motion';
import {
Provider as CookieManagerProvider,
Region,
TrackingCategory,
TrackingPreference,
} from '@coinbase/cookie-manager';
import { Layout } from '../src/components/Layout/Layout';
import ClientAnalyticsScript from '../src/components/ClientAnalyticsScript/ClientAnalyticsScript';
import { cookieManagerConfig } from '../src/utils/cookieManagerConfig';
/* Adding this to force NextJS to render the app on the server at runtime instead of statically
which allows us to use ENV vars in the way we expect (Codeflow does not insert ENV vars at Dockerfile build time, so statically rendered pages don't have access) */
@@ -17,18 +23,60 @@ export async function getInitialProps(context: AppContext) {
}
export default function StaticApp({ Component, pageProps }: AppProps) {
const router = useRouter();
// Cookie Manager Provider Configuration
const [isMounted, setIsMounted] = useState(false);
const trackingPreference = useRef<TrackingPreference | undefined>();
const setTrackingPreference = useCallback((newPreference: TrackingPreference) => {
const priorConsent = trackingPreference.current?.consent;
trackingPreference.current = newPreference;
if (!priorConsent) {
// The first time the modal appears, this function is called with nothing present in
// trackingPreference.current. To avoid an infinite refresh loop, we return early on
// the first call.
return;
}
const newConsent = newPreference.consent;
// Check if the preferences have changed.
const diff = [
...priorConsent.filter((elem: TrackingCategory) => !newConsent.includes(elem)),
...newConsent.filter((elem: TrackingCategory) => !priorConsent.includes(elem)),
];
// Reload if the preferences have changed.
if (diff.length > 0) {
window.location.reload();
}
}, []);
const handleLogError = useCallback((err: Error) => console.error(err), []);
useEffect(() => {
setIsMounted(true);
}, []);
if (!isMounted) return null;
return (
<MotionConfig reducedMotion="user">
<Script
src="https://static-assets.coinbase.com/js/cca/v0.0.1.js"
onLoad={useCallback(() => initCCA(router), [router])}
/>
<Layout>
<Component {...pageProps} />
</Layout>
</MotionConfig>
<CookieManagerProvider
projectName="base_web"
locale="en"
region={Region.DEFAULT}
log={console.log}
onError={handleLogError}
onPreferenceChange={setTrackingPreference}
config={cookieManagerConfig}
>
<MotionConfig reducedMotion="user">
<ClientAnalyticsScript />
<Layout>
<Component {...pageProps} />
</Layout>
</MotionConfig>
</CookieManagerProvider>
);
}

View File

@@ -0,0 +1,161 @@
import { useState, useCallback } from 'react';
import { CookiePreferencesModal } from '@coinbase/cookie-banner';
import Head from 'next/head';
export default function CookiePolicy() {
const [isOpen, setIsOpen] = useState(false);
const handleOpenModal = useCallback(() => setIsOpen(true), []);
const handleCloseModal = useCallback(() => setIsOpen(false), []);
return (
<>
<Head>
<title>Base | Cookie Policy</title>
<meta
content="Policy explaining how Base uses cookies on our website."
name="description"
/>
</Head>
<main className="flex w-full flex-col items-center bg-white">
<section className="flex w-full max-w-[1440px] flex-col gap-4 px-8 pb-10 pt-10 lg:pb-28 lg:pt-20">
<h1 className="mb-8 text-4xl font-bold">Base Cookie Policy</h1>
<p>
This Cookie Policy explains how Base (referred to here as{' '}
<strong>&quot;we&quot;</strong>, <strong>&quot;us&quot;</strong> and{' '}
<strong>&quot;our&quot;</strong>) uses cookies and similar technologies when you visit{' '}
<a href="https://base.org/" className="text-blue-500">
base.org
</a>{' '}
(the <strong>&quot;Site&quot;</strong>) and/or when you explore and use Base through the
Base protocol or any other applications, tools, and features we operate (collectively
referred to as the <strong>&quot;Services&quot;</strong>). It explains what these
technologies are and why we use them, as well as your rights to control our use of them.
</p>
<p>
In some cases, we may use cookies and similar technologies to collect personal
information, or information that becomes personal information if we combine it with
other information. In such cases, the{' '}
<a href="https://docs.base.org/privacy-policy/" className="text-blue-500">
Base Privacy Policy
</a>{' '}
will apply in addition to this Cookie Policy.
</p>
<h2 className="mt-8 text-2xl font-medium">1. What Are Cookies?</h2>
<p>
Browser cookies are text files with small pieces of data downloaded onto your computer
or mobile device. Browser cookies and other similar technologies enable websites and
apps to store information or facilitate access to information stored on your device to
enable certain features and distinguish you from other visitors. These technologies are
used by most website and app providers to let users navigate between pages efficiently,
ensure security of the webpage or application, understand how their websites are used,
remember user preferences and generally improve the user experience. More information on
cookies and their use can be found at{' '}
<a href="https://www.aboutcookies.org" className="text-blue-500">
aboutcookies.org
</a>{' '}
or{' '}
<a href="https://www.allaboutcookies.org" className="text-blue-500">
allaboutcookies.org
</a>
.
</p>
<p>
Cookies set by the website operator are called{' '}
<strong>&quot;first party cookies&quot;</strong> and Cookies set by parties other than
the website operator are called <strong>&quot;third party cookies&quot;</strong>. You
should check the third-party&apos;s website for more information on how they use
cookies.
</p>
<h2 className="mt-8 text-2xl font-medium">2. What Do We Use Cookies For?</h2>
<p>
When you access our Site and Services, we, or companies we work with, may place cookies
(collectively called <strong>&quot;cookies&quot;</strong> in this Cookie Policy) and
similar technologies (such as web beacons, software development kits (
<strong>&quot;SDKs&quot;</strong>), APIs, tags and local storage) on your computer or
other device for the following purposes:
</p>
<h3 className="mt-4 text-lg font-medium">Strictly Necessary purposes</h3>
<p>
Strictly Necessary cookies are essential for our Services to function and therefore
cannot be switched off. They are usually only set in response to actions made by you
which amount to a request for services, such as setting your privacy preferences,
logging in, or filling in forms. These also include cookies we may rely on for security
purposes, such as to prevent unauthorised access attempts. You can set your browser to
block or alert you about these cookies at any time, but some features of our Services
may not work.
</p>
<h3 className="mt-4 text-lg font-medium">Performance purposes</h3>
<p>
We use these cookies to count visits and traffic sources so we can measure and improve
the performance of our Services. These cookies help us to know which pages are the most
and least popular and see how visitors move around the Site, and to resolve any errors
that occur on the Services quickly to provide you with a better experience. For example,
our first party base_device_id cookie is used to provide analytics on how our Site and
Services perform. This cookie lasts for 3 months.
</p>
<h2 className="mt-8 text-2xl font-medium">
3. How to Manage Cookies, Similar Technologies and Targeted Online Mobile Advertising
</h2>
<p>
You have the right to decide whether to accept or reject cookies (except strictly
necessary cookies). You can enable or disable categories of cookies by visiting our{' '}
<button
type="button"
className="appearance-none text-blue-500"
onClick={handleOpenModal}
>
Cookie Preference Manager
</button>
. This includes both first party and third party cookies. You can use the browser with
which you are viewing this website to enable, disable or delete cookies. To do this,
follow the instructions provided by your browser (usually located within the
&quot;Help&quot;, &quot;Tools&quot; or &quot;Edit&quot; settings). However, please note,
if you set your browser to disable cookies, you may not be able to access secure areas
of our Services. Also, if you disable cookies, other parts of our Services may not
function properly.
</p>
<p>
There are also additional tools available to manage third party cookies. You can visit
the DAA&apos;s opt-out portal available at{' '}
<a href="http://optout.aboutads.info/" className="text-blue-500">
optout.aboutads.info
</a>
, the DAA of Canada&apos;s opt-out portal available at{' '}
<a href="https://youradchoices.ca/en/tools" className="text-blue-500">
youradchoices.ca/en/tools
</a>
, or visit the NAI&apos;s opt-out portal available at{' '}
<a href="http://optout.networkadvertising.org/?c=1" className="text-blue-500">
optout.networkadvertising.org/?c=1
</a>
. Residents of the European Union may opt-out of online behavioural advertising served
by the European Interactive Digital Advertising Alliance&apos;s participating member
organizations by visiting{' '}
<a href="https://www.youronlinechoices.eu/" className="text-blue-500">
youronlinechoices.eu
</a>{' '}
or through your mobile device settings, where available and the DAA&apos;s AppChoices
mobile application opt-out offering here:{' '}
<a href="https://youradchoices.com/appchoices" className="text-blue-500">
youradchoices.com/appchoices
</a>
.
</p>
<p>
If you have any questions about our use of cookies or other technologies, please submit
your request to privacy@base.org.
</p>
<h2 className="mt-8 text-2xl font-medium">4. Will This Cookie Policy Be Updated?</h2>
<p>
We may update this Cookie Policy from time to time to reflect, for example, changes to
the cookies we use or for other operational, legal or regulatory reasons. You can also
revisit this page if you wish to keep yourself informed.
</p>
</section>
</main>
{isOpen && <CookiePreferencesModal isOpen={isOpen} onClose={handleCloseModal} />}
</>
);
}

View File

@@ -32,6 +32,7 @@ body {
height: 100%;
margin: 0;
min-height: 100vh;
font-family: '__coinbaseDisplay_7118f1';
}
*,

View File

@@ -0,0 +1,23 @@
import { useCallback } from 'react';
import { useSavedTrackingPreference, useCookie } from '@coinbase/cookie-manager';
import { useRouter } from 'next/router';
import Script from 'next/script';
import initCCA from '../../utils/initCCA';
export default function ClientAnalyticsScript() {
const router = useRouter();
const trackingPreference = useSavedTrackingPreference();
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const [deviceIdCookie, setDeviceIdCookie] = useCookie('base_device_id');
return (
<Script
src="https://static-assets.coinbase.com/js/cca/v0.0.1.js"
onLoad={useCallback(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
() => initCCA(router, trackingPreference, deviceIdCookie, setDeviceIdCookie),
[router, trackingPreference, deviceIdCookie, setDeviceIdCookie],
)}
/>
);
}

View File

@@ -1,5 +1,7 @@
import { useState, useCallback } from 'react';
import getConfig from 'next/config';
import Link from 'next/link';
import { CookiePreferencesModal } from '@coinbase/cookie-banner';
import { Icon } from '../../Icon/Icon';
import { Logo } from '../../Logo/Logo';
@@ -7,7 +9,14 @@ import { Logo } from '../../Logo/Logo';
const { publicRuntimeConfig } = getConfig();
export function Footer() {
const [isOpen, setIsOpen] = useState(false);
const handleOpenModal = useCallback(() => setIsOpen(true), []);
const handleCloseModal = useCallback(() => setIsOpen(false), []);
const currentYear = new Date().getFullYear();
return (
<footer className="z-10 mt-auto flex w-full justify-center bg-gray lg:pb-64">
<div className="flex w-full max-w-[1440px] flex-col justify-between p-8 lg:flex-row">
@@ -44,6 +53,12 @@ export function Footer() {
<a href="https://docs.base.org/terms-of-service">Terms of Service</a>
<br />
<a href="https://docs.base.org/privacy-policy">Privacy Policy</a>
<br />
<Link href="/cookie-policy">Cookie Policy</Link>
<br />
<button type="button" className="appearance-none underline" onClick={handleOpenModal}>
Cookie Manager
</button>
</p>
<p>© {currentYear} Coinbase</p>
</div>
@@ -103,6 +118,7 @@ export function Footer() {
</div>
</div>
</div>
{isOpen && <CookiePreferencesModal isOpen={isOpen} onClose={handleCloseModal} />}
</footer>
);
}

View File

@@ -1,9 +1,10 @@
import { ReactElement, useMemo } from 'react';
import localFont from '@next/font/local';
import { useRouter } from 'next/router';
import { CookieBanner } from '@coinbase/cookie-banner';
import { Footer } from './Footer/Footer';
import { Nav } from './Nav/Nav';
import { Footer } from './Footer/Footer';
const coinbaseDisplay = localFont({
src: [
@@ -56,12 +57,54 @@ const coinbaseMono = localFont({
variable: '--font-coinbase-mono',
});
const cookieBannerTheme = {
colors: {
primary: '#1652F0',
positive: '#05B169',
negative: '#DF5F67',
warning: '#F4C622',
background: '#FFFFFF',
backgroundMuted: '#EEF0F3',
onBackground: '#050F1A',
onBackgroundMuted: '#0A0B0D',
onPrimary: '#FFFFFF',
overlay: 'rgba(17,52,83,0.6)',
},
border: {
border: '1px solid #D8D8D8',
borderRadius: '4px',
},
fontSize: {
sm: '14px',
md: '16px',
},
fontWeight: {
regular: '400',
bold: '500',
},
size: {
xs: '8px',
sm: '16px',
md: '24px',
lg: '32px',
},
breakpoints: {
phone: 560,
desktop: 992,
tablet: 768,
},
zIndex: {
high: 2,
overlay: 1000,
},
};
type LayoutProps = { children: ReactElement };
export function Layout({ children }: LayoutProps) {
const { pathname } = useRouter();
const color: 'black' | 'white' = useMemo(() => {
if (pathname === '/' || pathname === '/jobs/apply') {
if (pathname === '/' || pathname === '/jobs/apply' || pathname === '/cookie-policy') {
return 'black';
}
@@ -75,6 +118,7 @@ export function Layout({ children }: LayoutProps) {
<Nav color={color} />
{children}
<Footer />
<CookieBanner companyName="Base" link="/cookie-policy" theme={cookieBannerTheme} />
</div>
);
}

View File

@@ -0,0 +1,42 @@
import { Framework, Region, TrackerType, TrackingCategory } from '@coinbase/cookie-manager';
export const cookieManagerConfig = {
categories: [
{
id: TrackingCategory.NECESSARY,
expiry: 365,
required: true,
trackers: [
{
id: 'ip_country',
type: TrackerType.COOKIE,
expiry: 10,
},
{
id: 'locale',
type: TrackerType.COOKIE,
},
],
},
{
id: TrackingCategory.PERFORMANCE,
expiry: 365,
trackers: [
{
id: 'base_device_id',
type: TrackerType.COOKIE,
},
],
},
],
geolocationRules: [
{
region: Region.DEFAULT,
framework: Framework.OPT_OUT,
},
{
region: Region.EU,
framework: Framework.OPT_IN,
},
],
};

View File

@@ -3,12 +3,31 @@
/* eslint-disable */
// @ts-nocheck
import getConfig from 'next/config';
import { NextRouter } from 'next/router';
import { TrackingPreference } from '@coinbase/cookie-manager';
import { uuid } from 'uuidv4';
const { publicRuntimeConfig } = getConfig();
const isDevelopment = publicRuntimeConfig.nodeEnv === 'development';
// CCA library loads in _app.tsx
const initCCA = (router) => {
// CCA library loads in ClientAnalyticsScript component
const initCCA = (
router: NextRouter,
trackingPreference: TrackingPreference | undefined,
deviceIdCookie: string | undefined,
setDeviceIdCookie,
) => {
let deviceId: string | undefined = deviceIdCookie;
const trackingAllowed: boolean = trackingPreference?.consent.includes('performance');
const cookieDomain = isDevelopment ? document.location.hostname : 'base.org';
if (!trackingAllowed) {
deviceId = 'base_web_device_id';
} else if (!deviceId) {
deviceId = uuid();
setDeviceIdCookie(deviceId, { domain: cookieDomain });
}
if (window.ClientAnalytics) {
const { init, identify, PlatformName, initNextJsTrackPageview } = window.ClientAnalytics;
@@ -24,7 +43,7 @@ const initCCA = (router) => {
apiEndpoint: 'https://cca-lite.coinbase.com',
});
identify({ deviceId: 'base_web_device_id' });
identify({ deviceId: deviceId });
initNextJsTrackPageview({
nextJsRouter: router,

379
yarn.lock
View File

@@ -222,6 +222,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "@app/base-docs@workspace:apps/base-docs"
dependencies:
"@coinbase/cookie-banner": ^1.0.3
"@coinbase/cookie-manager": ^1.1.1
"@docusaurus/core": 2.3.1
"@docusaurus/logger": 2.3.1
"@docusaurus/module-type-aliases": 2.3.1
@@ -282,6 +284,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "@app/web@workspace:apps/web"
dependencies:
"@coinbase/cookie-banner": ^1.0.3
"@coinbase/cookie-manager": ^1.1.1
"@heroicons/react": ^2.0.18
"@next/font": ^13.1.5
"@types/node": 18.11.18
@@ -301,6 +305,7 @@ __metadata:
react-intl: ^6.2.1
tailwindcss: ^3.2.4
typescript: 4.9.4
uuidv4: ^6.2.13
languageName: unknown
linkType: soft
@@ -332,6 +337,16 @@ __metadata:
languageName: node
linkType: hard
"@babel/code-frame@npm:^7.23.5":
version: 7.23.5
resolution: "@babel/code-frame@npm:7.23.5"
dependencies:
"@babel/highlight": ^7.23.4
chalk: ^2.4.2
checksum: d90981fdf56a2824a9b14d19a4c0e8db93633fd488c772624b4e83e0ceac6039a27cd298a247c3214faa952bf803ba23696172ae7e7235f3b97f43ba278c569a
languageName: node
linkType: hard
"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9, @babel/compat-data@npm:^7.23.2":
version: 7.23.2
resolution: "@babel/compat-data@npm:7.23.2"
@@ -398,6 +413,18 @@ __metadata:
languageName: node
linkType: hard
"@babel/generator@npm:^7.23.6":
version: 7.23.6
resolution: "@babel/generator@npm:7.23.6"
dependencies:
"@babel/types": ^7.23.6
"@jridgewell/gen-mapping": ^0.3.2
"@jridgewell/trace-mapping": ^0.3.17
jsesc: ^2.5.1
checksum: 1a1a1c4eac210f174cd108d479464d053930a812798e09fee069377de39a893422df5b5b146199ead7239ae6d3a04697b45fc9ac6e38e0f6b76374390f91fc6c
languageName: node
linkType: hard
"@babel/helper-annotate-as-pure@npm:^7.22.5":
version: 7.22.5
resolution: "@babel/helper-annotate-as-pure@npm:7.22.5"
@@ -511,7 +538,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-module-imports@npm:^7.22.15, @babel/helper-module-imports@npm:^7.22.5":
"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.22.15, @babel/helper-module-imports@npm:^7.22.5":
version: 7.22.15
resolution: "@babel/helper-module-imports@npm:7.22.15"
dependencies:
@@ -618,6 +645,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-string-parser@npm:^7.23.4":
version: 7.23.4
resolution: "@babel/helper-string-parser@npm:7.23.4"
checksum: c0641144cf1a7e7dc93f3d5f16d5327465b6cf5d036b48be61ecba41e1eece161b48f46b7f960951b67f8c3533ce506b16dece576baef4d8b3b49f8c65410f90
languageName: node
linkType: hard
"@babel/helper-validator-identifier@npm:^7.22.20":
version: 7.22.20
resolution: "@babel/helper-validator-identifier@npm:7.22.20"
@@ -665,6 +699,17 @@ __metadata:
languageName: node
linkType: hard
"@babel/highlight@npm:^7.23.4":
version: 7.23.4
resolution: "@babel/highlight@npm:7.23.4"
dependencies:
"@babel/helper-validator-identifier": ^7.22.20
chalk: ^2.4.2
js-tokens: ^4.0.0
checksum: 643acecdc235f87d925979a979b539a5d7d1f31ae7db8d89047269082694122d11aa85351304c9c978ceeb6d250591ccadb06c366f358ccee08bb9c122476b89
languageName: node
linkType: hard
"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.8, @babel/parser@npm:^7.18.8, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0":
version: 7.23.0
resolution: "@babel/parser@npm:7.23.0"
@@ -674,6 +719,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/parser@npm:^7.23.6":
version: 7.23.6
resolution: "@babel/parser@npm:7.23.6"
bin:
parser: ./bin/babel-parser.js
checksum: 140801c43731a6c41fd193f5c02bc71fd647a0360ca616b23d2db8be4b9739b9f951a03fc7c2db4f9b9214f4b27c1074db0f18bc3fa653783082d5af7c8860d5
languageName: node
linkType: hard
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.15":
version: 7.22.15
resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.22.15"
@@ -1834,6 +1888,24 @@ __metadata:
languageName: node
linkType: hard
"@babel/traverse@npm:^7.4.5":
version: 7.23.7
resolution: "@babel/traverse@npm:7.23.7"
dependencies:
"@babel/code-frame": ^7.23.5
"@babel/generator": ^7.23.6
"@babel/helper-environment-visitor": ^7.22.20
"@babel/helper-function-name": ^7.23.0
"@babel/helper-hoist-variables": ^7.22.5
"@babel/helper-split-export-declaration": ^7.22.6
"@babel/parser": ^7.23.6
"@babel/types": ^7.23.6
debug: ^4.3.1
globals: ^11.1.0
checksum: d4a7afb922361f710efc97b1e25ec343fab8b2a4ddc81ca84f9a153f22d4482112cba8f263774be8d297918b6c4767c7a98988ab4e53ac73686c986711dd002e
languageName: node
linkType: hard
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.7, @babel/types@npm:^7.16.8, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3":
version: 7.23.0
resolution: "@babel/types@npm:7.23.0"
@@ -1845,6 +1917,17 @@ __metadata:
languageName: node
linkType: hard
"@babel/types@npm:^7.23.6":
version: 7.23.6
resolution: "@babel/types@npm:7.23.6"
dependencies:
"@babel/helper-string-parser": ^7.23.4
"@babel/helper-validator-identifier": ^7.22.20
to-fast-properties: ^2.0.0
checksum: 68187dbec0d637f79bc96263ac95ec8b06d424396678e7e225492be866414ce28ebc918a75354d4c28659be6efe30020b4f0f6df81cc418a2d30645b690a8de0
languageName: node
linkType: hard
"@base-org/base-web@workspace:.":
version: 0.0.0-use.local
resolution: "@base-org/base-web@workspace:."
@@ -1998,6 +2081,56 @@ __metadata:
languageName: node
linkType: hard
"@coinbase/cookie-banner@npm:1.0.1":
version: 1.0.1
resolution: "@coinbase/cookie-banner@npm:1.0.1"
dependencies:
"@coinbase/cookie-manager": ^1.0.0
react-intl: ^6.5.1
styled-components: ^5.3.6
peerDependencies:
react: ^18.2.0
react-dom: ^18.1.0
checksum: 61d7f165d0068a898c3acb29104915a66bfb1698b9637e98671473d599dcd74c8032fdb37ed9fb44e746c87a90e4330ef0166960e14dffa87ee53f1508c75537
languageName: node
linkType: hard
"@coinbase/cookie-banner@npm:^1.0.3":
version: 1.0.3
resolution: "@coinbase/cookie-banner@npm:1.0.3"
dependencies:
"@coinbase/cookie-manager": 1.1.1
react-intl: ^6.5.1
styled-components: ^5.3.6
peerDependencies:
react: ^18.2.0
react-dom: ^18.1.0
checksum: e2bf72e385aad07f847bbeca2d200b9bd7296f4c85131e2de0aebf318fadff796120c8b6ca26078d12ba98aae623ec69515dae50c276c2818c8c847493c0d37a
languageName: node
linkType: hard
"@coinbase/cookie-manager@npm:1.1.0":
version: 1.1.0
resolution: "@coinbase/cookie-manager@npm:1.1.0"
dependencies:
"@coinbase/cookie-banner": 1.0.1
"@coinbase/cookie-manager": 1.1.0
js-cookie: ^3.0.5
checksum: b392898328e97db5cc016b8e98252c35730f50df02ead64a43a80e573e31972ab7629d8fd909ab3315db2ee8f2550689fafeefa5fd68522bf1f5fab715114d3f
languageName: node
linkType: hard
"@coinbase/cookie-manager@npm:1.1.1, @coinbase/cookie-manager@npm:^1.0.0, @coinbase/cookie-manager@npm:^1.1.1":
version: 1.1.1
resolution: "@coinbase/cookie-manager@npm:1.1.1"
dependencies:
"@coinbase/cookie-banner": 1.0.1
"@coinbase/cookie-manager": 1.1.0
js-cookie: ^3.0.5
checksum: 20a67646cf1f37a1db0eb0b2e2e6989f6f34b1a4881d81dead1829b44fe7400f2acf2ebdf2da049ce9804fca4b6f07e6b71b9fe44ffe01c231c1c95f01ea466a
languageName: node
linkType: hard
"@coinbase/wallet-sdk@npm:^3.6.6":
version: 3.7.2
resolution: "@coinbase/wallet-sdk@npm:3.7.2"
@@ -2613,6 +2746,15 @@ __metadata:
languageName: node
linkType: hard
"@emotion/is-prop-valid@npm:^1.1.0":
version: 1.2.1
resolution: "@emotion/is-prop-valid@npm:1.2.1"
dependencies:
"@emotion/memoize": ^0.8.1
checksum: 8f42dc573a3fad79b021479becb639b8fe3b60bdd1081a775d32388bca418ee53074c7602a4c845c5f75fa6831eb1cbdc4d208cc0299f57014ed3a02abcad16a
languageName: node
linkType: hard
"@emotion/memoize@npm:0.7.4":
version: 0.7.4
resolution: "@emotion/memoize@npm:0.7.4"
@@ -2620,6 +2762,27 @@ __metadata:
languageName: node
linkType: hard
"@emotion/memoize@npm:^0.8.1":
version: 0.8.1
resolution: "@emotion/memoize@npm:0.8.1"
checksum: a19cc01a29fcc97514948eaab4dc34d8272e934466ed87c07f157887406bc318000c69ae6f813a9001c6a225364df04249842a50e692ef7a9873335fbcc141b0
languageName: node
linkType: hard
"@emotion/stylis@npm:^0.8.4":
version: 0.8.5
resolution: "@emotion/stylis@npm:0.8.5"
checksum: 67ff5958449b2374b329fb96e83cb9025775ffe1e79153b499537c6c8b2eb64b77f32d7b5d004d646973662356ceb646afd9269001b97c54439fceea3203ce65
languageName: node
linkType: hard
"@emotion/unitless@npm:^0.7.4":
version: 0.7.5
resolution: "@emotion/unitless@npm:0.7.5"
checksum: f976e5345b53fae9414a7b2e7a949aa6b52f8bdbcc84458b1ddc0729e77ba1d1dfdff9960e0da60183877873d3a631fa24d9695dd714ed94bcd3ba5196586a6b
languageName: node
linkType: hard
"@eslint-community/eslint-utils@npm:^4.2.0":
version: 4.4.0
resolution: "@eslint-community/eslint-utils@npm:4.4.0"
@@ -3139,6 +3302,16 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/ecma402-abstract@npm:1.18.0":
version: 1.18.0
resolution: "@formatjs/ecma402-abstract@npm:1.18.0"
dependencies:
"@formatjs/intl-localematcher": 0.5.2
tslib: ^2.4.0
checksum: 22be7f02397d565de621bba5d57135bf7a360b4f3f04e7d75194854f47c22fa8cc2e43ede2c6d1dea885d3cb5df6f58e82ea7ba457a7b3e208403372cd6b90f3
languageName: node
linkType: hard
"@formatjs/fast-memoize@npm:2.2.0":
version: 2.2.0
resolution: "@formatjs/fast-memoize@npm:2.2.0"
@@ -3159,6 +3332,17 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/icu-messageformat-parser@npm:2.7.3":
version: 2.7.3
resolution: "@formatjs/icu-messageformat-parser@npm:2.7.3"
dependencies:
"@formatjs/ecma402-abstract": 1.18.0
"@formatjs/icu-skeleton-parser": 1.7.0
tslib: ^2.4.0
checksum: 3efd07e26dfd768cfb4ebee72787f150eb7c65849610d0b08b09ffd26f127ce2a7027dc901a3a2ee536597a26bce289bff3f3d9de8247c274e364c0666f685d6
languageName: node
linkType: hard
"@formatjs/icu-skeleton-parser@npm:1.6.2":
version: 1.6.2
resolution: "@formatjs/icu-skeleton-parser@npm:1.6.2"
@@ -3169,6 +3353,16 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/icu-skeleton-parser@npm:1.7.0":
version: 1.7.0
resolution: "@formatjs/icu-skeleton-parser@npm:1.7.0"
dependencies:
"@formatjs/ecma402-abstract": 1.18.0
tslib: ^2.4.0
checksum: a461d95b0a39a52d2acb776cb60818188f32ca5d8be7d97440b892bb30564e852410c3fffe96c4c5e6793934f3f694958da8297bd7e3b0cbe114f11223a57013
languageName: node
linkType: hard
"@formatjs/intl-displaynames@npm:6.6.0":
version: 6.6.0
resolution: "@formatjs/intl-displaynames@npm:6.6.0"
@@ -3180,6 +3374,17 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/intl-displaynames@npm:6.6.4":
version: 6.6.4
resolution: "@formatjs/intl-displaynames@npm:6.6.4"
dependencies:
"@formatjs/ecma402-abstract": 1.18.0
"@formatjs/intl-localematcher": 0.5.2
tslib: ^2.4.0
checksum: f1300ac7e7cc05041464a7f5a19ced83361fb7846d4c2727ede3c3bae836f0995565e44128b9de8182f07c13e8594d4c84c73c375824c9ad991031c96b1cd141
languageName: node
linkType: hard
"@formatjs/intl-listformat@npm:7.5.0":
version: 7.5.0
resolution: "@formatjs/intl-listformat@npm:7.5.0"
@@ -3191,6 +3396,17 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/intl-listformat@npm:7.5.3":
version: 7.5.3
resolution: "@formatjs/intl-listformat@npm:7.5.3"
dependencies:
"@formatjs/ecma402-abstract": 1.18.0
"@formatjs/intl-localematcher": 0.5.2
tslib: ^2.4.0
checksum: c3f9ba6b6bf400e3f86da652b303db093516cae85c4dafed7ae675e905bbbcd595db402e42601f50ea1feba9229848a63602ab39ac85553df622b7645f111969
languageName: node
linkType: hard
"@formatjs/intl-localematcher@npm:0.4.2":
version: 0.4.2
resolution: "@formatjs/intl-localematcher@npm:0.4.2"
@@ -3200,6 +3416,15 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/intl-localematcher@npm:0.5.2":
version: 0.5.2
resolution: "@formatjs/intl-localematcher@npm:0.5.2"
dependencies:
tslib: ^2.4.0
checksum: a741d69e9d3b71bee19726484de4a296711d96dc27f588d995b9e2079d3bc5d06370b6e84136003197d558d45f9faf507321627a78d8cd986705b78ec701c016
languageName: node
linkType: hard
"@formatjs/intl@npm:2.9.4":
version: 2.9.4
resolution: "@formatjs/intl@npm:2.9.4"
@@ -3220,6 +3445,26 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/intl@npm:2.9.9":
version: 2.9.9
resolution: "@formatjs/intl@npm:2.9.9"
dependencies:
"@formatjs/ecma402-abstract": 1.18.0
"@formatjs/fast-memoize": 2.2.0
"@formatjs/icu-messageformat-parser": 2.7.3
"@formatjs/intl-displaynames": 6.6.4
"@formatjs/intl-listformat": 7.5.3
intl-messageformat: 10.5.8
tslib: ^2.4.0
peerDependencies:
typescript: 5
peerDependenciesMeta:
typescript:
optional: true
checksum: a235f5366834c751313763071036a321a24f429435de48a8f30289111ef96c4109ee65507e0aee82365629673744aaf9a017c38c2998d8e2df18514fd79811e4
languageName: node
linkType: hard
"@graphql-eslint/eslint-plugin@npm:^3.10.4":
version: 3.20.1
resolution: "@graphql-eslint/eslint-plugin@npm:3.20.1"
@@ -6505,6 +6750,13 @@ __metadata:
languageName: node
linkType: hard
"@types/uuid@npm:8.3.4":
version: 8.3.4
resolution: "@types/uuid@npm:8.3.4"
checksum: 6f11f3ff70f30210edaa8071422d405e9c1d4e53abbe50fdce365150d3c698fe7bbff65c1e71ae080cbfb8fded860dbb5e174da96fdbbdfcaa3fb3daa474d20f
languageName: node
linkType: hard
"@types/ws@npm:^7.4.4":
version: 7.4.7
resolution: "@types/ws@npm:7.4.7"
@@ -8533,6 +8785,21 @@ __metadata:
languageName: node
linkType: hard
"babel-plugin-styled-components@npm:>= 1.12.0":
version: 2.1.4
resolution: "babel-plugin-styled-components@npm:2.1.4"
dependencies:
"@babel/helper-annotate-as-pure": ^7.22.5
"@babel/helper-module-imports": ^7.22.5
"@babel/plugin-syntax-jsx": ^7.22.5
lodash: ^4.17.21
picomatch: ^2.3.1
peerDependencies:
styled-components: ">= 2"
checksum: d791aed68d975dae4f73055f86cd47afa99cb402b8113acdaf5678c8b6fba2cbc15543f2debe8ed09becb198aae8be2adfe268ad41f4bca917288e073a622bf8
languageName: node
linkType: hard
"babel-plugin-transform-react-remove-prop-types@npm:^0.4.24":
version: 0.4.24
resolution: "babel-plugin-transform-react-remove-prop-types@npm:0.4.24"
@@ -9180,6 +9447,13 @@ __metadata:
languageName: node
linkType: hard
"camelize@npm:^1.0.0":
version: 1.0.1
resolution: "camelize@npm:1.0.1"
checksum: 91d8611d09af725e422a23993890d22b2b72b4cabf7239651856950c76b4bf53fe0d0da7c5e4db05180e898e4e647220e78c9fbc976113bd96d603d1fcbfcb99
languageName: node
linkType: hard
"caniuse-api@npm:^3.0.0":
version: 3.0.0
resolution: "caniuse-api@npm:3.0.0"
@@ -10065,6 +10339,13 @@ __metadata:
languageName: node
linkType: hard
"css-color-keywords@npm:^1.0.0":
version: 1.0.0
resolution: "css-color-keywords@npm:1.0.0"
checksum: 8f125e3ad477bd03c77b533044bd9e8a6f7c0da52d49bbc0bbe38327b3829d6ba04d368ca49dd9ff3b667d2fc8f1698d891c198bbf8feade1a5501bf5a296408
languageName: node
linkType: hard
"css-declaration-sorter@npm:^6.3.1":
version: 6.4.1
resolution: "css-declaration-sorter@npm:6.4.1"
@@ -10147,6 +10428,17 @@ __metadata:
languageName: node
linkType: hard
"css-to-react-native@npm:^3.0.0":
version: 3.2.0
resolution: "css-to-react-native@npm:3.2.0"
dependencies:
camelize: ^1.0.0
css-color-keywords: ^1.0.0
postcss-value-parser: ^4.0.2
checksum: 263be65e805aef02c3f20c064665c998a8c35293e1505dbe6e3054fb186b01a9897ac6cf121f9840e5a9dfe3fb3994f6fcd0af84a865f1df78ba5bf89e77adce
languageName: node
linkType: hard
"css-tree@npm:^1.1.2, css-tree@npm:^1.1.3":
version: 1.1.3
resolution: "css-tree@npm:1.1.3"
@@ -10348,7 +10640,7 @@ __metadata:
languageName: node
linkType: hard
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4":
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4":
version: 4.3.4
resolution: "debug@npm:4.3.4"
dependencies:
@@ -13330,7 +13622,7 @@ __metadata:
languageName: node
linkType: hard
"hoist-non-react-statics@npm:^3.1.0, hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2":
"hoist-non-react-statics@npm:^3.0.0, hoist-non-react-statics@npm:^3.1.0, hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2":
version: 3.3.2
resolution: "hoist-non-react-statics@npm:3.3.2"
dependencies:
@@ -13789,6 +14081,18 @@ __metadata:
languageName: node
linkType: hard
"intl-messageformat@npm:10.5.8":
version: 10.5.8
resolution: "intl-messageformat@npm:10.5.8"
dependencies:
"@formatjs/ecma402-abstract": 1.18.0
"@formatjs/fast-memoize": 2.2.0
"@formatjs/icu-messageformat-parser": 2.7.3
tslib: ^2.4.0
checksum: f0fc0c4ce4f711ac46227e1b41e1494bfadfd047e4581299ef4fbf79dcbd85fcc9851e7051432a02239fab9673c212a50f03060b5f83a930c286d4ba6c2261de
languageName: node
linkType: hard
"invariant@npm:^2.2.4":
version: 2.2.4
resolution: "invariant@npm:2.2.4"
@@ -15028,6 +15332,13 @@ __metadata:
languageName: node
linkType: hard
"js-cookie@npm:^3.0.5":
version: 3.0.5
resolution: "js-cookie@npm:3.0.5"
checksum: 2dbd2809c6180fbcf060c6957cb82dbb47edae0ead6bd71cbeedf448aa6b6923115003b995f7d3e3077bfe2cb76295ea6b584eb7196cca8ba0a09f389f64967a
languageName: node
linkType: hard
"js-sha3@npm:0.8.0":
version: 0.8.0
resolution: "js-sha3@npm:0.8.0"
@@ -17898,7 +18209,7 @@ __metadata:
languageName: node
linkType: hard
"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0":
"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.0.2, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0":
version: 4.2.0
resolution: "postcss-value-parser@npm:4.2.0"
checksum: 819ffab0c9d51cf0acbabf8996dffbfafbafa57afc0e4c98db88b67f2094cb44488758f06e5da95d7036f19556a4a732525e84289a425f4f6fd8e412a9d7442f
@@ -18566,6 +18877,30 @@ __metadata:
languageName: node
linkType: hard
"react-intl@npm:^6.5.1":
version: 6.5.5
resolution: "react-intl@npm:6.5.5"
dependencies:
"@formatjs/ecma402-abstract": 1.18.0
"@formatjs/icu-messageformat-parser": 2.7.3
"@formatjs/intl": 2.9.9
"@formatjs/intl-displaynames": 6.6.4
"@formatjs/intl-listformat": 7.5.3
"@types/hoist-non-react-statics": ^3.3.1
"@types/react": 16 || 17 || 18
hoist-non-react-statics: ^3.3.2
intl-messageformat: 10.5.8
tslib: ^2.4.0
peerDependencies:
react: ^16.6.0 || 17 || 18
typescript: 5
peerDependenciesMeta:
typescript:
optional: true
checksum: 3413dc812f5a8723a9019905251eafae78fd3c2675291d97dd2ae0d105a52602c4be07b45e2ddeca7d42c5eb2b7b8bfe6b59719a9bdb0679fa497568b4e9d3e9
languageName: node
linkType: hard
"react-is@npm:^16.12.0 || ^17.0.0 || ^18.0.0, react-is@npm:^18.0.0, react-is@npm:^18.2.0":
version: 18.2.0
resolution: "react-is@npm:18.2.0"
@@ -20388,6 +20723,28 @@ __metadata:
languageName: node
linkType: hard
"styled-components@npm:^5.3.6":
version: 5.3.11
resolution: "styled-components@npm:5.3.11"
dependencies:
"@babel/helper-module-imports": ^7.0.0
"@babel/traverse": ^7.4.5
"@emotion/is-prop-valid": ^1.1.0
"@emotion/stylis": ^0.8.4
"@emotion/unitless": ^0.7.4
babel-plugin-styled-components: ">= 1.12.0"
css-to-react-native: ^3.0.0
hoist-non-react-statics: ^3.0.0
shallowequal: ^1.1.0
supports-color: ^5.5.0
peerDependencies:
react: ">= 16.8.0"
react-dom: ">= 16.8.0"
react-is: ">= 16.8.0"
checksum: 10edd4dae3b0231ec02d86bdd09c88e894eedfa7e9d4f8e562b09fb69c67a27d586cbcf35c785002d59b3bf11e6c0940b0efce40d13ae9ed148b26b1dc8f3284
languageName: node
linkType: hard
"styled-jsx@npm:5.0.2":
version: 5.0.2
resolution: "styled-jsx@npm:5.0.2"
@@ -20469,7 +20826,7 @@ __metadata:
languageName: node
linkType: hard
"supports-color@npm:^5.3.0":
"supports-color@npm:^5.3.0, supports-color@npm:^5.5.0":
version: 5.5.0
resolution: "supports-color@npm:5.5.0"
dependencies:
@@ -21789,7 +22146,7 @@ __metadata:
languageName: node
linkType: hard
"uuid@npm:^8.3.2":
"uuid@npm:8.3.2, uuid@npm:^8.3.2":
version: 8.3.2
resolution: "uuid@npm:8.3.2"
bin:
@@ -21798,6 +22155,16 @@ __metadata:
languageName: node
linkType: hard
"uuidv4@npm:^6.2.13":
version: 6.2.13
resolution: "uuidv4@npm:6.2.13"
dependencies:
"@types/uuid": 8.3.4
uuid: 8.3.2
checksum: 25b3ce1d5860c7fb2232270f20d36c320e52817356cc0dfc88610965e6faced3e7050b3628be08ed08092dffc099288ed24ac0404a2869d59963a2be1939f476
languageName: node
linkType: hard
"v8-compile-cache-lib@npm:^3.0.1":
version: 3.0.1
resolution: "v8-compile-cache-lib@npm:3.0.1"