@shopify/hydrogen-react
Version:
React components, hooks, and utilities for creating custom Shopify storefronts
147 lines (146 loc) • 4.96 kB
JavaScript
import { useEffect, useState, useRef } from "react";
import { stringify } from "worktop/cookie";
import { SHOPIFY_Y, SHOPIFY_S } from "./cart-constants.mjs";
import { buildUUID } from "./cookies-utils.mjs";
import { getTrackingValues, SHOPIFY_UNIQUE_TOKEN_HEADER, SHOPIFY_VISIT_TOKEN_HEADER } from "./tracking-utils.mjs";
const longTermLength = 60 * 60 * 24 * 360 * 1;
const shortTermLength = 60 * 30;
function useShopifyCookies(options) {
const {
hasUserConsent,
domain = "",
checkoutDomain = "",
storefrontAccessToken,
fetchTrackingValues,
ignoreDeprecatedCookies = false
} = options || {};
const coreCookiesReady = useCoreShopifyCookies({
storefrontAccessToken,
fetchTrackingValues,
checkoutDomain
});
useEffect(() => {
if (ignoreDeprecatedCookies || !coreCookiesReady) return;
let currentDomain = domain || window.location.host;
if (checkoutDomain) {
const checkoutDomainParts = checkoutDomain.split(".").reverse();
const currentDomainParts = currentDomain.split(".").reverse();
const sameDomainParts = [];
checkoutDomainParts.forEach((part, index) => {
if (part === currentDomainParts[index]) {
sameDomainParts.push(part);
}
});
currentDomain = sameDomainParts.reverse().join(".");
}
if (/^localhost/.test(currentDomain)) currentDomain = "";
const domainWithLeadingDot = currentDomain ? /^\./.test(currentDomain) ? currentDomain : `.${currentDomain}` : "";
if (hasUserConsent) {
const trackingValues = getTrackingValues();
if ((trackingValues.uniqueToken || trackingValues.visitToken || "").startsWith("00000000-")) {
return;
}
setCookie(
SHOPIFY_Y,
trackingValues.uniqueToken || buildUUID(),
longTermLength,
domainWithLeadingDot
);
setCookie(
SHOPIFY_S,
trackingValues.visitToken || buildUUID(),
shortTermLength,
domainWithLeadingDot
);
} else {
setCookie(SHOPIFY_Y, "", 0, domainWithLeadingDot);
setCookie(SHOPIFY_S, "", 0, domainWithLeadingDot);
}
}, [
coreCookiesReady,
hasUserConsent,
domain,
checkoutDomain,
ignoreDeprecatedCookies
]);
return coreCookiesReady;
}
function setCookie(name, value, maxage, domain) {
document.cookie = stringify(name, value, {
maxage,
domain,
samesite: "Lax",
path: "/"
});
}
async function fetchTrackingValuesFromBrowser(storefrontAccessToken, storefrontApiDomain = "") {
const { uniqueToken, visitToken } = getTrackingValues();
const response = await fetch(
// TODO: update this endpoint when it becomes stable
`${storefrontApiDomain.replace(/\/+$/, "")}/api/unstable/graphql.json`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
...storefrontAccessToken && {
"X-Shopify-Storefront-Access-Token": storefrontAccessToken
},
...visitToken || uniqueToken ? {
[SHOPIFY_VISIT_TOKEN_HEADER]: visitToken,
[SHOPIFY_UNIQUE_TOKEN_HEADER]: uniqueToken
} : void 0
},
body: JSON.stringify({
query: (
// This query ensures we get _cmp (consent) server-timing header, which is not available in other queries.
// This value can be passed later to consent-tracking-api and privacy-banner scripts to avoid extra requests.
"query ensureCookies { consentManagement { cookies(visitorConsent:{}) { cookieDomain } } }"
)
})
}
);
if (!response.ok) {
throw new Error(
`Failed to fetch consent from browser: ${response.status} ${response.statusText}`
);
}
await response.json();
getTrackingValues();
}
function useCoreShopifyCookies({
checkoutDomain,
storefrontAccessToken,
fetchTrackingValues = false
}) {
const [cookiesReady, setCookiesReady] = useState(!fetchTrackingValues);
const hasFetchedTrackingValues = useRef(false);
useEffect(() => {
if (!fetchTrackingValues) {
setCookiesReady(true);
return;
}
if (hasFetchedTrackingValues.current) return;
hasFetchedTrackingValues.current = true;
fetchTrackingValuesFromBrowser(storefrontAccessToken).catch(
(error) => checkoutDomain ? (
// Retry with checkout domain if available to at least
// get the server-timing values for tracking.
fetchTrackingValuesFromBrowser(
storefrontAccessToken,
checkoutDomain
)
) : Promise.reject(error)
).catch((error) => {
console.warn(
"[h2:warn:useShopifyCookies] Failed to fetch tracking values from browser: " + (error instanceof Error ? error.message : String(error))
);
}).finally(() => {
setCookiesReady(true);
});
}, [checkoutDomain, fetchTrackingValues, storefrontAccessToken]);
return cookiesReady;
}
export {
useShopifyCookies
};
//# sourceMappingURL=useShopifyCookies.mjs.map