import {
json,
type LoaderFunctionArgs,
} from '@shopify/remix-oxygen';
import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useLoaderData,
} from '@remix-run/react';
import {
Analytics,
getShopAnalytics,
useNonce,
createContentSecurityPolicy,
} from '@shopify/hydrogen';
import { GloodProvider, createGlood } from '@glood/hydrogen';
import { recommendations, search, wishlist } from '@glood/hydrogen';
// Create Glood client with basic configuration
const glood = createGlood({
apiKey: process.env.GLOOD_API_KEY!,
myShopifyDomain: process.env.PUBLIC_STORE_DOMAIN!,
debug: process.env.NODE_ENV === 'development',
})
.use(recommendations())
.use(search())
.use(wishlist());
export async function loader({context}: LoaderFunctionArgs) {
const {storefront, env} = context;
// Configure CSP with Glood domains
const {nonce, header} = createContentSecurityPolicy({
shop: {
checkoutDomain: env.PUBLIC_CHECKOUT_DOMAIN,
storeDomain: env.PUBLIC_STORE_DOMAIN,
},
connectSrc: [
'https://cdn.shopify.com',
'https://*.shopifycloud.com',
'https://events.glood.ai',
'https://s-pixel.glood.ai',
'https://w-pixel.glood.ai'
],
});
const analytics = getShopAnalytics({
storefront,
publicStorefrontId: env.PUBLIC_STOREFRONT_ID,
});
return json(
{
analytics,
nonce,
},
{
headers: {
'Content-Security-Policy': header,
},
}
);
}
export default function App() {
const nonce = useNonce();
const data = useLoaderData<typeof loader>();
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Analytics analytics={data.analytics}>
<GloodProvider client={glood} loaderData={data}>
<Layout>
<Outlet />
</Layout>
</GloodProvider>
</Analytics>
<ScrollRestoration nonce={nonce} />
<Scripts nonce={nonce} />
</body>
</html>
);
}