diff --git a/lib/bigcommerce/cart.tsx b/lib/bigcommerce/cart.tsx new file mode 100644 index 000000000..f0bb46ac2 --- /dev/null +++ b/lib/bigcommerce/cart.tsx @@ -0,0 +1,13 @@ +import { + CartProvider as CommerceCartProvider, + useCart as useCommerceCart, +} from '../commerce/cart'; +import { Cart } from './index'; + +export function CartProvider({ children }) { + return {children}; +} + +export function useCart() { + return useCommerceCart(); +} diff --git a/lib/bigcommerce/index.tsx b/lib/bigcommerce/index.tsx new file mode 100644 index 000000000..1f53b07d8 --- /dev/null +++ b/lib/bigcommerce/index.tsx @@ -0,0 +1,78 @@ +import { + CommerceProvider, + Connector, + HookResolver, + useCommerce as useComm, +} from '../commerce'; + +export type Cart = any; + +async function getText(res: Response) { + try { + return (await res.text()) || res.statusText; + } catch (error) { + return res.statusText; + } +} + +async function getError(res: Response) { + if (res.headers.get('Content-Type')?.includes('application/json')) { + const data = await res.json(); + return data.errors[0]; + } + return { message: await getText(res) }; +} + +async function fetcher( + url: string, + query: string, + resolver: HookResolver +) { + const res = await fetch(url); + + if (res.ok) { + return res.json(); + } + + throw await getError(res); +} + +export const bigcommerce: Connector = { + hooks: { + useCart: { + query: '', + resolver() { + return; + }, + }, + useAddItem: { + query: '', + resolver() { + return; + }, + }, + useUpdateItem: { + query: '', + resolver() { + return; + }, + }, + useRemoveItem: { + query: '', + resolver() { + return; + }, + }, + }, + locale: 'en-us', + fetcher, +}; + +// TODO: The connector should be extendable when a developer is using it +export function BigcommerceProvider({ children }) { + return ( + {children} + ); +} + +export const useCommerce = () => useComm(); diff --git a/lib/commerce/cart.tsx b/lib/commerce/cart.tsx new file mode 100644 index 000000000..12f420186 --- /dev/null +++ b/lib/commerce/cart.tsx @@ -0,0 +1,35 @@ +import { createContext, useContext } from 'react'; +import useSWR, { responseInterface } from 'swr'; +import { Cart, useCommerce } from '.'; + +export type CartResponse = responseInterface & { + isEmpty: boolean; +}; + +const CartContext = createContext>(null); + +function getCartCookie() { + // TODO: Figure how the cart should be persisted + return null; +} + +export function CartProvider({ children }) { + const { hooks, fetcher } = useCommerce(); + const { useCart } = hooks; + const cartId = getCartCookie(); + const response = useSWR( + () => (cartId ? [useCart.url, useCart.query, useCart.resolver] : null), + fetcher + ); + const isEmpty = true; + + return ( + + {children} + + ); +} + +export function useCart() { + return useContext(CartContext) as CartResponse; +} diff --git a/lib/commerce/index.tsx b/lib/commerce/index.tsx new file mode 100644 index 000000000..ac05c008e --- /dev/null +++ b/lib/commerce/index.tsx @@ -0,0 +1,53 @@ +import { createContext, ReactNode, useContext } from 'react'; + +const Commerce = createContext>(null); + +export type Cart = any; + +export type CommerceProps = { + children?: ReactNode; + connector: Connector; +}; + +export type Connector = { + hooks: { + useCart: Hook; + useAddItem: Hook; + useUpdateItem: Hook; + useRemoveItem: Hook; + }; + fetcher: Fetcher; + locale: string; +}; + +export type Hook = { + query?: string; + url?: string; + resolver: HookResolver; +}; + +export type HookResolver = ( + fetcher: Fetcher, + context: ResolverContext +) => T | Promise; + +export type Fetcher = (...args: any) => T | Promise; + +export type ResolverContext = { + query?: string; + locale: string; +}; + +export function CommerceProvider({ children, connector }: CommerceProps) { + if (!connector) { + throw new Error( + 'CommerceProvider requires a valid headless commerce connector' + ); + } + + return {children}; +} + +export function useCommerce() { + return useContext(Commerce) as Connector; +}