Skip to content

Commit

Permalink
refactor: cookie consent provider (#555)
Browse files Browse the repository at this point in the history
* refactor: cookie consent manager

* chore: swizzled layout/index

* chore: added GlobalLayout

* fix: build
  • Loading branch information
mehulmathur16 authored Nov 23, 2024
1 parent feb7233 commit 41c9bd5
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 45 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import {useCallback, useState} from "react"
import {useCookieConsent} from "./useCookieConsent"
import React, {createContext, useCallback, useContext, useState} from "react"
import {CookieConsentType, useCookieConsent} from "@site/src/utils/hooks/useCookieConsent"

export const useCookieConsentManager = () => {
interface CookieConsentContextType {
cookieConsent: CookieConsentType
isCookieConsentModalVisible: boolean
openCookieConsentModal: () => void
closeCookieConsentModal: () => void
onAccept: () => void
onDeny: () => void
onPartialAccept: (selectedPreferences: string[]) => void
}

const CookieConsentContext = createContext<CookieConsentContextType | null>(null)

const useCookieConsentContextValue = (): CookieConsentContextType => {
const {getCookieConsent, setCookieConsent} = useCookieConsent()
const [isCookieConsentModalVisible, setIsCookieConsentModalVisible] = useState(false)
const cookieConsent = getCookieConsent()
Expand Down Expand Up @@ -48,3 +60,16 @@ export const useCookieConsentManager = () => {
onPartialAccept,
}
}

export const CookieConsentProvider = ({children}: {children: React.ReactNode}) => {
const value = useCookieConsentContextValue()
return <CookieConsentContext.Provider value={value}>{children}</CookieConsentContext.Provider>
}

export const useCookieConsentManager = () => {
const context = useContext(CookieConsentContext)
if (!context) {
throw new Error("useCookieConsentManager must be used within a CookieConsentProvider")
}
return context
}
40 changes: 40 additions & 0 deletions src/components/shared/GlobalLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, {useEffect} from "react"
import {pageLinks} from "@site/src/constants/routes"
import CookieConsentModal from "./CookieConsentModal/CookieConsentModal"
import GlobalHead from "./GlobalHead"
import {useCookieConsentManager} from "./CookieConsentProvider"

const GlobalLayout: React.FC = () => {
const {
isCookieConsentModalVisible,
openCookieConsentModal,
closeCookieConsentModal,
onAccept,
onDeny,
onPartialAccept,
cookieConsent,
} = useCookieConsentManager()

useEffect(() => {
if (typeof window !== "undefined" && window.location.pathname.includes(pageLinks.privacyPolicy)) return

if (!cookieConsent) {
openCookieConsentModal()
}
}, [cookieConsent])

return (
<>
<CookieConsentModal
open={isCookieConsentModalVisible}
onClose={closeCookieConsentModal}
onAccept={onAccept}
onDeny={onDeny}
onPartialAccept={onPartialAccept}
/>
<GlobalHead isCookieConsentAccepted={Boolean(cookieConsent?.accepted)} preferences={cookieConsent?.preferences} />
</>
)
}

export default GlobalLayout
2 changes: 2 additions & 0 deletions src/theme/Layout/Provider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import GithubStarsProvider from "@site/src/components/shared/GithubStarsProvider
import Footer from "@site/src/components/shared/Footer"
import Announcement from "@site/src/components/shared/Announcement"
import WrappedCookiesProvider from "@site/src/components/shared/WrappedCookiesProvider"
import {CookieConsentProvider} from "@site/src/components/shared/CookieConsentProvider"

// Define the type for LayoutProvider props
type LayoutProviderProps = {
Expand All @@ -22,6 +23,7 @@ type LayoutProviderProps = {
const Provider = composeProviders([
ColorModeProvider,
WrappedCookiesProvider,
CookieConsentProvider,
AnnouncementBarProvider,
ScrollControllerProvider,
DocsPreferredVersionContextProvider,
Expand Down
84 changes: 45 additions & 39 deletions src/theme/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,50 @@
import React, {useEffect} from "react"
import Layout from "@theme-original/Layout"
import type LayoutType from "@theme/Layout"
import type {WrapperProps} from "@docusaurus/types"
import GlobalHead from "@site/src/components/shared/GlobalHead"
import CookieConsentModal from "@site/src/components/shared/CookieConsentModal/CookieConsentModal"
import {useCookieConsentManager} from "@site/src/utils/hooks/useCookieConsentManager"
import {pageLinks} from "@site/src/constants/routes"

type Props = WrapperProps<typeof LayoutType>

export default function LayoutWrapper(props: Props): JSX.Element {
import React from "react"
import clsx from "clsx"
import ErrorBoundary from "@docusaurus/ErrorBoundary"
import {PageMetadata, SkipToContentFallbackId, ThemeClassNames} from "@docusaurus/theme-common"
import {useKeyboardNavigation} from "@docusaurus/theme-common/internal"
import SkipToContent from "@theme/SkipToContent"
import AnnouncementBar from "@theme/AnnouncementBar"
import Navbar from "@theme/Navbar"
import Footer from "@theme/Footer"
import LayoutProvider from "@theme/Layout/Provider"
import ErrorPageContent from "@theme/ErrorPageContent"
import type {Props} from "@theme/Layout"
import styles from "./styles.module.css"
import GlobalLayout from "@site/src/components/shared/GlobalLayout"

export default function Layout(props: Props): JSX.Element {
const {
isCookieConsentModalVisible,
openCookieConsentModal,
closeCookieConsentModal,
onAccept,
onDeny,
onPartialAccept,
cookieConsent,
} = useCookieConsentManager()

useEffect(() => {
if (typeof window !== "undefined" && window.location.pathname.includes(pageLinks.privacyPolicy)) return

if (!cookieConsent) {
openCookieConsentModal()
}
}, [cookieConsent])
children,
noFooter,
wrapperClassName,
// Not really layout-related, but kept for convenience/retro-compatibility
title,
description,
} = props

useKeyboardNavigation()

return (
<>
<CookieConsentModal
open={isCookieConsentModalVisible}
onClose={closeCookieConsentModal}
onAccept={onAccept}
onDeny={onDeny}
onPartialAccept={onPartialAccept}
/>
<GlobalHead isCookieConsentAccepted={Boolean(cookieConsent?.accepted)} preferences={cookieConsent?.preferences} />
<Layout {...props} />
</>
<LayoutProvider>
<GlobalLayout />

<PageMetadata title={title} description={description} />

<SkipToContent />

<AnnouncementBar />

<Navbar />

<div
id={SkipToContentFallbackId}
className={clsx(ThemeClassNames.wrapper.main, styles.mainWrapper, wrapperClassName)}
>
<ErrorBoundary fallback={(params) => <ErrorPageContent {...params} />}>{children}</ErrorBoundary>
</div>

{!noFooter && <Footer />}
</LayoutProvider>
)
}
21 changes: 21 additions & 0 deletions src/theme/Layout/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
html,
body {
height: 100%;
}

.mainWrapper {
flex: 1 0 auto;
display: flex;
flex-direction: column;
}

/* Docusaurus-specific utility class */
:global(.docusaurus-mt-lg) {
margin-top: 3rem;
}

:global(#__docusaurus) {
min-height: 100%;
display: flex;
flex-direction: column;
}
6 changes: 3 additions & 3 deletions src/utils/hooks/useCookieConsent.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import {useCookies} from "react-cookie"
import {cookieConstants} from "@site/src/constants"

export interface CookieConsent {
export interface CookieConsentType {
accepted: boolean
preferences?: string[]
}

export const useCookieConsent = () => {
const [cookies, setCookie] = useCookies([cookieConstants.USER_CONSENT])

const getCookieConsent = (): CookieConsent => {
const getCookieConsent = (): CookieConsentType => {
return cookies.userConsent
}

const setCookieConsent = (consentData: CookieConsent) => {
const setCookieConsent = (consentData: CookieConsentType) => {
setCookie(cookieConstants.USER_CONSENT, JSON.stringify(consentData), {maxAge: 366 * 24 * 60 * 60})
}

Expand Down

0 comments on commit 41c9bd5

Please sign in to comment.