Skip to content

Commit c167134

Browse files
committed
Create event handlers hook + debug logger
1 parent b20bd53 commit c167134

10 files changed

Lines changed: 136 additions & 84 deletions

File tree

modules/@shopify/checkout-sheet-kit/ios/ShopifyCheckoutSheetKit.mm

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,20 @@ of this software and associated documentation files (the "Software"), to deal
1818
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
2223
*/
2324

2425
#import <React/RCTBridgeModule.h>
2526
#import <React/RCTViewManager.h>
2627

27-
@interface RCT_EXTERN_MODULE(RCTShopifyCheckoutSheetKit, NSObject)
28+
@interface RCT_EXTERN_MODULE (RCTShopifyCheckoutSheetKit, NSObject)
2829

2930
/// Present checkout
30-
RCT_EXTERN_METHOD(present:(NSString *)checkoutURLString);
31+
RCT_EXTERN_METHOD(present : (NSString *)checkoutURLString);
3132

3233
/// Preload checkout
33-
RCT_EXTERN_METHOD(preload:(NSString *)checkoutURLString);
34+
RCT_EXTERN_METHOD(preload : (NSString *)checkoutURLString);
3435

3536
/// Dismiss checkout
3637
RCT_EXTERN_METHOD(dismiss);
@@ -39,21 +40,31 @@ @interface RCT_EXTERN_MODULE(RCTShopifyCheckoutSheetKit, NSObject)
3940
RCT_EXTERN_METHOD(invalidateCache);
4041

4142
/// Set configuration for checkout
42-
RCT_EXTERN_METHOD(setConfig:(NSDictionary *)configuration);
43+
RCT_EXTERN_METHOD(setConfig : (NSDictionary *)configuration);
4344

4445
// Return configuration for checkout
45-
RCT_EXTERN_METHOD(getConfig: (RCTPromiseResolveBlock) resolve reject: (RCTPromiseRejectBlock) reject)
46+
RCT_EXTERN_METHOD(getConfig : (RCTPromiseResolveBlock)
47+
resolve reject : (RCTPromiseRejectBlock)reject)
4648

4749
/// Configure AcceleratedCheckouts
48-
RCT_EXTERN_METHOD(configureAcceleratedCheckouts:(NSString *)storefrontDomain storefrontAccessToken:(NSString *)storefrontAccessToken customerEmail:(NSString *)customerEmail customerPhoneNumber:(NSString *)customerPhoneNumber wallets:(NSArray *)wallets);
50+
RCT_EXTERN_METHOD(configureAcceleratedCheckouts : (NSString *)
51+
storefrontDomain storefrontAccessToken : (NSString *)
52+
storefrontAccessToken customerEmail : (NSString *)
53+
customerEmail customerPhoneNumber : (NSString *)
54+
customerPhoneNumber);
4955

5056
/// Check if accelerated checkout is available
51-
RCT_EXTERN_METHOD(isAcceleratedCheckoutAvailable:(NSString *)cartId variantId:(NSString *)variantId quantity:(NSNumber *)quantity resolve:(RCTPromiseResolveBlock) resolve reject:(RCTPromiseRejectBlock) reject);
57+
RCT_EXTERN_METHOD(
58+
isAcceleratedCheckoutAvailable : (NSString *)cartId variantId : (NSString *)
59+
variantId quantity : (nonnull NSNumber *)
60+
quantity resolve : (RCTPromiseResolveBlock)
61+
resolve reject : (RCTPromiseRejectBlock)reject);
5262

5363
@end
5464

5565
// AcceleratedCheckoutButtons View Manager
56-
@interface RCT_EXTERN_MODULE(RCTAcceleratedCheckoutButtonsManager, RCTViewManager)
66+
@interface RCT_EXTERN_MODULE (RCTAcceleratedCheckoutButtonsManager,
67+
RCTViewManager)
5768

5869
RCT_EXPORT_VIEW_PROPERTY(cartId, NSString *)
5970
RCT_EXPORT_VIEW_PROPERTY(variantId, NSString *)

modules/@shopify/checkout-sheet-kit/ios/ShopifyCheckoutSheetKit.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
210210
storefrontAccessToken: String,
211211
customerEmail: String?,
212212
customerPhoneNumber: String?,
213-
wallets: [String]
214213
) {
215214
if #available(iOS 17.0, *) {
216215
let customer = ShopifyAcceleratedCheckouts.Customer(
@@ -226,7 +225,6 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
226225

227226
// Update the shared configuration for the UI components
228227
AcceleratedCheckoutConfiguration.shared.configuration = acceleratedCheckoutsConfiguration as? ShopifyAcceleratedCheckouts.Configuration
229-
AcceleratedCheckoutConfiguration.shared.wallets = wallets
230228

231229
// Notify all button views to update with the new configuration
232230
NotificationCenter.default.post(name: Notification.Name("AcceleratedCheckoutConfigurationUpdated"), object: nil)
@@ -236,7 +234,7 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
236234
@objc func isAcceleratedCheckoutAvailable(
237235
_ cartId: String?,
238236
variantId: String?,
239-
quantity: NSNumber?,
237+
quantity: NSNumber,
240238
resolve: @escaping RCTPromiseResolveBlock,
241239
reject: @escaping RCTPromiseRejectBlock
242240
) {

modules/@shopify/checkout-sheet-kit/src/components/AcceleratedCheckoutButtons.tsx

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import type {ViewStyle} from 'react-native';
2727
import type {
2828
AcceleratedCheckoutWallet,
2929
CheckoutCompletedEvent,
30+
CheckoutException,
3031
PixelEvent,
3132
} from '..';
3233

@@ -76,7 +77,7 @@ interface AcceleratedCheckoutButtonsProps {
7677
/**
7778
* Called when checkout fails
7879
*/
79-
onFail?: (error: {message: string}) => void;
80+
onFail?: (error: CheckoutException) => void;
8081

8182
/**
8283
* Called when checkout is completed successfully
@@ -94,12 +95,6 @@ interface AcceleratedCheckoutButtonsProps {
9495
*/
9596
onRenderStateChange?: (state: RenderState) => void;
9697

97-
/**
98-
* Called to determine if the error should be recovered from
99-
* Return true to attempt recovery, false to show error state
100-
*/
101-
onShouldRecoverFromError?: (error: {message: string}) => boolean;
102-
10398
/**
10499
* Called when a web pixel event is triggered
105100
*/
@@ -119,13 +114,10 @@ interface NativeAcceleratedCheckoutButtonsProps {
119114
cornerRadius?: number;
120115
wallets?: AcceleratedCheckoutWallet[];
121116
onPress?: () => void;
122-
onFail?: (event: {nativeEvent: {message: string}}) => void;
117+
onFail?: (event: {nativeEvent: CheckoutException}) => void;
123118
onComplete?: (event: {nativeEvent: CheckoutCompletedEvent}) => void;
124119
onCancel?: () => void;
125120
onRenderStateChange?: (event: {nativeEvent: {state: string}}) => void;
126-
onShouldRecoverFromError?: (event: {
127-
nativeEvent: {message: string};
128-
}) => boolean;
129121
onWebPixelEvent?: (event: {nativeEvent: PixelEvent}) => void;
130122
onClickLink?: (event: {nativeEvent: {url: string}}) => void;
131123
}
@@ -176,32 +168,25 @@ export const AcceleratedCheckoutButtons: React.FC<
176168
}, [onPress]);
177169

178170
const handleFail = useCallback(
179-
(event: {nativeEvent?: {message: string}; message?: string}) => {
180-
log('Checkout failed', event.nativeEvent);
181-
const message = event.nativeEvent?.message || event.message;
182-
if (message) {
183-
onFail?.({message});
184-
}
171+
(event: {nativeEvent: CheckoutException}) => {
172+
onFail?.(event.nativeEvent);
185173
},
186174
[onFail],
187175
);
188176

189177
const handleComplete = useCallback(
190178
(event: {nativeEvent: CheckoutCompletedEvent}) => {
191-
log('Checkout completed', event.nativeEvent.orderDetails.id);
192179
onComplete?.(event.nativeEvent);
193180
},
194181
[onComplete],
195182
);
196183

197184
const handleCancel = useCallback(() => {
198-
log('Checkout cancelled');
199185
onCancel?.();
200186
}, [onCancel]);
201187

202188
const handleRenderStateChange = useCallback(
203189
(event: {nativeEvent: {state: string}}) => {
204-
log('Render state changed', event.nativeEvent);
205190
if (event.nativeEvent?.state) {
206191
onRenderStateChange?.(event.nativeEvent.state as RenderState);
207192
}
@@ -211,15 +196,13 @@ export const AcceleratedCheckoutButtons: React.FC<
211196

212197
const handleWebPixelEvent = useCallback(
213198
(event: {nativeEvent: PixelEvent}) => {
214-
log('Web pixel event', event.nativeEvent.name);
215199
onWebPixelEvent?.(event.nativeEvent);
216200
},
217201
[onWebPixelEvent],
218202
);
219203

220204
const handleClickLink = useCallback(
221205
(event: {nativeEvent: {url: string}}) => {
222-
log('Link clicked', event.nativeEvent);
223206
if (event.nativeEvent?.url) {
224207
onClickLink?.(event.nativeEvent.url);
225208
}
@@ -261,10 +244,3 @@ export const AcceleratedCheckoutButtons: React.FC<
261244
};
262245

263246
export default AcceleratedCheckoutButtons;
264-
265-
function log(message: string, data?: any) {
266-
if (__DEV__) {
267-
// eslint-disable-next-line no-console
268-
console.log(`[AcceleratedCheckoutButtons] ${message}`, data || '');
269-
}
270-
}

modules/@shopify/checkout-sheet-kit/src/index.d.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,6 @@ export interface AcceleratedCheckoutConfiguration {
196196
email?: string;
197197
phoneNumber?: string;
198198
};
199-
200-
/**
201-
* Wallets to display in the AcceleratedCheckoutButtons
202-
* Defaults to both shopPay and applePay
203-
*/
204-
wallets?: AcceleratedCheckoutWallet[];
205199
}
206200

207201
function addEventListener(

modules/@shopify/checkout-sheet-kit/src/index.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -217,18 +217,11 @@ class ShopifyCheckoutSheet implements ShopifyCheckoutSheetKit {
217217
public configureAcceleratedCheckouts(
218218
config: AcceleratedCheckoutConfiguration,
219219
): void {
220-
// Default to both shopPay and applePay if no wallets specified
221-
const wallets = config.wallets ?? [
222-
AcceleratedCheckoutWallet.shopPay,
223-
AcceleratedCheckoutWallet.applePay,
224-
];
225-
226220
RNShopifyCheckoutSheetKit.configureAcceleratedCheckouts(
227221
config.storefrontDomain,
228222
config.storefrontAccessToken,
229223
config.customer?.email || null,
230224
config.customer?.phoneNumber || null,
231-
wallets,
232225
);
233226
}
234227

sample/src/App.tsx

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import SettingsScreen from './screens/SettingsScreen';
4040

4141
import type {Configuration} from '@shopify/checkout-sheet-kit';
4242
import {
43-
AcceleratedCheckoutWallet,
4443
ColorScheme,
4544
ShopifyCheckoutSheetProvider,
4645
useShopifyCheckoutSheet,
@@ -58,18 +57,28 @@ import ProductDetailsScreen from './screens/ProductDetailsScreen';
5857
import type {ProductVariant, ShopifyProduct} from '../@types';
5958
import ErrorBoundary from './ErrorBoundary';
6059
import env from 'react-native-config';
60+
import {createDebugLogger} from './utils';
61+
import {useShopifyEventHandlers} from './hooks/useCheckoutEventHandlers';
62+
63+
const log = createDebugLogger('ENV');
6164

6265
const colorScheme = ColorScheme.web;
6366

64-
console.log('Environment variables loaded:', {
65-
STOREFRONT_DOMAIN: env.STOREFRONT_DOMAIN,
66-
STOREFRONT_ACCESS_TOKEN: env.STOREFRONT_ACCESS_TOKEN
67-
? '***' + env.STOREFRONT_ACCESS_TOKEN.slice(-4)
68-
: 'undefined',
69-
STOREFRONT_VERSION: env.STOREFRONT_VERSION,
70-
EMAIL: env.EMAIL,
71-
PHONE: env.PHONE,
72-
});
67+
function quote(str: string | undefined) {
68+
return `"${str}"`;
69+
}
70+
71+
log('--------------------------------');
72+
log('Using the following env');
73+
log('STOREFRONT_DOMAIN:', quote(env.STOREFRONT_DOMAIN));
74+
log(
75+
'STOREFRONT_ACCESS_TOKEN:',
76+
'*'.repeat(8) + env.STOREFRONT_ACCESS_TOKEN?.slice(-4),
77+
);
78+
log('STOREFRONT_VERSION:', quote(env.STOREFRONT_VERSION));
79+
log('EMAIL:', quote(env.EMAIL));
80+
log('PHONE:', quote(env.PHONE));
81+
log('--------------------------------');
7382

7483
const checkoutKitConfig: Configuration = {
7584
colorScheme,
@@ -174,42 +183,38 @@ class StorefrontURL {
174183

175184
function AppWithContext({children}: PropsWithChildren) {
176185
const shopify = useShopifyCheckoutSheet();
186+
const eventHandlers = useShopifyEventHandlers();
177187

178188
useEffect(() => {
179189
// Configure AcceleratedCheckouts with both wallets
180190
shopify.configureAcceleratedCheckouts({
181-
storefrontDomain: env.STOREFRONT_DOMAIN ?? '',
182-
storefrontAccessToken: env.STOREFRONT_ACCESS_TOKEN ?? '',
191+
storefrontDomain: env.STOREFRONT_DOMAIN!,
192+
storefrontAccessToken: env.STOREFRONT_ACCESS_TOKEN!,
183193
customer: {
184-
email: env.EMAIL ?? '',
185-
phoneNumber: env.PHONE ?? '',
194+
email: env.EMAIL!,
195+
phoneNumber: env.PHONE!,
186196
},
187-
wallets: [
188-
AcceleratedCheckoutWallet.shopPay,
189-
AcceleratedCheckoutWallet.applePay,
190-
],
191197
});
192198

193199
const close = shopify.addEventListener('close', () => {
194-
console.log('[CheckoutClose]');
200+
eventHandlers.onCancel?.();
195201
});
196202

197203
const pixel = shopify.addEventListener('pixel', (event: PixelEvent) => {
198-
console.log('[CheckoutPixelEvent]', event.name, event);
204+
eventHandlers.onWebPixelEvent?.(event);
199205
});
200206

201207
const completed = shopify.addEventListener(
202208
'completed',
203209
(event: CheckoutCompletedEvent) => {
204-
console.log('[CheckoutCompletedEvent]', event.orderDetails.id);
205-
console.log('[CheckoutCompletedEvent]', event);
210+
eventHandlers.onComplete?.(event);
206211
},
207212
);
208213

209214
const error = shopify.addEventListener(
210215
'error',
211216
(error: CheckoutException) => {
212-
console.log(error.constructor.name, error);
217+
eventHandlers.onFail?.(error);
213218
},
214219
);
215220

@@ -219,7 +224,7 @@ function AppWithContext({children}: PropsWithChildren) {
219224
close?.remove();
220225
error?.remove();
221226
};
222-
}, [shopify]);
227+
}, [shopify, eventHandlers]);
223228

224229
return (
225230
<ConfigProvider>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import {useMemo} from 'react';
2+
3+
import {createDebugLogger} from '../utils';
4+
5+
import {useCart} from '../context/Cart';
6+
import type {
7+
CheckoutCompletedEvent,
8+
CheckoutException,
9+
PixelEvent,
10+
RenderState,
11+
} from '@shopify/checkout-sheet-kit';
12+
import {Linking} from 'react-native';
13+
14+
interface EventHandlers {
15+
onPress?: () => void;
16+
onFail?: (error: CheckoutException) => void;
17+
onComplete?: (event: CheckoutCompletedEvent) => void;
18+
onCancel?: () => void;
19+
onRenderStateChange?: (state: RenderState) => void;
20+
onShouldRecoverFromError?: (error: {message: string}) => boolean;
21+
onWebPixelEvent?: (event: PixelEvent) => void;
22+
onClickLink?: (url: string) => void;
23+
}
24+
25+
export function useShopifyEventHandlers(name?: string): EventHandlers {
26+
const log = createDebugLogger(name ?? '');
27+
const {clearCart} = useCart();
28+
29+
return useMemo(() => {
30+
return {
31+
onPress: () => {
32+
log('onPress');
33+
},
34+
onFail: error => {
35+
log('onFail', error);
36+
},
37+
onComplete: event => {
38+
log('onComplete', event.orderDetails.id);
39+
clearCart();
40+
},
41+
onCancel: () => {
42+
log('onCancel');
43+
},
44+
onRenderStateChange: state => {
45+
log('onRenderStateChange', state);
46+
},
47+
onWebPixelEvent: event => {
48+
log('onWebPixelEvent', event.name);
49+
},
50+
onClickLink: async url => {
51+
log('onClickLink', url);
52+
53+
if (await Linking.canOpenURL(url)) {
54+
await Linking.openURL(url);
55+
}
56+
},
57+
};
58+
}, [log, clearCart]);
59+
}

0 commit comments

Comments
 (0)