diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts index 344be62d6..1f1a5c491 100644 --- a/framework/shopify/api/checkout/index.ts +++ b/framework/shopify/api/checkout/index.ts @@ -7,7 +7,7 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, SHOPIFY_CUSTOMER_TOKEN_COOKIE, -} from '@framework/config' +} from '@framework/provider' import { getConfig } from '..' import associateCustomerWithCheckoutMutation from '@framework/utils/mutations/associate-customer-with-checkout' diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index d246829ff..2a3c0c4ec 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -5,7 +5,19 @@ import { API_TOKEN, SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CUSTOMER_TOKEN_COOKIE, -} from '@framework/config' +} from '@framework/const' + +if (!API_URL) { + throw new Error( + `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` + ) +} + +if (!API_TOKEN) { + throw new Error( + `The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` + ) +} import fetchGraphqlApi from './utils/fetch-graphql-api' diff --git a/framework/shopify/api/utils/fetch-graphql-api.ts b/framework/shopify/api/utils/fetch-graphql-api.ts index 8ff4b27b7..a78eeed74 100644 --- a/framework/shopify/api/utils/fetch-graphql-api.ts +++ b/framework/shopify/api/utils/fetch-graphql-api.ts @@ -1,21 +1,9 @@ import type { GraphQLFetcher } from '@commerce/api' import fetch from './fetch' -import { API_URL, API_TOKEN } from '../../config' +import { API_URL, API_TOKEN } from '../../const' import { getError } from '@framework/utils/handle-fetch-response' -if (!API_URL) { - throw new Error( - `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` - ) -} - -if (!API_TOKEN) { - throw new Error( - `The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` - ) -} - const fetchGraphqlApi: GraphQLFetcher = async ( query: string, { variables } = {}, diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index a04c30def..e5ab8cafb 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -1,68 +1,4 @@ -import type { HookFetcher } from '@commerce/utils/types' -import type { SwrOptions } from '@commerce/utils/use-data' +import useCommerceCart, { UseCart } from '@commerce/cart/use-cart' +import type { ShopifyProvider } from '..' -import useResponse from '@commerce/utils/use-response' -import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' -import getCheckoutQuery from '@framework/utils/queries/get-checkout-query' - -import { Cart } from '@commerce/types' -import { checkoutToCart, checkoutCreate } from './utils' -import { getConfig } from '@framework/api' - -const defaultOpts = { - query: getCheckoutQuery, -} - -export const fetcher: HookFetcher = async ( - options, - { cartId: checkoutId }, - fetch -) => { - let checkout - - if (checkoutId) { - const data = await fetch({ - ...defaultOpts, - ...options, - variables: { - checkoutId, - }, - }) - checkout = data?.node - } - - if (checkout?.completedAt || !checkoutId) { - checkout = await checkoutCreate(fetch) - } - - return checkoutToCart({ checkout }) -} - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions -) { - const useCart = () => { - const response = useCommerceCart(defaultOpts, [], customFetcher, { - revalidateOnFocus: true, - ...swrOptions, - }) - const res = useResponse(response, { - descriptors: { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }, - }) - return res - } - - useCart.extend = extendHook - - return useCart -} - -export default extendHook(fetcher) +export default useCommerceCart as UseCart diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts index 17377e7f5..cb2038a10 100644 --- a/framework/shopify/cart/utils/checkout-create.ts +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -1,7 +1,7 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, -} from '@framework/config' +} from '@framework/provider' import checkoutCreateMutation from '@framework/utils/mutations/checkout-create' import Cookies from 'js-cookie' diff --git a/framework/shopify/config.ts b/framework/shopify/config.ts deleted file mode 100644 index 2444f6033..000000000 --- a/framework/shopify/config.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { CommerceConfig } from '@commerce' -import handleFetchResponse from './utils/handle-fetch-response' - -export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' - -export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' - -export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' - -export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN - -export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` - -export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN - -export type ShopifyConfig = { - locale: string - cartCookie: string - storeDomain: string | undefined -} & CommerceConfig - -const shopifyConfig: ShopifyConfig = { - locale: 'en-us', - cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, - storeDomain: STORE_DOMAIN, - async fetcher({ method = 'POST', query, variables }) { - return handleFetchResponse( - await fetch(API_URL, { - method, - body: JSON.stringify({ query, variables }), - headers: { - 'X-Shopify-Storefront-Access-Token': API_TOKEN!, - 'Content-Type': 'application/json', - }, - }) - ) - }, -} - -export default shopifyConfig diff --git a/framework/shopify/const.ts b/framework/shopify/const.ts new file mode 100644 index 000000000..a6e9e8d90 --- /dev/null +++ b/framework/shopify/const.ts @@ -0,0 +1,11 @@ +export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' + +export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' + +export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' + +export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN + +export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` + +export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx index 0d6f9fca9..5b25d6b21 100644 --- a/framework/shopify/index.tsx +++ b/framework/shopify/index.tsx @@ -2,11 +2,24 @@ import * as React from 'react' import { ReactNode } from 'react' import { + CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce, } from '@commerce' -import shopifyConfig, { ShopifyConfig } from './config' +import { shopifyProvider, ShopifyProvider } from './provider' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' + +export { shopifyProvider } +export type { ShopifyProvider } + +export const shopifyConfig: CommerceConfig = { + locale: 'en-us', + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, +} + +export type ShopifyConfig = Partial + export type ShopifyProps = { children?: ReactNode locale: string @@ -14,7 +27,10 @@ export type ShopifyProps = { export function CommerceProvider({ children, ...config }: ShopifyProps) { return ( - + {children} ) diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts new file mode 100644 index 000000000..bd8872b1f --- /dev/null +++ b/framework/shopify/provider.ts @@ -0,0 +1,63 @@ +import { Fetcher, HookHandler } from '@commerce/utils/types' +import { + API_TOKEN, + API_URL, + SHOPIFY_CHECKOUT_ID_COOKIE, + STORE_DOMAIN, +} from './const' +import { normalizeCart } from './lib/normalize' +import { Cart } from './types' + +import handleFetchResponse from './utils/handle-fetch-response' + +const useCart: HookHandler< + Cart | null, + [], + any, + any, + any, + { isEmpty?: boolean } +> = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'GET', + }, + swrOptions: { + revalidateOnFocus: false, + }, + normalizer: normalizeCart, + onResponse(response) { + return Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }) + }, +} + +const fetcher: Fetcher = async ({ method = 'GET', variables, query }) => { + return handleFetchResponse( + await fetch(API_URL, { + method, + body: JSON.stringify({ query, variables }), + headers: { + 'X-Shopify-Storefront-Access-Token': API_TOKEN!, + 'Content-Type': 'application/json', + }, + }) + ) +} + +export const shopifyProvider = { + locale: 'en-us', + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + storeDomain: STORE_DOMAIN, + fetcher, + cartNormalizer: normalizeCart, + cart: { useCart }, +} + +export type ShopifyProvider = typeof shopifyProvider diff --git a/framework/shopify/utils/customer-token.ts b/framework/shopify/utils/customer-token.ts index 119f465b5..beae54765 100644 --- a/framework/shopify/utils/customer-token.ts +++ b/framework/shopify/utils/customer-token.ts @@ -1,5 +1,5 @@ import Cookies from 'js-cookie' -import { SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '@framework/config' +import { SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../const' export const getCustomerToken = () => Cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE) diff --git a/framework/shopify/utils/get-checkout-id.ts b/framework/shopify/utils/get-checkout-id.ts index 623cf9577..11e3802d9 100644 --- a/framework/shopify/utils/get-checkout-id.ts +++ b/framework/shopify/utils/get-checkout-id.ts @@ -1,5 +1,5 @@ import Cookies from 'js-cookie' -import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../config' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../const' const getCheckoutId = (id?: string) => { return id ?? Cookies.get(SHOPIFY_CHECKOUT_ID_COOKIE)