UNPKG

@scaleway/use-analytics

Version:

A small hook to handle events analytics

157 lines (156 loc) 4.95 kB
import { jsx } from "react/jsx-runtime"; import { parse, serialize } from "cookie"; import { useState, useMemo, useEffect, useCallback, createContext, useContext } from "react"; import { useDestinations } from "../analytics/useDestinations.js"; import { HASH_COOKIE, CATEGORIES, COOKIE_PREFIX, COOKIES_OPTIONS, CONSENT_ADVERTISING_MAX_AGE, CONSENT_MAX_AGE } from "../constants.js"; import { uniq } from "../helpers/array.js"; import { IS_CLIENT } from "../helpers/isClient.js"; import { stringToHash } from "../helpers/misc.js"; import { isCategoryKind } from "../types.js"; const CookieConsentContext = createContext(void 0); const useCookieConsent = () => { const context = useContext(CookieConsentContext); if (context === void 0) { throw new Error( "useCookieConsent must be used within a CookieConsentProvider" ); } return context; }; const CookieConsentProvider = ({ children, isConsentRequired, manualDestinations, config, cookiePrefix = COOKIE_PREFIX, consentMaxAge = CONSENT_MAX_AGE, consentAdvertisingMaxAge = CONSENT_ADVERTISING_MAX_AGE, cookiesOptions = COOKIES_OPTIONS }) => { const [needConsent, setNeedsConsent] = useState(false); const [cookies, setCookies] = useState( IS_CLIENT ? parse(document.cookie) : {} ); const { destinations: analyticsDestinations, isLoaded: isDestinationsLoaded } = useDestinations(config); const destinations = useMemo( () => uniq([ ...(analyticsDestinations ?? []).map( (dest) => ({ category: dest.consents[0] ?? "essential", displayName: dest.displayName, name: dest.name }) ), ...manualDestinations ?? [] ]), [analyticsDestinations, manualDestinations] ); const destinationsHash = useMemo( () => stringToHash( uniq(destinations.map(({ name }) => name)).sort().join(void 0) ), [destinations] ); useEffect(() => { setNeedsConsent( isConsentRequired && cookies[HASH_COOKIE] !== destinationsHash.toString() && analyticsDestinations !== void 0 ); }, [isConsentRequired, destinationsHash, analyticsDestinations, cookies]); const cookieConsent = useMemo( () => CATEGORIES.reduce( (acc, category) => ({ ...acc, [category]: isConsentRequired || needConsent ? cookies[`${cookiePrefix}_${category}`] === "true" : true }), {} ), [isConsentRequired, cookiePrefix, needConsent, cookies] ); const saveConsent = useCallback( (categoriesConsent) => { for (const [consentName, consentValue] of Object.entries( categoriesConsent )) { const consentCategoryName = isCategoryKind(consentName) ? consentName : "unknown"; const cookieName = `${cookiePrefix}_${consentCategoryName}`; if (!consentValue) { document.cookie = serialize(cookieName, "", { ...cookiesOptions, expires: /* @__PURE__ */ new Date(0) }); } else { document.cookie = serialize(cookieName, consentValue.toString(), { ...cookiesOptions, maxAge: consentCategoryName === "advertising" ? consentAdvertisingMaxAge : consentMaxAge }); } setCookies((prevCookies) => ({ ...prevCookies, [cookieName]: consentValue ? "true" : "false" })); } document.cookie = serialize(HASH_COOKIE, destinationsHash.toString(), { ...cookiesOptions, // Here we use the shortest max age to force to ask again for expired consent maxAge: consentAdvertisingMaxAge }); setCookies((prevCookies) => ({ ...prevCookies, [HASH_COOKIE]: destinationsHash.toString() })); setNeedsConsent(false); }, [ destinationsHash, consentAdvertisingMaxAge, consentMaxAge, cookiePrefix, cookiesOptions ] ); const allowedConsents = useMemo( () => Object.keys(cookieConsent).filter( // oxlint-disable-next-line @typescript-eslint/no-unsafe-type-assertion (key) => !cookieConsent[key] ), [cookieConsent] ); const deniedConsents = useMemo( () => Object.keys(cookieConsent).filter( // oxlint-disable-next-line @typescript-eslint/no-unsafe-type-assertion (key) => !allowedConsents.includes(key) ), [cookieConsent, allowedConsents] ); const value = useMemo( () => ({ allowedConsents, categories: CATEGORIES, categoriesConsent: cookieConsent, cookies, deniedConsents, destinations, isDestinationsLoaded, needConsent, saveConsent }), [ destinations, isDestinationsLoaded, needConsent, allowedConsents, deniedConsents, cookieConsent, saveConsent, cookies ] ); return /* @__PURE__ */ jsx(CookieConsentContext.Provider, { value, children }); }; export { CookieConsentProvider, useCookieConsent };