Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send message to browser on React Native button click #42

Open
Anthony-Gaudino opened this issue Feb 9, 2023 · 5 comments
Open

Send message to browser on React Native button click #42

Anthony-Gaudino opened this issue Feb 9, 2023 · 5 comments

Comments

@Anthony-Gaudino
Copy link

It's not clear to me how one would send a message to the browser using Lepont when a user clicks for example in a button on the React Native side.

From what I can see one needs to call bridge.sendMessage but bridge is inside all this:

const [ref, onMessage] = useBridge((registry) => {
    registry.register('my-streaming-bridge', (_, bridge) => {
        bridge.sendMessage({
          type: 'my-streaming-event',
          payload: 'stream data!',
        })
    })
  })

Am I supposed to do something like this?

let stream = null;

const [ref, onMessage] = useBridge((registry) => {
    registry.register('my-streaming-bridge', (_, bridge) => {
        stream =  bridge.sendMessage;
    })
})
  
stream({
  type: 'my-streaming-event',
  payload: 'stream data!',
});
@kt3k
Copy link
Owner

kt3k commented Feb 14, 2023

Do you show the button with React Native or browser?

LePont supposes all UIs are rendered in browser (WebView) as HTML/DOM. LePont doesn't support the case where the button is rendered as React Native component.

@Anthony-Gaudino
Copy link
Author

The button is shown in React Native.

After reading the Lepont source code and thinking for a while I found a solution, but maybe it could be improved. I will share the solution I have found soon. If after that you have suggestions for improvement it would be great to hear.

@kt3k
Copy link
Owner

kt3k commented Feb 14, 2023

I'm curious about your usage of LePont. Do you show UIs both in React Native and browser(WebView)? What part do you use RN and what part browser?

@Anthony-Gaudino
Copy link
Author

We will have bidirectional communication between Native and WebView, so both can initiate or receive messages.

Currently we have buttons on native end that starts the event, but later this event would be initiated automatically on Native end. Events could also be initiated on WebView.

@Anthony-Gaudino
Copy link
Author

@kt3k

In our code we are sending a stream of data from the WebView to React Native, so we first start an operation, then send the data trough multiple messages and at the end close the operation.

Here's the code structure:

React Native

import React, {useEffect} from 'react';
import {WebView} from 'react-native-webview';
import {useBridge, BridgeImpl} from 'lepont';

const MyComponent = () => {
  const [webViewRef, onMessage, {registry}] = useBridge();
  const register = (f: BridgeImpl<unknown>) => registry.register(f.name, f);
  const sendMessage = registry.sendMessage.bind(registry);
  
  const doSomething: BridgeImpl<any> = async (payload, bridge) => {
    // Start doing something if it was not initiated by WebView.
    if (!payload) {
      sendMessage({
        type: 'do-something',
        payload: '',
      });

      return;
    }

    // Operation finished
    const endOp = () => {};

    // Operation started
    const newOp = async () => {};

    // Does something
    const doIt = () => {};

    switch (payload.action) {
      case 'new-op':
        await newOp();
        return 'started';
      case 'do-it':
        doIt();
        return 'did-something';
      case 'end-op':
        endOp();
        return 'ended-op';
      default:
        throw 'Unknown operation!';
    }
  };
  
  const doSomethingElse: BridgeImpl<any> = async (payload, bridge) => {
    // Start doing something if it was not initiated by WebView.
    if (!payload) {
      sendMessage({
        type: 'do-something-else',
        payload: '',
      });

      return;
    }

    // ...
  };
  
  useEffect(() => {
    register(doSomething);
    register(doSomethingElse);
  }, []);
  
  const handleNavigationStateChange = (state: WebViewNavigation) => {};
  
  return (
    <View style={styles.container}>
      <WebView
        source={{uri: "index.html"}}
        originWhitelist={['*']}
        allowFileAccessFromFileURLs={true}
        allowUniversalAccessFromFileURLs={true}
        allowFileAccess={true}
        onNavigationStateChange={handleNavigationStateChange}
        onMessage={onMessage}
        ref={webViewRef}
      />
      <Button
        title="Do something"
        onPress={() => registry.registry.doSomething()}
      />
      <Button
        title="Do something else"
        onPress={() => registry.registry.doSomethingElse()}
      />
    </View>
  );
}

WebView

import {sendMessage, on} from 'lepont/browser';
import {encode} from 'base64-arraybuffer';

on('do-something', async payload => {
  /** Type of message received on Native end. */
  const msgType = 'doSomething';

  /** Payload to send to Native end. */
  const nativePayload = {
    action: '',
    data: '',
  } as const;

  // Create a new operation
  await sendMessage({
    type: msgType,
    payload: {...nativePayload, action: 'new-op'},
  });

  // Send data chunks
  await getStream(async (arrayBuffer) => {
      const answer = await sendMessage({
        type: msgType,
        payload: {
          ...nativePayload,
          action: 'do-it',
          data: encode(arrayBuffer),
        },
      });
    });

  // End operation
  await sendMessage({
    type: msgType,
    payload: {...nativePayload, action: 'end-op'},
  });
});

on('do-something-else', async payload => {
  // ...
}

There's 2 functions defined on React Native doSomething() and doSomethingElse() and they could be initiated from React Native or WebView.
On the beginning of these functions I'm checking if there's a payload, if there's not then I know it was called form React Native, then it will send a message to the WebView and the WebView will send a message to Native running the function again with a payload. Of course one could also pass a payload from React Native and check in a different way.

It's important to register the functions inside a useEffect() because in the beginning when calling useBridge(), internally LePont will remove all registry entries as soon as it's called.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants