Skip to content
Open
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
8 changes: 8 additions & 0 deletions packages/turbo/.codegenconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "RNHotwireNative",
"type": "modules",
"jsSrcsDir": "src",
"android": {
"javaPackageName": "com.reactnativehotwirewebview"
}
}
28 changes: 14 additions & 14 deletions packages/turbo/RNHotwireNative.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -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
14 changes: 6 additions & 8 deletions packages/turbo/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -104,6 +97,11 @@ android {
}
}

react {
libraryName = "RNHotwireNative"
codegenJavaPackageName = "com.reactnativehotwirewebview"
}

repositories {
mavenCentral()
google()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -74,38 +66,29 @@ class RNVisitableViewManager(
view.webViewDebuggingEnabled = webViewDebuggingEnabled
}

override fun getCommandsMap(): MutableMap<String, Int> = 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<String, Any> =
RNVisitableViewEvent.values().associate {
it.name to mapOf(
"phasedRegistrationNames" to mapOf(
"bubbled" to it.jsCallbackName
)
)
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
return RNVisitableViewEvent.values().associate {
it.jsCallbackName to mapOf("registrationName" to it.jsCallbackName)
}
}

override fun createViewInstance(reactContext: ThemedReactContext) =
RNVisitableView(
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion packages/turbo/ios/RNSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

}
Expand Down
2 changes: 1 addition & 1 deletion packages/turbo/ios/RNSessionSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 5 additions & 8 deletions packages/turbo/ios/RNVisitableView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}

Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion packages/turbo/ios/RNVisitableViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/turbo/ios/RNVisitableViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion packages/turbo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand All @@ -53,6 +53,7 @@
"android",
"ios",
"scripts/postinstall.sh",
".codegenconfig",
"RNHotwireNative.podspec",
"!**/__tests__",
"!**/__fixtures__",
Expand Down
11 changes: 11 additions & 0 deletions packages/turbo/src/NativeRNSessionManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
getSessionHandles(): Promise<string[]>;
reloadSession(sessionHandle: string): Promise<void>;
refreshSession(sessionHandle: string): Promise<void>;
clearSessionSnapshotCache(sessionHandle: string): Promise<void>;
}

export default TurboModuleRegistry.getEnforcing<Spec>('RNSessionManager');
15 changes: 2 additions & 13 deletions packages/turbo/src/RNSessionManager.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
import { NativeModules } from 'react-native';

interface IRNSessionManager {
getSessionHandles(): Promise<string[]>;
reloadSession: (name: string) => Promise<void>;
refreshSession: (name: string) => Promise<void>;
clearSessionSnapshotCache: (name: string) => Promise<void>;
}

const { RNSessionManager } = NativeModules as {
RNSessionManager: IRNSessionManager;
};
import NativeRNSessionManager from './NativeRNSessionManager';

export const {
getSessionHandles,
reloadSession,
refreshSession,
clearSessionSnapshotCache,
} = RNSessionManager;
} = NativeRNSessionManager;
Loading