@shopify/hydrogen 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esnext/components/CartProvider/CartActions.client.d.ts +4 -2
- package/dist/esnext/components/CartProvider/CartActions.client.js +19 -19
- package/dist/esnext/components/CartProvider/CartProviderV2.client.d.ts +6 -1
- package/dist/esnext/components/CartProvider/CartProviderV2.client.js +152 -14
- package/dist/esnext/components/CartProvider/types.d.ts +16 -0
- package/dist/esnext/components/CartProvider/useCartAPIStateMachine.client.d.ts +7 -1
- package/dist/esnext/components/CartProvider/useCartAPIStateMachine.client.js +33 -13
- package/dist/esnext/utilities/storefrontApi.js +18 -14
- package/dist/esnext/version.d.ts +1 -1
- package/dist/esnext/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AttributeInput, CartBuyerIdentityInput, CartInput, CartLineInput, CartLineUpdateInput } from '../../storefront-api-types.js';
|
|
1
|
+
import { AttributeInput, CartBuyerIdentityInput, CartInput, CartLineInput, CartLineUpdateInput, CountryCode } from '../../storefront-api-types.js';
|
|
2
2
|
import { CartAttributesUpdateMutation } from './graphql/CartAttributesUpdateMutation.js';
|
|
3
3
|
import { CartBuyerIdentityUpdateMutation } from './graphql/CartBuyerIdentityUpdateMutation.js';
|
|
4
4
|
import { CartCreateMutation } from './graphql/CartCreateMutation.js';
|
|
@@ -13,11 +13,13 @@ import { CartQueryQuery } from './graphql/CartQuery.js';
|
|
|
13
13
|
*
|
|
14
14
|
* See [cart API graphql mutations](https://shopify.dev/api/storefront/2022-07/objects/Cart)
|
|
15
15
|
*/
|
|
16
|
-
export declare function useCartActions({ numCartLines, cartFragment, }: {
|
|
16
|
+
export declare function useCartActions({ numCartLines, cartFragment, countryCode, }: {
|
|
17
17
|
/** Maximum number of cart lines to fetch. Defaults to 250 cart lines. */
|
|
18
18
|
numCartLines?: number;
|
|
19
19
|
/** A fragment used to query the Storefront API's [Cart object](https://shopify.dev/api/storefront/latest/objects/cart) for all queries and mutations. A default value is used if no argument is provided. */
|
|
20
20
|
cartFragment?: string;
|
|
21
|
+
/** The ISO country code for i18n. */
|
|
22
|
+
countryCode?: CountryCode;
|
|
21
23
|
}): {
|
|
22
24
|
cartFetch: (cartId: string) => Promise<{
|
|
23
25
|
data: CartQueryQuery | undefined;
|
|
@@ -7,7 +7,7 @@ import { useCartFetch } from './hooks.client.js';
|
|
|
7
7
|
*
|
|
8
8
|
* See [cart API graphql mutations](https://shopify.dev/api/storefront/2022-07/objects/Cart)
|
|
9
9
|
*/
|
|
10
|
-
export function useCartActions({ numCartLines, cartFragment = defaultCartFragment, }) {
|
|
10
|
+
export function useCartActions({ numCartLines, cartFragment = defaultCartFragment, countryCode = CountryCode.Us, }) {
|
|
11
11
|
const fetchCart = useCartFetch();
|
|
12
12
|
const cartFetch = useCallback((cartId) => {
|
|
13
13
|
return fetchCart({
|
|
@@ -15,20 +15,20 @@ export function useCartActions({ numCartLines, cartFragment = defaultCartFragmen
|
|
|
15
15
|
variables: {
|
|
16
16
|
id: cartId,
|
|
17
17
|
numCartLines,
|
|
18
|
-
country:
|
|
18
|
+
country: countryCode,
|
|
19
19
|
},
|
|
20
20
|
});
|
|
21
|
-
}, [fetchCart, cartFragment, numCartLines]);
|
|
21
|
+
}, [fetchCart, cartFragment, numCartLines, countryCode]);
|
|
22
22
|
const cartCreate = useCallback((cart) => {
|
|
23
23
|
return fetchCart({
|
|
24
24
|
query: CartCreate(cartFragment),
|
|
25
25
|
variables: {
|
|
26
26
|
input: cart,
|
|
27
27
|
numCartLines,
|
|
28
|
-
country:
|
|
28
|
+
country: countryCode,
|
|
29
29
|
},
|
|
30
30
|
});
|
|
31
|
-
}, [cartFragment, fetchCart, numCartLines]);
|
|
31
|
+
}, [cartFragment, countryCode, fetchCart, numCartLines]);
|
|
32
32
|
const cartLineAdd = useCallback((cartId, lines) => {
|
|
33
33
|
return fetchCart({
|
|
34
34
|
query: CartLineAdd(cartFragment),
|
|
@@ -36,10 +36,10 @@ export function useCartActions({ numCartLines, cartFragment = defaultCartFragmen
|
|
|
36
36
|
cartId,
|
|
37
37
|
lines,
|
|
38
38
|
numCartLines,
|
|
39
|
-
country:
|
|
39
|
+
country: countryCode,
|
|
40
40
|
},
|
|
41
41
|
});
|
|
42
|
-
}, [cartFragment, fetchCart, numCartLines]);
|
|
42
|
+
}, [cartFragment, countryCode, fetchCart, numCartLines]);
|
|
43
43
|
const cartLineUpdate = useCallback((cartId, lines) => {
|
|
44
44
|
return fetchCart({
|
|
45
45
|
query: CartLineUpdate(cartFragment),
|
|
@@ -47,10 +47,10 @@ export function useCartActions({ numCartLines, cartFragment = defaultCartFragmen
|
|
|
47
47
|
cartId,
|
|
48
48
|
lines,
|
|
49
49
|
numCartLines,
|
|
50
|
-
country:
|
|
50
|
+
country: countryCode,
|
|
51
51
|
},
|
|
52
52
|
});
|
|
53
|
-
}, [cartFragment, fetchCart, numCartLines]);
|
|
53
|
+
}, [cartFragment, countryCode, fetchCart, numCartLines]);
|
|
54
54
|
const cartLineRemove = useCallback((cartId, lines) => {
|
|
55
55
|
return fetchCart({
|
|
56
56
|
query: CartLineRemove(cartFragment),
|
|
@@ -58,10 +58,10 @@ export function useCartActions({ numCartLines, cartFragment = defaultCartFragmen
|
|
|
58
58
|
cartId,
|
|
59
59
|
lines,
|
|
60
60
|
numCartLines,
|
|
61
|
-
country:
|
|
61
|
+
country: countryCode,
|
|
62
62
|
},
|
|
63
63
|
});
|
|
64
|
-
}, [cartFragment, fetchCart, numCartLines]);
|
|
64
|
+
}, [cartFragment, countryCode, fetchCart, numCartLines]);
|
|
65
65
|
const noteUpdate = useCallback((cartId, note) => {
|
|
66
66
|
return fetchCart({
|
|
67
67
|
query: CartNoteUpdate(cartFragment),
|
|
@@ -69,10 +69,10 @@ export function useCartActions({ numCartLines, cartFragment = defaultCartFragmen
|
|
|
69
69
|
cartId,
|
|
70
70
|
note,
|
|
71
71
|
numCartLines,
|
|
72
|
-
country:
|
|
72
|
+
country: countryCode,
|
|
73
73
|
},
|
|
74
74
|
});
|
|
75
|
-
}, [fetchCart, cartFragment, numCartLines]);
|
|
75
|
+
}, [fetchCart, cartFragment, numCartLines, countryCode]);
|
|
76
76
|
const buyerIdentityUpdate = useCallback((cartId, buyerIdentity) => {
|
|
77
77
|
return fetchCart({
|
|
78
78
|
query: CartBuyerIdentityUpdate(cartFragment),
|
|
@@ -80,10 +80,10 @@ export function useCartActions({ numCartLines, cartFragment = defaultCartFragmen
|
|
|
80
80
|
cartId,
|
|
81
81
|
buyerIdentity,
|
|
82
82
|
numCartLines,
|
|
83
|
-
country:
|
|
83
|
+
country: countryCode,
|
|
84
84
|
},
|
|
85
85
|
});
|
|
86
|
-
}, [cartFragment, fetchCart, numCartLines]);
|
|
86
|
+
}, [cartFragment, countryCode, fetchCart, numCartLines]);
|
|
87
87
|
const cartAttributesUpdate = useCallback((cartId, attributes) => {
|
|
88
88
|
return fetchCart({
|
|
89
89
|
query: CartAttributesUpdate(cartFragment),
|
|
@@ -91,10 +91,10 @@ export function useCartActions({ numCartLines, cartFragment = defaultCartFragmen
|
|
|
91
91
|
cartId,
|
|
92
92
|
attributes,
|
|
93
93
|
numCartLines,
|
|
94
|
-
country:
|
|
94
|
+
country: countryCode,
|
|
95
95
|
},
|
|
96
96
|
});
|
|
97
|
-
}, [cartFragment, fetchCart, numCartLines]);
|
|
97
|
+
}, [cartFragment, countryCode, fetchCart, numCartLines]);
|
|
98
98
|
const discountCodesUpdate = useCallback((cartId, discountCodes) => {
|
|
99
99
|
return fetchCart({
|
|
100
100
|
query: CartDiscountCodesUpdate(cartFragment),
|
|
@@ -102,10 +102,10 @@ export function useCartActions({ numCartLines, cartFragment = defaultCartFragmen
|
|
|
102
102
|
cartId,
|
|
103
103
|
discountCodes,
|
|
104
104
|
numCartLines,
|
|
105
|
-
country:
|
|
105
|
+
country: countryCode,
|
|
106
106
|
},
|
|
107
107
|
});
|
|
108
|
-
}, [cartFragment, fetchCart, numCartLines]);
|
|
108
|
+
}, [cartFragment, countryCode, fetchCart, numCartLines]);
|
|
109
109
|
return useMemo(() => ({
|
|
110
110
|
cartFetch,
|
|
111
111
|
cartCreate,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { CartFragmentFragment } from './graphql/CartFragment.js';
|
|
3
|
-
|
|
3
|
+
import { CartBuyerIdentityInput, CountryCode } from '../../storefront-api-types.js';
|
|
4
|
+
export declare function CartProviderV2({ children, numCartLines, onCreate, onLineAdd, onLineRemove, onLineUpdate, onNoteUpdate, onBuyerIdentityUpdate, onAttributesUpdate, onDiscountCodesUpdate, onCreateComplete, onLineAddComplete, onLineRemoveComplete, onLineUpdateComplete, onNoteUpdateComplete, onBuyerIdentityUpdateComplete, onAttributesUpdateComplete, onDiscountCodesUpdateComplete, data, cartFragment, customerAccessToken, countryCode, }: {
|
|
4
5
|
/** Any `ReactNode` elements. */
|
|
5
6
|
children: React.ReactNode;
|
|
6
7
|
/** Maximum number of cart lines to fetch. Defaults to 250 cart lines. */
|
|
@@ -41,4 +42,8 @@ export declare function CartProviderV2({ children, numCartLines, onCreate, onLin
|
|
|
41
42
|
data?: CartFragmentFragment;
|
|
42
43
|
/** A fragment used to query the Storefront API's [Cart object](https://shopify.dev/api/storefront/latest/objects/cart) for all queries and mutations. A default value is used if no argument is provided. */
|
|
43
44
|
cartFragment?: string;
|
|
45
|
+
/** A customer access token that's accessible on the server if there's a customer login. */
|
|
46
|
+
customerAccessToken?: CartBuyerIdentityInput['customerAccessToken'];
|
|
47
|
+
/** The ISO country code for i18n. */
|
|
48
|
+
countryCode?: CountryCode;
|
|
44
49
|
}): JSX.Element;
|
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { CountryCode, } from '../../storefront-api-types.js';
|
|
2
3
|
import { CartContext } from './context.js';
|
|
3
4
|
import { useCartActions } from './CartActions.client.js';
|
|
4
5
|
import { useCartAPIStateMachine } from './useCartAPIStateMachine.client.js';
|
|
5
6
|
import { CART_ID_STORAGE_KEY } from './constants.js';
|
|
6
|
-
|
|
7
|
+
import { ClientAnalytics } from '../../foundation/Analytics/ClientAnalytics.js';
|
|
8
|
+
export function CartProviderV2({ children, numCartLines, onCreate, onLineAdd, onLineRemove, onLineUpdate, onNoteUpdate, onBuyerIdentityUpdate, onAttributesUpdate, onDiscountCodesUpdate, onCreateComplete, onLineAddComplete, onLineRemoveComplete, onLineUpdateComplete, onNoteUpdateComplete, onBuyerIdentityUpdateComplete, onAttributesUpdateComplete, onDiscountCodesUpdateComplete, data: cart, cartFragment, customerAccessToken, countryCode = CountryCode.Us, }) {
|
|
9
|
+
if (countryCode)
|
|
10
|
+
countryCode = countryCode.toUpperCase();
|
|
11
|
+
const [prevCountryCode, setPrevCountryCode] = useState(countryCode);
|
|
12
|
+
const [prevCustomerAccessToken, setPrevCustomerAccessToken] = useState(customerAccessToken);
|
|
13
|
+
const customerOverridesCountryCode = useRef(false);
|
|
14
|
+
if (prevCountryCode !== countryCode ||
|
|
15
|
+
prevCustomerAccessToken !== customerAccessToken) {
|
|
16
|
+
setPrevCountryCode(countryCode);
|
|
17
|
+
setPrevCustomerAccessToken(customerAccessToken);
|
|
18
|
+
customerOverridesCountryCode.current = false;
|
|
19
|
+
}
|
|
7
20
|
const { cartFragment: usedCartFragment } = useCartActions({
|
|
8
21
|
numCartLines,
|
|
9
22
|
cartFragment,
|
|
23
|
+
countryCode,
|
|
10
24
|
});
|
|
11
25
|
const [cartState, cartSend] = useCartAPIStateMachine({
|
|
12
26
|
numCartLines,
|
|
@@ -31,25 +45,68 @@ export function CartProviderV2({ children, numCartLines, onCreate, onLineAdd, on
|
|
|
31
45
|
return onDiscountCodesUpdate?.();
|
|
32
46
|
}
|
|
33
47
|
},
|
|
48
|
+
onCartActionOptimisticUI(context, event) {
|
|
49
|
+
if (!context?.cart)
|
|
50
|
+
return { cart: undefined };
|
|
51
|
+
switch (event.type) {
|
|
52
|
+
case 'CARTLINE_REMOVE':
|
|
53
|
+
return {
|
|
54
|
+
...context,
|
|
55
|
+
lastValidCart: context.cart,
|
|
56
|
+
cart: {
|
|
57
|
+
...context.cart,
|
|
58
|
+
lines: context?.cart?.lines.filter(({ id }) => !event.payload.lines.includes(id)),
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
case 'CARTLINE_UPDATE':
|
|
62
|
+
return {
|
|
63
|
+
...context,
|
|
64
|
+
lastValidCart: context.cart,
|
|
65
|
+
cart: {
|
|
66
|
+
...context.cart,
|
|
67
|
+
lines: context.cart.lines.map((line) => {
|
|
68
|
+
const updatedLine = event.payload.lines.find(({ id }) => id === line.id);
|
|
69
|
+
if (updatedLine && updatedLine.quantity) {
|
|
70
|
+
return {
|
|
71
|
+
...line,
|
|
72
|
+
quantity: updatedLine.quantity,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return line;
|
|
76
|
+
}),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return { cart: context.cart ? { ...context.cart } : undefined };
|
|
81
|
+
},
|
|
34
82
|
onCartActionComplete(context, event) {
|
|
83
|
+
const cartActionEvent = event.payload.cartActionEvent;
|
|
35
84
|
switch (event.type) {
|
|
36
85
|
case 'RESOLVE':
|
|
37
|
-
switch (
|
|
86
|
+
switch (cartActionEvent.type) {
|
|
38
87
|
case 'CART_CREATE':
|
|
88
|
+
publishCreateAnalytics(context, cartActionEvent);
|
|
39
89
|
return onCreateComplete?.();
|
|
40
90
|
case 'CARTLINE_ADD':
|
|
91
|
+
publishLineAddAnalytics(context, cartActionEvent);
|
|
41
92
|
return onLineAddComplete?.();
|
|
42
93
|
case 'CARTLINE_REMOVE':
|
|
94
|
+
publishLineRemoveAnalytics(context, cartActionEvent);
|
|
43
95
|
return onLineRemoveComplete?.();
|
|
44
96
|
case 'CARTLINE_UPDATE':
|
|
97
|
+
publishLineUpdateAnalytics(context, cartActionEvent);
|
|
45
98
|
return onLineUpdateComplete?.();
|
|
46
99
|
case 'NOTE_UPDATE':
|
|
47
100
|
return onNoteUpdateComplete?.();
|
|
48
101
|
case 'BUYER_IDENTITY_UPDATE':
|
|
102
|
+
if (countryCodeNotUpdated(context, cartActionEvent)) {
|
|
103
|
+
customerOverridesCountryCode.current = true;
|
|
104
|
+
}
|
|
49
105
|
return onBuyerIdentityUpdateComplete?.();
|
|
50
106
|
case 'CART_ATTRIBUTES_UPDATE':
|
|
51
107
|
return onAttributesUpdateComplete?.();
|
|
52
108
|
case 'DISCOUNT_CODES_UPDATE':
|
|
109
|
+
publishDiscountCodesUpdateAnalytics(context, cartActionEvent);
|
|
53
110
|
return onDiscountCodesUpdateComplete?.();
|
|
54
111
|
}
|
|
55
112
|
}
|
|
@@ -57,6 +114,25 @@ export function CartProviderV2({ children, numCartLines, onCreate, onLineAdd, on
|
|
|
57
114
|
});
|
|
58
115
|
const [cartReady, setCartReady] = useState(false);
|
|
59
116
|
const cartCompleted = cartState.matches('cartCompleted');
|
|
117
|
+
const countryChanged = (cartState.value === 'idle' ||
|
|
118
|
+
cartState.value === 'error' ||
|
|
119
|
+
cartState.value === 'cartCompleted') &&
|
|
120
|
+
countryCode !== cartState?.context?.cart?.buyerIdentity?.countryCode &&
|
|
121
|
+
!cartState.context.errors;
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (!countryChanged || customerOverridesCountryCode.current)
|
|
124
|
+
return;
|
|
125
|
+
cartSend({
|
|
126
|
+
type: 'BUYER_IDENTITY_UPDATE',
|
|
127
|
+
payload: { buyerIdentity: { countryCode, customerAccessToken } },
|
|
128
|
+
});
|
|
129
|
+
}, [
|
|
130
|
+
countryCode,
|
|
131
|
+
customerAccessToken,
|
|
132
|
+
countryChanged,
|
|
133
|
+
customerOverridesCountryCode,
|
|
134
|
+
cartSend,
|
|
135
|
+
]);
|
|
60
136
|
// send cart events when ready
|
|
61
137
|
const onCartReadySend = useCallback((cartEvent) => {
|
|
62
138
|
if (!cartReady) {
|
|
@@ -102,23 +178,42 @@ export function CartProviderV2({ children, numCartLines, onCreate, onLineAdd, on
|
|
|
102
178
|
setCartReady(true);
|
|
103
179
|
}
|
|
104
180
|
}, [cartReady, cartSend]);
|
|
181
|
+
const cartCreate = useCallback((cartInput) => {
|
|
182
|
+
if (countryCode && !cartInput.buyerIdentity?.countryCode) {
|
|
183
|
+
if (cartInput.buyerIdentity == null) {
|
|
184
|
+
cartInput.buyerIdentity = {};
|
|
185
|
+
}
|
|
186
|
+
cartInput.buyerIdentity.countryCode = countryCode;
|
|
187
|
+
}
|
|
188
|
+
if (customerAccessToken &&
|
|
189
|
+
!cartInput.buyerIdentity?.customerAccessToken) {
|
|
190
|
+
if (cartInput.buyerIdentity == null) {
|
|
191
|
+
cartInput.buyerIdentity = {};
|
|
192
|
+
}
|
|
193
|
+
cartInput.buyerIdentity.customerAccessToken = customerAccessToken;
|
|
194
|
+
}
|
|
195
|
+
onCartReadySend({
|
|
196
|
+
type: 'CART_CREATE',
|
|
197
|
+
payload: cartInput,
|
|
198
|
+
});
|
|
199
|
+
}, [countryCode, customerAccessToken, onCartReadySend]);
|
|
105
200
|
const cartContextValue = useMemo(() => {
|
|
106
201
|
return {
|
|
107
202
|
...(cartState?.context?.cart ?? { lines: [], attributes: [] }),
|
|
108
203
|
status: transposeStatus(cartState.value),
|
|
109
204
|
error: cartState?.context?.errors,
|
|
110
205
|
totalQuantity: cartState?.context?.cart?.totalQuantity ?? 0,
|
|
111
|
-
cartCreate
|
|
112
|
-
onCartReadySend({
|
|
113
|
-
type: 'CART_CREATE',
|
|
114
|
-
payload: cartInput,
|
|
115
|
-
});
|
|
116
|
-
},
|
|
206
|
+
cartCreate,
|
|
117
207
|
linesAdd(lines) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
208
|
+
if (cartState?.context?.cart?.id) {
|
|
209
|
+
onCartReadySend({
|
|
210
|
+
type: 'CARTLINE_ADD',
|
|
211
|
+
payload: { lines },
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
cartCreate({ lines });
|
|
216
|
+
}
|
|
122
217
|
},
|
|
123
218
|
linesRemove(lines) {
|
|
124
219
|
onCartReadySend({
|
|
@@ -171,6 +266,7 @@ export function CartProviderV2({ children, numCartLines, onCreate, onLineAdd, on
|
|
|
171
266
|
cartFragment: usedCartFragment,
|
|
172
267
|
};
|
|
173
268
|
}, [
|
|
269
|
+
cartCreate,
|
|
174
270
|
cartState?.context?.cart,
|
|
175
271
|
cartState?.context?.errors,
|
|
176
272
|
cartState.value,
|
|
@@ -182,11 +278,11 @@ export function CartProviderV2({ children, numCartLines, onCreate, onLineAdd, on
|
|
|
182
278
|
function transposeStatus(status) {
|
|
183
279
|
switch (status) {
|
|
184
280
|
case 'uninitialized':
|
|
281
|
+
case 'initializationError':
|
|
185
282
|
return 'uninitialized';
|
|
186
283
|
case 'idle':
|
|
187
284
|
case 'cartCompleted':
|
|
188
285
|
case 'error':
|
|
189
|
-
case 'initializationError':
|
|
190
286
|
return 'idle';
|
|
191
287
|
case 'cartFetching':
|
|
192
288
|
return 'fetching';
|
|
@@ -230,3 +326,45 @@ function storageAvailable(type) {
|
|
|
230
326
|
storage.length !== 0);
|
|
231
327
|
}
|
|
232
328
|
}
|
|
329
|
+
function countryCodeNotUpdated(context, event) {
|
|
330
|
+
return (event.payload.buyerIdentity.countryCode &&
|
|
331
|
+
context.cart?.buyerIdentity?.countryCode !==
|
|
332
|
+
event.payload.buyerIdentity.countryCode);
|
|
333
|
+
}
|
|
334
|
+
// Cart Analytics
|
|
335
|
+
function publishCreateAnalytics(context, event) {
|
|
336
|
+
ClientAnalytics.publish(ClientAnalytics.eventNames.ADD_TO_CART, true, {
|
|
337
|
+
addedCartLines: event.payload.lines,
|
|
338
|
+
cart: context.rawCartResult,
|
|
339
|
+
prevCart: null,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
function publishLineAddAnalytics(context, event) {
|
|
343
|
+
ClientAnalytics.publish(ClientAnalytics.eventNames.ADD_TO_CART, true, {
|
|
344
|
+
addedCartLines: event.payload.lines,
|
|
345
|
+
cart: context.rawCartResult,
|
|
346
|
+
prevCart: context.prevCart,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
function publishLineUpdateAnalytics(context, event) {
|
|
350
|
+
ClientAnalytics.publish(ClientAnalytics.eventNames.UPDATE_CART, true, {
|
|
351
|
+
updatedCartLines: event.payload.lines,
|
|
352
|
+
oldCart: context.prevCart,
|
|
353
|
+
cart: context.rawCartResult,
|
|
354
|
+
prevCart: context.prevCart,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
function publishLineRemoveAnalytics(context, event) {
|
|
358
|
+
ClientAnalytics.publish(ClientAnalytics.eventNames.REMOVE_FROM_CART, true, {
|
|
359
|
+
removedCartLines: event.payload.lines,
|
|
360
|
+
cart: context.rawCartResult,
|
|
361
|
+
prevCart: context.prevCart,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
function publishDiscountCodesUpdateAnalytics(context, event) {
|
|
365
|
+
ClientAnalytics.publish(ClientAnalytics.eventNames.DISCOUNT_CODE_UPDATED, true, {
|
|
366
|
+
updatedDiscountCodes: event.payload.discountCodes,
|
|
367
|
+
cart: context.rawCartResult,
|
|
368
|
+
prevCart: context.prevCart,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
@@ -97,6 +97,7 @@ export declare type CartAction = {
|
|
|
97
97
|
} | {
|
|
98
98
|
type: 'resolve';
|
|
99
99
|
cart: Cart;
|
|
100
|
+
rawCartResult?: CartFragmentFragment;
|
|
100
101
|
} | {
|
|
101
102
|
type: 'reject';
|
|
102
103
|
errors: any;
|
|
@@ -105,6 +106,9 @@ export declare type CartAction = {
|
|
|
105
106
|
};
|
|
106
107
|
export declare type CartMachineContext = {
|
|
107
108
|
cart?: Cart;
|
|
109
|
+
lastValidCart?: Cart;
|
|
110
|
+
rawCartResult?: CartFragmentFragment;
|
|
111
|
+
prevCart?: Cart;
|
|
108
112
|
errors?: any;
|
|
109
113
|
};
|
|
110
114
|
export declare type CartFetchEvent = {
|
|
@@ -170,6 +174,7 @@ export declare type CartMachineFetchResultEvent = {
|
|
|
170
174
|
payload: {
|
|
171
175
|
cartActionEvent: CartMachineActionEvent;
|
|
172
176
|
cart: Cart;
|
|
177
|
+
rawCartResult: CartFragmentFragment;
|
|
173
178
|
};
|
|
174
179
|
} | {
|
|
175
180
|
type: 'ERROR';
|
|
@@ -183,30 +188,40 @@ export declare type CartMachineTypeState = {
|
|
|
183
188
|
value: 'uninitialized';
|
|
184
189
|
context: CartMachineContext & {
|
|
185
190
|
cart: undefined;
|
|
191
|
+
lastValidCart: undefined;
|
|
192
|
+
prevCart: undefined;
|
|
186
193
|
errors?: any;
|
|
187
194
|
};
|
|
188
195
|
} | {
|
|
189
196
|
value: 'initializationError';
|
|
190
197
|
context: CartMachineContext & {
|
|
191
198
|
cart: undefined;
|
|
199
|
+
lastValidCart: undefined;
|
|
200
|
+
prevCart: undefined;
|
|
192
201
|
errors: any;
|
|
193
202
|
};
|
|
194
203
|
} | {
|
|
195
204
|
value: 'cartCompleted';
|
|
196
205
|
context: CartMachineContext & {
|
|
197
206
|
cart: undefined;
|
|
207
|
+
prevCart?: Cart;
|
|
208
|
+
lastValidCart: undefined;
|
|
198
209
|
errors: any;
|
|
199
210
|
};
|
|
200
211
|
} | {
|
|
201
212
|
value: 'idle';
|
|
202
213
|
context: CartMachineContext & {
|
|
203
214
|
cart: Cart;
|
|
215
|
+
prevCart?: Cart;
|
|
216
|
+
lastValidCart?: Cart;
|
|
204
217
|
errors?: any;
|
|
205
218
|
};
|
|
206
219
|
} | {
|
|
207
220
|
value: 'error';
|
|
208
221
|
context: CartMachineContext & {
|
|
209
222
|
cart?: Cart;
|
|
223
|
+
prevCart?: Cart;
|
|
224
|
+
lastValidCart?: Cart;
|
|
210
225
|
errors: any;
|
|
211
226
|
};
|
|
212
227
|
} | {
|
|
@@ -249,5 +264,6 @@ export declare type CartMachineActions = {
|
|
|
249
264
|
cartAttributesUpdateAction: CartMachineAction;
|
|
250
265
|
discountCodesUpdateAction: CartMachineAction;
|
|
251
266
|
onCartActionEntry?: CartMachineAction;
|
|
267
|
+
onCartActionOptimisticUI?: StateMachine.AssignActionObject<CartMachineContext, CartMachineEvent>;
|
|
252
268
|
onCartActionComplete?: CartMachineAction;
|
|
253
269
|
};
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { StateMachine } from '@xstate/fsm';
|
|
2
2
|
import { CartFragmentFragment } from './graphql/CartFragment.js';
|
|
3
3
|
import { Cart, CartMachineActionEvent, CartMachineContext, CartMachineEvent, CartMachineFetchResultEvent, CartMachineTypeState } from './types.js';
|
|
4
|
-
|
|
4
|
+
import { CountryCode } from '../../storefront-api-types.js';
|
|
5
|
+
export declare function useCartAPIStateMachine({ numCartLines, onCartActionEntry, onCartActionOptimisticUI, onCartActionComplete, data, cartFragment, countryCode, }: {
|
|
5
6
|
/** Maximum number of cart lines to fetch. Defaults to 250 cart lines. */
|
|
6
7
|
numCartLines?: number;
|
|
7
8
|
/** A callback that is invoked just before a Cart API action executes. */
|
|
8
9
|
onCartActionEntry?: (context: CartMachineContext, event: CartMachineActionEvent) => void;
|
|
10
|
+
/** A callback that is invoked after executing the entry actions for optimistic UI changes. */
|
|
11
|
+
onCartActionOptimisticUI?: (context: CartMachineContext, event: CartMachineEvent) => Partial<CartMachineContext>;
|
|
9
12
|
/** A callback that is invoked after a Cart API completes. */
|
|
10
13
|
onCartActionComplete?: (context: CartMachineContext, event: CartMachineFetchResultEvent) => void;
|
|
14
|
+
/** A callback that is invoked after a Cart API completes. */
|
|
11
15
|
/** An object with fields that correspond to the Storefront API's [Cart object](https://shopify.dev/api/storefront/latest/objects/cart). */
|
|
12
16
|
data?: CartFragmentFragment;
|
|
13
17
|
/** A fragment used to query the Storefront API's [Cart object](https://shopify.dev/api/storefront/latest/objects/cart) for all queries and mutations. A default value is used if no argument is provided. */
|
|
14
18
|
cartFragment?: string;
|
|
19
|
+
/** The ISO country code for i18n. */
|
|
20
|
+
countryCode?: CountryCode;
|
|
15
21
|
}): readonly [StateMachine.State<CartMachineContext, CartMachineEvent, CartMachineTypeState>, (event: "CART_FETCH" | "CART_CREATE" | "CARTLINE_ADD" | "CARTLINE_REMOVE" | "CARTLINE_UPDATE" | "NOTE_UPDATE" | "BUYER_IDENTITY_UPDATE" | "CART_ATTRIBUTES_UPDATE" | "DISCOUNT_CODES_UPDATE" | "CART_COMPLETED" | "RESOLVE" | "ERROR" | CartMachineEvent) => void, StateMachine.Service<CartMachineContext, CartMachineEvent, CartMachineTypeState>];
|
|
16
22
|
export declare function cartFromGraphQL(cart: CartFragmentFragment): Cart;
|
|
@@ -5,28 +5,41 @@ import { useCartActions } from './CartActions.client.js';
|
|
|
5
5
|
import { useMemo } from 'react';
|
|
6
6
|
function invokeCart(action, options) {
|
|
7
7
|
return {
|
|
8
|
-
entry: [
|
|
8
|
+
entry: [
|
|
9
|
+
...(options?.entryActions || []),
|
|
10
|
+
'onCartActionEntry',
|
|
11
|
+
'onCartActionOptimisticUI',
|
|
12
|
+
action,
|
|
13
|
+
],
|
|
9
14
|
on: {
|
|
10
15
|
RESOLVE: {
|
|
11
16
|
target: options?.resolveTarget || 'idle',
|
|
12
17
|
actions: [
|
|
13
18
|
assign({
|
|
19
|
+
prevCart: (context) => context?.cart,
|
|
14
20
|
cart: (_, event) => event?.payload?.cart,
|
|
15
|
-
|
|
21
|
+
rawCartResult: (_, event) => event?.payload?.rawCartResult,
|
|
22
|
+
errors: (_) => undefined,
|
|
16
23
|
}),
|
|
17
24
|
],
|
|
18
25
|
},
|
|
19
26
|
ERROR: {
|
|
20
27
|
target: options?.errorTarget || 'error',
|
|
21
|
-
actions:
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
actions: [
|
|
29
|
+
assign({
|
|
30
|
+
prevCart: (context) => context?.cart,
|
|
31
|
+
cart: (context, _) => context?.lastValidCart,
|
|
32
|
+
errors: (_, event) => event?.payload?.errors,
|
|
33
|
+
}),
|
|
34
|
+
],
|
|
24
35
|
},
|
|
25
36
|
CART_COMPLETED: {
|
|
26
37
|
target: 'cartCompleted',
|
|
27
38
|
actions: assign({
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
prevCart: (_) => undefined,
|
|
40
|
+
cart: (_) => undefined,
|
|
41
|
+
lastValidCart: (_) => undefined,
|
|
42
|
+
errors: (_) => undefined,
|
|
30
43
|
}),
|
|
31
44
|
},
|
|
32
45
|
},
|
|
@@ -40,9 +53,6 @@ const INITIALIZING_CART_EVENTS = {
|
|
|
40
53
|
CART_CREATE: {
|
|
41
54
|
target: 'cartCreating',
|
|
42
55
|
},
|
|
43
|
-
CARTLINE_ADD: {
|
|
44
|
-
target: 'cartCreating',
|
|
45
|
-
},
|
|
46
56
|
};
|
|
47
57
|
const UPDATING_CART_EVENTS = {
|
|
48
58
|
CARTLINE_ADD: {
|
|
@@ -101,10 +111,11 @@ const cartMachine = createMachine({
|
|
|
101
111
|
discountCodesUpdating: invokeCart('discountCodesUpdateAction'),
|
|
102
112
|
},
|
|
103
113
|
});
|
|
104
|
-
export function useCartAPIStateMachine({ numCartLines, onCartActionEntry, onCartActionComplete, data: cart, cartFragment, }) {
|
|
114
|
+
export function useCartAPIStateMachine({ numCartLines, onCartActionEntry, onCartActionOptimisticUI, onCartActionComplete, data: cart, cartFragment, countryCode, }) {
|
|
105
115
|
const { cartFetch, cartCreate, cartLineAdd, cartLineUpdate, cartLineRemove, noteUpdate, buyerIdentityUpdate, cartAttributesUpdate, discountCodesUpdate, } = useCartActions({
|
|
106
116
|
numCartLines,
|
|
107
117
|
cartFragment,
|
|
118
|
+
countryCode,
|
|
108
119
|
});
|
|
109
120
|
const [state, send, service] = useMachine(cartMachine, {
|
|
110
121
|
actions: {
|
|
@@ -116,7 +127,7 @@ export function useCartAPIStateMachine({ numCartLines, onCartActionEntry, onCart
|
|
|
116
127
|
send(resultEvent);
|
|
117
128
|
},
|
|
118
129
|
cartCreateAction: async (_, event) => {
|
|
119
|
-
if (event.type !== 'CART_CREATE'
|
|
130
|
+
if (event.type !== 'CART_CREATE')
|
|
120
131
|
return;
|
|
121
132
|
const { data, errors } = await cartCreate(event?.payload);
|
|
122
133
|
const resultEvent = eventFromFetchResult(event, data?.cartCreate?.cart, errors);
|
|
@@ -178,6 +189,11 @@ export function useCartAPIStateMachine({ numCartLines, onCartActionEntry, onCart
|
|
|
178
189
|
}
|
|
179
190
|
},
|
|
180
191
|
}),
|
|
192
|
+
...(onCartActionOptimisticUI && {
|
|
193
|
+
onCartActionOptimisticUI: assign((context, event) => {
|
|
194
|
+
return onCartActionOptimisticUI(context, event);
|
|
195
|
+
}),
|
|
196
|
+
}),
|
|
181
197
|
...(onCartActionComplete && {
|
|
182
198
|
onCartActionComplete: (context, event) => {
|
|
183
199
|
if (isCartFetchResultEvent(event)) {
|
|
@@ -211,7 +227,11 @@ function eventFromFetchResult(cartActionEvent, cart, errors) {
|
|
|
211
227
|
}
|
|
212
228
|
return {
|
|
213
229
|
type: 'RESOLVE',
|
|
214
|
-
payload: {
|
|
230
|
+
payload: {
|
|
231
|
+
cart: cartFromGraphQL(cart),
|
|
232
|
+
rawCartResult: cart,
|
|
233
|
+
cartActionEvent,
|
|
234
|
+
},
|
|
215
235
|
};
|
|
216
236
|
}
|
|
217
237
|
function isCartActionEvent(event) {
|
|
@@ -5,24 +5,28 @@ let secretTokenWarned = false;
|
|
|
5
5
|
let storefrontIdWarned = false;
|
|
6
6
|
export function getStorefrontApiRequestHeaders({ buyerIp, publicStorefrontToken, privateStorefrontToken, storefrontId, }) {
|
|
7
7
|
const headers = {};
|
|
8
|
-
if (!privateStorefrontToken
|
|
9
|
-
secretTokenWarned = true;
|
|
8
|
+
if (!privateStorefrontToken) {
|
|
10
9
|
privateStorefrontToken = getOxygenVariable(OXYGEN_SECRET_TOKEN_ENVIRONMENT_VARIABLE);
|
|
11
|
-
if (!
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
if (!secretTokenWarned) {
|
|
11
|
+
secretTokenWarned = true;
|
|
12
|
+
if (!privateStorefrontToken && !__HYDROGEN_DEV__) {
|
|
13
|
+
log.error('No secret Shopify storefront API token was defined. This means your app will be rate limited!\nSee how to add the token: ');
|
|
14
|
+
}
|
|
15
|
+
else if (privateStorefrontToken) {
|
|
16
|
+
log.warn('The private shopify storefront API token was loaded implicitly by an environment variable. This is deprecated, and instead the variable should be defined directly in the Hydrogen Config.\nFor more information: ');
|
|
17
|
+
}
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
|
-
if (!storefrontId
|
|
19
|
-
storefrontIdWarned = true;
|
|
20
|
+
if (!storefrontId) {
|
|
20
21
|
storefrontId = getOxygenVariable(SHOPIFY_STOREFRONT_ID_VARIABLE);
|
|
21
|
-
if (!
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
if (!storefrontIdWarned) {
|
|
23
|
+
storefrontIdWarned = true;
|
|
24
|
+
if (!storefrontId && !__HYDROGEN_DEV__) {
|
|
25
|
+
log.warn('No storefrontId was defined. This means the analytics on your admin dashboard will be broken!\nSee how to fix it: ');
|
|
26
|
+
}
|
|
27
|
+
else if (storefrontId) {
|
|
28
|
+
log.warn('The storefrontId was loaded implicitly by an environment variable. This is deprecated, and instead the variable should be defined directly in the Hydrogen Config.\nFor more information: ');
|
|
29
|
+
}
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
/**
|
package/dist/esnext/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const LIB_VERSION = "1.4.
|
|
1
|
+
export declare const LIB_VERSION = "1.4.1";
|
package/dist/esnext/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const LIB_VERSION = '1.4.
|
|
1
|
+
export const LIB_VERSION = '1.4.1';
|