Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.0.0-beta.1 - Improved error handling + graceful recovery #86

Merged
merged 15 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- run: |
yarn module clean
yarn module build
./scripts/compare_snapshot
yarn compare-snapshot

lint:
name: Lint module + sample
Expand All @@ -52,6 +52,7 @@ jobs:
uses: ./.github/actions/setup

- run: |
yarn module build
yarn module lint
yarn sample lint

Expand Down
1 change: 1 addition & 0 deletions modules/@shopify/checkout-sheet-kit/.swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ disabled_rules:
- redundant_void_return
- todo
- switch_case_alignment
- function_body_length

opt_in_rules:
- array_init
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Pod::Spec.new do |s|
s.source_files = "ios/*.{h,m,mm,swift}"

s.dependency "React-Core"
s.dependency "ShopifyCheckoutSheetKit", "~> 2.0.1"
s.dependency "ShopifyCheckoutSheetKit", "~> 3.0.0-beta.5"

if fabric_enabled
install_modules_dependencies(s)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ ndkVersion=23.1.7779620
buildToolsVersion = "33.0.0"

# Version of Shopify Checkout SDK to use with React Native
SHOPIFY_CHECKOUT_SDK_VERSION=2.0.1
SHOPIFY_CHECKOUT_SDK_VERSION=3.0.0-beta.2
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@ of this software and associated documentation files (the "Software"), to deal
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.shopify.checkoutsheetkit.*;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.bridge.ReactApplicationContext;
import com.shopify.checkoutsheetkit.pixelevents.PixelEvent;
import com.shopify.checkoutsheetkit.lifecycleevents.CheckoutCompletedEvent;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class CustomCheckoutEventProcessor extends DefaultCheckoutEventProcessor {
private final ReactApplicationContext reactContext;
Expand Down Expand Up @@ -69,11 +72,42 @@ public void onWebPixelEvent(@NonNull PixelEvent event) {

@Override
public void onCheckoutFailed(CheckoutException checkoutError) {
WritableNativeMap error = new WritableNativeMap();
try {
String data = mapper.writeValueAsString(populateErrorDetails(checkoutError));
sendEventWithStringData("error", data);
} catch (IOException e) {
Log.e("ShopifyCheckoutSheetKit", "Error processing checkout failed event", e);
}
}

error.putString("message", checkoutError.getErrorDescription());
private Map<String, Object> populateErrorDetails(CheckoutException checkoutError) {
Map<String, Object> errorMap = new HashMap();
errorMap.put("__typename", getErrorTypeName(checkoutError));
errorMap.put("message", checkoutError.getErrorDescription());
errorMap.put("recoverable", checkoutError.isRecoverable());
errorMap.put("code", checkoutError.getErrorCode());

sendEvent("error", error);
if (checkoutError instanceof HttpException) {
errorMap.put("statusCode", ((HttpException) checkoutError).getStatusCode());
}

return errorMap;
}

private String getErrorTypeName(CheckoutException error) {
if (error instanceof CheckoutExpiredException) {
return "CheckoutExpiredError";
} else if (error instanceof ClientException) {
return "CheckoutClientError";
} else if (error instanceof HttpException) {
return "CheckoutHTTPError";
} else if (error instanceof ConfigurationException) {
return "ConfigurationError";
} else if (error instanceof CheckoutSheetKitException) {
return "InternalError";
} else {
return "UnknownError";
}
}

@Override
Expand All @@ -89,7 +123,7 @@ private void sendEvent(String eventName, @Nullable WritableNativeMap params) {

private void sendEventWithStringData(String name, String data) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(name, data);
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(name, data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,17 @@ public class ShopifyCheckoutSheetKitModule extends ReactContextBaseJavaModule {

private final ReactApplicationContext reactContext;

private CheckoutSheetKitDialog checkoutSheet;

public ShopifyCheckoutSheetKitModule(ReactApplicationContext reactContext) {
super(reactContext);

this.reactContext = reactContext;

ShopifyCheckoutSheetKit.configure(configuration -> {
configuration.setPlatform(Platform.REACT_NATIVE);
checkoutConfig = configuration;
});
}

@NonNull
Expand Down Expand Up @@ -82,12 +89,20 @@ public void present(String checkoutURL) {
DefaultCheckoutEventProcessor checkoutEventProcessor = new CustomCheckoutEventProcessor(currentActivity,
this.reactContext);
currentActivity.runOnUiThread(() -> {
ShopifyCheckoutSheetKit.present(checkoutURL, (ComponentActivity) currentActivity,
checkoutSheet = ShopifyCheckoutSheetKit.present(checkoutURL, (ComponentActivity) currentActivity,
checkoutEventProcessor);
});
}
}

@ReactMethod
public void dismiss() {
if (checkoutSheet != null) {
checkoutSheet.dismiss();
checkoutSheet = null;
}
}

@ReactMethod
public void preload(String checkoutURL) {
Activity currentActivity = getCurrentActivity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ @interface RCT_EXTERN_MODULE(RCTShopifyCheckoutSheetKit, NSObject)
/// Preload checkout
RCT_EXTERN_METHOD(preload:(NSString *)checkoutURLString);

/// Dismiss checkout
RCT_EXTERN_METHOD(dismiss);

/// Set configuration for checkout
RCT_EXTERN_METHOD(setConfig:(NSDictionary *)configuration);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
return true
}

override init() {
ShopifyCheckoutSheetKit.configure {
$0.platform = ShopifyCheckoutSheetKit.Platform.reactNative
}

super.init()
}

override func supportedEvents() -> [String]! {
return ["close", "completed", "error", "pixel"]
}
Expand All @@ -58,12 +66,60 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
}
}

func checkoutDidFail(error checkoutError: ShopifyCheckoutSheetKit.CheckoutError) {
if hasListeners {
let errorInfo: [String: Any] = [
"message": checkoutError.localizedDescription
]
self.sendEvent(withName: "error", body: errorInfo)
func shouldRecoverFromError(error: CheckoutError) -> Bool {
return error.isRecoverable
}

func checkoutDidFail(error: ShopifyCheckoutSheetKit.CheckoutError) {
guard hasListeners else { return }

if case .checkoutExpired(let message, let code, let recoverable) = error {
self.sendEvent(withName: "error", body: [
"__typename": "CheckoutExpiredError",
"message": message,
"code": code.rawValue,
"recoverable": recoverable
])
} else if case .checkoutUnavailable(let message, let code, let recoverable) = error {
switch code {
case .clientError(let clientErrorCode):
self.sendEvent(withName: "error", body: [
"__typename": "CheckoutClientError",
"message": message,
"code": clientErrorCode.rawValue,
"recoverable": recoverable
])
case .httpError(let statusCode):
self.sendEvent(withName: "error", body: [
"__typename": "CheckoutHTTPError",
"message": message,
"code": "http_error",
"statusCode": statusCode,
"recoverable": recoverable
])
}
} else if case .configurationError(let message, let code, let recoverable) = error {
self.sendEvent(withName: "error", body: [
"__typename": "ConfigurationError",
"message": message,
"code": code.rawValue,
"recoverable": recoverable
])
} else if case .sdkError(let underlying, let recoverable) = error {
var errorMessage = "\(underlying.localizedDescription)"
self.sendEvent(withName: "error", body: [
"__typename": "InternalError",
"code": "unknown",
"message": errorMessage,
"recoverable": recoverable
])
} else {
self.sendEvent(withName: "error", body: [
"__typename": "UnknownError",
"code": "unknown",
"message": error.localizedDescription,
"recoverable": error.isRecoverable
])
}
}

Expand Down Expand Up @@ -120,6 +176,12 @@ class RCTShopifyCheckoutSheetKit: RCTEventEmitter, CheckoutDelegate {
return controller
}

@objc func dismiss() {
DispatchQueue.main.async {
self.checkoutSheet?.dismiss(animated: true)
}
}

@objc func present(_ checkoutURL: String) {
DispatchQueue.main.async {
if let url = URL(string: checkoutURL), let viewController = self.getCurrentViewController() {
Expand Down
2 changes: 1 addition & 1 deletion modules/@shopify/checkout-sheet-kit/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@shopify/checkout-sheet-kit",
"license": "MIT",
"version": "2.0.1",
"version": "3.0.0-beta.1",
"main": "lib/commonjs/index.js",
"types": "src/index.ts",
"source": "src/index.ts",
Expand Down
5 changes: 5 additions & 0 deletions modules/@shopify/checkout-sheet-kit/package.snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"ios/ShopifyCheckoutSheetKit.swift",
"lib/commonjs/context.js",
"lib/commonjs/context.js.map",
"lib/commonjs/errors.d.js",
"lib/commonjs/errors.d.js.map",
"lib/commonjs/events.d.js",
"lib/commonjs/events.d.js.map",
"lib/commonjs/index.d.js",
Expand All @@ -23,6 +25,8 @@
"lib/commonjs/pixels.d.js.map",
"lib/module/context.js",
"lib/module/context.js.map",
"lib/module/errors.d.js",
"lib/module/errors.d.js.map",
"lib/module/events.d.js",
"lib/module/events.d.js.map",
"lib/module/index.d.js",
Expand All @@ -37,6 +41,7 @@
"lib/typescript/src/index.d.ts.map",
"package.json",
"src/context.tsx",
"src/errors.d.ts",
"src/events.d.ts",
"src/index.d.ts",
"src/index.ts",
Expand Down
8 changes: 8 additions & 0 deletions modules/@shopify/checkout-sheet-kit/src/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ interface Context {
removeEventListeners: RemoveEventListeners;
preload: (checkoutUrl: string) => void;
present: (checkoutUrl: string) => void;
dismiss: () => void;
version: Maybe<string>;
}

Expand All @@ -52,6 +53,7 @@ const ShopifyCheckoutSheetContext = React.createContext<Context>({
getConfig: async () => undefined,
preload: noop,
present: noop,
dismiss: noop,
version: undefined,
});

Expand Down Expand Up @@ -92,6 +94,10 @@ export function ShopifyCheckoutSheetProvider({
}
}, []);

const dismiss = useCallback(() => {
instance.current?.dismiss();
}, []);

const setConfig = useCallback((config: Configuration) => {
instance.current?.setConfig(config);
}, []);
Expand All @@ -103,6 +109,7 @@ export function ShopifyCheckoutSheetProvider({
const context = useMemo((): Context => {
return {
addEventListener,
dismiss,
setConfig,
getConfig,
preload,
Expand All @@ -112,6 +119,7 @@ export function ShopifyCheckoutSheetProvider({
};
}, [
addEventListener,
dismiss,
removeEventListeners,
getConfig,
setConfig,
Expand Down
Loading
Loading