4
0
forked from crowetic/commerce

First try at trying to create extendable hooks

This commit is contained in:
Luis Alvarez 2020-09-25 13:00:03 -05:00
parent 4387358451
commit 381b19e820
4 changed files with 179 additions and 0 deletions

13
lib/bigcommerce/cart.tsx Normal file
View File

@ -0,0 +1,13 @@
import {
CartProvider as CommerceCartProvider,
useCart as useCommerceCart,
} from '../commerce/cart';
import { Cart } from './index';
export function CartProvider({ children }) {
return <CommerceCartProvider>{children}</CommerceCartProvider>;
}
export function useCart() {
return useCommerceCart<Cart>();
}

78
lib/bigcommerce/index.tsx Normal file
View File

@ -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<Cart>
) {
const res = await fetch(url);
if (res.ok) {
return res.json();
}
throw await getError(res);
}
export const bigcommerce: Connector<Cart> = {
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 (
<CommerceProvider connector={bigcommerce}>{children}</CommerceProvider>
);
}
export const useCommerce = () => useComm<Cart>();

35
lib/commerce/cart.tsx Normal file
View File

@ -0,0 +1,35 @@
import { createContext, useContext } from 'react';
import useSWR, { responseInterface } from 'swr';
import { Cart, useCommerce } from '.';
export type CartResponse<C extends Cart> = responseInterface<C, Error> & {
isEmpty: boolean;
};
const CartContext = createContext<CartResponse<Cart>>(null);
function getCartCookie() {
// TODO: Figure how the cart should be persisted
return null;
}
export function CartProvider({ children }) {
const { hooks, fetcher } = useCommerce<Cart>();
const { useCart } = hooks;
const cartId = getCartCookie();
const response = useSWR(
() => (cartId ? [useCart.url, useCart.query, useCart.resolver] : null),
fetcher
);
const isEmpty = true;
return (
<CartContext.Provider value={{ ...response, isEmpty }}>
{children}
</CartContext.Provider>
);
}
export function useCart<C extends Cart>() {
return useContext(CartContext) as CartResponse<C>;
}

53
lib/commerce/index.tsx Normal file
View File

@ -0,0 +1,53 @@
import { createContext, ReactNode, useContext } from 'react';
const Commerce = createContext<Connector<any>>(null);
export type Cart = any;
export type CommerceProps<C extends Cart> = {
children?: ReactNode;
connector: Connector<C>;
};
export type Connector<C extends Cart> = {
hooks: {
useCart: Hook<C>;
useAddItem: Hook<C>;
useUpdateItem: Hook<C>;
useRemoveItem: Hook<C>;
};
fetcher: Fetcher<any>;
locale: string;
};
export type Hook<T extends any> = {
query?: string;
url?: string;
resolver: HookResolver<T>;
};
export type HookResolver<T> = (
fetcher: Fetcher<T>,
context: ResolverContext
) => T | Promise<T>;
export type Fetcher<T> = (...args: any) => T | Promise<T>;
export type ResolverContext = {
query?: string;
locale: string;
};
export function CommerceProvider({ children, connector }: CommerceProps<Cart>) {
if (!connector) {
throw new Error(
'CommerceProvider requires a valid headless commerce connector'
);
}
return <Commerce.Provider value={connector}>{children}</Commerce.Provider>;
}
export function useCommerce<C extends Cart>() {
return useContext(Commerce) as Connector<C>;
}