Skip to content

Commit 8bbeebf

Browse files
authored
Add support for Checkout events, add ShopifyCheckoutKitProvider context (#20)
* Update API * Write events for Android * Add license to context file * Update snapshot * Add SwiftLint pod dependency + yarn task to run locally * dismiss -> cancel * Update Android to 0.3.3, expose error message in checkoutDidFail event * cancel -> close * Remove listener count * Simplify ShopifyCheckoutKit interface, remove redundant NativeModule listeners, expose add/remove to class
1 parent 9f95b99 commit 8bbeebf

File tree

20 files changed

+418
-58
lines changed

20 files changed

+418
-58
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,9 @@ sample/ios/Pods/
8282
lib
8383
dist
8484
.turbo
85+
86+
# Module
87+
modules/react-native-shopify-checkout-kit/android/gradle/wrapper/gradle-wrapper.jar
88+
modules/react-native-shopify-checkout-kit/android/gradle/wrapper/gradle-wrapper.properties
89+
modules/react-native-shopify-checkout-kit/android/gradlew
90+
modules/react-native-shopify-checkout-kit/android/gradlew.bat

README.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,6 @@ function YourReactNativeApp() {
8383
The `checkoutURL` object is a standard web checkout URL that can be opened in any browser. To present a native checkout sheet in your application, provide the `checkoutURL` alongside optional runtime configuration settings to the `present(checkoutURL)` function provided by the SDK:
8484

8585
```tsx
86-
import {ShopifyCheckoutKit} from "react-native-shopify-checkout-kit"
87-
8886
function App() {
8987
const [createCart] = useMutation(createCartMutation)
9088
const [addToCart] = useMutation(addToCartMutation)
@@ -98,9 +96,24 @@ function App() {
9896
The `checkoutUrl` value is a standard web checkout URL that can be opened in any browser. To present a native checkout sheet in your application, provide the `checkoutUrl` alongside optional runtime configuration settings to the `present(checkoutUrl)` function provided by the SDK:
9997

10098
```tsx
101-
import {ShopifyCheckoutKit} from "react-native-shopify-checkout-kit"
99+
import {ShopifyCheckoutKitProvider, ColorScheme} from "react-native-shopify-checkout-kit"
100+
101+
function AppWithContext() {
102+
return (
103+
<ShopifyCheckoutKitProvider
104+
configuration={{
105+
colorScheme: ColorScheme.automatic
106+
}}
107+
>
108+
<ApolloProvider>
109+
<App />
110+
</ApolloProvider>
111+
</ShopifyCheckoutKitProvider>
112+
)
113+
}
102114

103115
function App() {
116+
const shopifyCheckout = useShopifyCheckoutKit()
104117
const checkoutUrl = useRef<string>(null)
105118
const [createCart] = useMutation(createCartMutation)
106119
const [addToCart] = useMutation(addToCartMutation)
@@ -117,11 +130,15 @@ function App() {
117130
})
118131
// Retrieve checkoutUrl from the Storefront response
119132
checkoutUrl.current = addToCartResponse.cartLinesAdd.cart.checkoutUrl
133+
134+
// Preload the checkout in the background for faster presentation
135+
shopifyCheckout.preload(checkoutUrl.current)
120136
}, []);
121137

122138
const handleCheckout = useCallback(() => {
123139
if (checkoutURL.current) {
124-
ShopifyCheckoutKit.present(checkoutURL.current)
140+
// Present the checkout to the buyer
141+
shopifyCheckout.present(checkoutURL.current)
125142
}
126143
}, [])
127144

.swiftlint.yml renamed to modules/react-native-shopify-checkout-kit/.swiftlint.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ disabled_rules:
22
- line_length
33
- redundant_void_return
44
- todo
5-
- tailing_comma
65

76
opt_in_rules:
87
- array_init

modules/react-native-shopify-checkout-kit/android/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ ndkVersion=23.1.7779620
55
buildToolsVersion = "33.0.0"
66

77
# Version of Shopify Checkout SDK to use with React Native
8-
SHOPIFY_CHECKOUT_SDK_VERSION=0.3.2
8+
SHOPIFY_CHECKOUT_SDK_VERSION=0.3.3

modules/react-native-shopify-checkout-kit/android/src/main/java/com/shopifycheckoutkit/CustomCheckoutEventProcessor.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,46 @@ of this software and associated documentation files (the "Software"), to deal
2525

2626
import android.content.Context;
2727
import com.shopify.checkoutkit.*;
28+
import com.facebook.react.modules.core.DeviceEventManagerModule;
29+
import com.facebook.react.bridge.ReactMethod;
30+
import com.facebook.react.bridge.ReactApplicationContext;
31+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
32+
import com.facebook.react.bridge.ReactContext;
33+
import com.facebook.react.bridge.WritableMap;
34+
import com.facebook.react.bridge.Arguments;
35+
import org.jetbrains.annotations.Nullable;
2836

2937
public class CustomCheckoutEventProcessor extends DefaultCheckoutEventProcessor {
30-
public CustomCheckoutEventProcessor(Context context) {
38+
private ReactContext reactContext;
39+
40+
public CustomCheckoutEventProcessor(Context context, ReactContext reactContext) {
3141
super(context);
42+
43+
this.reactContext = reactContext;
3244
}
3345

3446
@Override
3547
public void onCheckoutCompleted() {
36-
// Handle checkout completion
48+
sendEvent(this.reactContext, "completed", null);
3749
}
3850

3951
@Override
40-
public void onCheckoutFailed(CheckoutException error) {
41-
// Handle checkout failure
52+
public void onCheckoutFailed(CheckoutException checkoutError) {
53+
WritableMap error = Arguments.createMap();
54+
55+
error.putString("message", checkoutError.getErrorDescription());
56+
57+
sendEvent(this.reactContext, "error", error);
4258
}
4359

4460
@Override
4561
public void onCheckoutCanceled() {
46-
// Handle checkout cancellation
62+
sendEvent(this.reactContext, "close", null);
63+
}
64+
65+
private void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
66+
reactContext
67+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
68+
.emit(eventName, params);
4769
}
4870
}

modules/react-native-shopify-checkout-kit/android/src/main/java/com/shopifycheckoutkit/ShopifyCheckoutKitModule.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,12 @@ public class ShopifyCheckoutKitModule extends ReactContextBaseJavaModule {
4646

4747
private static Configuration checkoutConfig = new Configuration();
4848

49+
private ReactApplicationContext reactContext;
50+
4951
public ShopifyCheckoutKitModule(ReactApplicationContext reactContext) {
5052
super(reactContext);
53+
54+
this.reactContext = reactContext;
5155
}
5256

5357
@Override
@@ -62,12 +66,22 @@ public Map<String, Object> getConstants() {
6266
return constants;
6367
}
6468

69+
@ReactMethod
70+
public void addListener(String eventName) {
71+
// No-op but required for RN to register module
72+
}
73+
74+
@ReactMethod
75+
public void removeListeners(Integer count) {
76+
// No-op but required for RN to register module
77+
}
78+
6579
@ReactMethod
6680
public void present(String checkoutURL) {
6781
Activity currentActivity = getCurrentActivity();
6882
if (currentActivity instanceof ComponentActivity) {
6983
Context appContext = getReactApplicationContext();
70-
CheckoutEventProcessor checkoutEventProcessor = new CustomCheckoutEventProcessor(appContext);
84+
CheckoutEventProcessor checkoutEventProcessor = new CustomCheckoutEventProcessor(appContext, this.reactContext);
7185
currentActivity.runOnUiThread(() -> {
7286
ShopifyCheckoutKit.present(checkoutURL, (ComponentActivity) currentActivity,
7387
checkoutEventProcessor);

modules/react-native-shopify-checkout-kit/ios/ShopifyCheckoutKit.swift

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,64 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SO
2424
import Foundation
2525
import ShopifyCheckoutKit
2626
import UIKit
27+
import React
2728

2829
@objc(RCTShopifyCheckoutKit)
29-
class RCTShopifyCheckoutKit: UIViewController, CheckoutDelegate {
30-
func checkoutDidComplete() {}
30+
class RCTShopifyCheckoutKit: RCTEventEmitter, CheckoutDelegate {
31+
private var rootViewController: UIViewController?
3132

32-
func checkoutDidFail(error _: ShopifyCheckoutKit.CheckoutError) {}
33+
private var hasListeners = false
34+
35+
override init() {
36+
super.init()
37+
self.rootViewController = UIApplication.shared.delegate?.window??.rootViewController
38+
}
39+
40+
override var methodQueue: DispatchQueue! {
41+
return DispatchQueue.main
42+
}
43+
44+
@objc override static func requiresMainQueueSetup() -> Bool {
45+
return true
46+
}
47+
48+
override func supportedEvents() -> [String]! {
49+
return ["close", "completed", "error"]
50+
}
51+
52+
override func startObserving() {
53+
hasListeners = true
54+
}
55+
56+
override func stopObserving() {
57+
hasListeners = false
58+
}
59+
60+
func checkoutDidComplete() {
61+
if hasListeners {
62+
self.sendEvent(withName: "completed", body: nil)
63+
}
64+
}
65+
66+
func checkoutDidFail(error checkoutError: ShopifyCheckoutKit.CheckoutError) {
67+
if hasListeners {
68+
let errorInfo: [String: Any] = [
69+
"message": checkoutError.localizedDescription
70+
]
71+
self.sendEvent(withName: "error", body: errorInfo)
72+
}
73+
}
3374

3475
func checkoutDidCancel() {
3576
DispatchQueue.main.async {
36-
if let rootViewController = UIApplication.shared.delegate?.window??.rootViewController {
37-
rootViewController.dismiss(animated: true)
77+
if self.hasListeners {
78+
self.sendEvent(withName: "close", body: nil)
3879
}
80+
self.rootViewController?.dismiss(animated: true)
3981
}
4082
}
4183

42-
@objc func constantsToExport() -> [String: String]! {
84+
@objc override func constantsToExport() -> [AnyHashable: Any]! {
4385
return [
4486
"version": ShopifyCheckoutKit.version
4587
]
@@ -96,7 +138,7 @@ class RCTShopifyCheckoutKit: UIViewController, CheckoutDelegate {
96138

97139
if let backgroundColorHex = iosConfig?["backgroundColor"] as? String {
98140
ShopifyCheckoutKit.configuration.backgroundColor = UIColor(hex: backgroundColorHex)
99-
}
141+
}
100142
}
101143

102144
@objc func getConfig(_ resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) {
@@ -107,10 +149,6 @@ class RCTShopifyCheckoutKit: UIViewController, CheckoutDelegate {
107149

108150
resolve(config)
109151
}
110-
111-
@objc static func requiresMainQueueSetup() -> Bool {
112-
return true
113-
}
114152
}
115153

116154
extension UIColor {

modules/react-native-shopify-checkout-kit/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"scripts": {
1616
"clean": "rm -rf lib",
1717
"build": "bob build",
18+
"lint:swift": "./../../sample/ios/Pods/SwiftLint/swiftlint --strict --quiet ios",
1819
"lint": "yarn typecheck && eslint src",
1920
"prepack": "yarn clean && yarn build",
2021
"typecheck": "tsc --noEmit"

modules/react-native-shopify-checkout-kit/package.snap

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,25 @@
1111
"ios/ShopifyCheckoutKit-Bridging-Header.h",
1212
"ios/ShopifyCheckoutKit.mm",
1313
"ios/ShopifyCheckoutKit.swift",
14+
"lib/commonjs/context.js",
15+
"lib/commonjs/context.js.map",
1416
"lib/commonjs/index.d.js",
1517
"lib/commonjs/index.d.js.map",
1618
"lib/commonjs/index.js",
1719
"lib/commonjs/index.js.map",
20+
"lib/module/context.js",
21+
"lib/module/context.js.map",
1822
"lib/module/index.d.js",
1923
"lib/module/index.d.js.map",
2024
"lib/module/index.js",
2125
"lib/module/index.js.map",
26+
"lib/typescript/src/context.d.ts",
27+
"lib/typescript/src/context.d.ts.map",
2228
"lib/typescript/src/index.d.ts",
2329
"lib/typescript/src/index.d.ts.map",
2430
"package.json",
2531
"react-native-shopify-checkout-kit.podspec",
32+
"src/context.tsx",
2633
"src/index.d.ts",
2734
"src/index.tsx"
2835
]

0 commit comments

Comments
 (0)