@anker-in/shopify-react 0.1.1-beta.0

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.
@@ -0,0 +1,1130 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var shopifySdk = require('@anker-in/shopify-sdk');
5
+ var Cookies5 = require('js-cookie');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+ var Decimal2 = require('decimal.js');
8
+ var useSWR = require('swr');
9
+ var useSWRMutation8 = require('swr/mutation');
10
+ var ahooks = require('ahooks');
11
+
12
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
+
14
+ var Cookies5__default = /*#__PURE__*/_interopDefault(Cookies5);
15
+ var Decimal2__default = /*#__PURE__*/_interopDefault(Decimal2);
16
+ var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
17
+ var useSWRMutation8__default = /*#__PURE__*/_interopDefault(useSWRMutation8);
18
+
19
+ // src/provider/context.ts
20
+ var ShopifyContext = react.createContext(null);
21
+ var browserCookieAdapter = {
22
+ get(key) {
23
+ return Cookies5__default.default.get(key);
24
+ },
25
+ set(key, value, options) {
26
+ Cookies5__default.default.set(key, value, {
27
+ domain: options?.domain,
28
+ path: options?.path || "/",
29
+ expires: options?.expires,
30
+ secure: options?.secure,
31
+ sameSite: options?.sameSite
32
+ });
33
+ },
34
+ remove(key) {
35
+ Cookies5__default.default.remove(key);
36
+ }
37
+ };
38
+ function getCartCookieName(locale) {
39
+ return `${locale}-shopify-cart-id`;
40
+ }
41
+ var browserCartCookieAdapter = {
42
+ getCartId(locale) {
43
+ return Cookies5__default.default.get(getCartCookieName(locale));
44
+ },
45
+ setCartId(locale, cartId) {
46
+ Cookies5__default.default.set(getCartCookieName(locale), cartId, {
47
+ path: "/",
48
+ expires: 30
49
+ // 30 days
50
+ });
51
+ },
52
+ removeCartId(locale) {
53
+ Cookies5__default.default.remove(getCartCookieName(locale));
54
+ }
55
+ };
56
+ function ShopifyProvider({
57
+ config,
58
+ locale,
59
+ locales,
60
+ cookieAdapter = browserCookieAdapter,
61
+ cartCookieAdapter = browserCartCookieAdapter,
62
+ routerAdapter,
63
+ userAdapter,
64
+ children
65
+ }) {
66
+ const client = react.useMemo(() => {
67
+ return shopifySdk.createShopifyClient(config, locale);
68
+ }, [config, locale]);
69
+ const value = react.useMemo(() => {
70
+ return {
71
+ client,
72
+ config,
73
+ locale,
74
+ locales: locales || config.getLocales(),
75
+ cookieAdapter,
76
+ cartCookieAdapter,
77
+ routerAdapter,
78
+ userAdapter
79
+ };
80
+ }, [
81
+ client,
82
+ config,
83
+ locale,
84
+ locales,
85
+ cookieAdapter,
86
+ cartCookieAdapter,
87
+ routerAdapter,
88
+ userAdapter
89
+ ]);
90
+ return /* @__PURE__ */ jsxRuntime.jsx(ShopifyContext.Provider, { value, children });
91
+ }
92
+ function useShopify() {
93
+ const context = react.useContext(ShopifyContext);
94
+ if (!context) {
95
+ throw new Error("useShopify must be used within a ShopifyProvider");
96
+ }
97
+ return context;
98
+ }
99
+
100
+ // src/hooks/cart/const.ts
101
+ var CUSTOMER_ATTRIBUTE_KEY = "_discounts_function_env";
102
+ var CUSTOMER_SCRIPT_GIFT_KEY = "_giveaway_gradient_gifts";
103
+ var CODE_AMOUNT_KEY = "_sku_code_money";
104
+ var SCRIPT_CODE_AMOUNT_KEY = "_code_money";
105
+ var MAIN_PRODUCT_CODE = ["WS24", "WSTD", "WS7D", "WSCP", "WSPE", "WSPD"];
106
+
107
+ // src/hooks/cart/utils/index.ts
108
+ var getQuery = () => {
109
+ const url = typeof window !== "undefined" ? window.location.search : "";
110
+ const theRequest = {};
111
+ if (url.indexOf("?") != -1) {
112
+ const str = url.substr(1), strs = str.split("&");
113
+ for (let i = 0; i < strs.length; i++) {
114
+ const parts = strs[i]?.split("=");
115
+ const key = parts?.[0];
116
+ const value = parts?.[1];
117
+ if (key && value) {
118
+ theRequest[key] = decodeURIComponent(value);
119
+ }
120
+ }
121
+ }
122
+ return theRequest;
123
+ };
124
+ function atobID(id) {
125
+ if (id && typeof id === "string" && id.includes("/")) {
126
+ return id.split("/").pop()?.split("?")?.shift();
127
+ } else {
128
+ return id;
129
+ }
130
+ }
131
+ function btoaID(id, type = "ProductVariant") {
132
+ return `gid://shopify/${type}/${id}`;
133
+ }
134
+ var getMatchedMainProductSubTotal = (cartData, variant_list, main_product) => {
135
+ const isAllStoreVariant = main_product?.all_store_variant ?? false;
136
+ const matchedList = cartData?.lineItems?.filter((line) => {
137
+ const { is_gift } = getDiscountEnvAttributeValue(line.customAttributes);
138
+ return isAllStoreVariant ? !is_gift : variant_list?.find((item) => {
139
+ return !is_gift && atobID(line.variantId) === item;
140
+ });
141
+ });
142
+ return matchedList?.reduce((acc, line) => {
143
+ return acc + (main_product?.spend_money_type === 1 /* ORIGIN_PRICE */ ? Number(line.subtotalAmount) || 0 : Number(line.totalAmount) || 0);
144
+ }, 0) || 0;
145
+ };
146
+ var getDiscountEnvAttributeValue = (attributes = []) => {
147
+ const attr = attributes.find((attr2) => attr2.key === CUSTOMER_ATTRIBUTE_KEY);
148
+ return safeParseJson(attr?.value ?? "") ?? {};
149
+ };
150
+ var isAttributesEqual = (attrs1 = [], attrs2 = []) => {
151
+ if (attrs1.length !== attrs2.length) return false;
152
+ const sorted1 = [...attrs1].sort((a, b) => a.key.localeCompare(b.key));
153
+ const sorted2 = [...attrs2].sort((a, b) => a.key.localeCompare(b.key));
154
+ return sorted1.every(
155
+ (attr, i) => attr.key === sorted2[i]?.key && attr.value === sorted2[i]?.value
156
+ );
157
+ };
158
+ var safeParseJson = (str) => {
159
+ try {
160
+ return JSON.parse(str);
161
+ } catch (err) {
162
+ return {};
163
+ }
164
+ };
165
+ var containsAll = (source, requiredItems = []) => {
166
+ if (!requiredItems?.length) return true;
167
+ const sourceSet = new Set(source);
168
+ return requiredItems.every((item) => sourceSet.has(item));
169
+ };
170
+ var containsNone = (source, forbiddenItems = []) => {
171
+ if (!forbiddenItems?.length) return true;
172
+ const sourceSet = new Set(source);
173
+ return !forbiddenItems.some((item) => sourceSet.has(item));
174
+ };
175
+ function preCheck(rule_conditions, userTags, currentDealsTypes) {
176
+ if (!Array.isArray(rule_conditions)) return false;
177
+ if (rule_conditions.length === 0) return true;
178
+ return rule_conditions.some((rule) => {
179
+ const tagsAreValid = containsAll(userTags, rule.with_user_tags) && containsNone(userTags, rule.without_user_tags);
180
+ const paramsAreValid = containsAll(currentDealsTypes, rule.with_special_url_params) && containsNone(currentDealsTypes, rule.without_special_url_params);
181
+ return tagsAreValid && paramsAreValid;
182
+ });
183
+ }
184
+ var formatScriptAutoFreeGiftCache = null;
185
+ var formatScriptAutoFreeGift = ({
186
+ scriptAutoFreeGiftResult,
187
+ gradient_gifts,
188
+ locale
189
+ }) => {
190
+ const cacheKey = JSON.stringify({
191
+ freeGiftLevel: scriptAutoFreeGiftResult?.freeGiftLevel ? {
192
+ items: scriptAutoFreeGiftResult.freeGiftLevel.giveawayProducts?.map((item) => ({
193
+ handle: item.handle,
194
+ sku: item.sku,
195
+ quantity: 1
196
+ })) || []
197
+ } : null,
198
+ giftProductsLength: scriptAutoFreeGiftResult?.giftProductsResult?.length || 0,
199
+ giftProductsIds: scriptAutoFreeGiftResult?.giftProductsResult?.map((p) => p.id).sort() || [],
200
+ gradientGiftsId: gradient_gifts?.id || gradient_gifts?.name || "",
201
+ locale
202
+ });
203
+ if (formatScriptAutoFreeGiftCache && formatScriptAutoFreeGiftCache.key === cacheKey) {
204
+ return formatScriptAutoFreeGiftCache.result;
205
+ }
206
+ const result = scriptAutoFreeGiftResult?.freeGiftLevel?.giveawayProducts?.filter(
207
+ (item) => scriptAutoFreeGiftResult?.giftProductsResult?.some(
208
+ (product) => product.handle === item.handle
209
+ )
210
+ ).map((item, index) => {
211
+ const product = scriptAutoFreeGiftResult?.giftProductsResult?.find(
212
+ (product2) => product2.handle === item.handle
213
+ );
214
+ const variants = product?.variants;
215
+ const variant = Array.isArray(variants) ? variants.find((v) => v.sku === item.sku) : void 0;
216
+ const query = getQuery();
217
+ const utmCampaign = Cookies5__default.default.get("utm_campaign") || query?.utm_campaign;
218
+ const addUTMFreeItem = gradient_gifts.activityAvailableQuery && utmCampaign?.includes(gradient_gifts.activityAvailableQuery);
219
+ let points_subscribe = false;
220
+ if (locale === "au") {
221
+ const isPointsSubscribe = Cookies5__default.default.get("points_subscribe");
222
+ points_subscribe = !!isPointsSubscribe;
223
+ }
224
+ const customAttributes = [
225
+ {
226
+ key: "_giveaway_gradient_gifts",
227
+ value: "_giveaway_gradient_gifts"
228
+ },
229
+ ...points_subscribe ? [
230
+ { key: "_amount_upgrade_multiple", value: "1.2" },
231
+ { key: "_amount_upgrade_value", value: "40" }
232
+ ] : [],
233
+ ...addUTMFreeItem && gradient_gifts?.activityQroperty ? [
234
+ {
235
+ key: gradient_gifts.activityQroperty,
236
+ value: gradient_gifts.activityQroperty
237
+ }
238
+ ] : []
239
+ ];
240
+ const line = {
241
+ id: product?.id + "_" + index,
242
+ variantId: String(variant?.id),
243
+ productId: String(product?.id),
244
+ name: product?.name || product?.title || "",
245
+ quantity: 1,
246
+ discounts: [],
247
+ path: product?.handle || "",
248
+ variant,
249
+ totalAmount: 0,
250
+ subtotalAmount: new Decimal2__default.default(
251
+ typeof variant?.price === "object" ? variant?.price?.amount || 0 : variant?.price || 0
252
+ ).toNumber(),
253
+ options: [],
254
+ discountAllocations: [],
255
+ product,
256
+ customAttributes,
257
+ freeGiftVariant: void 0,
258
+ relatedVariant: void 0
259
+ };
260
+ return {
261
+ line,
262
+ isSoldOut: !variant?.availableForSale
263
+ };
264
+ }) || [];
265
+ formatScriptAutoFreeGiftCache = {
266
+ key: cacheKey,
267
+ result
268
+ };
269
+ return result;
270
+ };
271
+ var formatFunctionAutoFreeGiftCache = null;
272
+ var formatFunctionAutoFreeGift = ({
273
+ qualifyingGift,
274
+ giftProductsResult,
275
+ locale
276
+ }) => {
277
+ const cacheKey = JSON.stringify({
278
+ qualifyingGift: qualifyingGift ? {
279
+ spend: qualifyingGift.tier?.spend_sum_money,
280
+ items: qualifyingGift.itemsToAdd?.map((item) => ({
281
+ variantId: item.variant.id,
282
+ handle: item.variant.handle,
283
+ sku: item.variant.sku,
284
+ quantity: item.quantity ?? 1,
285
+ attributes: item.attributes
286
+ })) || []
287
+ } : null,
288
+ giftProductsLength: giftProductsResult?.length || 0,
289
+ giftProductsIds: giftProductsResult?.map((p) => p.id).sort() || [],
290
+ locale
291
+ });
292
+ if (formatFunctionAutoFreeGiftCache && formatFunctionAutoFreeGiftCache.key === cacheKey) {
293
+ return formatFunctionAutoFreeGiftCache.result;
294
+ }
295
+ const result = qualifyingGift?.itemsToAdd?.map((item, index) => {
296
+ const product = giftProductsResult?.find((product2) => product2.handle === item.variant.handle);
297
+ const variants = product?.variants;
298
+ const variant = Array.isArray(variants) ? variants.find((v) => v.sku === item.variant.sku) : void 0;
299
+ console.log("qualifyingGift variant", product, variant);
300
+ const line = {
301
+ id: product?.id + "_" + index,
302
+ variantId: String(variant?.id),
303
+ productId: String(product?.id),
304
+ name: product?.name || product?.title || "",
305
+ quantity: 1,
306
+ discounts: [],
307
+ path: product?.handle || "",
308
+ variant,
309
+ totalAmount: 0,
310
+ subtotalAmount: new Decimal2__default.default(
311
+ typeof variant?.price === "object" ? variant?.price?.amount || 0 : variant?.price || 0
312
+ ).toNumber(),
313
+ options: [],
314
+ discountAllocations: [],
315
+ product,
316
+ customAttributes: item.attributes,
317
+ freeGiftVariant: void 0,
318
+ relatedVariant: void 0
319
+ };
320
+ return {
321
+ line,
322
+ isSoldOut: !variant?.availableForSale
323
+ };
324
+ }) || [];
325
+ formatFunctionAutoFreeGiftCache = {
326
+ key: cacheKey,
327
+ result
328
+ };
329
+ return result;
330
+ };
331
+ var useCalcAutoFreeGift = (cart, autoFreeGiftConfig, customer) => {
332
+ const tags = react.useMemo(() => customer?.tags || [], [customer?.tags]);
333
+ const isCustomerLoading = react.useMemo(() => !customer ? true : false, [customer]);
334
+ const dealsType = "";
335
+ const { client, locale } = useShopify();
336
+ const giftProductsCache = react.useRef(null);
337
+ const { activeCampaign, subtotal } = react.useMemo(() => {
338
+ for (const campaign of autoFreeGiftConfig) {
339
+ const { rule_conditions = [], rule_result } = campaign;
340
+ const { spend_get_reward } = rule_result || {};
341
+ const isPreCheckPassed = preCheck(rule_conditions, tags, []);
342
+ if (isPreCheckPassed && spend_get_reward) {
343
+ const matchedSubtotal = getMatchedMainProductSubTotal(
344
+ cart,
345
+ spend_get_reward.main_product?.variant_list?.map((v) => v.variant_id) || [],
346
+ {
347
+ spend_money_type: spend_get_reward.main_product?.spend_money_type || 1,
348
+ variant_id_list: spend_get_reward.main_product?.variant_list?.map((v) => v.variant_id) || [],
349
+ all_store_variant: spend_get_reward.main_product?.all_store_variant || false
350
+ }
351
+ );
352
+ if (matchedSubtotal > 0) {
353
+ return { activeCampaign: campaign, subtotal: matchedSubtotal };
354
+ }
355
+ }
356
+ }
357
+ return { activeCampaign: null, subtotal: 0 };
358
+ }, [autoFreeGiftConfig, cart, tags, dealsType]);
359
+ const { qualifyingGift, nextTierGoal } = react.useMemo(() => {
360
+ if (!activeCampaign || !activeCampaign.rule_result?.spend_get_reward?.gift_product) {
361
+ return { qualifyingGift: null, nextTierGoal: null };
362
+ }
363
+ const giftTiers = activeCampaign.rule_result.spend_get_reward.gift_product;
364
+ const qualifyingTier = [...giftTiers].reverse().find((tier) => subtotal >= Number(tier.spend_sum_money));
365
+ const nextGoal = giftTiers.find((tier) => subtotal < Number(tier.spend_sum_money));
366
+ if (!qualifyingTier) {
367
+ return { qualifyingGift: null, nextTierGoal: nextGoal || null };
368
+ }
369
+ const formattedGift = {
370
+ tier: qualifyingTier,
371
+ itemsToAdd: qualifyingTier.reward_list?.map((reward) => {
372
+ const giftProduct = reward?.variant_list?.[0];
373
+ if (!giftProduct) return null;
374
+ return {
375
+ variant: {
376
+ id: btoaID(giftProduct.variant_id),
377
+ handle: giftProduct.handle,
378
+ sku: giftProduct.sku
379
+ },
380
+ quantity: reward?.get_unit || 1,
381
+ attributes: [
382
+ {
383
+ key: CUSTOMER_ATTRIBUTE_KEY,
384
+ value: JSON.stringify({
385
+ is_gift: true,
386
+ rule_id: activeCampaign.rule_id,
387
+ spend_sum_money: qualifyingTier.spend_sum_money
388
+ })
389
+ }
390
+ ]
391
+ };
392
+ }).filter((item) => item !== null)
393
+ };
394
+ return { qualifyingGift: formattedGift, nextTierGoal: nextGoal || null };
395
+ }, [activeCampaign, subtotal]);
396
+ const giftHandles = react.useMemo(() => {
397
+ const giftVariant = autoFreeGiftConfig.map(
398
+ (item) => item.rule_result?.spend_get_reward?.gift_product?.map(
399
+ (v) => v.reward_list.map((reward) => reward.variant_list.map((variant) => variant.handle))
400
+ ).flat()
401
+ ).flat();
402
+ return giftVariant.flat(2).filter(Boolean);
403
+ }, [autoFreeGiftConfig]);
404
+ const shouldFetch = react.useMemo(() => {
405
+ if (!giftHandles.length) return false;
406
+ if (giftProductsCache.current && JSON.stringify(giftProductsCache.current.giftHandles) === JSON.stringify(giftHandles)) {
407
+ return false;
408
+ }
409
+ return true;
410
+ }, [giftHandles]);
411
+ const { data: giftProductsResult } = useSWR__default.default(shouldFetch ? giftHandles : null, async () => {
412
+ const res = await shopifySdk.getProductsByHandles(client, {
413
+ handles: giftHandles,
414
+ locale
415
+ });
416
+ const result = Array.isArray(res) ? res : [];
417
+ giftProductsCache.current = {
418
+ data: result,
419
+ giftHandles: [...giftHandles]
420
+ };
421
+ return result;
422
+ });
423
+ const finalGiftProductsResult = react.useMemo(() => {
424
+ if (giftProductsCache.current && !shouldFetch) {
425
+ return giftProductsCache.current.data || void 0;
426
+ }
427
+ return giftProductsResult;
428
+ }, [giftProductsResult, shouldFetch]);
429
+ return {
430
+ qualifyingGift,
431
+ nextTierGoal,
432
+ activeCampaign,
433
+ isLoading: isCustomerLoading,
434
+ giftProductsResult: finalGiftProductsResult
435
+ };
436
+ };
437
+ var useScriptAutoFreeGift = ({
438
+ campaign,
439
+ _giveaway,
440
+ cart,
441
+ locale: providedLocale
442
+ }) => {
443
+ const { client, locale: contextLocale } = useShopify();
444
+ const locale = providedLocale || contextLocale;
445
+ const [points_subscribe, set_points_subscribe] = react.useState(false);
446
+ const giftProductsCache = react.useRef(null);
447
+ react.useEffect(() => {
448
+ if (locale === "au") {
449
+ const isPointsSubscribe = Cookies5__default.default.get("points_subscribe");
450
+ set_points_subscribe(!!isPointsSubscribe);
451
+ }
452
+ }, [locale]);
453
+ const [upgrade_multiple, upgrade_value] = react.useMemo(() => {
454
+ let upgrade_multiple2 = 1;
455
+ let upgrade_value2 = 0;
456
+ if (points_subscribe) {
457
+ upgrade_multiple2 = 1.2;
458
+ upgrade_value2 = 40;
459
+ }
460
+ cart?.lineItems?.forEach(({ customAttributes }) => {
461
+ customAttributes?.forEach(({ key, value }) => {
462
+ if (key === "_amount_upgrade_multiple") upgrade_multiple2 = Number(value) || 1;
463
+ if (key === "_amount_upgrade_value") upgrade_value2 = Number(value) || 0;
464
+ });
465
+ });
466
+ return [upgrade_multiple2, upgrade_value2];
467
+ }, [cart?.lineItems, points_subscribe]);
468
+ const breakpoints = react.useMemo(() => {
469
+ if (!campaign) return [];
470
+ return (campaign.breakpoints || []).map((item) => ({
471
+ breakpoint: new Decimal2__default.default(item.breakpoint).minus(new Decimal2__default.default(upgrade_value)).dividedBy(new Decimal2__default.default(upgrade_multiple)).toFixed(2, Decimal2__default.default.ROUND_DOWN),
472
+ giveawayProducts: item.giveawayProducts || []
473
+ }));
474
+ }, [campaign, upgrade_multiple, upgrade_value]);
475
+ const giftHandles = react.useMemo(
476
+ () => (
477
+ // 使用 Set 去重,然后拼接字符串
478
+ [
479
+ ...new Set(
480
+ breakpoints.flatMap((b) => b.giveawayProducts.map((p) => p.handle)).filter(Boolean)
481
+ )
482
+ ]
483
+ ),
484
+ [breakpoints]
485
+ );
486
+ const shouldFetch = react.useMemo(() => {
487
+ if (!giftHandles.length) return false;
488
+ if (giftProductsCache.current && JSON.stringify(giftProductsCache.current.giftHandles) === JSON.stringify(giftHandles)) {
489
+ return false;
490
+ }
491
+ return true;
492
+ }, [giftHandles]);
493
+ const involvedLines = react.useMemo(() => {
494
+ if (!campaign) return [];
495
+ return (cart?.lineItems || []).filter((line) => {
496
+ const isNotGift = line?.totalAmount && Number(line.totalAmount) > 0 && line.customAttributes?.every(
497
+ (item) => item.key !== _giveaway
498
+ );
499
+ const hasCampaignTag = line.product?.tags?.some(
500
+ (tag) => campaign.includeTags?.includes(tag.trim()) && line.variant?.availableForSale
501
+ );
502
+ return isNotGift && hasCampaignTag;
503
+ });
504
+ }, [cart?.lineItems, campaign, _giveaway]);
505
+ const involvedSubTotal = react.useMemo(() => {
506
+ if (!campaign) return new Decimal2__default.default(0);
507
+ return involvedLines.reduce((prev, item) => {
508
+ const amount = campaign.useTotalAmount ? item.totalAmount : item.subtotalAmount;
509
+ return new Decimal2__default.default(prev).plus(new Decimal2__default.default(amount || 0));
510
+ }, new Decimal2__default.default(0));
511
+ }, [involvedLines, campaign]);
512
+ const [freeGiftLevel, nextFreeGiftLevel] = react.useMemo(() => {
513
+ const sortedLevels = [...breakpoints].sort(
514
+ (a, b) => Number(b.breakpoint) - Number(a.breakpoint)
515
+ );
516
+ const levelIndex = sortedLevels.findIndex(
517
+ (level) => involvedSubTotal.gte(new Decimal2__default.default(level.breakpoint)) && involvedLines.length > 0
518
+ );
519
+ if (levelIndex === -1) {
520
+ return [
521
+ null,
522
+ sortedLevels.length > 0 ? sortedLevels[sortedLevels.length - 1] ?? null : null
523
+ ];
524
+ }
525
+ const currentLevel = sortedLevels[levelIndex] ?? null;
526
+ const nextLevel = levelIndex > 0 ? sortedLevels[levelIndex - 1] ?? null : null;
527
+ return [currentLevel, nextLevel];
528
+ }, [breakpoints, involvedSubTotal, involvedLines.length]);
529
+ const { data: giftProductsResult } = useSWR__default.default(shouldFetch ? giftHandles : null, async () => {
530
+ const res = await shopifySdk.getProductsByHandles(client, {
531
+ handles: giftHandles,
532
+ locale
533
+ });
534
+ const result = Array.isArray(res) ? res : [];
535
+ giftProductsCache.current = {
536
+ data: result,
537
+ giftHandles: [...giftHandles]
538
+ };
539
+ return result;
540
+ });
541
+ const finalGiftProductsResult = react.useMemo(() => {
542
+ if (giftProductsCache.current && !shouldFetch) {
543
+ return giftProductsCache.current.data || void 0;
544
+ }
545
+ return giftProductsResult;
546
+ }, [giftProductsResult, shouldFetch]);
547
+ const reorder = react.useCallback(
548
+ (a, b) => {
549
+ const getPriority = (item) => {
550
+ if (item.customAttributes?.some(
551
+ (attribute) => attribute.key === _giveaway
552
+ ))
553
+ return 0;
554
+ if (item.product?.tags?.some((tag) => campaign?.includeTags?.includes(tag)))
555
+ return 1;
556
+ return 2;
557
+ };
558
+ return getPriority(b) - getPriority(a);
559
+ },
560
+ [campaign?.includeTags, _giveaway]
561
+ );
562
+ return {
563
+ involvedLines,
564
+ reorder,
565
+ disableCodeRemove: involvedLines.length > 0,
566
+ nextFreeGiftLevel,
567
+ freeGiftLevel,
568
+ involvedSubTotal,
569
+ giftProductsResult: finalGiftProductsResult
570
+ };
571
+ };
572
+ function useUpdateCartAttributes(mutate, metafieldIdentifiers, options) {
573
+ const { client, locale, cartCookieAdapter } = useShopify();
574
+ const updateAttributes = react.useCallback(
575
+ async (_key, { arg }) => {
576
+ const updatedCart = await shopifySdk.updateCartAttributes(client, {
577
+ ...arg,
578
+ metafieldIdentifiers,
579
+ cookieAdapter: cartCookieAdapter
580
+ });
581
+ console.log("useUpdateCartAttributes updatedCart", updatedCart);
582
+ if (updatedCart) {
583
+ mutate(updatedCart);
584
+ }
585
+ return updatedCart;
586
+ },
587
+ [client, locale, cartCookieAdapter, mutate]
588
+ );
589
+ return useSWRMutation8__default.default("update-cart-attributes", updateAttributes, options);
590
+ }
591
+ function useHasPlusMemberInCart({
592
+ memberSetting,
593
+ cart
594
+ }) {
595
+ const { plus_monthly_product, plus_annual_product } = memberSetting || {};
596
+ return react.useMemo(() => {
597
+ if (!cart?.lineItems) {
598
+ return {
599
+ hasPlusMember: false,
600
+ hasMonthlyPlus: false,
601
+ hasAnnualPlus: false
602
+ };
603
+ }
604
+ const monthlyPlusItem = cart.lineItems.find(
605
+ (item) => item.product?.handle === plus_monthly_product?.handle && item.variant?.sku === plus_monthly_product?.sku
606
+ );
607
+ const annualPlusItem = cart.lineItems.find(
608
+ (item) => item.product?.handle === plus_annual_product?.handle && item.variant?.sku === plus_annual_product?.sku
609
+ );
610
+ const hasMonthlyPlus = !!monthlyPlusItem;
611
+ const hasAnnualPlus = !!annualPlusItem;
612
+ const hasPlusMember = hasMonthlyPlus || hasAnnualPlus;
613
+ return {
614
+ hasPlusMember,
615
+ hasMonthlyPlus,
616
+ hasAnnualPlus,
617
+ monthlyPlusItem,
618
+ annualPlusItem
619
+ };
620
+ }, [cart?.lineItems, plus_monthly_product, plus_annual_product]);
621
+ }
622
+
623
+ // src/hooks/cart/feature/use-cart-attributes.ts
624
+ var getReferralAttributes = () => {
625
+ const inviteCode = Cookies5__default.default.get("invite_code");
626
+ const playModeId = Cookies5__default.default.get("playModeId");
627
+ const popup = Cookies5__default.default.get("_popup");
628
+ if (inviteCode && playModeId) {
629
+ return popup ? [
630
+ { key: "_invite_code", value: inviteCode ? inviteCode : "" },
631
+ { key: "_play_mode_id", value: playModeId ? playModeId : "" },
632
+ { key: "_popup", value: popup }
633
+ ] : [
634
+ { key: "_invite_code", value: inviteCode ? inviteCode : "" },
635
+ { key: "_play_mode_id", value: playModeId ? playModeId : "" }
636
+ ];
637
+ }
638
+ return [];
639
+ };
640
+ var useCartAttributes = ({
641
+ profile,
642
+ customer,
643
+ cart,
644
+ memberSetting
645
+ }) => {
646
+ const [currentUrl, setCurrentUrl] = react.useState("");
647
+ const { hasPlusMember } = useHasPlusMemberInCart({
648
+ memberSetting,
649
+ cart
650
+ });
651
+ console.log("memberSetting", memberSetting);
652
+ console.log("hasPlusMember", hasPlusMember);
653
+ react.useEffect(() => {
654
+ setCurrentUrl(window.location.href);
655
+ }, []);
656
+ const userType = react.useMemo(() => {
657
+ let userInfo = Cookies5__default.default.get("userInfo");
658
+ if (userInfo) {
659
+ userInfo = JSON.parse(userInfo);
660
+ let arr = typeof userInfo?.id == "string" && userInfo?.id.split("/");
661
+ userInfo.setId = arr[arr.length - 1];
662
+ }
663
+ const customerInfo = userInfo || customer;
664
+ if (!customerInfo) {
665
+ return "new_user_unlogin";
666
+ }
667
+ if (customer) {
668
+ const { orders = {} } = customer;
669
+ if (orders?.edges?.length === 1) {
670
+ return "old_user_orders_once";
671
+ } else if (orders?.edges?.length > 1) {
672
+ return "old_user_orders_twice";
673
+ }
674
+ }
675
+ return "new_user_login";
676
+ }, [customer]);
677
+ const memberAttributes = react.useMemo(() => {
678
+ return [
679
+ {
680
+ key: "_token",
681
+ value: profile?.token
682
+ //是否登录
683
+ },
684
+ {
685
+ key: "_member_type",
686
+ value: hasPlusMember ? "2" : profile?.memberType
687
+ //:0(游客),1(普通会员),2(付费会员)
688
+ },
689
+ {
690
+ key: "_user_type",
691
+ value: userType
692
+ // n
693
+ },
694
+ {
695
+ key: "_is_login",
696
+ value: profile?.token ? "true" : "false"
697
+ }
698
+ ];
699
+ }, [profile?.memberType, profile?.token, userType, hasPlusMember]);
700
+ const functionAttributes = react.useMemo(() => {
701
+ return [
702
+ cart?.discountCodes && {
703
+ key: "_discounts_function_env",
704
+ value: JSON.stringify({
705
+ discount_code: cart?.discountCodes.map((item) => item.code),
706
+ user_tags: customer?.tags || []
707
+ })
708
+ }
709
+ ];
710
+ }, [cart]);
711
+ const presellAttributes = react.useMemo(() => {
712
+ return [
713
+ {
714
+ key: "_presale",
715
+ value: cart?.lineItems.some((item) => item?.variant?.metafields?.presell === "presell")
716
+ }
717
+ ];
718
+ }, [cart]);
719
+ const weightAttributes = react.useMemo(() => {
720
+ return [
721
+ {
722
+ key: "_weight",
723
+ value: cart?.lineItems.reduce((acc, item) => {
724
+ return new Decimal2__default.default(acc).plus(item.variant.weight ?? 0).toNumber();
725
+ }, 0).toString()
726
+ },
727
+ {
728
+ key: "_app_source_name",
729
+ value: "dtc"
730
+ }
731
+ ];
732
+ }, [cart]);
733
+ const trackingAttributes = react.useMemo(() => {
734
+ return [
735
+ {
736
+ key: "utm_params",
737
+ value: currentUrl
738
+ }
739
+ ];
740
+ }, [currentUrl]);
741
+ return react.useMemo(
742
+ () => ({
743
+ attributes: [
744
+ ...memberAttributes,
745
+ ...functionAttributes,
746
+ ...presellAttributes,
747
+ ...weightAttributes,
748
+ ...trackingAttributes,
749
+ ...getReferralAttributes()
750
+ ].filter((item) => item?.value)
751
+ }),
752
+ [memberAttributes, functionAttributes, presellAttributes, weightAttributes, trackingAttributes]
753
+ );
754
+ };
755
+ var useUpdateLineCodeAmountAttributes = ({
756
+ cart,
757
+ mutateCart,
758
+ isCartLoading,
759
+ setLoadingState,
760
+ metafieldIdentifiers
761
+ }) => {
762
+ const { client, cartCookieAdapter } = useShopify();
763
+ const mainProductDiscountCodes = react.useMemo(
764
+ () => cart?.discountCodes.filter(
765
+ ({ code, applicable }) => applicable && MAIN_PRODUCT_CODE.some((codePrefix) => code.startsWith(codePrefix))
766
+ ).map(({ code }) => code),
767
+ [cart]
768
+ );
769
+ const linesNeedUpdate = react.useMemo(
770
+ () => cart?.lineItems.map((line) => {
771
+ const attrNeedUpdate = [];
772
+ const attrNeedDelete = [];
773
+ const codeDiscount = line.discountAllocations?.find(
774
+ (allocation) => mainProductDiscountCodes?.includes(allocation.code)
775
+ );
776
+ const hasFunctionEnvAttribute = line.customAttributes?.find(
777
+ (attr) => attr.key === CUSTOMER_ATTRIBUTE_KEY
778
+ );
779
+ const functionEnvValue = getDiscountEnvAttributeValue(line.customAttributes);
780
+ const hasSameFunctionEnvAttribute = Number(functionEnvValue.discounted_amount) === Number(line.totalAmount);
781
+ if (!hasSameFunctionEnvAttribute && hasFunctionEnvAttribute) {
782
+ attrNeedUpdate.push({
783
+ key: CUSTOMER_ATTRIBUTE_KEY,
784
+ value: JSON.stringify({
785
+ ...functionEnvValue,
786
+ discounted_amount: Number(line.totalAmount)
787
+ })
788
+ });
789
+ }
790
+ const codeDiscountAmount = codeDiscount?.amount || 0;
791
+ const hasCodeAmountAttribute = line.customAttributes?.find(
792
+ (attr) => attr.key === CODE_AMOUNT_KEY || attr.key === SCRIPT_CODE_AMOUNT_KEY
793
+ );
794
+ const hasSameCodeAmountAttribute = line.customAttributes?.find(
795
+ (attr) => attr.key === CODE_AMOUNT_KEY && attr.value === String(codeDiscountAmount)
796
+ ) && line.customAttributes?.find(
797
+ (attr) => attr.key === SCRIPT_CODE_AMOUNT_KEY && attr.value === String(codeDiscountAmount)
798
+ );
799
+ if (codeDiscount && !hasSameCodeAmountAttribute) {
800
+ attrNeedUpdate.push({
801
+ key: CODE_AMOUNT_KEY,
802
+ value: String(codeDiscountAmount)
803
+ });
804
+ attrNeedUpdate.push({
805
+ key: SCRIPT_CODE_AMOUNT_KEY,
806
+ value: String(codeDiscountAmount)
807
+ });
808
+ } else if (!codeDiscount && hasCodeAmountAttribute) {
809
+ attrNeedDelete.push(CODE_AMOUNT_KEY);
810
+ attrNeedDelete.push(SCRIPT_CODE_AMOUNT_KEY);
811
+ }
812
+ return {
813
+ line,
814
+ attrNeedUpdate,
815
+ attrNeedDelete
816
+ };
817
+ }).filter(
818
+ ({ attrNeedUpdate, attrNeedDelete }) => attrNeedUpdate.length || attrNeedDelete.length
819
+ ).map(({ line, attrNeedUpdate, attrNeedDelete }) => {
820
+ if (attrNeedUpdate.length) {
821
+ return {
822
+ id: line.id,
823
+ attributes: [
824
+ ...line.customAttributes?.filter(
825
+ (attr) => !attrNeedUpdate.some((updateAttr) => updateAttr.key === attr.key)
826
+ ) || [],
827
+ ...attrNeedUpdate
828
+ ]
829
+ };
830
+ } else if (attrNeedDelete.length) {
831
+ return {
832
+ id: line.id,
833
+ attributes: line.customAttributes?.filter(
834
+ (attr) => !attrNeedDelete.includes(attr.key)
835
+ ) || []
836
+ };
837
+ } else {
838
+ return {
839
+ id: line.id,
840
+ attributes: line.customAttributes || []
841
+ };
842
+ }
843
+ }),
844
+ [cart?.lineItems, mainProductDiscountCodes]
845
+ );
846
+ const { loading } = ahooks.useRequest(
847
+ async () => {
848
+ if (linesNeedUpdate?.length && !isCartLoading) {
849
+ const result = await shopifySdk.updateCartLines(client, {
850
+ cartId: cart?.id || "",
851
+ lines: linesNeedUpdate,
852
+ metafieldIdentifiers,
853
+ cookieAdapter: cartCookieAdapter
854
+ });
855
+ if (result) {
856
+ mutateCart(result);
857
+ }
858
+ }
859
+ },
860
+ {
861
+ throttleWait: 3e3,
862
+ // 3 秒内只触发最后一次更新
863
+ throttleTrailing: true,
864
+ refreshDeps: [linesNeedUpdate, isCartLoading]
865
+ }
866
+ );
867
+ react.useEffect(() => {
868
+ setLoadingState((prev) => {
869
+ return {
870
+ ...prev,
871
+ editLineCodeAmountLoading: loading
872
+ };
873
+ });
874
+ }, [loading, setLoadingState]);
875
+ };
876
+ var createInitialValue = () => ({
877
+ zipCode: "",
878
+ plusMemberMetafields: {},
879
+ setZipCode: () => {
880
+ },
881
+ allowNextDayDelivery: false,
882
+ setAllowNextDayDelivery: () => {
883
+ },
884
+ allowThirdDayDelivery: false,
885
+ setAllowThirdDayDelivery: () => {
886
+ },
887
+ selectedPlusMemberMode: "free",
888
+ setSelectedPlusMemberMode: () => {
889
+ },
890
+ showAreaCheckModal: false,
891
+ setShowAreaCheckModal: () => {
892
+ },
893
+ selectedShippingMethod: void 0,
894
+ setSelectedShippingMethod: () => {
895
+ },
896
+ showTip: false,
897
+ setShowTip: () => {
898
+ },
899
+ showMoreShippingMethod: false,
900
+ setShowMoreShippingMethod: () => {
901
+ },
902
+ variant: {},
903
+ product: {},
904
+ shippingMethodsContext: {
905
+ freeShippingMethods: [],
906
+ paymentShippingMethods: [],
907
+ nddOverweight: false,
908
+ tddOverweight: false
909
+ },
910
+ selectedPlusMemberProduct: null,
911
+ plusMemberProducts: [],
912
+ showPlusMemberBenefit: false,
913
+ setShowPlusMemberBenefit: () => {
914
+ },
915
+ deleteMarginBottom: false,
916
+ setDeleteMarginBottom: () => {
917
+ },
918
+ profile: void 0,
919
+ locale: void 0
920
+ });
921
+ react.createContext(createInitialValue());
922
+ var CartContext = react.createContext(null);
923
+ function CartProvider({
924
+ children,
925
+ // swrOptions,
926
+ autoFreeGiftConfig,
927
+ gradientGiftsConfig,
928
+ profile,
929
+ customer,
930
+ locale,
931
+ metafieldIdentifiers,
932
+ memberSetting
933
+ }) {
934
+ const { client, cartCookieAdapter } = useShopify();
935
+ const [customAttributes, setCustomAttributes] = react.useState([]);
936
+ const [isCodeChanging, setIsCodeChanging] = react.useState(false);
937
+ const [loadingState, setLoadingState] = react.useState({
938
+ editLineQuantityLoading: false,
939
+ editLineCodeAmountLoading: false,
940
+ listingAutoCodeApplying: false,
941
+ userCodeApplying: false
942
+ });
943
+ const [scriptAutoFreeGift, setScriptAutoFreeGift] = react.useState([]);
944
+ const [functionAutoFreeGift, setFunctionAutoFreeGift] = react.useState([]);
945
+ const {
946
+ run: fetchCart,
947
+ data: cart,
948
+ loading: isCartLoading,
949
+ mutate: mutateCart
950
+ } = ahooks.useRequest(
951
+ async () => {
952
+ return shopifySdk.getCart(client, {
953
+ cookieAdapter: cartCookieAdapter,
954
+ metafieldIdentifiers
955
+ });
956
+ },
957
+ {
958
+ refreshDeps: [locale]
959
+ }
960
+ );
961
+ const { trigger: updateAttributes } = useUpdateCartAttributes(mutateCart, metafieldIdentifiers);
962
+ const { attributes } = useCartAttributes({ profile, customer, cart, memberSetting });
963
+ ahooks.useRequest(
964
+ () => {
965
+ const newAttributes = [...attributes, ...customAttributes];
966
+ const needUpdate = cart && !isAttributesEqual(cart.customAttributes, newAttributes);
967
+ if (needUpdate) {
968
+ return updateAttributes({ attributes: newAttributes });
969
+ } else {
970
+ return Promise.resolve(cart);
971
+ }
972
+ },
973
+ {
974
+ throttleWait: 1e3,
975
+ // 1 秒内只触发最后一次更新
976
+ throttleTrailing: true,
977
+ throttleLeading: false,
978
+ refreshDeps: [attributes, customAttributes]
979
+ }
980
+ );
981
+ useUpdateLineCodeAmountAttributes({
982
+ cart,
983
+ mutateCart,
984
+ isCartLoading: isCartLoading || isCodeChanging,
985
+ setLoadingState
986
+ });
987
+ const removeCustomAttributes = react.useCallback((attributes2) => {
988
+ setCustomAttributes(
989
+ (prev) => prev.filter((attr) => !attributes2.some((a) => a.key === attr.key))
990
+ );
991
+ }, []);
992
+ const addCustomAttributes = react.useCallback(
993
+ (attributes2) => {
994
+ const sameAttributes = attributes2.filter(
995
+ (attr) => customAttributes.some((a) => a.key === attr.key)
996
+ );
997
+ if (sameAttributes.length) {
998
+ setCustomAttributes((prev) => {
999
+ const removedAttributes = prev.filter(
1000
+ (attr) => !sameAttributes.some((a) => a.key === attr.key)
1001
+ );
1002
+ return [...removedAttributes, ...attributes2];
1003
+ });
1004
+ } else {
1005
+ setCustomAttributes((prev) => [...prev, ...attributes2]);
1006
+ }
1007
+ },
1008
+ [customAttributes]
1009
+ );
1010
+ const functionAutoFreeGiftResult = useCalcAutoFreeGift(cart, autoFreeGiftConfig || [], customer);
1011
+ const scriptAutoFreeGiftResult = useScriptAutoFreeGift({
1012
+ campaign: gradientGiftsConfig || null,
1013
+ _giveaway: CUSTOMER_SCRIPT_GIFT_KEY,
1014
+ cart
1015
+ });
1016
+ const formattedScriptGifts = react.useMemo(() => {
1017
+ return formatScriptAutoFreeGift({
1018
+ scriptAutoFreeGiftResult,
1019
+ gradient_gifts: gradientGiftsConfig,
1020
+ locale
1021
+ });
1022
+ }, [scriptAutoFreeGiftResult, gradientGiftsConfig, locale]);
1023
+ const formattedFunctionGifts = react.useMemo(() => {
1024
+ return formatFunctionAutoFreeGift({
1025
+ qualifyingGift: functionAutoFreeGiftResult?.qualifyingGift || null,
1026
+ giftProductsResult: functionAutoFreeGiftResult?.giftProductsResult || [],
1027
+ locale
1028
+ });
1029
+ }, [functionAutoFreeGiftResult, locale]);
1030
+ react.useEffect(() => {
1031
+ if (loadingState.userCodeApplying || loadingState.editLineQuantityLoading || loadingState.editLineCodeAmountLoading || loadingState.listingAutoCodeApplying) {
1032
+ return;
1033
+ }
1034
+ setScriptAutoFreeGift(formattedScriptGifts);
1035
+ setFunctionAutoFreeGift(formattedFunctionGifts);
1036
+ }, [
1037
+ formattedScriptGifts,
1038
+ formattedFunctionGifts,
1039
+ loadingState.userCodeApplying,
1040
+ loadingState.editLineQuantityLoading,
1041
+ loadingState.editLineCodeAmountLoading,
1042
+ loadingState.listingAutoCodeApplying
1043
+ ]);
1044
+ const giftNeedAddToCartLines = react.useMemo(() => {
1045
+ const cartGiftLineItems = cart?.lineItems.filter(
1046
+ (line) => line.customAttributes?.some(
1047
+ (attr) => [CUSTOMER_ATTRIBUTE_KEY, CUSTOMER_SCRIPT_GIFT_KEY].includes(attr.key)
1048
+ )
1049
+ );
1050
+ const result = [...scriptAutoFreeGift, ...functionAutoFreeGift].filter(
1051
+ (item) => (
1052
+ // 如果购物车中已经存在对应赠品,且标记了 function 满赠的属性,则不添加
1053
+ !cartGiftLineItems?.find((line) => {
1054
+ return line.variantId === item.line.variantId && line.customAttributes?.some((attr) => {
1055
+ if (attr.key === "_discounts_function_env") {
1056
+ return getDiscountEnvAttributeValue(line.customAttributes).is_gift;
1057
+ }
1058
+ return false;
1059
+ });
1060
+ })
1061
+ )
1062
+ );
1063
+ return result;
1064
+ }, [cart?.lineItems, scriptAutoFreeGift, functionAutoFreeGift]);
1065
+ const value = react.useMemo(
1066
+ () => ({
1067
+ cart,
1068
+ isCartLoading,
1069
+ triggerFetch: fetchCart,
1070
+ mutateCart,
1071
+ addCustomAttributes,
1072
+ removeCustomAttributes,
1073
+ setCustomAttributes,
1074
+ locale,
1075
+ isCodeChanging,
1076
+ setIsCodeChanging,
1077
+ autoFreeGiftConfig,
1078
+ setLoadingState,
1079
+ loadingState,
1080
+ // function满赠
1081
+ functionAutoFreeGift,
1082
+ functionAutoFreeGiftResult,
1083
+ setFunctionAutoFreeGift,
1084
+ // script满赠
1085
+ scriptAutoFreeGift,
1086
+ scriptAutoFreeGiftResult,
1087
+ setScriptAutoFreeGift,
1088
+ giftNeedAddToCartLines,
1089
+ metafieldIdentifiers
1090
+ }),
1091
+ [
1092
+ cart,
1093
+ isCartLoading,
1094
+ fetchCart,
1095
+ mutateCart,
1096
+ addCustomAttributes,
1097
+ removeCustomAttributes,
1098
+ locale,
1099
+ isCodeChanging,
1100
+ autoFreeGiftConfig,
1101
+ loadingState,
1102
+ // function满赠
1103
+ functionAutoFreeGift,
1104
+ functionAutoFreeGiftResult,
1105
+ setFunctionAutoFreeGift,
1106
+ // script满赠
1107
+ scriptAutoFreeGift,
1108
+ scriptAutoFreeGiftResult,
1109
+ setScriptAutoFreeGift,
1110
+ giftNeedAddToCartLines,
1111
+ metafieldIdentifiers
1112
+ ]
1113
+ );
1114
+ return /* @__PURE__ */ jsxRuntime.jsx(CartContext.Provider, { value, children });
1115
+ }
1116
+ function useCartContext() {
1117
+ const context = react.useContext(CartContext);
1118
+ if (!context) {
1119
+ throw new Error("useCartContext must be used within a CartProvider");
1120
+ }
1121
+ return context;
1122
+ }
1123
+
1124
+ exports.CartProvider = CartProvider;
1125
+ exports.ShopifyContext = ShopifyContext;
1126
+ exports.ShopifyProvider = ShopifyProvider;
1127
+ exports.useCartContext = useCartContext;
1128
+ exports.useShopify = useShopify;
1129
+ //# sourceMappingURL=index.js.map
1130
+ //# sourceMappingURL=index.js.map