diff --git a/packages/turbo/.codegenconfig b/packages/turbo/.codegenconfig new file mode 100644 index 00000000..6295d035 --- /dev/null +++ b/packages/turbo/.codegenconfig @@ -0,0 +1,8 @@ +{ + "name": "RNHotwireNative", + "type": "modules", + "jsSrcsDir": "src", + "android": { + "javaPackageName": "com.reactnativehotwirewebview" + } +} diff --git a/packages/turbo/RNHotwireNative.podspec b/packages/turbo/RNHotwireNative.podspec index 8ff3329a..e01e224a 100644 --- a/packages/turbo/RNHotwireNative.podspec +++ b/packages/turbo/RNHotwireNative.podspec @@ -22,18 +22,18 @@ Pod::Spec.new do |s| s.dependency "React-Core" - # Don't install the dependencies when we run `pod install` in the old architecture. - if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then - s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" - s.pod_target_xcconfig = { - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", - "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" - } - - s.dependency "React-Codegen" - s.dependency "RCT-Folly" - s.dependency "RCTRequired" - s.dependency "RCTTypeSafety" - s.dependency "ReactCommon/turbomodule/core" - end + # New Architecture is required + s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" + s.pod_target_xcconfig = { + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" + } + + s.dependency "React-Codegen" + s.dependency "RCT-Folly" + s.dependency "RCTRequired" + s.dependency "RCTTypeSafety" + s.dependency "ReactCommon/turbomodule/core" + + install_modules_dependencies(s) end diff --git a/packages/turbo/android/build.gradle b/packages/turbo/android/build.gradle index 4b046db2..683d689a 100644 --- a/packages/turbo/android/build.gradle +++ b/packages/turbo/android/build.gradle @@ -16,16 +16,9 @@ buildscript { } } -def isNewArchitectureEnabled() { - return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" -} - apply plugin: "com.android.library" apply plugin: "kotlin-android" - -if (isNewArchitectureEnabled()) { - apply plugin: "com.facebook.react" -} +apply plugin: "com.facebook.react" def getExtOrDefault(name) { return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["HotwireWebview_" + name] @@ -104,6 +97,11 @@ android { } } +react { + libraryName = "RNHotwireNative" + codegenJavaPackageName = "com.reactnativehotwirewebview" +} + repositories { mavenCentral() google() diff --git a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNSession.kt b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNSession.kt index 859af6be..aaf5820f 100644 --- a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNSession.kt +++ b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNSession.kt @@ -106,9 +106,7 @@ class RNSession( inner class JavaScriptInterface { @JavascriptInterface fun postMessage(messageStr: String) { - // Android interface works only with primitive types, that's why we need to use JSON - val messageObj = Utils.convertJsonToMap(JSONObject(messageStr)) - visitableView?.handleMessage(messageObj) + visitableView?.handleMessage(messageStr) } } diff --git a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNSessionManager.kt b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNSessionManager.kt index f591d69a..81e473b6 100644 --- a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNSessionManager.kt +++ b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNSessionManager.kt @@ -33,13 +33,11 @@ class RNSessionManager(reactContext: ReactApplicationContext) : @ReactMethod fun getSessionHandles(promise: Promise) { - if(sessions.keys.isNotEmpty()) { - val sessionHandles = Arguments.createArray() - sessions.keys.forEach { - sessionHandles.pushString(it) - } - promise.resolve(sessionHandles) + val sessionHandles = Arguments.createArray() + sessions.keys.forEach { + sessionHandles.pushString(it) } + promise.resolve(sessionHandles) } @ReactMethod diff --git a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNVisitableView.kt b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNVisitableView.kt index 84413e48..64a298d9 100644 --- a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNVisitableView.kt +++ b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNVisitableView.kt @@ -296,7 +296,7 @@ class RNVisitableView(context: Context) : LinearLayout(context), SessionSubscrib } private fun sendEvent(event: RNVisitableViewEvent, params: WritableMap) { - reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, event.name, params) + reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(id, event.jsCallbackName, params) } // region SessionSubscriber @@ -305,8 +305,10 @@ class RNVisitableView(context: Context) : LinearLayout(context), SessionSubscrib webView?.evaluateJavascript(script, null) } - override fun handleMessage(message: WritableMap) { - sendEvent(RNVisitableViewEvent.MESSAGE, message) + override fun handleMessage(message: String) { + sendEvent(RNVisitableViewEvent.MESSAGE, Arguments.createMap().apply { + putString("message", message) + }) } override fun didOpenExternalUrl(url: String) { @@ -401,9 +403,8 @@ class RNVisitableView(context: Context) : LinearLayout(context), SessionSubscrib onAlertHandler = null } - fun sendConfirmResult(result: String) { - val confirmResult = result == "true" - onConfirmHandler?.invoke(confirmResult) + fun sendConfirmResult(result: Boolean) { + onConfirmHandler?.invoke(result) onConfirmHandler = null } diff --git a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNVisitableViewManager.kt b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNVisitableViewManager.kt index 33c809af..ae232248 100644 --- a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNVisitableViewManager.kt +++ b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNVisitableViewManager.kt @@ -23,14 +23,6 @@ enum class RNVisitableViewEvent(val jsCallbackName: String) { CONTENT_PROCESS_DID_TERMINATE("onContentProcessDidTerminate") } -enum class RNVisitableViewCommand(val jsCallbackName: String) { - INJECT_JAVASCRIPT("injectJavaScript"), - RELOAD_PAGE("reload"), - REFRESH_PAGE("refresh"), - SEND_ALERT_RESULT("sendAlertResult"), - SEND_CONFIRM_RESULT("sendConfirmResult") -} - private const val REACT_CLASS = "RNVisitableView" class RNVisitableViewManager( @@ -74,38 +66,29 @@ class RNVisitableViewManager( view.webViewDebuggingEnabled = webViewDebuggingEnabled } - override fun getCommandsMap(): MutableMap = RNVisitableViewCommand.values() - .associate { - it.jsCallbackName to it.ordinal - }.toMutableMap() - override fun receiveCommand(root: RNVisitableView, commandId: String, args: ReadableArray?) { - super.receiveCommand(root, commandId, args) - when (RNVisitableViewCommand.values()[commandId.toInt()]) { - RNVisitableViewCommand.INJECT_JAVASCRIPT -> { + when (commandId) { + "injectJavaScript" -> { args?.getString(0)?.let { root.injectJavaScript(it) } } - RNVisitableViewCommand.RELOAD_PAGE -> root.reload(true) - RNVisitableViewCommand.REFRESH_PAGE -> root.refresh() - RNVisitableViewCommand.SEND_ALERT_RESULT -> root.sendAlertResult() - RNVisitableViewCommand.SEND_CONFIRM_RESULT -> { - args?.getString(0)?.let { + "reload" -> root.reload(true) + "refresh" -> root.refresh() + "sendAlertResult" -> root.sendAlertResult() + "sendConfirmResult" -> { + args?.getBoolean(0)?.let { root.sendConfirmResult(it) } } } } - override fun getExportedCustomBubblingEventTypeConstants(): Map = - RNVisitableViewEvent.values().associate { - it.name to mapOf( - "phasedRegistrationNames" to mapOf( - "bubbled" to it.jsCallbackName - ) - ) + override fun getExportedCustomDirectEventTypeConstants(): Map { + return RNVisitableViewEvent.values().associate { + it.jsCallbackName to mapOf("registrationName" to it.jsCallbackName) } + } override fun createViewInstance(reactContext: ThemedReactContext) = RNVisitableView( @@ -114,10 +97,10 @@ class RNVisitableViewManager( override fun onDropViewInstance(view: RNVisitableView) { super.onDropViewInstance(view) - + // If the applicationContext is null, it can indicate that the application // has not been fully created yet or the application process is being terminated. - // In such cases, we stop the execution of the method. The similar check is done + // In such cases, we stop the execution of the method. The similar check is done // in the `onDropViewInstance` method of the `ViewManager`. if (callerContext.applicationContext == null) { return diff --git a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNWebChromeClient.kt b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNWebChromeClient.kt index cd32433d..9bd03454 100644 --- a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNWebChromeClient.kt +++ b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/RNWebChromeClient.kt @@ -14,7 +14,6 @@ import com.facebook.react.bridge.ActivityEventListener import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.WritableMap -import com.facebook.react.uimanager.events.RCTEventEmitter import com.reactnativehotwirewebview.RNSession diff --git a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/SessionSubscriber.kt b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/SessionSubscriber.kt index 9f0cdf40..61e16851 100644 --- a/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/SessionSubscriber.kt +++ b/packages/turbo/android/src/main/java/com/reactnativehotwirewebview/SessionSubscriber.kt @@ -4,7 +4,7 @@ import com.facebook.react.bridge.WritableMap interface SessionSubscriber: SessionCallbackAdapter { fun detachWebView() - fun handleMessage(message: WritableMap) + fun handleMessage(message: String) fun injectJavaScript(script: String) fun didOpenExternalUrl(url: String) fun didStartFormSubmission(url: String) diff --git a/packages/turbo/ios/RNSession.swift b/packages/turbo/ios/RNSession.swift index 789c8a98..69f4892d 100644 --- a/packages/turbo/ios/RNSession.swift +++ b/packages/turbo/ios/RNSession.swift @@ -121,7 +121,10 @@ extension RNSession: SessionDelegate { extension RNSession: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { - visitableView?.handleMessage(message: message) + guard let body = message.body as? String else { + return + } + visitableView?.handleMessage(message: body) } } diff --git a/packages/turbo/ios/RNSessionSubscriber.swift b/packages/turbo/ios/RNSessionSubscriber.swift index c37ce23a..703ced99 100644 --- a/packages/turbo/ios/RNSessionSubscriber.swift +++ b/packages/turbo/ios/RNSessionSubscriber.swift @@ -10,7 +10,7 @@ protocol RNSessionSubscriber { var id: UUID { get set } var controller: RNVisitableViewController? { get } - func handleMessage(message: WKScriptMessage) + func handleMessage(message: String) func didProposeVisit(proposal: VisitProposal) func didFailRequestForVisitable(visitable: Visitable, error: Error) func didOpenExternalUrl(url: URL) diff --git a/packages/turbo/ios/RNVisitableView.swift b/packages/turbo/ios/RNVisitableView.swift index 06175286..615aa42f 100644 --- a/packages/turbo/ios/RNVisitableView.swift +++ b/packages/turbo/ios/RNVisitableView.swift @@ -172,10 +172,8 @@ class RNVisitableView: UIView, RNSessionSubscriber { controller = nil } - public func handleMessage(message: WKScriptMessage) { - if let messageBody = message.body as? [AnyHashable : Any] { - onMessage?(messageBody) - } + public func handleMessage(message: String) { + onMessage?(["message": message]) } public func injectJavaScript(code: NSString) -> Void { @@ -187,9 +185,8 @@ class RNVisitableView: UIView, RNSessionSubscriber { self.onAlertHandler = nil } - public func sendConfirmResult(result: NSString) -> Void { - let confirmResult = result == "true" - self.onConfirmHandler?(confirmResult) + public func sendConfirmResult(result: Bool) -> Void { + self.onConfirmHandler?(result) self.onConfirmHandler = nil } @@ -305,7 +302,7 @@ extension RNVisitableView: RNVisitableViewControllerDelegate { // Ensure that all completion handlers have been called. // Otherwise, an NSInternalInconsistencyException might occur. sendAlertResult() - sendConfirmResult(result: "") + sendConfirmResult(result: false) } func visitableDidDisappear(visitable: Visitable) { diff --git a/packages/turbo/ios/RNVisitableViewManager.m b/packages/turbo/ios/RNVisitableViewManager.m index 52fd3efe..dd1414d9 100644 --- a/packages/turbo/ios/RNVisitableViewManager.m +++ b/packages/turbo/ios/RNVisitableViewManager.m @@ -40,7 +40,7 @@ @interface RCT_EXTERN_MODULE(RNVisitableViewManager, NSObject) RCT_EXTERN_METHOD(refresh: (nonnull NSNumber) node) RCT_EXTERN_METHOD(sendAlertResult: (nonnull NSNumber) node) RCT_EXTERN_METHOD(sendConfirmResult: (nonnull NSNumber) node - result: (nonnull NSString) code) + result: (BOOL) result) @end diff --git a/packages/turbo/ios/RNVisitableViewManager.swift b/packages/turbo/ios/RNVisitableViewManager.swift index 15ea2e2e..63a5f404 100644 --- a/packages/turbo/ios/RNVisitableViewManager.swift +++ b/packages/turbo/ios/RNVisitableViewManager.swift @@ -51,7 +51,7 @@ class RNVisitableViewManager: RCTViewManager { } @objc - func sendConfirmResult(_ node: NSNumber, result: NSString) { + func sendConfirmResult(_ node: NSNumber, result: Bool) { DispatchQueue.main.async { let component = self.bridge.uiManager.view(forReactTag: node) as! RNVisitableView component.sendConfirmResult(result: result) diff --git a/packages/turbo/package.json b/packages/turbo/package.json index d8034ce2..6cc32248 100644 --- a/packages/turbo/package.json +++ b/packages/turbo/package.json @@ -43,7 +43,7 @@ "@react-navigation/core": ">=6.0.0", "@react-navigation/native": ">=6.0.0", "react": "*", - "react-native": "*", + "react-native": ">=0.76.0", "react-native-safe-area-context": "5.2.0", "react-native-screens": "^4.10.0" }, @@ -53,6 +53,7 @@ "android", "ios", "scripts/postinstall.sh", + ".codegenconfig", "RNHotwireNative.podspec", "!**/__tests__", "!**/__fixtures__", diff --git a/packages/turbo/src/NativeRNSessionManager.ts b/packages/turbo/src/NativeRNSessionManager.ts new file mode 100644 index 00000000..2785431c --- /dev/null +++ b/packages/turbo/src/NativeRNSessionManager.ts @@ -0,0 +1,11 @@ +import type { TurboModule } from 'react-native'; +import { TurboModuleRegistry } from 'react-native'; + +export interface Spec extends TurboModule { + getSessionHandles(): Promise; + reloadSession(sessionHandle: string): Promise; + refreshSession(sessionHandle: string): Promise; + clearSessionSnapshotCache(sessionHandle: string): Promise; +} + +export default TurboModuleRegistry.getEnforcing('RNSessionManager'); diff --git a/packages/turbo/src/RNSessionManager.ts b/packages/turbo/src/RNSessionManager.ts index b7f38712..a01c3502 100644 --- a/packages/turbo/src/RNSessionManager.ts +++ b/packages/turbo/src/RNSessionManager.ts @@ -1,19 +1,8 @@ -import { NativeModules } from 'react-native'; - -interface IRNSessionManager { - getSessionHandles(): Promise; - reloadSession: (name: string) => Promise; - refreshSession: (name: string) => Promise; - clearSessionSnapshotCache: (name: string) => Promise; -} - -const { RNSessionManager } = NativeModules as { - RNSessionManager: IRNSessionManager; -}; +import NativeRNSessionManager from './NativeRNSessionManager'; export const { getSessionHandles, reloadSession, refreshSession, clearSessionSnapshotCache, -} = RNSessionManager; +} = NativeRNSessionManager; diff --git a/packages/turbo/src/RNVisitableView.ts b/packages/turbo/src/RNVisitableView.ts index 9e258e55..5a664555 100644 --- a/packages/turbo/src/RNVisitableView.ts +++ b/packages/turbo/src/RNVisitableView.ts @@ -1,14 +1,13 @@ import { NativeSyntheticEvent, - Platform, - requireNativeComponent, StyleProp, - UIManager, ViewStyle, Linking, - findNodeHandle, } from 'react-native'; +import RNVisitableViewNativeComponent, { + Commands, +} from './RNVisitableViewNativeComponent'; import type { AlertHandler, DispatchCommandTypes, @@ -55,19 +54,6 @@ export interface RNVisitableViewProps { style?: StyleProp; } -const LINKING_ERROR = - `The package react-native-turbo doesn't seem to be linked. Make sure: \n\n` + - Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) + - '- You rebuilt the app after installing the package\n' + - '- You are not using Expo Go\n'; - -function transformCommandToAcceptableType(command: number): number | string { - if (Platform.OS === 'ios') { - return command; - } - return command.toString(); -} - const initializeWebView = async (webViewRef: React.RefObject) => { const initializationPromise = new Promise((resolve) => setTimeout(resolve, 1) @@ -88,17 +74,7 @@ export async function dispatchCommand( command: DispatchCommandTypes, ...args: any[] ) { - const viewConfig = UIManager.getViewManagerConfig('RNVisitableView'); - - if (!viewConfig) { - throw new Error(LINKING_ERROR); - } - - const transformedCommand = transformCommandToAcceptableType( - viewConfig.Commands[command]! - ); - - if (transformedCommand === undefined) { + if (!ref?.current) { return; } @@ -106,13 +82,23 @@ export async function dispatchCommand( // after the native sessionHandle prop is set. await initializeWebView(ref); - const reactTag = findNodeHandle(ref.current); - - if (!reactTag) { - return; + switch (command) { + case 'injectJavaScript': + Commands.injectJavaScript(ref.current, args[0] as string); + break; + case 'reload': + Commands.reload(ref.current); + break; + case 'refresh': + Commands.refresh(ref.current); + break; + case 'sendAlertResult': + Commands.sendAlertResult(ref.current); + break; + case 'sendConfirmResult': + Commands.sendConfirmResult(ref.current, args[0] as boolean); + break; } - - UIManager.dispatchViewManagerCommand(reactTag, transformedCommand, args); } export async function openExternalURL({ @@ -127,7 +113,4 @@ export async function openExternalURL({ } } -const RNVisitableView = - requireNativeComponent('RNVisitableView'); - -export default RNVisitableView; +export default RNVisitableViewNativeComponent; diff --git a/packages/turbo/src/RNVisitableViewNativeComponent.ts b/packages/turbo/src/RNVisitableViewNativeComponent.ts new file mode 100644 index 00000000..66795f24 --- /dev/null +++ b/packages/turbo/src/RNVisitableViewNativeComponent.ts @@ -0,0 +1,117 @@ +import type { HostComponent, ViewProps } from 'react-native'; +import type { + DirectEventHandler, + Double, + Int32, + WithDefault, +} from 'react-native/Libraries/Types/CodegenTypes'; +import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; +import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; + +// Event payload types +export interface LoadEvent { + title: string; + url: string; +} + +export type MessageEvent = { + message: string; +}; + +export interface ErrorEvent { + url: string; + statusCode: Int32; + description?: string; +} + +export interface VisitProposal { + url: string; + action: 'advance' | 'replace' | 'restore'; +} + +export interface AlertHandler { + message: string; +} + +export interface OpenExternalUrlEvent { + url: string; +} + +export interface FormSubmissionEvent { + url: string; +} + +export interface ContentProcessDidTerminateEvent { + url: string; +} + +export interface ShowLoadingEvent {} + +export interface HideLoadingEvent {} + +export interface ContentInset { + top?: WithDefault; + left?: WithDefault; + bottom?: WithDefault; + right?: WithDefault; +} + +export interface ProgressViewOffset { + scale: boolean; + start: Double; + end: Double; +} + +export interface NativeProps extends ViewProps { + url: string; + sessionHandle?: string; + applicationNameForUserAgent?: string; + pullToRefreshEnabled?: WithDefault; + scrollEnabled?: WithDefault; + contentInset?: ContentInset; + progressViewOffset?: ProgressViewOffset; + refreshControlTopAnchor?: WithDefault; + webViewDebuggingEnabled?: WithDefault; + + // Events + onLoad?: DirectEventHandler; + onMessage?: DirectEventHandler; + onError?: DirectEventHandler; + onVisitProposal?: DirectEventHandler; + onWebAlert?: DirectEventHandler; + onWebConfirm?: DirectEventHandler; + onOpenExternalUrl?: DirectEventHandler; + onFormSubmissionStarted?: DirectEventHandler; + onFormSubmissionFinished?: DirectEventHandler; + onShowLoading?: DirectEventHandler; + onHideLoading?: DirectEventHandler; + onContentProcessDidTerminate?: DirectEventHandler; +} + +export type ComponentType = HostComponent; + +interface NativeCommands { + injectJavaScript: ( + viewRef: React.ElementRef, + script: string + ) => void; + reload: (viewRef: React.ElementRef) => void; + refresh: (viewRef: React.ElementRef) => void; + sendAlertResult: (viewRef: React.ElementRef) => void; + sendConfirmResult: ( + viewRef: React.ElementRef, + result: boolean + ) => void; +} + +export const Commands = codegenNativeCommands({ + supportedCommands: [ + 'injectJavaScript', + 'reload', + 'refresh', + 'sendAlertResult', + 'sendConfirmResult', + ], +}); + +export default codegenNativeComponent('RNVisitableView'); diff --git a/packages/turbo/src/hooks/useMessageQueue.ts b/packages/turbo/src/hooks/useMessageQueue.ts index b0bccea6..81341b89 100644 --- a/packages/turbo/src/hooks/useMessageQueue.ts +++ b/packages/turbo/src/hooks/useMessageQueue.ts @@ -29,8 +29,16 @@ export function useMessageQueue( const handleOnMessage = useCallback( (e: NativeSyntheticEvent) => { + let message: object; + try { + message = JSON.parse(e.nativeEvent.message); + } catch (error) { + console.error('Failed to parse message from native:', error); + return; + } + onMessageCallbacks.current.forEach((listener) => { - listener?.(e.nativeEvent); + listener?.(message); }); }, [] diff --git a/packages/turbo/src/hooks/useWebViewDialogs.ts b/packages/turbo/src/hooks/useWebViewDialogs.ts index 51f00aa5..09ea3be7 100644 --- a/packages/turbo/src/hooks/useWebViewDialogs.ts +++ b/packages/turbo/src/hooks/useWebViewDialogs.ts @@ -32,11 +32,7 @@ export function useWebViewDialogs( const handleConfirm = useCallback( ({ nativeEvent: { message } }: NativeSyntheticEvent) => { const dispatch = (value: boolean) => - dispatchCommand( - visitableViewRef, - 'sendConfirmResult', - value.toString() - ); + dispatchCommand(visitableViewRef, 'sendConfirmResult', value); if (onConfirm) { onConfirm(message, (value) => dispatch(value)); diff --git a/packages/turbo/src/types.ts b/packages/turbo/src/types.ts index e84aec4b..9ea021ee 100644 --- a/packages/turbo/src/types.ts +++ b/packages/turbo/src/types.ts @@ -22,7 +22,9 @@ export interface ContentProcessDidTerminateEvent { url: string; } -export type MessageEvent = object; +export type MessageEvent = { + message: string; +}; export interface AlertHandler { message: string; diff --git a/packages/turbo/src/utils/stradaBridgeScript.ts b/packages/turbo/src/utils/stradaBridgeScript.ts index 44269145..b2219368 100644 --- a/packages/turbo/src/utils/stradaBridgeScript.ts +++ b/packages/turbo/src/utils/stradaBridgeScript.ts @@ -79,9 +79,11 @@ export const stradaBridgeScript = ` // Native handler postMessage(message) { + const messageString = JSON.stringify(message); + ${Platform.select({ - android: 'AndroidInterface.postMessage(JSON.stringify(message))', - ios: 'webkit.messageHandlers.nativeApp.postMessage(message)', + android: 'AndroidInterface.postMessage(messageString)', + ios: 'webkit.messageHandlers.nativeApp.postMessage(messageString)', })} } diff --git a/yarn.lock b/yarn.lock index 19b8a5c7..9ce62d06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1532,7 +1532,20 @@ "@babel/parser" "^7.27.0" "@babel/types" "^7.27.0" -"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.8", "@babel/traverse@^7.27.0": +"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3": + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz" + integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.27.0" + "@babel/parser" "^7.27.0" + "@babel/template" "^7.27.0" + "@babel/types" "^7.27.0" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/traverse@^7.25.3", "@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.8", "@babel/traverse@^7.27.0": version "7.27.0" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz" integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA== @@ -8870,9 +8883,9 @@ react-native-safe-area-context@5.2.0: integrity sha512-QpcGA6MRKe8Zbpf1hirCBudNQYGsMv0n/CTTROMOFcXbqRUoEXLCsYxUmYKi7JJb3ziL2DbyzWXyH2/gw4Tkfw== react-native-screens@^4.10.0: - version "4.10.0" - resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.10.0.tgz#40634aead590c6b7034ded6a9f92465d1d611906" - integrity sha512-Tw21NGuXm3PbiUGtZd0AnXirUixaAbPXDjNR0baBH7/WJDaDTTELLcQ7QRXuqAWbmr/EVCrKj1348ei1KFIr8A== + version "4.18.0" + resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.18.0.tgz#ba5a951b3f145e3b773d201143c19e1b1c1337ff" + integrity sha512-mRTLWL7Uc1p/RFNveEIIrhP22oxHduC2ZnLr/2iHwBeYpGXR0rJZ7Bgc0ktxQSHRjWTPT70qc/7yd4r9960PBQ== dependencies: react-freeze "^1.0.0" warn-once "^0.1.0" @@ -9725,7 +9738,16 @@ strict-uri-encode@^2.0.0: resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz" integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9801,7 +9823,7 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9815,6 +9837,13 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" @@ -10590,7 +10619,7 @@ wonka@^6.3.2: resolved "https://registry.npmjs.org/wonka/-/wonka-6.3.4.tgz" integrity sha512-CjpbqNtBGNAeyNS/9W6q3kSkKE52+FjIj7AkFlLr11s/VWGUu6a2CdYSdGxocIhIVjaW/zchesBQUKPVU69Cqg== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10608,6 +10637,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz"