@tagadapay/plugin-sdk 2.7.20 → 2.7.22
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/react/components/DebugDrawer.js +80 -2
- package/dist/react/hooks/useFunnel.d.ts +87 -3
- package/dist/react/hooks/useFunnel.js +77 -5
- package/dist/react/providers/TagadaProvider.d.ts +8 -0
- package/dist/react/providers/TagadaProvider.js +19 -0
- package/dist/v2/core/resources/funnel.d.ts +71 -5
- package/dist/v2/core/resources/funnel.js +27 -3
- package/dist/v2/index.d.ts +3 -2
- package/dist/v2/index.js +2 -1
- package/dist/v2/react/components/DebugDrawer.js +229 -15
- package/dist/v2/react/hooks/useFunnel.d.ts +4 -4
- package/dist/v2/react/hooks/useFunnel.js +37 -5
- package/dist/v2/react/hooks/usePostPurchasesQuery.js +129 -43
- package/dist/v2/react/index.d.ts +1 -0
- package/dist/v2/react/index.js +2 -0
- package/dist/v2/react/providers/TagadaProvider.d.ts +8 -0
- package/dist/v2/react/providers/TagadaProvider.js +94 -55
- package/package.json +1 -1
|
@@ -3,16 +3,54 @@
|
|
|
3
3
|
* Handles post-purchase offers with automatic caching
|
|
4
4
|
*/
|
|
5
5
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
6
|
-
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
7
|
import { PostPurchasesResource } from '../../core/resources/postPurchases';
|
|
8
8
|
import { useTagadaContext } from '../providers/TagadaProvider';
|
|
9
|
-
import { getGlobalApiClient } from './useApiQuery';
|
|
9
|
+
import { getGlobalApiClient, getGlobalApiClientOrNull } from './useApiQuery';
|
|
10
10
|
export function usePostPurchasesQuery(options) {
|
|
11
|
+
console.log('usePostPurchasesQuery');
|
|
11
12
|
const { orderId, enabled = true, autoInitializeCheckout = false } = options;
|
|
12
13
|
const queryClient = useQueryClient();
|
|
13
|
-
const { session } = useTagadaContext();
|
|
14
|
+
const { session, isSessionInitialized, apiService } = useTagadaContext();
|
|
15
|
+
// Track if token is available on API service
|
|
16
|
+
const [hasToken, setHasToken] = useState(() => apiService.getCurrentToken() !== null);
|
|
17
|
+
// Update token availability when session is initialized
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (isSessionInitialized) {
|
|
20
|
+
const checkToken = () => {
|
|
21
|
+
const token = apiService.getCurrentToken();
|
|
22
|
+
const tokenAvailable = token !== null;
|
|
23
|
+
setHasToken(tokenAvailable);
|
|
24
|
+
// Also ensure API client has the token
|
|
25
|
+
const apiClient = getGlobalApiClientOrNull();
|
|
26
|
+
if (apiClient && token) {
|
|
27
|
+
apiClient.updateToken(token);
|
|
28
|
+
}
|
|
29
|
+
return tokenAvailable;
|
|
30
|
+
};
|
|
31
|
+
// Check immediately
|
|
32
|
+
if (checkToken()) {
|
|
33
|
+
return; // Token is available, no need to poll
|
|
34
|
+
}
|
|
35
|
+
// Poll for token if not immediately available (handles async token setting)
|
|
36
|
+
const interval = setInterval(() => {
|
|
37
|
+
if (checkToken()) {
|
|
38
|
+
clearInterval(interval);
|
|
39
|
+
}
|
|
40
|
+
}, 100); // Check every 100ms
|
|
41
|
+
return () => clearInterval(interval);
|
|
42
|
+
}
|
|
43
|
+
}, [isSessionInitialized, apiService]);
|
|
14
44
|
// State for checkout sessions per offer
|
|
15
45
|
const [checkoutSessions, setCheckoutSessions] = useState({});
|
|
46
|
+
// Ref to track checkout sessions for synchronous access
|
|
47
|
+
const checkoutSessionsRef = useRef({});
|
|
48
|
+
// Ref to track offers being initialized to prevent duplicate initialization
|
|
49
|
+
const initializingOffers = useRef(new Set());
|
|
50
|
+
// Keep ref in sync with state
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
checkoutSessionsRef.current = checkoutSessions;
|
|
53
|
+
}, [checkoutSessions]);
|
|
16
54
|
// Create post purchases resource client
|
|
17
55
|
const postPurchasesResource = useMemo(() => {
|
|
18
56
|
try {
|
|
@@ -70,28 +108,102 @@ export function usePostPurchasesQuery(options) {
|
|
|
70
108
|
throw error;
|
|
71
109
|
}
|
|
72
110
|
}, [postPurchasesResource]);
|
|
111
|
+
// Initialize checkout session for an offer
|
|
112
|
+
const initializeOfferCheckout = useCallback(async (offerId) => {
|
|
113
|
+
try {
|
|
114
|
+
// Check if customer ID is available
|
|
115
|
+
if (!session?.customerId) {
|
|
116
|
+
throw new Error('Customer ID is required. Make sure the session is properly initialized.');
|
|
117
|
+
}
|
|
118
|
+
// Check if already initializing to avoid race conditions
|
|
119
|
+
if (initializingOffers.current.has(offerId)) {
|
|
120
|
+
return; // Already initializing, skip
|
|
121
|
+
}
|
|
122
|
+
// Check if already initialized using ref for synchronous access
|
|
123
|
+
if (checkoutSessionsRef.current[offerId]?.checkoutSessionId) {
|
|
124
|
+
return; // Already initialized, exit early
|
|
125
|
+
}
|
|
126
|
+
// Mark as initializing
|
|
127
|
+
initializingOffers.current.add(offerId);
|
|
128
|
+
try {
|
|
129
|
+
// Initialize checkout session
|
|
130
|
+
const initResult = await postPurchasesResource.initCheckoutSession(offerId, orderId, session.customerId);
|
|
131
|
+
if (!initResult.checkoutSessionId) {
|
|
132
|
+
throw new Error('Failed to initialize checkout session');
|
|
133
|
+
}
|
|
134
|
+
const sessionId = initResult.checkoutSessionId;
|
|
135
|
+
// Initialize session state
|
|
136
|
+
setCheckoutSessions(prev => {
|
|
137
|
+
// Double-check we're not overwriting an existing session
|
|
138
|
+
if (prev[offerId]?.checkoutSessionId) {
|
|
139
|
+
return prev;
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
...prev,
|
|
143
|
+
[offerId]: {
|
|
144
|
+
checkoutSessionId: sessionId,
|
|
145
|
+
orderSummary: null,
|
|
146
|
+
selectedVariants: {},
|
|
147
|
+
loadingVariants: {},
|
|
148
|
+
isUpdatingSummary: false,
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
// Fetch order summary with variant options
|
|
153
|
+
await fetchOrderSummary(offerId, sessionId);
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
// Remove from initializing set
|
|
157
|
+
initializingOffers.current.delete(offerId);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
// Remove from initializing set on error
|
|
162
|
+
initializingOffers.current.delete(offerId);
|
|
163
|
+
console.error(`[SDK] Failed to initialize checkout for offer ${offerId}:`, error);
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
}, [session?.customerId, orderId, postPurchasesResource, fetchOrderSummary]);
|
|
73
167
|
// Main post-purchase offers query
|
|
74
168
|
const { data: offers = [], isLoading, error, refetch, } = useQuery({
|
|
75
169
|
queryKey: ['post-purchase-offers', orderId],
|
|
76
|
-
queryFn: () =>
|
|
77
|
-
|
|
170
|
+
queryFn: async () => {
|
|
171
|
+
// Double-check token is available before making the request
|
|
172
|
+
const token = apiService.getCurrentToken();
|
|
173
|
+
if (!token) {
|
|
174
|
+
throw new Error('No authentication token available. Please wait for session initialization.');
|
|
175
|
+
}
|
|
176
|
+
// Ensure API client has the token
|
|
177
|
+
const apiClient = getGlobalApiClientOrNull();
|
|
178
|
+
if (apiClient && token) {
|
|
179
|
+
apiClient.updateToken(token);
|
|
180
|
+
}
|
|
181
|
+
return postPurchasesResource.getPostPurchaseOffers(orderId);
|
|
182
|
+
},
|
|
183
|
+
enabled: enabled && !!orderId && isSessionInitialized && hasToken, // Wait for CMS token/session to be ready AND token to be set on API client
|
|
78
184
|
staleTime: 30000, // 30 seconds
|
|
79
185
|
refetchOnWindowFocus: false,
|
|
186
|
+
retry: (failureCount, error) => {
|
|
187
|
+
// Retry if token is not available yet (might be a timing issue)
|
|
188
|
+
if (error instanceof Error && error.message.includes('No authentication token')) {
|
|
189
|
+
return failureCount < 3; // Retry up to 3 times
|
|
190
|
+
}
|
|
191
|
+
return false; // Don't retry other errors
|
|
192
|
+
},
|
|
80
193
|
});
|
|
81
194
|
// Auto-initialize checkout sessions if enabled
|
|
82
195
|
useEffect(() => {
|
|
83
|
-
if (autoInitializeCheckout && offers.length > 0) {
|
|
196
|
+
if (autoInitializeCheckout && offers.length > 0 && session?.customerId && isSessionInitialized) {
|
|
84
197
|
// Initialize checkout sessions for all offers
|
|
85
198
|
offers.forEach((offer) => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
199
|
+
// The initializeOfferCheckout function will check if already initialized or initializing
|
|
200
|
+
void initializeOfferCheckout(offer.id).catch((error) => {
|
|
201
|
+
// Log errors but don't throw - auto-initialization failures shouldn't break the UI
|
|
202
|
+
console.error(`[SDK] Failed to auto-initialize checkout for offer ${offer.id}:`, error);
|
|
203
|
+
});
|
|
92
204
|
});
|
|
93
205
|
}
|
|
94
|
-
}, [autoInitializeCheckout, offers]);
|
|
206
|
+
}, [autoInitializeCheckout, offers, session?.customerId, isSessionInitialized, initializeOfferCheckout]);
|
|
95
207
|
// Accept offer mutation
|
|
96
208
|
const acceptMutation = useMutation({
|
|
97
209
|
mutationFn: ({ offerId, items }) => {
|
|
@@ -201,40 +313,14 @@ export function usePostPurchasesQuery(options) {
|
|
|
201
313
|
getCheckoutSessionState: (offerId) => {
|
|
202
314
|
return checkoutSessions[offerId] || null;
|
|
203
315
|
},
|
|
204
|
-
initializeOfferCheckout
|
|
205
|
-
try {
|
|
206
|
-
// Check if customer ID is available
|
|
207
|
-
if (!session?.customerId) {
|
|
208
|
-
throw new Error('Customer ID is required. Make sure the session is properly initialized.');
|
|
209
|
-
}
|
|
210
|
-
// Initialize checkout session
|
|
211
|
-
const initResult = await postPurchasesResource.initCheckoutSession(offerId, orderId, session.customerId);
|
|
212
|
-
if (!initResult.checkoutSessionId) {
|
|
213
|
-
throw new Error('Failed to initialize checkout session');
|
|
214
|
-
}
|
|
215
|
-
const sessionId = initResult.checkoutSessionId;
|
|
216
|
-
// Initialize session state
|
|
217
|
-
setCheckoutSessions(prev => ({
|
|
218
|
-
...prev,
|
|
219
|
-
[offerId]: {
|
|
220
|
-
checkoutSessionId: sessionId,
|
|
221
|
-
orderSummary: null,
|
|
222
|
-
selectedVariants: {},
|
|
223
|
-
loadingVariants: {},
|
|
224
|
-
isUpdatingSummary: false,
|
|
225
|
-
}
|
|
226
|
-
}));
|
|
227
|
-
// Fetch order summary with variant options
|
|
228
|
-
await fetchOrderSummary(offerId, sessionId);
|
|
229
|
-
}
|
|
230
|
-
catch (_error) {
|
|
231
|
-
throw _error;
|
|
232
|
-
}
|
|
233
|
-
},
|
|
316
|
+
initializeOfferCheckout,
|
|
234
317
|
getAvailableVariants: (offerId, productId) => {
|
|
235
318
|
const sessionState = checkoutSessions[offerId];
|
|
319
|
+
console.log('sessionState', sessionState);
|
|
236
320
|
if (!sessionState?.orderSummary?.options?.[productId])
|
|
237
321
|
return [];
|
|
322
|
+
console.log('getAvailableVariants', offerId, productId);
|
|
323
|
+
console.log('sessionState.orderSummary.options', sessionState.orderSummary.options);
|
|
238
324
|
return sessionState.orderSummary.options[productId].map((variant) => ({
|
|
239
325
|
variantId: variant.id,
|
|
240
326
|
variantName: variant.name,
|
package/dist/v2/react/index.d.ts
CHANGED
|
@@ -55,6 +55,7 @@ export type { ExtractedAddress, GooglePlaceDetails, GooglePrediction, UseGoogleA
|
|
|
55
55
|
export type { ISOCountry, ISORegion, UseISODataResult } from './hooks/useISOData';
|
|
56
56
|
export type { UsePluginConfigOptions, UsePluginConfigResult } from './hooks/usePluginConfig';
|
|
57
57
|
export type { TranslateFunction, UseTranslationOptions, UseTranslationResult } from './hooks/useTranslation';
|
|
58
|
+
export { FunnelEventType } from '../core/resources/funnel';
|
|
58
59
|
export type { UseCheckoutQueryOptions as UseCheckoutOptions, UseCheckoutQueryResult as UseCheckoutResult } from './hooks/useCheckoutQuery';
|
|
59
60
|
export type { UseDiscountsQueryOptions as UseDiscountsOptions, UseDiscountsQueryResult as UseDiscountsResult } from './hooks/useDiscountsQuery';
|
|
60
61
|
export type { FunnelEvent, FunnelNavigationAction, FunnelNavigationResult, SimpleFunnelContext, UseFunnelOptions, UseFunnelResult } from './hooks/useFunnel';
|
package/dist/v2/react/index.js
CHANGED
|
@@ -44,5 +44,7 @@ export { useTranslation } from './hooks/useTranslation';
|
|
|
44
44
|
export { useVipOffersQuery as useVipOffers } from './hooks/useVipOffersQuery';
|
|
45
45
|
// Funnel hooks
|
|
46
46
|
export { useFunnel, useSimpleFunnel } from './hooks/useFunnel';
|
|
47
|
+
// TanStack Query types
|
|
48
|
+
export { FunnelEventType } from '../core/resources/funnel';
|
|
47
49
|
// Re-export utilities from main react
|
|
48
50
|
export { formatMoney } from '../../react/utils/money';
|
|
@@ -26,6 +26,14 @@ interface TagadaContextValue {
|
|
|
26
26
|
lastUpdated: Date | null;
|
|
27
27
|
};
|
|
28
28
|
updateCheckoutDebugData: (data: any, error?: Error | null, isLoading?: boolean) => void;
|
|
29
|
+
debugFunnel: {
|
|
30
|
+
isActive: boolean;
|
|
31
|
+
data: any;
|
|
32
|
+
error: Error | null;
|
|
33
|
+
isLoading: boolean;
|
|
34
|
+
lastUpdated: Date | null;
|
|
35
|
+
};
|
|
36
|
+
updateFunnelDebugData: (data: any, error?: Error | null, isLoading?: boolean) => void;
|
|
29
37
|
refreshCoordinator: {
|
|
30
38
|
registerCheckoutRefresh: (refreshFn: () => Promise<void>, name?: string) => void;
|
|
31
39
|
registerOrderBumpRefresh: (refreshFn: () => Promise<void>) => void;
|
|
@@ -295,6 +295,13 @@ rawPluginConfig, }) {
|
|
|
295
295
|
isLoading: false,
|
|
296
296
|
lastUpdated: null,
|
|
297
297
|
});
|
|
298
|
+
const [debugFunnel, setDebugFunnel] = useState({
|
|
299
|
+
isActive: false,
|
|
300
|
+
data: null,
|
|
301
|
+
error: null,
|
|
302
|
+
isLoading: false,
|
|
303
|
+
lastUpdated: null,
|
|
304
|
+
});
|
|
298
305
|
// Initialize auth state
|
|
299
306
|
const [auth, setAuth] = useState({
|
|
300
307
|
isAuthenticated: false,
|
|
@@ -480,6 +487,65 @@ rawPluginConfig, }) {
|
|
|
480
487
|
setIsLoading(false);
|
|
481
488
|
}
|
|
482
489
|
}, [apiService, hasAttemptedAnonymousToken, initializeSession, finalDebugMode, isActiveInstance, instanceId]);
|
|
490
|
+
// Initialize token from storage or create anonymous token (extracted to stable callback)
|
|
491
|
+
const initializeToken = useCallback(async () => {
|
|
492
|
+
if (!isActiveInstance) {
|
|
493
|
+
console.log(`🚫 [TagadaProvider] Instance ${instanceId} is not active, skipping token initialization`);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
try {
|
|
497
|
+
console.debug('[SDK] Initializing token...');
|
|
498
|
+
setIsLoading(true);
|
|
499
|
+
// Check for existing token
|
|
500
|
+
const existingToken = getClientToken();
|
|
501
|
+
let tokenToUse = null;
|
|
502
|
+
// Check URL params for token
|
|
503
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
504
|
+
const queryToken = urlParams.get('token');
|
|
505
|
+
if (queryToken) {
|
|
506
|
+
console.debug('[SDK] Found token in URL params');
|
|
507
|
+
tokenToUse = queryToken;
|
|
508
|
+
setClientToken(queryToken);
|
|
509
|
+
}
|
|
510
|
+
else if (existingToken && !isTokenExpired(existingToken)) {
|
|
511
|
+
console.debug('[SDK] Using existing token from storage');
|
|
512
|
+
tokenToUse = existingToken;
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
console.debug('[SDK] No valid token found');
|
|
516
|
+
// Determine storeId for anonymous token
|
|
517
|
+
const targetStoreId = storeId || 'default-store';
|
|
518
|
+
await createAnonymousToken(targetStoreId);
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
if (tokenToUse) {
|
|
522
|
+
setToken(tokenToUse);
|
|
523
|
+
// Update the API service with the token
|
|
524
|
+
apiService.updateToken(tokenToUse);
|
|
525
|
+
// IMPORTANT: Immediately sync token to API client
|
|
526
|
+
apiClient.updateToken(tokenToUse);
|
|
527
|
+
console.log('[SDK] Token immediately synced to ApiClient:', tokenToUse.substring(0, 8) + '...');
|
|
528
|
+
// Decode token to get session data
|
|
529
|
+
const decodedSession = decodeJWTClient(tokenToUse);
|
|
530
|
+
if (decodedSession) {
|
|
531
|
+
setSession(decodedSession);
|
|
532
|
+
// Initialize session with API call
|
|
533
|
+
await initializeSession(decodedSession);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
console.error('[SDK] Failed to decode token');
|
|
537
|
+
setIsInitialized(true);
|
|
538
|
+
setIsSessionInitialized(false); // Session failed to initialize
|
|
539
|
+
setIsLoading(false);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
console.error('[SDK] Error initializing token:', error);
|
|
545
|
+
setIsInitialized(true);
|
|
546
|
+
setIsLoading(false);
|
|
547
|
+
}
|
|
548
|
+
}, [apiService, apiClient, storeId, createAnonymousToken, initializeSession, isActiveInstance, instanceId]);
|
|
483
549
|
// Initialize token from storage or create anonymous token
|
|
484
550
|
// This runs in the background after phases 1 & 2 complete, but doesn't block rendering
|
|
485
551
|
useEffect(() => {
|
|
@@ -500,62 +566,23 @@ rawPluginConfig, }) {
|
|
|
500
566
|
return;
|
|
501
567
|
}
|
|
502
568
|
isInitializing.current = true;
|
|
503
|
-
const initializeToken = async () => {
|
|
504
|
-
try {
|
|
505
|
-
console.debug('[SDK] Initializing token...');
|
|
506
|
-
setIsLoading(true);
|
|
507
|
-
// Check for existing token
|
|
508
|
-
const existingToken = getClientToken();
|
|
509
|
-
let tokenToUse = null;
|
|
510
|
-
// Check URL params for token
|
|
511
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
512
|
-
const queryToken = urlParams.get('token');
|
|
513
|
-
if (queryToken) {
|
|
514
|
-
console.debug('[SDK] Found token in URL params');
|
|
515
|
-
tokenToUse = queryToken;
|
|
516
|
-
setClientToken(queryToken);
|
|
517
|
-
}
|
|
518
|
-
else if (existingToken && !isTokenExpired(existingToken)) {
|
|
519
|
-
console.debug('[SDK] Using existing token from storage');
|
|
520
|
-
tokenToUse = existingToken;
|
|
521
|
-
}
|
|
522
|
-
else {
|
|
523
|
-
console.debug('[SDK] No valid token found');
|
|
524
|
-
// Determine storeId for anonymous token
|
|
525
|
-
const targetStoreId = storeId || 'default-store';
|
|
526
|
-
await createAnonymousToken(targetStoreId);
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
|
-
if (tokenToUse) {
|
|
530
|
-
setToken(tokenToUse);
|
|
531
|
-
// Update the API service with the token
|
|
532
|
-
apiService.updateToken(tokenToUse);
|
|
533
|
-
// IMPORTANT: Immediately sync token to API client
|
|
534
|
-
apiClient.updateToken(tokenToUse);
|
|
535
|
-
console.log('[SDK] Token immediately synced to ApiClient:', tokenToUse.substring(0, 8) + '...');
|
|
536
|
-
// Decode token to get session data
|
|
537
|
-
const decodedSession = decodeJWTClient(tokenToUse);
|
|
538
|
-
if (decodedSession) {
|
|
539
|
-
setSession(decodedSession);
|
|
540
|
-
// Initialize session with API call
|
|
541
|
-
await initializeSession(decodedSession);
|
|
542
|
-
}
|
|
543
|
-
else {
|
|
544
|
-
console.error('[SDK] Failed to decode token');
|
|
545
|
-
setIsInitialized(true);
|
|
546
|
-
setIsSessionInitialized(false); // Session failed to initialize
|
|
547
|
-
setIsLoading(false);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
catch (error) {
|
|
552
|
-
console.error('[SDK] Error initializing token:', error);
|
|
553
|
-
setIsInitialized(true);
|
|
554
|
-
setIsLoading(false);
|
|
555
|
-
}
|
|
556
|
-
};
|
|
557
569
|
void initializeToken();
|
|
558
|
-
}, [storeId,
|
|
570
|
+
}, [storeId, initializeToken, configLoading, isActiveInstance, instanceId]);
|
|
571
|
+
// Listen for storage changes (e.g., token updated in another tab)
|
|
572
|
+
useEffect(() => {
|
|
573
|
+
if (!isActiveInstance) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
function onStorage() {
|
|
577
|
+
// Re-run initialization when token may have changed in another tab
|
|
578
|
+
isInitializing.current = false;
|
|
579
|
+
void initializeToken();
|
|
580
|
+
}
|
|
581
|
+
window.addEventListener('storage', onStorage);
|
|
582
|
+
return () => {
|
|
583
|
+
window.removeEventListener('storage', onStorage);
|
|
584
|
+
};
|
|
585
|
+
}, [initializeToken, isActiveInstance]);
|
|
559
586
|
// Update auth state when customer/session changes
|
|
560
587
|
useEffect(() => {
|
|
561
588
|
setAuth({
|
|
@@ -671,6 +698,16 @@ rawPluginConfig, }) {
|
|
|
671
698
|
pluginConfigLoading: configLoading,
|
|
672
699
|
debugCheckout,
|
|
673
700
|
updateCheckoutDebugData: memoizedUpdateDebugData,
|
|
701
|
+
debugFunnel,
|
|
702
|
+
updateFunnelDebugData: (data, error, isLoading) => {
|
|
703
|
+
setDebugFunnel({
|
|
704
|
+
isActive: true,
|
|
705
|
+
data,
|
|
706
|
+
error: error ?? null,
|
|
707
|
+
isLoading: isLoading ?? false,
|
|
708
|
+
lastUpdated: new Date(),
|
|
709
|
+
});
|
|
710
|
+
},
|
|
674
711
|
refreshCoordinator,
|
|
675
712
|
money: memoizedMoneyUtils,
|
|
676
713
|
}), [
|
|
@@ -685,6 +722,8 @@ rawPluginConfig, }) {
|
|
|
685
722
|
isLoading,
|
|
686
723
|
isInitialized,
|
|
687
724
|
isSessionInitialized,
|
|
725
|
+
debugCheckout,
|
|
726
|
+
debugFunnel,
|
|
688
727
|
finalDebugMode,
|
|
689
728
|
pluginConfig,
|
|
690
729
|
configLoading,
|