{"version":3,"sources":["consent.ts"],"sourcesContent":["import { useIsOnFramerCanvas } from \"framer\"\nimport { isBrowser } from \"framer-motion\"\nimport { useEffect, useReducer, useState, startTransition } from \"react\"\nimport { initGTM, sendToGTM } from \"../lib/send.ts\"\nimport { safeJSONParse, yieldBeforeCb } from \"../lib/utils.ts\"\n\ntype GTMConsentType = \"denied\" | \"granted\"\ntype GTMConsentMode =\n    | \"ad_storage\"\n    | \"ad_user_data\"\n    | \"ad_personalization\"\n    | \"analytics_storage\"\n    | \"personalization_storage\"\n    | \"functionality_storage\"\n    | \"security_storage\"\n\ntype GTMConsent = Record<GTMConsentMode, GTMConsentType>\n\nexport type ConsentModeName =\n    | \"necessary\"\n    | \"preferences\"\n    | \"analytics\"\n    | \"marketing\"\nexport type ConsentModes = Record<ConsentModeName, boolean>\n\ninterface UseConsentOptions {\n    gtmId?: string\n    defaultConsent?: ConsentModes\n    gtmLoadedExternally: boolean\n    defaultsFromGTM: boolean\n}\n\nfunction toGTMConsent(consent: ConsentModes): GTMConsent {\n    return {\n        functionality_storage: consent.necessary ? \"granted\" : \"denied\",\n        security_storage: consent.necessary ? \"granted\" : \"denied\",\n        ad_storage: consent.marketing ? \"granted\" : \"denied\",\n        ad_user_data: consent.marketing ? \"granted\" : \"denied\",\n        ad_personalization: consent.marketing ? \"granted\" : \"denied\",\n        analytics_storage: consent.analytics ? \"granted\" : \"denied\",\n        personalization_storage: consent.preferences ? \"granted\" : \"denied\",\n    }\n}\n\nfunction fromGTMConsent(consent: GTMConsent): ConsentModes {\n    return {\n        necessary:\n            consent.functionality_storage === \"granted\" &&\n            consent.security_storage === \"granted\",\n        marketing:\n            consent.ad_storage === \"granted\" &&\n            consent.ad_user_data === \"granted\" &&\n            consent.ad_personalization === \"granted\",\n        analytics: consent.analytics_storage === \"granted\",\n        preferences: consent.personalization_storage === \"granted\",\n    }\n}\n\ninterface State {\n    modes: ConsentModes | null\n    dismissed: boolean\n    autoAccepted: boolean\n    initializedFromLocalStorage: boolean\n    initializedFromGTM: boolean\n    hasSynced: boolean\n    sync: boolean\n}\n\ntype Action =\n    | {\n          type:\n              | \"autoAccept\"\n              | \"acceptAll\"\n              | \"acceptCurrent\"\n              | \"rejectAll\"\n              | \"dismiss\"\n      }\n    | { type: \"synced\"; success: boolean }\n    | {\n          type: \"initFromLocalStorage\"\n          modes: ConsentModes\n          dismissed: boolean\n          autoAccepted: boolean\n      }\n    | {\n          type: \"initFromGTM\"\n          modes: ConsentModes\n      }\n    | { type: \"update\"; modes: Partial<ConsentModes>; sync?: boolean }\n    | { type: \"toggle\"; mode: ConsentModeName }\n\nfunction reducer(state: State, action: Action): State {\n    switch (action.type) {\n        case \"autoAccept\":\n            return {\n                ...state,\n                sync: true,\n                autoAccepted: true,\n                modes: {\n                    analytics: true,\n                    marketing: true,\n                    necessary: true,\n                    preferences: true,\n                },\n            }\n\n        case \"acceptAll\":\n            return {\n                ...state,\n                sync: true,\n                dismissed: true,\n                modes: {\n                    analytics: true,\n                    marketing: true,\n                    necessary: true,\n                    preferences: true,\n                },\n            }\n        case \"rejectAll\":\n            return {\n                ...state,\n                sync: true,\n                dismissed: true,\n                modes: {\n                    analytics: false,\n                    marketing: false,\n                    necessary: false,\n                    preferences: false,\n                },\n            }\n        case \"acceptCurrent\":\n            return {\n                ...state,\n                dismissed: true,\n                sync: true,\n            }\n        case \"update\":\n            return {\n                ...state,\n                modes: {\n                    ...state.modes,\n                    ...action.modes,\n                },\n                sync: action.sync,\n            }\n\n        case \"toggle\":\n            return {\n                ...state,\n                modes: {\n                    ...state.modes,\n                    [action.mode]: !state.modes[action.mode],\n                },\n            }\n        case \"initFromLocalStorage\":\n            return {\n                ...state,\n                modes: action.modes,\n                dismissed: action.dismissed,\n                autoAccepted: action.autoAccepted,\n                initializedFromLocalStorage: true,\n                sync: true,\n            }\n        case \"initFromGTM\":\n            return {\n                ...state,\n                modes: action.modes,\n                initializedFromGTM: true,\n            }\n        case \"dismiss\":\n            return {\n                ...state,\n                dismissed: true,\n            }\n        case \"synced\":\n            return {\n                ...state,\n                sync: false,\n                hasSynced: state.hasSynced || action.success,\n            }\n        default:\n            return state\n    }\n}\n\nconst initialState: State = {\n    dismissed: false,\n    autoAccepted: false,\n    modes: null,\n    sync: false,\n    initializedFromLocalStorage: false,\n    initializedFromGTM: false,\n    hasSynced: false,\n}\n\nexport const defaultConsent = {\n    necessary: false,\n    analytics: false,\n    marketing: false,\n    preferences: false,\n}\n\n// Keep track of if GTM has been loaded as a script and default consent has been set,\n// to ensure the script does not keep appending between page switches.\nlet hasInitializedGTM = false\n\nexport function useConsent({\n    gtmId,\n    defaultConsent,\n    gtmLoadedExternally = false,\n    defaultsFromGTM = false,\n}: UseConsentOptions) {\n    const [state, dispatch] = useReducer(reducer, initialState)\n\n    const isOnFramerCanvas = useIsOnFramerCanvas()\n\n    const consentModeLocalStorageKey = \"framerCookiesConsentMode\"\n    const dismissedLocalStorageKey = \"framerCookiesDismissed\"\n    const autoAcceptedLocalStorageKey = \"framerCookiesAutoAccepted\"\n\n    function getStateFromLocalStorage() {\n        const consentFromLocalStorage = localStorage.getItem(\n            consentModeLocalStorageKey\n        )\n        const dismissedFromLocalStorage = localStorage.getItem(\n            dismissedLocalStorageKey\n        )\n        const autoAcceptedFromLocalStorage = localStorage.getItem(\n            autoAcceptedLocalStorageKey\n        )\n\n        const isDismissed = dismissedFromLocalStorage !== null\n        const isAutoAccepted = autoAcceptedFromLocalStorage !== null\n        const hasConsentInLocalStorage = consentFromLocalStorage !== null\n        const consentInLocalStorageIsNotDefault = isDismissed || isAutoAccepted\n        const shouldLoadConsentFromLocalStorage =\n            hasConsentInLocalStorage && consentInLocalStorageIsNotDefault\n\n        dispatch({\n            type: \"initFromLocalStorage\",\n            dismissed: isDismissed,\n            autoAccepted: isAutoAccepted,\n            modes: shouldLoadConsentFromLocalStorage\n                ? safeJSONParse(consentFromLocalStorage, () =>\n                      localStorage.removeItem(consentModeLocalStorageKey)\n                  )\n                : defaultConsent,\n        })\n    }\n\n    function syncToGTM() {\n        if (gtmId) {\n            if (\n                !hasInitializedGTM &&\n                !gtmLoadedExternally &&\n                !defaultsFromGTM\n            ) {\n                // This is the first time we sync consent, so we save it as \"default\" and initialize tag manager.\n                // This order is important, because we need to have set the default consent BEFORE we initialize\n                // GTM. See https://developers.google.com/tag-platform/devguides/consent?tab=tag-manager&sjid=11348191096952324675-EU#implementation_example\n\n                // It might seem weird that we're \"sending\" before initializing, but \"sending\" here means building up\n                // the \"dataLayer\" object that GTM picks up when it initializes.\n                sendToGTM(\"consent\", \"default\", toGTMConsent(state.modes))\n\n                initGTM({\n                    dataLayer: undefined,\n                    dataLayerName: \"dataLayer\",\n                    environment: undefined,\n                    nonce: undefined,\n                    injectScript: true,\n                    id: gtmId,\n                })\n\n                hasInitializedGTM = true\n            } else {\n                hasInitializedGTM = true\n\n                sendToGTM(\"consent\", \"update\", toGTMConsent(state.modes))\n                // must be sent like this or else GTM doesn't act on it(?)\n                // Publically documented: https://www.framer.com/academy/lessons/cookie-banner-component#:~:text=Run%20a%20Tag%20After%20The%20Consent%20Update\n                window.dataLayer.push({ event: \"cookie_consent_update\" })\n            }\n        }\n    }\n\n    useEffect(() => {\n        if (isOnFramerCanvas) return\n        if (!defaultsFromGTM) return\n        if (defaultConsent === null) return\n        if (state.initializedFromGTM) return\n        if (state.modes !== null) return // State was already set (from localStorage)\n\n        yieldBeforeCb(\n            () => {\n                dispatch({ type: \"initFromGTM\", modes: defaultConsent })\n            },\n            {\n                priority: \"user-blocking\",\n            }\n        )\n    }, [defaultsFromGTM, defaultConsent, state.initializedFromGTM, state.modes])\n\n    useEffect(() => {\n        if (isOnFramerCanvas) return\n\n        yieldBeforeCb(() => getStateFromLocalStorage(), {\n            priority: \"user-blocking\",\n        })\n    }, [])\n\n    // Anytime the dismissed value is updated, we need to persist it in local storage.\n    useEffect(() => {\n        if (isOnFramerCanvas) return\n\n        if (state.dismissed) {\n            localStorage.setItem(dismissedLocalStorageKey, \"true\")\n        }\n    }, [state.dismissed])\n\n    // Anytime consent is auto accepted, we need to persist it in local storage.\n    useEffect(() => {\n        if (isOnFramerCanvas) return\n\n        if (state.autoAccepted) {\n            localStorage.setItem(autoAcceptedLocalStorageKey, \"true\")\n        }\n    }, [state.autoAccepted])\n\n    // Sync data to dataLayer and localStorage.\n    useEffect(() => {\n        if (isOnFramerCanvas) return\n\n        const shouldSync = state.sync && isBrowser && state.modes !== null\n\n        yieldBeforeCb(\n            () => {\n                if (!shouldSync) {\n                    dispatch({ type: \"synced\", success: false })\n                    return\n                } // else\n\n                syncToGTM()\n\n                // Save locally\n                localStorage.setItem(\n                    consentModeLocalStorageKey,\n                    JSON.stringify(state.modes)\n                )\n\n                dispatch({ type: \"synced\", success: true })\n            },\n            {\n                priority: \"user-blocking\",\n            }\n        )\n    }, [state.sync])\n\n    function dismiss() {\n        dispatch({ type: \"dismiss\" })\n        localStorage.setItem(dismissedLocalStorageKey, \"true\")\n    }\n\n    function autoAccept() {\n        dispatch({ type: \"autoAccept\" })\n    }\n\n    function acceptAll() {\n        dispatch({ type: \"acceptAll\" })\n    }\n\n    function rejectAll() {\n        dispatch({ type: \"rejectAll\" })\n    }\n\n    function acceptCurrent() {\n        dispatch({ type: \"acceptCurrent\" })\n    }\n\n    function toggleMode(mode: ConsentModeName) {\n        dispatch({ type: \"toggle\", mode })\n    }\n\n    return {\n        modes: state.modes,\n        isInitialized: state.hasSynced || state.initializedFromGTM,\n        isDismissed: state.dismissed,\n        isAutoAccepted: state.autoAccepted,\n        dismiss,\n        autoAccept,\n        acceptAll,\n        rejectAll,\n        acceptCurrent,\n        toggleMode,\n    } as const\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\nconst PROMISE_NAME = \"__framer_cookieBannerDefaultConsentPromise\"\n\n// NOTE: Error can be ignored, because of `isBrowser`\nconst gtmSetsDefaultConsent = isBrowser && !!window[PROMISE_NAME]\n\nexport function useGTMDefaultConsent() {\n    const [consent, setConsent] = useState<null | GTMConsent>(null)\n\n    useEffect(() => {\n        if (!gtmSetsDefaultConsent) return\n\n        window[PROMISE_NAME].then((result) => {\n            startTransition(() => {\n                setConsent(result)\n            })\n        })\n    }, [])\n\n    // Simplified region to use for the cookie banner\n    let region: undefined | \"EU\" | \"World\"\n    if (consent) {\n        region = Object.values(consent).every((v) => v === \"granted\")\n            ? \"World\"\n            : \"EU\"\n    }\n\n    return {\n        available: gtmSetsDefaultConsent,\n        consent: consent ? fromGTMConsent(consent) : null,\n        region,\n    }\n}\n"],"names":[],"mappings":"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"}