Open Anthony-Gaudino opened 1 year ago
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.
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.
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?
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.
@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.
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
butbridge
is inside all this:Am I supposed to do something like this?