Merge branch 'master' of github.com:vercel/commerce into outgrow-reaction-commerce-provider

Signed-off-by: Loan Laux <loan@outgrow.io>
This commit is contained in:
Loan Laux
2021-06-14 11:31:52 +04:00
190 changed files with 34523 additions and 1653 deletions

View File

@@ -0,0 +1,4 @@
export { default as useCart } from './use-cart'
export { default as useAddItem } from './use-add-item'
export { default as useUpdateItem } from './use-update-item'
export { default as useRemoveItem } from './use-remove-item'

View File

@@ -0,0 +1,54 @@
import { useCallback } from 'react'
import type { MutationHook } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
import useCart from './use-cart'
import * as mutation from '../utils/mutations'
import { getCheckoutId, checkoutToCart } from '../utils'
import { Mutation, MutationCheckoutLinesAddArgs } from '../schema'
import { AddItemHook } from '@commerce/types/cart'
export default useAddItem as UseAddItem<typeof handler>
export const handler: MutationHook<AddItemHook> = {
fetchOptions: { query: mutation.CheckoutLineAdd },
async fetcher({ input: item, options, fetch }) {
if (item.quantity && (!Number.isInteger(item.quantity) || item.quantity! < 1)) {
throw new CommerceError({
message: 'The item quantity has to be a valid integer greater than 0',
})
}
const { checkoutLinesAdd } = await fetch<Mutation, MutationCheckoutLinesAddArgs>({
...options,
variables: {
checkoutId: getCheckoutId().checkoutId,
lineItems: [
{
variantId: item.variantId,
quantity: item.quantity ?? 1,
},
],
},
})
return checkoutToCart(checkoutLinesAdd)
},
useHook:
({ fetch }) =>
() => {
const { mutate } = useCart()
return useCallback(
async function addItem(input) {
const data = await fetch({ input })
await mutate(data, false)
return data
},
[fetch, mutate]
)
},
}

View File

@@ -0,0 +1,53 @@
import { useMemo } from 'react'
import useCommerceCart, { UseCart } from '@commerce/cart/use-cart'
import { SWRHook } from '@commerce/utils/types'
import { checkoutCreate, checkoutToCart, getCheckoutId } from '../utils'
import * as query from '../utils/queries'
import { GetCartHook } from '@commerce/types/cart'
export default useCommerceCart as UseCart<typeof handler>
export const handler: SWRHook<GetCartHook> = {
fetchOptions: {
query: query.CheckoutOne,
},
async fetcher({ input: { cartId: checkoutId }, options, fetch }) {
let checkout
if (checkoutId) {
const checkoutId = getCheckoutId().checkoutToken
const data = await fetch({
...options,
variables: { checkoutId },
})
checkout = data
}
if (checkout?.completedAt || !checkoutId) {
checkout = await checkoutCreate(fetch)
}
return checkoutToCart(checkout)
},
useHook:
({ useData }) =>
(input) => {
const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
})
return useMemo(
() =>
Object.create(response, {
isEmpty: {
get() {
return (response.data?.lineItems.length ?? 0) <= 0
},
enumerable: true,
},
}),
[response]
)
},
}

View File

@@ -0,0 +1,39 @@
import { useCallback } from 'react'
import type { MutationHookContext, HookFetcherContext, MutationHook } from '@commerce/utils/types'
import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item'
import useCart from './use-cart'
import * as mutation from '../utils/mutations'
import { getCheckoutId, checkoutToCart } from '../utils'
import { Mutation, MutationCheckoutLineDeleteArgs } from '../schema'
import { LineItem, RemoveItemHook } from '../types/cart'
export default useRemoveItem as UseRemoveItem<typeof handler>
export const handler = {
fetchOptions: { query: mutation.CheckoutLineDelete },
async fetcher({ input: { itemId }, options, fetch }: HookFetcherContext<RemoveItemHook>) {
const data = await fetch<Mutation, MutationCheckoutLineDeleteArgs>({
...options,
variables: {
checkoutId: getCheckoutId().checkoutId,
lineId: itemId,
},
})
return checkoutToCart(data.checkoutLineDelete)
},
useHook:
({ fetch }: MutationHookContext<RemoveItemHook>) =>
<T extends LineItem | undefined = undefined>() => {
const { mutate } = useCart()
return useCallback(
async function removeItem(input) {
const data = await fetch({ input: { itemId: input.id } })
await mutate(data, false)
return data
},
[fetch, mutate]
)
},
}

View File

@@ -0,0 +1,96 @@
import { useCallback } from 'react'
import debounce from 'lodash.debounce'
import type { HookFetcherContext, MutationHookContext } from '@commerce/utils/types'
import { ValidationError } from '@commerce/utils/errors'
import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item'
import useCart from './use-cart'
import { handler as removeItemHandler } from './use-remove-item'
import type { LineItem } from '../types'
import { checkoutToCart } from '../utils'
import { getCheckoutId } from '../utils'
import { Mutation, MutationCheckoutLinesUpdateArgs } from '../schema'
import * as mutation from '../utils/mutations'
import type { UpdateItemHook } from '../types/cart'
export type UpdateItemActionInput<T = any> = T extends LineItem
? Partial<UpdateItemHook['actionInput']>
: UpdateItemHook['actionInput']
export default useUpdateItem as UseUpdateItem<typeof handler>
export const handler = {
fetchOptions: { query: mutation.CheckoutLineUpdate },
async fetcher({ input: { itemId, item }, options, fetch }: HookFetcherContext<UpdateItemHook>) {
if (Number.isInteger(item.quantity)) {
// Also allow the update hook to remove an item if the quantity is lower than 1
if (item.quantity! < 1) {
return removeItemHandler.fetcher({
options: removeItemHandler.fetchOptions,
input: { itemId },
fetch,
})
}
} else if (item.quantity) {
throw new ValidationError({
message: 'The item quantity has to be a valid integer',
})
}
const checkoutId = getCheckoutId().checkoutId
const { checkoutLinesUpdate } = await fetch<Mutation, MutationCheckoutLinesUpdateArgs>({
...options,
variables: {
checkoutId,
lineItems: [
{
variantId: item.variantId,
quantity: item.quantity,
},
],
},
})
return checkoutToCart(checkoutLinesUpdate)
},
useHook:
({ fetch }: MutationHookContext<UpdateItemHook>) =>
<T extends LineItem | undefined = undefined>(
ctx: {
item?: T
wait?: number
} = {}
) => {
const { item } = ctx
const { mutate } = useCart() as any
return useCallback(
debounce(async (input: UpdateItemActionInput<T>) => {
const itemId = input.id ?? item?.id
const productId = input.productId ?? item?.productId
const variantId = input.productId ?? item?.variantId
if (!itemId || !productId || !variantId) {
throw new ValidationError({
message: 'Invalid input used for this operation',
})
}
const data = await fetch({
input: {
item: {
productId,
variantId,
quantity: input.quantity,
},
itemId,
},
})
await mutate(data, false)
return data
}, ctx.wait ?? 500),
[fetch, mutate]
)
},
}