diff --git a/example/screens/PresentActionScreen.tsx b/example/screens/PresentActionScreen.tsx index 36310d9..b3713de 100644 --- a/example/screens/PresentActionScreen.tsx +++ b/example/screens/PresentActionScreen.tsx @@ -8,6 +8,7 @@ import { Alert, TextInput, Platform, + Switch, } from 'react-native'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import type { RootStackParamList } from '../App'; @@ -31,6 +32,7 @@ const PresentActionScreen: React.FC = () => { const [isLoading, setIsLoading] = useState(false); const [presentationStyleIOS, setPresentationStyleIOS] = useState(PresentationStyles.formSheet); + const [debugEnabled, setDebugEnabled] = useState(false); const environmentOptions = [ { key: 'sandbox' as EnvironmentOption, label: 'Sandbox' }, @@ -85,6 +87,7 @@ const PresentActionScreen: React.FC = () => { id: actionId.trim(), environment: getEnvironment(), presentationStyleIOS, + setDebug: debugEnabled, onLaunch: () => { console.log('Action launched'); setIsLoading(false); @@ -172,6 +175,22 @@ const PresentActionScreen: React.FC = () => { + + Debug Mode + + + Off + + On + + + + How it works diff --git a/example/screens/TransactScreen.tsx b/example/screens/TransactScreen.tsx index 220cedd..7b2d282 100644 --- a/example/screens/TransactScreen.tsx +++ b/example/screens/TransactScreen.tsx @@ -46,6 +46,7 @@ const TransactScreen: React.FC = () => { const [deeplinkStep, setDeeplinkStep] = useState( Step.LOGIN_COMPANY ); + const [debugEnabled, setDebugEnabled] = useState(false); const products = [ { key: Product.DEPOSIT, label: 'Deposit' }, @@ -142,6 +143,7 @@ const TransactScreen: React.FC = () => { config, environment: getEnvironment(), presentationStyleIOS, + setDebug: debugEnabled, onInteraction: (interaction: any) => { console.log('Interaction:', interaction); }, @@ -239,6 +241,20 @@ const TransactScreen: React.FC = () => { /> + + + Debug Mode + + Off + + On + + diff --git a/ios/TransactReactNative.m b/ios/TransactReactNative.m index 4bbd2a5..8a24cc2 100644 --- a/ios/TransactReactNative.m +++ b/ios/TransactReactNative.m @@ -7,12 +7,14 @@ @interface RCT_EXTERN_MODULE(TransactReactNative, RCTEventEmitter) RCT_EXTERN_METHOD(presentTransact:(NSDictionary *)config environment:(NSDictionary *)environment presentationStyle:(nullable NSString *)presentationStyle + setDebug:(nullable NSNumber *)setDebug withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(presentAction:(NSString *)id environment:(NSDictionary *)environment presentationStyle:(nullable NSString *)presentationStyle + setDebug:(nullable NSNumber *)setDebug withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) diff --git a/ios/TransactReactNative.swift b/ios/TransactReactNative.swift index 8818942..a4df37d 100644 --- a/ios/TransactReactNative.swift +++ b/ios/TransactReactNative.swift @@ -37,28 +37,34 @@ class TransactReactNative: RCTEventEmitter { } } - @objc(presentTransact:environment:presentationStyle:withResolver:withRejecter:) - func presentTransact(config: [String: Any], environment: [String: Any], presentationStyle: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { - DispatchQueue.main.async { + @objc(presentTransact:environment:presentationStyle:setDebug:withResolver:withRejecter:) + func presentTransact(config: [String: Any], environment: [String: Any], presentationStyle: String?, setDebug: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { + let debugEnabled = setDebug?.boolValue ?? false + + Task { @MainActor in + await Atomic.setDebug(isEnabled: debugEnabled, forwardLogs: { logMessage in + self.sendEvent(withName: "onDebugLog", body: ["message": logMessage]) + }) + guard let source = RCTPresentedViewController() else { return } - + let decoder = JSONDecoder() let parsedEnvironment = self.parseEnvironment(environment) - + do { var json = config let parsedPresentationStyle = self.parsePresentationStyle(presentationStyle) - + if var platform = AtomicConfig.Platform().encode() as? [String: Any] { platform["sdkVersion"] = platform["sdkVersion"] as! String + "-react" json["platform"] = platform } guard let data = try? JSONSerialization.data(withJSONObject: json, options: []) else { return } - + let config = try decoder.decode(AtomicConfig.self, from: data) - + Atomic.presentTransact( from: source, config: config, environment: parsedEnvironment, presentationStyle: parsedPresentationStyle, onInteraction: { interaction in @@ -67,7 +73,7 @@ class TransactReactNative: RCTEventEmitter { onDataRequest: { request async -> TransactDataResponse? in // Create a task to handle the async request to React Native return await withCheckedContinuation { continuation in - // Store the completion handler + // Store the completion handler self.dataResponseHandler = { responseData in if let responseDict = responseData as? [String: Any] { // The SDK expects the response data to be passed directly @@ -87,7 +93,7 @@ class TransactReactNative: RCTEventEmitter { continuation.resume(returning: nil) } } - + // Send event with request data to React Native self.sendEvent(withName: "onDataRequest", body: request.data) } @@ -130,14 +136,20 @@ class TransactReactNative: RCTEventEmitter { } } - @objc(presentAction:environment:presentationStyle:withResolver:withRejecter:) - func presentAction(id: String, environment: [String: Any], presentationStyle: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { - DispatchQueue.main.async { + @objc(presentAction:environment:presentationStyle:setDebug:withResolver:withRejecter:) + func presentAction(id: String, environment: [String: Any], presentationStyle: String?, setDebug: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { + let debugEnabled = setDebug?.boolValue ?? false + + Task { @MainActor in + await Atomic.setDebug(isEnabled: debugEnabled, forwardLogs: { logMessage in + self.sendEvent(withName: "onDebugLog", body: ["message": logMessage]) + }) + guard let source = RCTPresentedViewController() else { return } - + let parsedEnvironment = self.parseEnvironment(environment) let parsedPresentationStyle = self.parsePresentationStyle(presentationStyle) - + Atomic.presentAction( from: source, id: id, @@ -181,7 +193,7 @@ class TransactReactNative: RCTEventEmitter { } @objc override func supportedEvents() -> [String] { - return ["onInteraction", "onDataRequest", "onLaunch", "onCompletion", "onAuthStatusUpdate", "onTaskStatusUpdate"] + return ["onInteraction", "onDataRequest", "onLaunch", "onCompletion", "onAuthStatusUpdate", "onTaskStatusUpdate", "onDebugLog"] } @objc override static func requiresMainQueueSetup() -> Bool { diff --git a/src/index.tsx b/src/index.tsx index 2a01e1c..5b19f84 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -102,6 +102,7 @@ export const Atomic = { onAuthStatusUpdate, onTaskStatusUpdate, presentationStyleIOS, + setDebug, }: { config: Config; environment?: CONSTANTS.TransactEnvironment; @@ -112,6 +113,7 @@ export const Atomic = { onFinish?: Function; onClose?: Function; presentationStyleIOS?: PresentationStyleIOS; + setDebug?: boolean; }): void { config.language = config.language || 'en'; config.theme = config.theme || {}; @@ -131,6 +133,7 @@ export const Atomic = { onAuthStatusUpdate, onTaskStatusUpdate, presentationStyleIOS, + setDebug, }; switch (Platform.OS) { @@ -153,6 +156,7 @@ export const Atomic = { onClose, onAuthStatusUpdate, onTaskStatusUpdate, + setDebug, }: { id: String; environment?: CONSTANTS.TransactEnvironment; @@ -162,6 +166,7 @@ export const Atomic = { onClose?: Function; onAuthStatusUpdate?: Function; onTaskStatusUpdate?: Function; + setDebug?: boolean; }): void { const args = { TransactReactNative, @@ -173,6 +178,7 @@ export const Atomic = { onClose, onAuthStatusUpdate, onTaskStatusUpdate, + setDebug, }; switch (Platform.OS) { diff --git a/src/ios.tsx b/src/ios.tsx index 48563e9..de0ccf5 100644 --- a/src/ios.tsx +++ b/src/ios.tsx @@ -13,6 +13,7 @@ export const AtomicIOS = { onAuthStatusUpdate, onTaskStatusUpdate, presentationStyleIOS, + setDebug, }: { TransactReactNative: any; config: any; @@ -24,6 +25,7 @@ export const AtomicIOS = { onAuthStatusUpdate?: Function; onTaskStatusUpdate?: Function; presentationStyleIOS?: CONSTANTS.PresentationStyleIOS; + setDebug?: boolean; }): void { const TransactReactNativeEvents = new NativeEventEmitter( TransactReactNative @@ -31,13 +33,22 @@ export const AtomicIOS = { let onInteractionListener: any; let onDataRequestListener: any; let onAuthStatusUpdateListener: any; + let onDebugLogListener: any; const removeListeners = () => { if (onInteractionListener) onInteractionListener.remove(); if (onDataRequestListener) onDataRequestListener.remove(); if (onAuthStatusUpdateListener) onAuthStatusUpdateListener.remove(); + if (onDebugLogListener) onDebugLogListener.remove(); }; + onDebugLogListener = TransactReactNativeEvents.addListener( + 'onDebugLog', + (log) => { + console.debug('[TransactNative]', log.message); + } + ); + if (onInteraction) { onInteractionListener = TransactReactNativeEvents.addListener( 'onInteraction', @@ -84,7 +95,8 @@ export const AtomicIOS = { TransactReactNative.presentTransact( config, environment, - presentationStyleIOS + presentationStyleIOS, + setDebug ).then((event: any) => { if (event.finished && onFinish) { removeListeners(); @@ -105,6 +117,7 @@ export const AtomicIOS = { onClose, onAuthStatusUpdate, onTaskStatusUpdate, + setDebug, }: { TransactReactNative: any; id: String; @@ -115,18 +128,28 @@ export const AtomicIOS = { onClose?: Function; onAuthStatusUpdate?: Function; onTaskStatusUpdate?: Function; + setDebug?: boolean; }): void { const TransactReactNativeEvents = new NativeEventEmitter( TransactReactNative ); let onLaunchListener: any; let onAuthStatusUpdateListener: any; + let onDebugLogListener: any; const removeListeners = () => { if (onLaunchListener) onLaunchListener.remove(); if (onAuthStatusUpdateListener) onAuthStatusUpdateListener.remove(); + if (onDebugLogListener) onDebugLogListener.remove(); }; + onDebugLogListener = TransactReactNativeEvents.addListener( + 'onDebugLog', + (log) => { + console.debug('[TransactNative]', log.message); + } + ); + if (onLaunch) { onLaunchListener = TransactReactNativeEvents.addListener('onLaunch', () => onLaunch() @@ -150,7 +173,8 @@ export const AtomicIOS = { TransactReactNative.presentAction( id, environment, - presentationStyleIOS + presentationStyleIOS, + setDebug ).then((event: any) => { if (event.finished && onFinish) { removeListeners();