Skip to content

Commit 7720ab6

Browse files
committed
Working cookie banner
1 parent a18bd94 commit 7720ab6

File tree

8 files changed

+395
-5
lines changed

8 files changed

+395
-5
lines changed

package-lock.json

Lines changed: 42 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
"posthog-docusaurus": "^2.0.4",
2929
"prism-react-renderer": "^2.3.0",
3030
"react": "^19.0.0",
31+
"react-cookie-consent": "^9.0.0",
3132
"react-dom": "^19.0.0",
32-
"react-icons": "^5.5.0"
33+
"react-icons": "^5.5.0",
34+
"tldts": "^7.0.14"
3335
},
3436
"devDependencies": {
3537
"@docusaurus/module-type-aliases": "^3.8.1",
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import React, { useState, useEffect } from 'react';
2+
import ReactCookieConsent from 'react-cookie-consent';
3+
import { getDomain } from 'tldts';
4+
import { CONSENT_COOKIE_NAME } from '../../constants';
5+
import { dispatchConsentEvent } from '../../lib/analytics/consent-manager';
6+
import styles from './styles.module.css';
7+
8+
export function CookieConsent() {
9+
const [cookieDomain, setCookieDomain] = useState<string | null>(null);
10+
11+
useEffect(() => {
12+
// Get the appropriate domain using tldts
13+
if (typeof window !== 'undefined') {
14+
if(window.location.hostname === 'localhost') {
15+
setCookieDomain('localhost');
16+
return;
17+
} else {
18+
setCookieDomain(getDomain(window.location.hostname));
19+
}
20+
}
21+
}, []);
22+
23+
const handleAccept = () => {
24+
dispatchConsentEvent(true);
25+
};
26+
27+
const handleDecline = () => {
28+
dispatchConsentEvent(false);
29+
};
30+
31+
const extraCookieOptions = cookieDomain
32+
? {
33+
domain: cookieDomain,
34+
}
35+
: {};
36+
37+
console.log("cookieDomain", cookieDomain);
38+
39+
return (
40+
<div role="banner" aria-label="Cookie consent banner" aria-live="polite">
41+
<ReactCookieConsent
42+
location="bottom"
43+
buttonText="Accept"
44+
declineButtonText="Decline"
45+
cookieName={CONSENT_COOKIE_NAME}
46+
expires={365}
47+
enableDeclineButton={true}
48+
onAccept={handleAccept}
49+
onDecline={handleDecline}
50+
containerClasses={styles.container}
51+
buttonClasses={styles.acceptButton}
52+
buttonWrapperClasses={styles.buttonWrapper}
53+
declineButtonClasses={styles.declineButton}
54+
extraCookieOptions={extraCookieOptions}
55+
disableStyles={true}
56+
ariaAcceptLabel="Accept cookies"
57+
ariaDeclineLabel="Decline cookies"
58+
>
59+
<div className={styles.content}>
60+
<svg
61+
className={styles.cookieIcon}
62+
xmlns="http://www.w3.org/2000/svg"
63+
width="1.5em"
64+
height="1.5em"
65+
viewBox="0 0 24 24"
66+
fill="none"
67+
stroke="currentColor"
68+
strokeWidth="2"
69+
strokeLinecap="round"
70+
strokeLinejoin="round">
71+
<path d="M12 2a10 10 0 1 0 10 10 4 4 0 0 1-5-5 4 4 0 0 1-5-5" /><path d="M8.5 8.5v.01" /><path d="M16 15.5v.01" /><path d="M12 12v.01" /><path d="M11 17v.01" /><path d="M7 14v.01" />
72+
</svg>
73+
<span>Like most of the internet, we use cookies. Are you OK with that?</span>
74+
</div>
75+
</ReactCookieConsent>
76+
</div>
77+
);
78+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
.container {
2+
position: fixed;
3+
bottom: 0;
4+
left: 8px;
5+
right: 8px;
6+
z-index: 999;
7+
color: white;
8+
font-weight: 600;
9+
border-radius: 8px 8px 0 0;
10+
padding: 16px;
11+
display: flex;
12+
flex-wrap: wrap;
13+
align-items: center;
14+
justify-content: space-between;
15+
gap: 16px;
16+
font-size: 14px;
17+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
18+
}
19+
20+
.container {
21+
background: black !important;
22+
color: white;
23+
border-top-color: #222;
24+
}
25+
26+
/* Dark mode support */
27+
[data-theme='dark'] .container {
28+
background-color: white !important;
29+
color: black;
30+
border-top-color: #e5e5e5;
31+
}
32+
33+
@media (min-width: 768px) {
34+
.container {
35+
padding: 16px 32px;
36+
}
37+
}
38+
39+
.content {
40+
display: flex;
41+
align-items: center;
42+
gap: 8px;
43+
}
44+
45+
.cookieIcon {
46+
display: none;
47+
flex-shrink: 0;
48+
}
49+
50+
@media (min-width: 768px) {
51+
.cookieIcon {
52+
display: block;
53+
}
54+
}
55+
56+
.buttonWrapper {
57+
display: flex;
58+
flex-direction: row-reverse;
59+
align-items: center;
60+
gap: 8px;
61+
}
62+
63+
.acceptButton {
64+
background-color: white;
65+
color: black;
66+
border: 1px solid #262626;
67+
border-radius: 6px;
68+
padding: 8px 16px;
69+
margin-right: 8px;
70+
font-size: 14px;
71+
font-weight: bold;
72+
cursor: pointer;
73+
transition: opacity 0.2s;
74+
}
75+
76+
.acceptButton:hover {
77+
opacity: 0.7;
78+
}
79+
80+
.acceptButton:focus {
81+
outline: none;
82+
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
83+
}
84+
85+
[data-theme='dark'] .acceptButton {
86+
background-color: black;
87+
color: white;
88+
border-color: #e5e5e5;
89+
}
90+
91+
.declineButton {
92+
background-color: black;
93+
color: white;
94+
border: 1px solid #262626;
95+
border-radius: 6px;
96+
padding: 8px 16px;
97+
font-size: 14px;
98+
font-weight: bold;
99+
cursor: pointer;
100+
transition: opacity 0.2s;
101+
}
102+
103+
.declineButton:hover {
104+
opacity: 0.7;
105+
}
106+
107+
.declineButton:focus {
108+
outline: none;
109+
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
110+
}
111+
112+
[data-theme='dark'] .declineButton {
113+
background-color: white;
114+
color: black;
115+
border-color: #e5e5e5;
116+
}
117+
118+
/* Ensure the banner is above other content */
119+
.container {
120+
animation: slideUp 0.3s ease-out;
121+
}
122+
123+
@keyframes slideUp {
124+
from {
125+
transform: translateY(100%);
126+
}
127+
to {
128+
transform: translateY(0);
129+
}
130+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { hasConsent, onConsentChange } from '../../lib/analytics/consent-manager';
3+
4+
declare global {
5+
interface Window {
6+
posthog?: any;
7+
}
8+
}
9+
10+
export function PostHogProvider({ children }: { children: React.ReactNode }) {
11+
const [isPostHogEnabled, setIsPostHogEnabled] = useState(false);
12+
13+
useEffect(() => {
14+
// Check initial consent status
15+
const consentGiven = hasConsent();
16+
setIsPostHogEnabled(consentGiven);
17+
18+
if (consentGiven) {
19+
enablePostHog();
20+
} else {
21+
disablePostHog();
22+
}
23+
24+
// Listen for consent changes
25+
const cleanup = onConsentChange((granted) => {
26+
setIsPostHogEnabled(granted);
27+
if (granted) {
28+
enablePostHog();
29+
} else {
30+
disablePostHog();
31+
}
32+
});
33+
34+
return cleanup;
35+
}, []);
36+
37+
const enablePostHog = () => {
38+
if (typeof window !== 'undefined' && window.posthog) {
39+
// Re-initialize PostHog if it was previously disabled
40+
window.posthog.opt_in_capturing();
41+
window.posthog.start_session_recording();
42+
}
43+
};
44+
45+
const disablePostHog = () => {
46+
if (typeof window !== 'undefined' && window.posthog) {
47+
window.posthog.opt_out_capturing();
48+
window.posthog.stop_session_recording();
49+
if (document) {
50+
const cookies = document.cookie.split(';');
51+
cookies.forEach(cookie => {
52+
const eqPos = cookie.indexOf('=');
53+
const name = eqPos > -1 ? cookie.substr(0, eqPos).trim() : cookie.trim();
54+
if (name.startsWith('ph_') || name.startsWith('posthog')) {
55+
// Delete PostHog related cookies
56+
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=.roocode.com`;
57+
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/`;
58+
}
59+
});
60+
}
61+
}
62+
};
63+
64+
return <>{children}</>;
65+
}

src/constants.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,7 @@ export const OPEN_VSX_URL = 'https://open-vsx.org/extension/RooVeterinaryInc/roo
2828
export const CONTACT_EMAIL = 'mailto:[email protected]';
2929
export const CAREERS_URL = 'https://careers.roocode.com';
3030
export const WEBSITE_PRIVACY_URL = 'https://roocode.com/privacy';
31-
export const EXTENSION_PRIVACY_URL = `${GITHUB_MAIN_REPO_URL}/blob/main/PRIVACY.md`;
31+
export const EXTENSION_PRIVACY_URL = `${GITHUB_MAIN_REPO_URL}/blob/main/PRIVACY.md`;
32+
33+
// Cookies
34+
export const CONSENT_COOKIE_NAME = "roo-code-cookie-consent";

0 commit comments

Comments
 (0)