Git Product home page Git Product logo

react-native-turbo-demo's Introduction

React Native Turbo

React Native Turbo is an open source library that can bring your turbo-enabled web application into the React Native world. It allows you to render web pages as if they were native iOS or Android screens. It provides native navigation animations resulting in mobile-like user experience. You can easily move your entire web app, or embed a few screens that pretend to be native, without reimplementing them in React Native.

NPM version Licence MIT Github issues Github activity

screen_rec.mov

An example app adapted from turbo-native-demo using react-native-web-screen


Documentation 📖

Check out the full documentation page.

This an early stage version of the library, so issues may appear. You can help us by reporting them in the issues section.

Contributing 💪

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT

react-native-turbo-demo's People

Contributors

aleqsio avatar dependabot[bot] avatar fryzu avatar lukmccall avatar mironiasty avatar pfeiffer avatar pklatka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-native-turbo-demo's Issues

Communication between React Native and WebView's content

Turbo iOS and Turbo Android supports communication between native app and JavaScript (the visited page). This is very useful for eg. programatically doing something natively, triggered from the website or for programatically doing something on the website, triggered natively.

Examples of this could be prompting for Push Permissions and upon acceptance returning the push token back to the visited website, or reloading the web page when a native push notification is received.

It would be very useful to be able to register a handler for messages being passed from the website to native, eg:

// In React Native
<VisitableView onMessage={alert} />

// In website:
website.messageHandlers["someName"].postMessage("Hi from website!")

And vice-versa being able to send messages back to the web page:

// In React Native
visitableViewRef.injectJavaScript("alert('Hello from native!');")

Simply having these low-level methods of passing data back and forth and could be extended with eg. JSON serialization, async callbacks etc. with a bit of JS code on React Native side and website.

Prior art:

react-native-webview: https://github.com/react-native-webview/react-native-webview/blob/master/docs/Guide.md#communicating-between-js-and-native
turbo-ios: https://github.com/hotwired/turbo-ios/blob/main/Docs/Advanced.md#native---javascript-integration
turbo-android: https://github.com/hotwired/turbo-android/blob/main/docs/ADVANCED-OPTIONS.md#native---javascript-integration

iOS: Crash when an alert/confirm is showing in a non-topmost WebView

We added support for showing alert(..) and confirm(..) in #76. During my testing, I've seen a a consistent reproducible crash when showing alerts in more than one webview:

NSInternalInconsistencyException: Completion handler passed to -[RNTurbo.TurboUIDelegate webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:] was not called
  0   CoreFoundation                      0x000000010dc1e330 __exceptionPreprocess + 172
  1   libobjc.A.dylib                     0x00000001075dd274 objc_exception_throw + 56
  2   CoreFoundation                      0x000000010dc1e240 -[NSException initWithCoder:] + 0
  3   WebKit                              0x0000000112f3e6e8 _ZN6WebKit28CompletionHandlerCallCheckerD2Ev + 140
  4   WebKit                              0x0000000112fb4b6c _ZNK3WTF20ThreadSafeRefCountedIN6WebKit28CompletionHandlerCallCheckerELNS_17DestructionThreadE0EE5derefEv + 40
  5   WebKit                              0x0000000113134778 _ZZN3WTF8BlockPtrIFvvEE12fromCallableIZN6WebKit10UIDelegate8UIClient18runJavaScriptAlertERNS4_12WebPageProxyERKNS_6StringEPNS4_13WebFrameProxyEONS4_13FrameInfoDataEONS_8FunctionIS1_EEE3$_4EES2_T_ENUlPKvE_8__invokeESM_ + 32

Reproducable case:

  1. Have a Tab navigator with eg. 3 tabs
  2. Add an alert(..) on the web pages shown by the tabs (or use the same URL for multiple tab screens)
  3. Navigate to a tab, but before the alert(..) is shown, navigate back to the previous one
  4. App crashes as the now-hidden tab is loaded

The same thing happens for confirm(..). The tabs use different session handles.

Support Android file uploads

For iOS, input[type=file] inputs are handled transparently by WKWebView, showing a native file picker or camera capture. This is not the case for Android, where we need to setup a onShowFileChooser method for the WebView in order to handle file inputs.

Turbo Android does this and has delegates setup to show a native file picker or capture from camera and return a file pointer to the WebView.

This is for some reason not working properly at the moment in this library.

Path configuration support

Hey there!

Are there any plans to support something like Turbo iOS or Turbo Android path configuration?

Basically, to control with regular expressions the presentation and some additional settings, like pull to refresh, modal flows, etc.

Thanks!

Upgrading to latest react-native 0.73

I can tell you guys are updating this project. It's currently in a state where it won't build and run properly, but if I go back to the latest tagged commit then things work.

While upgrading the example app, are you planning to upgrade its react native version to the latest 0.73 or is there an inherent incompatiblity? I pulled your repo and am trying to do it myself but I've not been able it.

Navigation to complex routes

I want to create an Stack app that allows me to navigate through an authentication process like

import React from 'react';
import {
  NavigationContainer,
  useNavigationContainerRef,
} from '@react-navigation/native';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import {buildWebScreen} from 'react-native-web-screen';

const Stack = createNativeStackNavigator();

const webScreenConfig = {
  baseURL: 'https://lifi.mx/beta',
  routes: {
    Initial: {
      urlPattern: '',
      title: 'Sign In',
    },
    SignUp: {
      urlPattern: 'register/sign_up',
      title: 'sign up',
    },
    Listing: {
      urlPattern: 'listing',
      title: 'sign up',
    },
    Likes: {
      urlPattern: 'likes',
      title: 'product',
    },
  },
};

const webScreens = buildWebScreen(webScreenConfig);

function App() {
  const navigation = useNavigationContainerRef();

  return (
    <SafeAreaProvider>
      <NavigationContainer linking={webScreens.linking} ref={navigation}>
        <Stack.Navigator>
          <Stack.Screen {...webScreens.screens.Initial} />
          <Stack.Screen {...webScreens.screens.SignUp} />
          <Stack.Screen {...webScreens.screens.Listing} />
          <Stack.Screen {...webScreens.screens.Likes} />
        </Stack.Navigator>
      </NavigationContainer>
    </SafeAreaProvider>
  );
}

export default App;

Navigation will be from https://lifi.mx/beta to https://lifi.mx/register/sign_up

But I'm getting the following error:

Screenshot 2023-08-01 at 20 22 52

This will enable in the future a stack navigation like /listing to /product_PeZNpaW5Tw53XAWv

Does anyone has an example of how to achieve this?

Possible issues while getting this project working

@pfeiffer Maybe I should open separate issues for these, but while I have your attention let me alert you to a few obstacles I encountered while getting everything working on RN 0.73. Feel free to close this out if it's not helpful:

  1. The published NPM modules you have are out of date with the repo (https://www.npmjs.com/package/react-native-turbo). Usually, I just point directly to the repo but I could not get my package.json file to point directly to the https://github.com/software-mansion-labs/react-native-turbo-demo, I suspect it's because of how it's setup as a mono repo. Ultimately, I had to fork and publish my own so that I could include the latest code from main in my package.json (https://www.npmjs.com/package/krschacht-react-native-turbo).

  2. I kept encountering a build issue and eventually resolved it by reverting "Enable WebView debugging (#77)" in my local branch. I'm no iOS expert but the error was related to isInspectable on this line. I could not figure out how to make XCode happy:

   #if DEBUG
   if #available(iOS 16.4, *) {
     session.webView.isInspectable = true
   }
   #endif

  1. For some of my own code I need access to the VisitableView ref and since this is buried within WebView I had to add a React.forwardRef() around WebView so I could call into the visitableViewRef.current.injectJavaScript(). I'm still learning your code so I don't know if this is a bug/oversight or if I'm doing something odd which is requiring me to access that ref. But flagging for you in case it's helpful.

Make user-agent of WebView match Turbo Native's

The Turbo Native WebViews includes "Turbo Native iOS" (source) or "Turbo Native Android" (source) in the user-agent used by the webviews.

The turbo-rails integration uses these to determine how to handle special navigation cases:

# Turbo Native applications are identified by having the string "Turbo Native" as part of their user agent.
def turbo_native_app?
  request.user_agent.to_s.match?(/Turbo Native/)
end

I believe this library should match that behavior, adding Turbo Native [iOS|Android] to the user-agent string to match behavior of the native libraries.

modal does not work

hello.
Love the library and the way it looks easy.
The only thing we are noticing is presentation: 'modal' does open a page but a normal screen not in modal.

[Routes.New]: { urlPattern: 'posts/new', title: 'A Modal ', presentation: 'modal', },

Should we configure it differently for it to work?

Thanks again

Suggestion : guide to create APK/IPA

Hello,
I think it would be nice to have a small guide to build APK/IPA file.
I'm new on theses dev environments and even with the existing scripts, it's not very intuitive (without mentioning the versions compatibility resolution with all the tech stack).

I think gradle files and version are old on this project. I can't make ./gradlew assembleRelease works

"Unexpectedly found nil" on all versions of react-native-turbo > 0.1.5 (No registered RNSessionModule?)

Unexpected Fatal Error: Unexpectedly found nil while implicitly unwrapping an Optional value in RNVisitableView.swift with react-native-turbo > 0.1.5

Description

When using versions of react-native-turbo greater than 0.1.5, the app encounters a fatal error with the message "RNTurbo/RNVisitableView.swift:44: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value". This error occurs instantly upon starting the app.

Visit with action `replace` should replace the top-most screen without animation

The behavior in the Turbo app is that a visit with action replace, will replace the top-most screen without animation. In this library, we replace the top-most screen but do so with animation.

The relevant code in the turbo-ios repo looks like this:

https://github.com/hotwired/turbo-ios/blob/2ace9ebe01fe3bce3e7e8df38cb82df052493663/Demo/Navigation/TurboNavigationController.swift#L92-L94

While react-navigation provides us with animationTypeForReplace prop, I don't think it's usable here. The prop is provided as navigator or screen options, only supports pop and push and we cannot pass it dynamically to eg. the navigator.navigate(..) method.

Instead, a potential (untested) solution is to do a stack reset, removing the top-most screen and replacing it with the new like:

navigator.dispatch(state => {
  // manipulate the state, replacing the top-most route with the new route
  const newRoute = ..
  const routes = [...routes.slice(0, -1), newRoute]

  return CommonActions.reset({
    ...state,
    routes
  })
})

Webview freezes when modal closed

Description

When the webview screen with presentation: "modal" is closed, the screen underneath doesn't gain focus.

Why?

visitableWillAppear in the RNVistableView is not called, see why here.

Could not read script '...\node_modules\react-native-turbo\android\turbo-android-dependencies.gradle' as it does not exist.

Description

Hello,
I'm on Windows 11 with this Android Studio version :
Android Studio Jellyfish | 2023.3.1
Build #AI-233.14808.21.2331.11709847, built on April 12, 2024
Runtime version: 17.0.10+0--11572160 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
Windows 11.0
GC: G1 Young Generation, G1 Old Generation

I have this error when I use the command "yarn dev:android" :
Could not read script '...\node_modules\react-native-turbo\android\turbo-android-dependencies.gradle' as it does not exist.

I have also this warning :
Warning: SDK processing. This version only understands SDK XML versions up to 3 but an SDK XML file of version 4 was encountered. This can happen if you use versions of Android Studio and the command-line tools that were released at different times.

Steps to reproduce

  1. Git clone
  2. yarn install
  3. yarn dev:android

Snack or a link to a repository

https://github.com/software-mansion-labs/react-native-turbo-demo/

React Native Turbo version

1.0.0

React Native version

0.73.6

Platforms

Android

JavaScript runtime

None

Workflow

None

Architecture

None

Build type

None

Device

None

Device model

No response

Plan to integrate with expo go

I am new to this library but i was wondering is there anyway to use this with the expo go app so I can develop and test it on a device through the expo go app?

Support for New Architecture / Interop Layer

Description

I know it may be a bit early as react-native-turbo is still in beta, but since React Native 0.74 will enable the New Architecture by default with bridgeless mode on, I'd like to know if there is a plan for react-native-turbo to support the new Architecture directly or via Interop Layer.

So far I've tried an Expo 50 app with New Arch with the following react-native.config.js, but it does not seem to work.

module.exports = {
  project: {
    ios: {
      unstable_reactLegacyComponentNames: ['RNVisitableView'],
    },
  },
}

Steps to reproduce

https://docs.expo.dev/guides/new-architecture/#enable-the-new-architecture-in-an-existing-project

Follow the steps in the docs to enable New Arch and run with the react-native-config.js above.

Snack or a link to a repository

https://github.com/software-mansion-labs/react-native-turbo-demo/tree/main/examples/turbo-demo-expo-example

React Native Turbo version

1.0.0-beta.5

React Native version

0.73.6

Platforms

iOS

JavaScript runtime

Hermes

Workflow

Expo Dev Client

Architecture

Fabric (New Architecture)

Build type

Debug app & dev bundle

Device

iOS simulator

Device model

No response

Uncatched inputs with yarn dev commands

When Metro Bundler is started with a "yarn dev" command, inputs are not catched :

example:dev: › Using development build
example:dev: › Press s │ switch to Expo Go
example:dev:
example:dev: › Press a │ open Android
example:dev: › Press w │ open web
example:dev:
example:dev: › Press j │ open debugger
example:dev: › Press r │ reload app
example:dev: › Press m │ toggle menu
example:dev: › Press o │ open project code in your editor
example:dev:
example:dev: › Press ? │ show all commands

Edit : It's seems locked after the first input.

Logs don't show error details on a webpage loading

I try to change baseURL in the demo and "Logs for your project will appear below." don't show anything about "Error loading. An error occured" shown on client side in android dev mode.

Edit : It's seems to work after restarting everything but crashes if baseURL is changed in live.

Issue with nested navigators and `useWebviewNavigate`

There is an issue with how useWebviewNavigate() works with nested navigators. In getAction a key is added to uniquely identify a screen to be pushed. This assumes that the action.payload is always a flat object, eg:

{
  "type": "NAVIGATE",
  "payload": {
     "name": "SomeScreen",
     "params": { .. }
  }
}

However, for nested navigators, the payload can look like this:

{
  "type": "NAVIGATE",
  "payload": {
    "name": "FocusedFlow",
    "params": {
      "initial": true,
      "screen": "Subscription",
      "params": {
        "step": "checkout",
        "baseURL": "https://.../",
        "fullPath": "/subscribe/checkout"
      },
      "path": "/subscribe/checkout"
    }
  }
}

... in which case the fullPath will be tried extracted from the top-level object and not the Subscription screen in this case. We want to add the key and extract the fullPath in the deepest screen.

In practice, this causes screens to even not be pushed in case of a fallback match (as they have an undefined key) or we might push certain screens multiple times (in this case the FocusedFlow is a modal and navigating in that will open a new modal on top of the previous one instead of pushing the screen in the nested stack navigator)

Transparent background for VisitableView

Today the VisitableView is opaque with a white background. It would be great if the view background was instead transparent unless explicitly set eg. via styles={{ backgroundColor: .. }}.

Having a transparent background would allow a VisitableView be shown as a screen with presentation: transparentModal, covering the screen, but allowing the content behind to be shown and the <body> of the web page to control the transparency, eg:

<html>
  <style>
    body {
      background: rgba(#000, 0.3);
      color: #fff;
      display: flex;
      align-items: center;
      justify-content: center;
    }
  </style>
  <body>
    <h1>Full page with semi-transparent background</h1>
  </body>
</html>

image

The functionality would allow showing webpages that blend in with the content behind it - for instance dialogs.

Remove additional installation step

Currently when install the react-native-turbo or react-native-web-screen we need to add this line to the project Podfile:

pod 'Turbo', :git => 'https://github.com/hotwired/turbo-ios.git', :tag => '7.0.0-rc.6'

Thats because I can't add the turbo to the react-native-turbo library Podspec as the turbo isn't release by the Hotwired team and podspecs doesn't support github links.

Strada not working on Android

Hi again,

I've noticed that none of my strada components appear to be working when I'm on Android.
So I dug around a little and found out that when Strada Web sends the message on Android this exception is returned:

Error: Java exception was raised during method invocation

I have no more info than that unfortunately. Nothing's showing in the expo log.

Have you run into this before? Is there something I can do? It's working perfectly fine on iOS.

Thanks!

Uncaught TypeError: (0 , _index.requireNativeComponent) is not a function on web version

On the demo, when I run "yarn dev" and go the web URL with my browser, the page is blank and the console shows this error :

Uncaught TypeError: (0 , _index.requireNativeComponent) is not a function
    at RNVisitableView.js:53:31

It seems related to the Android version but Android version works.
Any help would be appreciated to make the web version works.

✍️ Refactor package name to react-native-turbo

I noticed that you had pushed to:
https://www.npmjs.com/package/react-native-turbo

With the package updating, to react-native-turbo, there are a couple lines that should be updated as well. Basically playing replace with react-native-turbo-webview. Perhaps the repo name should adjust too.

Such as the package.json's

"name": "react-native-turbo-webview",

"name": "@react-native-turbo-webview/core",

And README

npm install react-native-turbo-webview

How to break out of a turbo frame?

Hi,

I'm trying to break out of a turbo frame. The turbo documentation states that I can just use target="_top", but this results in opening the browser as if this was an external URL.

Thanks,
Chris

Trouble getting example app started

Description

Seems like since the big commit: #143 there is an issue getting the example demo app started.
Cannot read property 'getSessionHandles' of null, js engine: hermes

I have done quite a bit trying to figure what is going on so i thought maybe someone can point me in the right direction.

Steps to reproduce

  1. clone repo,
  2. yarn install
  3. yarn dev
  4. Fails

Snack or a link to a repository

https://github.com/software-mansion-labs/react-native-turbo-demo

React Native Turbo version

50.0.1

React Native version

0.73.4

Platforms

iOS

JavaScript runtime

Hermes

Workflow

None

Architecture

None

Build type

None

Device

None

Device model

No response

Issues with nested navigators

During my testing, I've found that there's a few issues related to using nested navigators:

1. State being dispatched from navigation change should be 'unpacked' accoding to current state

In useWebviewNavigate we first use getStateFromPath(..) to translate a path to a desired state. For replace actions, we "unpack" the state, meaning that for nested screens, we dig out the bottom-most leaf and dispatch that.

The problem here is that we'd like to unpack state, but only up to a point where the new state differ from the current state. We want to do that for both advance and replace actions. We should recursively remove the parent(s) from the new state that is also shared with the current state, so that the new state we dispatch - and which is bubbled up - is handled by the bottom-most shared navigator.

Example:

# Current state:
Screen A
  Screen B
    Screen C

# Proposed new state from getStateFromPath:
Screen A
  Screen B
    Screen Y

In this case, we’d like to getActionFromState(..) with ‘Screen A’ and ‘Screen B’ removed, as they are shared between the two, eg Screen Y. As we bubble up from the bottom-most navigator, the action will be handled by Screen B

Example 2:

# Current state:
Screen A
  Screen B
    Screen C

# Proposed new state from getStateFromPath:
Screen A
  Screen Z
    Screen Y

In this case, since only Screen A matches both, we'd like to dispatch:

Screen Z
   Screen Y

2. key added to the dispatched action is added to the root screen and not to the bottom-most screen when dispatchin

We are using key to ensure that pushing the same screen, but with an different params/URL set would be considered a new screen (ie. useful for Fallback screens). We are adding the key to the root object.

I'm thinking here that for nested navigators, with state unpacked as above, we'd like to add the key to the bottom-most screen and not the root screen. This ensures that the nested navigator object is not re-added (which sometimes triggers animations!) but merely the bottom-most screen would be considered changed.

On a side note, it seems that key is removed in React Navigation 7.0. Maybe it's better to find a different pattern for doing this?

3. Logic to determine if setParams(..) is wrong with nested navigators.

I have a feeling that this would be solved by the first issue mentioned above. Today we are comparing the name of the root screen being pushed (action.payload.name == routeName) to determine if we can use setParams(..).

This doesn't work well with nested navigators, as the root screen (action.payload.name) today might refer to a parent navigator instead of the actual current screen (routeName). Instead, we should compare the names of all the leaves between the two states to ensure that they're identical.

Support for Strada

Strada was recently released, making it easier for the WebView pages to trigger native actions.

On the native side, "components" are registered and the web pages can communicate with those and trigger their actions. The native components can return data to the corresponding element on the web page.

The communication is built on top the message passing (JS-bridge) between the native side and WebView.

The registered components from the native side is also added to the user-agent string, so that the web site knows what components are available (see: https://github.com/hotwired/strada-ios/blob/d8c1bcf1d9511c03e7beed07c05d29f33f9b6cde/docs/QUICK-START.md?plain=1#L34-L35)

Support multiple Sessions

In Turbo Native, each Session controls exactly one WebView. This WebView is re-used between navigations and can only display 1 URL at a time.

In order to support multiple navigation contexts, we need to initialize multiple Sessions. This could for instance be a tab-based navigator or a modal, where each tab would have their own session, so that navigating between tabs, does not reset scroll positions, or closing a modal does not cause a navigation event in the screen behind, potentially loosing state (inputs, scroll positions etc.)

A possible solution would be to:

  1. Make each VisitableView accept a arbritary sessionName prop (could have a default value for simple apps with just one session)
  2. Use this sessionName prop to initialize and identify a Session and store of the mapping between names and Sessions in the view manager
  3. When updating the url prop of a VisitableView, update the URL of the session with the provided sessionName

This would allow multiple Sessions to co-exist throughout the app and be navigated independently

Change to react-native-turbo

Hey guys you can change the project name to react-native-turbo if you want now. I removed my old package from npm registry.

is there a working version?

Hi,

I absolutely love the idea of this project and am very grateful for all the work you've already put into this.
I cannot, however, seem to make it work. I've almost everything I could find, but was unable to get it to work with either your example/, @pklatka's example project, nor in my own code. The errors thrown, if any, are vastly different. Right now I'm at a point where the app will launch and then just throw a white/blank screen without apprently loading the turbo native demo website. Nothing is shown in the console, so I'm at a complete loss here.

I understand that this is a work in progress, but from the docs and other users here I can see that it is working in some cases. So, is there some kind of a proven way to make this work? I'd really love to use this!

Thanks a bunch!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.