Skip to content
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ yarn-error.log*
npm-debug.log
yarn-error.log
vendor/
turbo-android-dependencies.gradle
hotwire-native-android-dependencies.gradle

# macOS
.DS_Store
Expand Down
3 changes: 2 additions & 1 deletion examples/turbo-demo-expo-example/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"expo-build-properties",
{
"android": {
"minSdkVersion": 26
"minSdkVersion": 28,
"kotlinVersion": "1.9.25"
},
"ios": {
"deploymentTarget": "15.6"
Expand Down
4 changes: 2 additions & 2 deletions packages/turbo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ For Android you need to adjust your SDK version in your `build.gradle`.

> [_Turbo Documentation:_](https://github.com/hotwired/turbo-android#requirements)
>
> Android SDK 26+ is required as the minSdkVersion in your build.gradle.
> Android SDK 28+ is required as the minSdkVersion in your build.gradle.

For iOS, you need to set the deployment target to 14.0 or higher.
For iOS, you need to set the deployment target to 15.6 or higher.

## Example

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'

turbo_ios_source_files = "ios/vendor/turbo-ios/Source/**/*.{h,m,mm,swift}"
turbo_ios_resource_files = "ios/vendor/turbo-ios/Source/**/*.{js}"
hotwire_native_ios_source_files = "ios/vendor/hotwire-native-ios/Source/**/*.{h,m,mm,swift}"
hotwire_native_ios_resource_files = "ios/vendor/hotwire-native-ios/Source/**/*.{js}"

Pod::Spec.new do |s|
s.name = "RNTurbo"
s.name = "RNHotwireNative"
s.version = package["version"]
s.summary = package["description"]
s.homepage = package["homepage"]
Expand All @@ -17,8 +17,8 @@ Pod::Spec.new do |s|
s.platforms = { :ios => "14.0" }
s.source = { :git => "https://github.com/software-mansion-labs/react-native-turbo-demo.git", :tag => "#{s.version}" }

s.source_files = "ios/*.{h,m,mm,swift}", turbo_ios_source_files
s.resource = turbo_ios_resource_files
s.source_files = "ios/*.{h,m,mm,swift}", hotwire_native_ios_source_files
s.resource = hotwire_native_ios_resource_files

s.dependency "React-Core"

Expand Down
21 changes: 12 additions & 9 deletions packages/turbo/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
apply from: './turbo-android-dependencies.gradle'
apply from: './hotwire-native-android-dependencies.gradle'

buildscript {
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["TurboWebview_kotlinVersion"]
def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["HotwireWebview_kotlinVersion"]

repositories {
google()
Expand All @@ -28,11 +28,11 @@ if (isNewArchitectureEnabled()) {
}

def getExtOrDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["TurboWebview_" + name]
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["HotwireWebview_" + name]
}

def getExtOrIntegerDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["TurboWebview_" + name]).toInteger()
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["HotwireWebview_" + name]).toInteger()
}

def supportsNamespace() {
Expand All @@ -44,7 +44,7 @@ def supportsNamespace() {
return (major == 7 && minor >= 3) || major >= 8
}

def addTurboDependencies(Map<String, String> libraries) {
def addHotwireDependencies(Map<String, String> libraries) {
libraries.each { type, lib ->
if (type.startsWith("dep")) {
dependencies {
Expand All @@ -68,9 +68,10 @@ allprojects {
}
}


android {
if (supportsNamespace()) {
namespace "com.reactnativeturbowebview"
namespace "com.reactnativehotwirewebview"

sourceSets {
main {
Expand Down Expand Up @@ -113,6 +114,7 @@ repositories {
}
}


def kotlin_version = getExtOrDefault("kotlinVersion")

dependencies {
Expand All @@ -121,8 +123,9 @@ dependencies {
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.hotwire:turbo:local"
implementation "dev.hotwire:core:local"
implementation "dev.hotwire:navigation-fragments:local"
}

addTurboDependencies(deps)
addTurboDependencies(apis)
addHotwireDependencies(deps)
addHotwireDependencies(apis)
10 changes: 5 additions & 5 deletions packages/turbo/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
TurboWebview_kotlinVersion=1.9.10
TurboWebview_minSdkVersion=26
TurboWebview_targetSdkVersion=31
TurboWebview_compileSdkVersion=31
TurboWebview_ndkversion=21.4.7075529
HotwireWebview_kotlinVersion=1.9.25
HotwireWebview_minSdkVersion=26
HotwireWebview_targetSdkVersion=31
HotwireWebview_compileSdkVersion=31
HotwireWebview_ndkversion=21.4.7075529
2 changes: 1 addition & 1 deletion packages/turbo/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.reactnativeturbowebview">
package="com.reactnativehotwirewebview">
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.reactnativeturbowebview
package com.reactnativehotwirewebview

import android.Manifest
import android.app.Activity
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.reactnativeturbowebview
package com.reactnativehotwirewebview

import android.webkit.JavascriptInterface
import android.webkit.WebSettings
Expand All @@ -8,12 +8,17 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.whenStateAtLeast
import com.facebook.react.bridge.ReactApplicationContext
import dev.hotwire.turbo.errors.TurboVisitError
import dev.hotwire.turbo.session.TurboSession
import dev.hotwire.turbo.views.TurboWebView
import dev.hotwire.turbo.visit.TurboVisit
import dev.hotwire.turbo.visit.TurboVisitAction
import dev.hotwire.turbo.visit.TurboVisitOptions
import com.reactnativehotwirewebview.RNWebChromeClient
import com.reactnativehotwirewebview.SessionCallbackAdapter
import com.reactnativehotwirewebview.SessionSubscriber
import com.reactnativehotwirewebview.Utils
import dev.hotwire.core.config.Hotwire
import dev.hotwire.core.turbo.errors.VisitError
import dev.hotwire.core.turbo.session.Session
import dev.hotwire.core.turbo.webview.HotwireWebView
import dev.hotwire.core.turbo.visit.Visit
import dev.hotwire.core.turbo.visit.VisitAction
import dev.hotwire.core.turbo.visit.VisitOptions
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand All @@ -27,10 +32,10 @@ class RNSession(

var visitableView: SessionSubscriber? = null

private val turboSession: TurboSession = run {
private val turboSession: Session = run {
val activity = reactContext.currentActivity as AppCompatActivity
val webView = TurboWebView(activity, null)
val session = TurboSession(sessionHandle, activity, webView)
val webView = HotwireWebView(activity, null)
val session = Session(sessionHandle, activity, webView)

webView.settings.setJavaScriptEnabled(true)
webView.addJavascriptInterface(JavaScriptInterface(), "AndroidInterface")
Expand All @@ -39,38 +44,38 @@ class RNSession(
session.isRunningInAndroidNavigation = false
session
}
val webView: TurboWebView get() = turboSession.webView
val currentVisit: TurboVisit? get() = turboSession.currentVisit
val webView: HotwireWebView get() = turboSession.webView
val currentVisit: Visit? get() = turboSession.currentVisit

internal fun registerVisitableView(newView: SessionSubscriber) {
visitableView = newView
}

private fun setUserAgentString(webView: TurboWebView, applicationNameForUserAgent: String?) {
private fun setUserAgentString(webView: HotwireWebView, applicationNameForUserAgent: String?) {
var userAgentString = WebSettings.getDefaultUserAgent(webView.context)
if (applicationNameForUserAgent != null) {
userAgentString = "$userAgentString $applicationNameForUserAgent"
}
webView.settings.userAgentString = userAgentString
}

fun visit(url: String, restoreWithCachedSnapshot: Boolean, reload: Boolean, viewTreeLifecycleOwner: LifecycleOwner?, visitOptions: TurboVisitOptions?){
fun visit(url: String, restoreWithCachedSnapshot: Boolean, reload: Boolean, viewTreeLifecycleOwner: LifecycleOwner?, visitOptions: VisitOptions?){
val restore = restoreWithCachedSnapshot && !reload

val options = visitOptions ?: when {
restore -> TurboVisitOptions(action = TurboVisitAction.RESTORE)
else -> TurboVisitOptions()
restore -> VisitOptions(action = VisitAction.RESTORE)
else -> VisitOptions()
}

viewTreeLifecycleOwner?.lifecycleScope?.launch {
val snapshot = when (options.action) {
TurboVisitAction.ADVANCE -> fetchCachedSnapshot(url)
VisitAction.ADVANCE -> fetchCachedSnapshot(url)
else -> null
}

viewTreeLifecycleOwner.lifecycle.whenStateAtLeast(Lifecycle.State.STARTED) {
turboSession.visit(
TurboVisit(
Visit(
location = url,
destinationIdentifier = url.hashCode(),
restoreWithCachedSnapshot = restoreWithCachedSnapshot,
Expand All @@ -85,7 +90,7 @@ class RNSession(

private suspend fun fetchCachedSnapshot(url: String): String? {
return withContext(Dispatchers.IO) {
val response = turboSession.offlineRequestHandler?.getCachedSnapshot(
val response = Hotwire.config.offlineRequestHandler?.getCachedSnapshot(
url = url
)
response?.data?.use {
Expand Down Expand Up @@ -120,15 +125,15 @@ class RNSession(
}

fun clearSnapshotCache() {
// turbo-android doesn't expose a way to clear the snapshot cache, so we have to do it manually
// hotwire-native-android doesn't expose a way to clear the snapshot cache, so we have to do it manually
webView.post {
webView.evaluateJavascript("window.Turbo.session.clearCache();", null)
}
}

// region SessionCallbackAdapter

override fun onReceivedError(error: TurboVisitError) {
override fun onReceivedError(error: VisitError) {
visitableView?.onReceivedError(error)
}

Expand All @@ -152,7 +157,11 @@ class RNSession(
visitableView?.visitLocationStarted(location)
}

override fun visitProposedToLocation(location: String, options: TurboVisitOptions) {
override fun visitProposedToCrossOriginRedirect(location: String) {
visitableView?.visitProposedToCrossOriginRedirect(location)
}

override fun visitProposedToLocation(location: String, options: VisitOptions) {
visitableView?.visitProposedToLocation(location, options)
}

Expand All @@ -168,7 +177,7 @@ class RNSession(
visitableView?.didFinishFormSubmission(location)
}

override fun requestFailedWithError(visitHasCachedSnapshot: Boolean, error: TurboVisitError) {
override fun requestFailedWithError(visitHasCachedSnapshot: Boolean, error: VisitError) {
visitableView?.requestFailedWithError(visitHasCachedSnapshot, error)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.reactnativeturbowebview
package com.reactnativehotwirewebview

import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Promise;
import com.facebook.react.module.annotations.ReactModule
import com.reactnativehotwirewebview.RNSession

private const val NAME = "RNSessionManager"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.reactnativeturbowebview
package com.reactnativehotwirewebview

import android.webkit.WebViewClient
import dev.hotwire.turbo.errors.HttpError
import dev.hotwire.turbo.errors.LoadError
import dev.hotwire.turbo.errors.TurboVisitError
import dev.hotwire.turbo.errors.WebError
import dev.hotwire.turbo.errors.WebSslError
import dev.hotwire.core.turbo.errors.HttpError
import dev.hotwire.core.turbo.errors.LoadError
import dev.hotwire.core.turbo.errors.VisitError
import dev.hotwire.core.turbo.errors.WebError
import dev.hotwire.core.turbo.errors.WebSslError

enum class RNTurboError(val code: Int) {
HTTP(1),
Expand All @@ -16,7 +16,7 @@ enum class RNTurboError(val code: Int) {
UNKNOWN(-4);

companion object {
fun getErrorCode(error: TurboVisitError): Int {
fun getErrorCode(error: VisitError): Int {
val errorCode = when (error) {
is HttpError -> error.statusCode
is WebError -> error.errorCode
Expand All @@ -28,7 +28,7 @@ enum class RNTurboError(val code: Int) {
return when (errorCode) {
WebViewClient.ERROR_CONNECT -> 0
WebViewClient.ERROR_TIMEOUT -> -1
// turbo-android returns ERROR_UNKNOWN on SSL error and on turboFailedToLoad
// hotwire-native-android returns ERROR_UNKNOWN on SSL error and on turboFailedToLoad
WebViewClient.ERROR_UNKNOWN -> -3
else -> if (errorCode > 0) errorCode else -4
}
Expand All @@ -38,7 +38,7 @@ enum class RNTurboError(val code: Int) {
return values().firstOrNull { it.code == code } ?: UNKNOWN
}

fun errorDescription(error: TurboVisitError): String {
fun errorDescription(error: VisitError): String {
val errorCode = getErrorCode(error)
return when (fromCode(errorCode)) {
NETWORK_FAILURE -> "A network error occurred."
Expand Down
Loading