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