From d34e57382fc3ced6035c8dec09719b468cfdd6d9 Mon Sep 17 00:00:00 2001 From: Debbie Matthews Date: Thu, 16 Oct 2025 21:17:05 -0700 Subject: [PATCH] Revert "Onetrust cookie banner (#1354)" This reverts commit e0398a66f2dad9e3d212424f6c0951c0cf71ac41. --- README.md | 1 + components/navigation/cookiePolicyButton.js | 11 - components/navigation/footer.js | 14 +- components/utilities/breadCrumbs.js | 2 +- components/utilities/cookieSettingsModal.js | 73 ++++++ .../utilities/cookieSettingsModal.module.css | 167 ++++++++++++++ components/utilities/gdpr.js | 217 ++++++++++++++++++ components/utilities/gdpr.module.css | 42 ++++ content/cookie-settings.md | 21 ++ .../develop/concepts/configuration/options.md | 14 ++ content/gdpr-banner.md | 8 + netlify.toml | 6 - next-sitemap.config.js | 9 +- next.config.mjs | 100 ++++---- pages/404.js | 13 -- pages/[...slug].js | 58 +++-- pages/index.js | 58 +++-- public/scripts/segment.js | 63 ----- styles/cookie-banner.scss | 148 ------------ styles/main.scss | 1 - 20 files changed, 693 insertions(+), 333 deletions(-) delete mode 100644 components/navigation/cookiePolicyButton.js create mode 100644 components/utilities/cookieSettingsModal.js create mode 100644 components/utilities/cookieSettingsModal.module.css create mode 100644 components/utilities/gdpr.js create mode 100644 components/utilities/gdpr.module.css create mode 100644 content/cookie-settings.md create mode 100644 content/gdpr-banner.md delete mode 100644 public/scripts/segment.js delete mode 100644 styles/cookie-banner.scss diff --git a/README.md b/README.md index 032871aa2..02caf52f3 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ To add, edit, or delete content in our documentation, you have to modify Markdow - `kb/` Contains `.md` files that populate the Knowledge Base. - `library/` Contains `.md` files that populate the Streamlit Library section. - `streamlit-cloud/`Contains `.md` files that populate the Streamlit Community Cloud section. +- `gdpr-banner.md` You'll never have to edit this file. - `index.md` Contains text that populates the index page. - `menu.md` This is a special file containing only front matter that defines the docs Menu. You will need to add an entry on this file for each new page you create within the docs' site. diff --git a/components/navigation/cookiePolicyButton.js b/components/navigation/cookiePolicyButton.js deleted file mode 100644 index 3ffd3cc7f..000000000 --- a/components/navigation/cookiePolicyButton.js +++ /dev/null @@ -1,11 +0,0 @@ -const CookiePolicyButton = ({ children, className, ...props }) => { - const classes = `ot-sdk-show-settings${className ? ` ${className}` : ""}`; - - return ( - - ); -}; - -export default CookiePolicyButton; diff --git a/components/navigation/footer.js b/components/navigation/footer.js index b43d84c23..474b8e874 100644 --- a/components/navigation/footer.js +++ b/components/navigation/footer.js @@ -1,13 +1,8 @@ import Link from "next/link"; -import dynamic from "next/dynamic"; import styles from "./footer.module.css"; -const CookiePolicyButton = dynamic(() => import("./cookiePolicyButton"), { - ssr: false, -}); - -const Footer = () => { +const Footer = ({ setIsTelemetryModalVisible }) => { return ( diff --git a/components/utilities/breadCrumbs.js b/components/utilities/breadCrumbs.js index c0a50f68a..e0d021174 100644 --- a/components/utilities/breadCrumbs.js +++ b/components/utilities/breadCrumbs.js @@ -68,7 +68,7 @@ const BreadCrumbs = ({ slug, menu }) => { } // Then, we add a couple pages that don't need breadcrumbs, such as /menu, /index, etc. - filesToExclude.push("index", "menu"); + filesToExclude.push("index", "gdpr-banner", "menu", "cookie-settings"); // Now, we throw the error if any page that's not on the filesToExclude array is missing in menu.md if (path.length === 0 && !filesToExclude.includes(slug[0])) { diff --git a/components/utilities/cookieSettingsModal.js b/components/utilities/cookieSettingsModal.js new file mode 100644 index 000000000..25536dfae --- /dev/null +++ b/components/utilities/cookieSettingsModal.js @@ -0,0 +1,73 @@ +import classNames from "classnames"; + +import content from "../../content/cookie-settings.md"; + +import styles from "./cookieSettingsModal.module.css"; + +export default function CookieSettingsModal({ + setIsTelemetryModalVisible, + declineTelemetryAndCloseBanner, + allowTelemetryAndCloseBanner, +}) { + return ( +
+
+ +
+ + +
+
+ ); +} diff --git a/components/utilities/cookieSettingsModal.module.css b/components/utilities/cookieSettingsModal.module.css new file mode 100644 index 000000000..825ba029a --- /dev/null +++ b/components/utilities/cookieSettingsModal.module.css @@ -0,0 +1,167 @@ +.Container > div { + @apply text-gray-90 grid grid-cols-1 gap-4 text-base leading-loose; +} + +.Container h1, +.Container h2, +.Container h3, +.Container h4, +.Container h5, +.Container h6 { + @apply scroll-mt-0 md:scroll-mt-32; +} + +.Container h2, +.Container h3, +.Container h4, +.Container h5 { + @apply mt-4 text-lg font-bold text-gray-90; +} + +.Container h2 { + @apply text-xl; +} + +.Container h3 { + @apply text-2xl; +} + +.Container h2:first-child, +.Container h3:first-child, +.Container h4:first-child, +.Container h5:first-child { + @apply mt-0; +} + +.Container p { + @apply text-gray-90; +} + +.Container a { + @apply underline; +} + +.Container a:hover { + @apply opacity-80; +} + +.Container ol { + @apply list-decimal pl-8 grid grid-cols-1 gap-4; +} + +.Container section ol { + @apply list-none pl-0 gap-0 mb-5; +} + +.Container ul { + @apply list-disc pl-8 grid grid-cols-1 gap-4; +} + +.Container li { + @apply pl-2; +} + +.Container blockquote { + @apply p-6 md:p-8 lg:p-16 + -mx-6 md:-mx-8 lg:-mx-16 + my-0 md:my-4 lg:my-12 + grid grid-cols-1 gap-4 + bg-lightBlue-10 + text-gray-80 + rounded-lg; +} + +.PrivacyContainer blockquote ~ h4 { + @apply mt-12 md:mt-8 lg:mt-0 text-gray-90; +} + +.PrivacyContainer blockquote h2 { + @apply hidden; +} + +.Container blockquote h2 { + @apply text-gray-90 text-3xl; +} + +.Container hr { + @apply border-gray-40 border-t-0 border-l-0 border-r-0 border-b my-8; +} + +.Container address { + @apply whitespace-pre-line pl-4 border-l-2 border-gray-30 mt-4; +} + +.Container pre { + @apply bg-gray-10 p-4 rounded-md overflow-x-auto text-gray-90; +} + +.Container pre code { + @apply text-lg; +} + +.Container figure { + @apply flex flex-col items-start; +} + +.Container figure img { + @apply h-24 ml-2 mb-2; +} + +.Container figure figcaption { + @apply text-sm text-gray-70; +} + +/* For table in Privacy notice */ +.Container table { + @apply table-fixed my-8 w-full; +} + +.Container table thead tr th { + @apply px-4 py-3 text-left bg-gray-20 bg-opacity-50 border border-gray-30 text-gray-90 font-semibold text-lg leading-normal align-top; +} + +.Container table tbody tr td { + @apply px-4 py-3 border border-gray-30 text-gray-90 text-lg leading-normal align-top; +} + +.DeploymentTerms ul, +.DeploymentTerms ol { + @apply list-none; +} + +/* Specific classes for colors */ +.Container:global(.red) a { + @apply text-red-70; +} + +.Container:global(.orange) a { + @apply text-orange-70; +} + +.Container:global(.yellow) a { + @apply text-yellow-90; +} + +.Container:global(.green) a { + @apply text-green-70; +} + +.Container:global(.acqua) a { + @apply text-acqua-70; +} + +.Container:global(.lightBlue) a { + @apply text-lightBlue-70; +} + +.Container:global(.darkBlue) a { + @apply text-darkBlue-70; +} + +.Container:global(.indigo) a { + @apply text-indigo-70; +} + +.Container:global(.gray) a { + @apply text-gray-70; +} diff --git a/components/utilities/gdpr.js b/components/utilities/gdpr.js new file mode 100644 index 000000000..110ab94f1 --- /dev/null +++ b/components/utilities/gdpr.js @@ -0,0 +1,217 @@ +import React, { useEffect } from "react"; +import classNames from "classnames"; + +import content from "../../content/gdpr-banner.md"; + +import styles from "./gdpr.module.css"; + +const TELEMETRY_PREFERENCE = "InsertTelemetry"; +const TELEMETRY_PREFERENCE_DATE = "TelemetryDate"; + +export function setTelemetryPreference(accepted) { + localStorage.setItem(TELEMETRY_PREFERENCE, JSON.stringify(accepted)); + localStorage.setItem(TELEMETRY_PREFERENCE_DATE, JSON.stringify(Date.now())); +} + +// Check if the stored date is > 6 months old +const isConsentStale = (timestamp) => { + const consent_date = new Date(parseInt(timestamp * 1000)); + const today = new Date(); + + const six_months_in_ms = + 1000 /*ms*/ * 60 /*s*/ * 60 /*min*/ * 24 /*h*/ * 30 /*days*/ * 6; /*months*/ + return today - consent_date > six_months_in_ms; +}; + +function getTelemetryPreference() { + // Returns true/false if user accepted/denied telemetry. + // Returns null if user never accepted/denied or consent is stale. + + const telemetryPref = localStorage.getItem(TELEMETRY_PREFERENCE); + const consentIsStale = isConsentStale( + localStorage.getItem(TELEMETRY_PREFERENCE_DATE), + ); + + if (telemetryPref == null || consentIsStale) return null; + + return telemetryPref == "true"; +} + +export default function GDPRBanner({ + isTelemetryModalVisible, + setIsTelemetryModalVisible, + isTelemetryBannerVisible, + setIsTelemetryBannerVisible, + insertTelemetryCode, + setInsertTelemetryCode, + allowTelemetryAndCloseBanner, + declineTelemetryAndCloseBanner, +}) { + useEffect(() => { + const pref = getTelemetryPreference(); + + switch (pref) { + case true: + setInsertTelemetryCode(true); + return; + + case false: + // This is already false at initialization, but it doesn't hurt to do the right thing + // here and make sure it's indeed false. + setInsertTelemetryCode(false); + return; + + case null: + localStorage.clear(); // Do we even need this line?? Seems dangerous to just clear the entire localStorage, as maybe some other library could be using it. + setIsTelemetryBannerVisible(true); + return; + + default: + console.log(`Unexpected telemetry preference: ${pref}`); + return; + } + }, []); + + useEffect(() => { + if (insertTelemetryCode) { + insertTelemetry(); + } + }, [insertTelemetryCode]); + + return ( + <> + {isTelemetryBannerVisible && ( +
+
+
+
+ + + +
+
+
+ )} + + ); +} + +function insertTelemetry() { + (function () { + var analytics = (window.analytics = window.analytics || []); + if (!analytics.initialize) + if (analytics.invoked) + window.console && + console.error && + console.error("Segment snippet included twice."); + else { + analytics.invoked = !0; + analytics.methods = [ + "trackSubmit", + "trackClick", + "trackLink", + "trackForm", + "pageview", + "identify", + "reset", + "group", + "track", + "ready", + "alias", + "debug", + "page", + "once", + "off", + "on", + "addSourceMiddleware", + "addIntegrationMiddleware", + "setAnonymousId", + "addDestinationMiddleware", + ]; + analytics.factory = function (e) { + return function () { + var t = Array.prototype.slice.call(arguments); + t.unshift(e); + analytics.push(t); + return analytics; + }; + }; + for (var e = 0; e < analytics.methods.length; e++) { + var key = analytics.methods[e]; + analytics[key] = analytics.factory(key); + } + analytics.load = function (key, e) { + var t = document.createElement("script"); + t.type = "text/javascript"; + t.async = !0; + t.src = + "https://cdn.segment.com/analytics.js/v1/" + + key + + "/analytics.min.js"; + var n = document.getElementsByTagName("script")[0]; + n.parentNode.insertBefore(t, n); + analytics._loadOptions = e; + }; + analytics._writeKey = "pUoB6ihRTAFLDtLp2NWEuJvBNtiooQwE"; + analytics.SNIPPET_VERSION = "4.13.2"; + analytics.load("pUoB6ihRTAFLDtLp2NWEuJvBNtiooQwE"); + analytics.page(); + } + })(); +} diff --git a/components/utilities/gdpr.module.css b/components/utilities/gdpr.module.css new file mode 100644 index 000000000..78d5d8bb3 --- /dev/null +++ b/components/utilities/gdpr.module.css @@ -0,0 +1,42 @@ +.Container { + backdrop-filter: blur(10px); + background: #fffd; +} + +.Markdown div { + @apply flex flex-col; +} + +.Markdown h1 { + @apply text-gray-90 text-lg sm:text-xl font-bold mb-1; +} + +.Markdown p { + @apply text-gray-90 text-base mb-0; +} + +.Markdown p a { + @apply text-gray-90 underline hover:text-gray-70; +} + +:global(.dark) .Container { + @apply bg-gray-100 border-gray-90; +} + +:global(.dark) .Markdown p, +:global(.dark) .Markdown p a { + @apply text-gray-40; +} + +:global(.dark) .Link, +:global(.dark) .Button { + @apply text-gray-40; +} + +:global(.dark) .Button { + @apply border-gray-80; +} + +:global(.dark) .Markdown h1 { + @apply text-gray-40; +} diff --git a/content/cookie-settings.md b/content/cookie-settings.md new file mode 100644 index 000000000..b422adc73 --- /dev/null +++ b/content/cookie-settings.md @@ -0,0 +1,21 @@ +--- +visible: false +--- + +### Cookie settings + +##### Strictly necessary cookies + +These cookies are necessary for the website to function and 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. + +##### Performance cookies + +These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited. + +##### Functional cookies + +These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences. + +##### Targeting cookies + +These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites. diff --git a/content/develop/concepts/configuration/options.md b/content/develop/concepts/configuration/options.md index 05c25705e..72baefa4a 100644 --- a/content/develop/concepts/configuration/options.md +++ b/content/develop/concepts/configuration/options.md @@ -56,6 +56,20 @@ When using command line options to override `config.toml` and environment variab enableStaticServing = true ``` +## Telemetry + +As mentioned during the installation process, Streamlit collects usage statistics. You can find out +more by reading our [Privacy Notice](https://streamlit.io/privacy-policy), but the high-level +summary is that although we collect telemetry data we cannot see and do not store information +contained in Streamlit apps. + +If you'd like to opt out of usage statistics, add the following to your config file: + +```toml +[browser] +gatherUsageStats = false +``` + ## Theming You can change the base colors of your app using the `[theme]` section of the configuration system. diff --git a/content/gdpr-banner.md b/content/gdpr-banner.md new file mode 100644 index 000000000..af0887a47 --- /dev/null +++ b/content/gdpr-banner.md @@ -0,0 +1,8 @@ +--- +--- + +# Hello there 👋 + +Thanks for stopping by! We use cookies to help us understand how you interact with our website. + +By clicking “Accept all”, you consent to our use of cookies. For more information, please see our [privacy policy](www.streamlit.io/privacy-policy). diff --git a/netlify.toml b/netlify.toml index 0de4b8bec..515983015 100644 --- a/netlify.toml +++ b/netlify.toml @@ -30,9 +30,6 @@ connect-src \ https://*.algolia.net/ \ https://*.algolianet.com/ \ https://kapa-widget-proxy-la7dkmplpq-uc.a.run.app/ \ - https://cdn.cookielaw.org/ \ - https://cookie-cdn.cookiepro.com/ \ - https://cdn.jsdelivr.net/npm/@segment/ \ ; \ default-src 'none' ; \ font-src 'self' ; \ @@ -65,9 +62,6 @@ script-src \ https://www.google.com/recaptcha/api.js \ https://www.gstatic.com/recaptcha/releases/ \ https://www.google.com/recaptcha/enterprise.js \ - https://cdn.cookielaw.org/ \ - https://cookie-cdn.cookiepro.com/ \ - https://cdn.jsdelivr.net/npm/@segment/ \ ; \ style-src \ 'self' \ diff --git a/next-sitemap.config.js b/next-sitemap.config.js index aeab62695..2d51e354e 100644 --- a/next-sitemap.config.js +++ b/next-sitemap.config.js @@ -1,5 +1,12 @@ module.exports = { siteUrl: process.env.NEXT_PUBLIC_HOSTNAME || "https://docs.streamlit.io", generateRobotsTxt: false, - exclude: ["/menu", "/.keep", "/style-guide", "/index", "/develop"], + exclude: [ + "/menu", + "/.keep", + "/style-guide", + "/gdpr-banner", + "/index", + "/develop", + ], }; diff --git a/next.config.mjs b/next.config.mjs index 6e5e86415..14c2bfa4c 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -26,76 +26,70 @@ const PROD_OPTIMIZATIONS = IS_DEV const CSP_HEADER = [ "upgrade-insecure-requests;", "frame-ancestors", - "'self'", + "'self'", ";", "frame-src", - "https:", + "https:", ";", "connect-src", - "'self'", - "https://*.streamlit.app/", - "wss://*.streamlit.app/", - "https://streamlit.ghost.io/ghost/api/", // Blog API - "https://api.segment.io/", // Analytics - "https://cdn.segment.com/", // Analytics - "https://*.auryc.com/", // Analytics (Heap) - "https://www.google-analytics.com/", // Analytics - "https://stats.g.doubleclick.net/", // Analytics - "https://px.ads.linkedin.com/", // LinkedIn ad pixel - "https://*.algolia.net/", // Search - "https://*.algolianet.com/", // Search - "https://widget.kapa.ai/kapa-widget.bundle.js", // Kapa.ai - "https://kapa-widget-proxy-la7dkmplpq-uc.a.run.app/", // Kapa.ai - "https://www.google.com/recaptcha/api.js", // Recaptcha for Kapa.ai - "https://www.gstatic.com/recaptcha/releases/", // Recaptchas for Kapa.ai - "https://www.google.com/recaptcha/enterprise.js", // Recaptchas for Kapa.ai - "https://cdn.cookielaw.org/", // Onetrust cookie banner - "https://cookie-cdn.cookiepro.com/", // Onetrust cookie banner - "https://cdn.jsdelivr.net/npm/@segment/", // Segment Onetrust Wrapper + "'self'", + "https://*.streamlit.app/", + "wss://*.streamlit.app/", + "https://streamlit.ghost.io/ghost/api/", // Blog API + "https://api.segment.io/", // Analytics + "https://cdn.segment.com/", // Analytics + "https://*.auryc.com/", // Analytics (Heap) + "https://www.google-analytics.com/", // Analytics + "https://stats.g.doubleclick.net/", // Analytics + "https://px.ads.linkedin.com/", // LinkedIn ad pixel + "https://*.algolia.net/", // Search + "https://*.algolianet.com/", // Search + "https://widget.kapa.ai/kapa-widget.bundle.js", // Kapa.ai + "https://kapa-widget-proxy-la7dkmplpq-uc.a.run.app/", // Kapa.ai + "https://www.google.com/recaptcha/api.js", // Recaptcha for Kapa.ai + "https://www.gstatic.com/recaptcha/releases/", // Recaptchas for Kapa.ai + "https://www.google.com/recaptcha/enterprise.js", // Recaptchas for Kapa.ai ";", "default-src 'none';", "font-src 'self';", "form-action 'self';", "img-src", - "'self'", - "data:", - "https:", + "'self'", + "data:", + "https:", ";", "media-src", - "'self'", - "https://s3-us-west-2.amazonaws.com/assets.streamlit.io/", // Videos + "'self'", + "https://s3-us-west-2.amazonaws.com/assets.streamlit.io/", // Videos ";", "script-src", - "'self'", - "'unsafe-inline'", // NextJS payload - "'unsafe-eval'", // Required for MDXRemote in [...slug].js. Using App Router may fix this. - "https://cdn.heapanalytics.com/", // Analytics - "https://cdn.segment.com/", // Analytics - "https://www.google-analytics.com/", // Analytics - "https://www.googletagmanager.com/", // Analytics - "https://identity.netlify.com/", // Netlify dev tools - "https://netlify-cdp-loader.netlify.app/netlify.js", // Netlify dev tools - "https://www.youtube.com/iframe_api/", // YouTube Embed - "https://snap.licdn.com/", // LinkedIn ad pixel - "https://connect.facebook.net/", // Facebook ad pixel - "https://*.algolia.net/", // Search - "https://*.algolianet.com/", // Search - "https://widget.kapa.ai/kapa-widget.bundle.js", // Kapa.ai - "https://kapa-widget-proxy-la7dkmplpq-uc.a.run.app/", // Kapa.ai - "https://www.google.com/recaptcha/api.js", // Recaptcha for Kapa.ai - "https://www.gstatic.com/recaptcha/releases/", // Recaptchas for Kapa.ai - "https://www.google.com/recaptcha/enterprise.js", // Recaptchas for Kapa.ai - "https://cdn.cookielaw.org/", // Onetrust cookie banner - "https://cookie-cdn.cookiepro.com/", // Onetrust cookie banner - "https://cdn.jsdelivr.net/npm/@segment/", // Segment Onetrust Wrapper + "'self'", + "'unsafe-inline'", // NextJS payload + "'unsafe-eval'", // Required for MDXRemote in [...slug].js. Using App Router may fix this. + "https://cdn.heapanalytics.com/", // Analytics + "https://cdn.segment.com/", // Analytics + "https://www.google-analytics.com/", // Analytics + "https://www.googletagmanager.com/", // Analytics + "https://identity.netlify.com/", // Netlify dev tools + "https://netlify-cdp-loader.netlify.app/netlify.js", // Netlify dev tools + "https://www.youtube.com/iframe_api/", // YouTube Embed + "https://snap.licdn.com/", // LinkedIn ad pixel + "https://connect.facebook.net/", // Facebook ad pixel + "https://*.algolia.net/", // Search + "https://*.algolianet.com/", // Search + "https://widget.kapa.ai/kapa-widget.bundle.js", // Kapa.ai + "https://kapa-widget-proxy-la7dkmplpq-uc.a.run.app/", // Kapa.ai + "https://www.google.com/recaptcha/api.js", // Recaptcha for Kapa.ai + "https://www.gstatic.com/recaptcha/releases/", // Recaptchas for Kapa.ai + "https://www.google.com/recaptcha/enterprise.js", // Recaptchas for Kapa.ai ";", "style-src", - "'self'", - "'unsafe-inline'", // Twitter CSS + "'self'", + "'unsafe-inline'", // Twitter CSS ";", "worker-src", - "'self'", - "blob:", + "'self'", + "blob:", ";", ]; diff --git a/pages/404.js b/pages/404.js index b1d8696ae..477713d4c 100644 --- a/pages/404.js +++ b/pages/404.js @@ -41,19 +41,6 @@ export default function Home({ window, menu }) { name="twitter:image" content={`https://${process.env.NEXT_PUBLIC_HOSTNAME}/sharing-image-twitter.jpg`} /> - - - {/* Add Segment's OneTrust Consent Wrapper */} - - {/* Add Segment Analytics Snippet */} -
diff --git a/pages/[...slug].js b/pages/[...slug].js index a92158cc3..a89fb3407 100644 --- a/pages/[...slug].js +++ b/pages/[...slug].js @@ -18,6 +18,10 @@ const { serverRuntimeConfig, publicRuntimeConfig } = getConfig(); // Site Components import { looksLikeVersionAndPlatformString } from "../lib/next/utils"; +import CookieSettingsModal from "../components/utilities/cookieSettingsModal"; +import GDPRBanner, { + setTelemetryPreference, +} from "../components/utilities/gdpr"; import { getArticleSlugs, getArticleSlugFromString, @@ -111,6 +115,28 @@ export default function Article({ let versionWarning; let currentLink; + const [isTelemetryModalVisible, setIsTelemetryModalVisible] = useState(false); + const [isTelemetryBannerVisible, setIsTelemetryBannerVisible] = + useState(false); + const [insertTelemetryCode, setInsertTelemetryCode] = useState(false); + + const allowTelemetryAndCloseBanner = useCallback(() => { + setIsTelemetryBannerVisible(false); + setIsTelemetryModalVisible(false); + setInsertTelemetryCode(true); + setTelemetryPreference(true); + }, [isTelemetryBannerVisible, insertTelemetryCode]); + + const declineTelemetryAndCloseBanner = useCallback(() => { + setIsTelemetryBannerVisible(false); + setIsTelemetryModalVisible(false); + setInsertTelemetryCode(false); + setTelemetryPreference(false); + + // If previous state was true, and now it's false, reload the page to remove telemetry JS + if (insertTelemetryCode) router.reload(); + }, [isTelemetryBannerVisible, insertTelemetryCode]); + const { version, platform, goToLatest, goToOpenSource } = useVersionContext(); const isVersionedPage = currMenuItem && currMenuItem.isVersioned; const isUnversionedURL = !versionFromSlug || !platformFromSlug; @@ -237,6 +263,23 @@ export default function Article({ }} > + {isTelemetryModalVisible && ( + + )} +
@@ -286,19 +329,6 @@ export default function Article({ name="twitter:image" content={`https://${process.env.NEXT_PUBLIC_HOSTNAME}/sharing-image-twitter.jpg`} /> - - - {/* Add Segment's OneTrust Consent Wrapper */} - - {/* Add Segment Analytics Snippet */} -
{versionWarning} @@ -315,7 +345,7 @@ export default function Article({
-