1

I have a React Native application built with Expo. On How can I add links in a Highcharts tooltip that will open on mobile's browser? I was able to pass a URL to Highcharts so that when a tooltip is clicked, that URL is opened:

return(
    <View style={styles.container}>
        <ChartView
            onMessage={m => this.onMessage(m)}
            config={config}
        />
    </View>

This triggers this method to open the URL:

onMessage = (m) => {
    let data = JSON.parse(m.nativeEvent.data);
    Linking.openURL(data.url)
};

And the URL gets populated through a global variable window.myURL and sending the message with postMessage():

render() {
    let Highcharts = "Highcharts";
    let config ={
        ...
        plotOptions: {
            series: {
                stickyTracking: false,
                point: {
                    events: {
                        click: function(e) {
                            window.postMessage(JSON.stringify({'url': window.myUrl}));
                        }
                    }
                }
            },
        },
        tooltip: {
            useHTML: true,
            formatter: function () {
                window.myUrl = extras.url;
                return `<div class="text">some text</div>`;
            }
    };

This works well on iOS (both physical and emulator), but does not on Android (neither physical nor emulator).

I have gone through different Github issues such as onMessage not called in native code even though sent from webview( android) and react native html postMessage can not reach to WebView and they tend to suggest using some timeout on window.postMessage(). However, I see that even this does not work:

plotOptions: {
    series: {
        point: {
            events: {
                click: function(e) {
                    setTimeout(function() {
                        console.log('bla');
                        window.postMessage(JSON.stringify({'url': window.myUrl}));
                    }, 200);
                }
            }
        }
    },
},

Since even console.log() does not work, it looks to me as if the click event is not being caught by Android.

How can I make Android aware of this event so that I can go through the message and then open the URL?

fedorqui
  • 275,237
  • 103
  • 548
  • 598
  • Can you change the console.log to an alert or a dom change, to make sure the console.log is not hidden and the click event does fire? – basbase Apr 12 '19 at 08:05
  • @basbase thanks! I tried with an `alert` and it was not triggered either. – fedorqui Apr 15 '19 at 10:25
  • Can you create a buildable repo for us to poke at? – basbase Apr 16 '19 at 08:17
  • Are you able to declare view of webview or set any callback to webview? – Sebastian Bochan Apr 16 '19 at 09:51
  • @SebastianBochan no. I see in highchart's code that [CharView is in fact a WebView](https://github.com/TradingPal/react-native-highcharts/blob/master/react-native-highcharts.js#L81) but if I say for example ` { Alert.alert('info', 'hey'); console.log('>>>>>>>>>` it does not work, while the same works on ` { Alert.alert('info', 'hola'); console.log('>>>>>>>>> `. – fedorqui Apr 16 '19 at 10:18
  • @SebastianBochan I see you are a dev on Highcharts!! Do you know if this is a bug or something I am missing to do? Not to bang my head with this if it is not something I can do. – fedorqui Apr 17 '19 at 10:05
  • As I see, you use external react-native wrapper, developed by third party developers. I recommend you to post the problem in their github. We are still developing our official react-native wrapper, which should be released soon. – Sebastian Bochan Apr 17 '19 at 10:38
  • From what I can see, you are trying to execute the javascript code inside the webview but the configuration is defined in react native. Can you try `eval` to execute the code on click like: `eval("setTimeout(function() {console.log('bla');window.postMessage(JSON.stringify({'url': window.myUrl}));}, 200);")` – Dani Akash Apr 17 '19 at 11:07
  • This way, it should execute the string with your code inside the webview – Dani Akash Apr 17 '19 at 11:08
  • @DaniAkash thanks a lot, I will give it a try. The interesting point here is: why is it working on iOS but not in Android? That is, something is being sent through the Webview in iOS, but not in Android and does not make sense to me. – fedorqui Apr 17 '19 at 13:23
  • @fedorqui I check the RN github issue [link](https://github.com/facebook/react-native/issues/11594) you post, have you gone through them carefully? A couple tricks are mentioned there. `setTimeout` turns out to be least reliable one. – hackape Apr 17 '19 at 16:33
  • 1
    Notably the `awaitPostMessage` fix, and the `window.__REACT_WEB_VIEW_BRIDGE.postMessage` fix, both look promising. Have you tried them? – hackape Apr 17 '19 at 16:41
  • @fedorqui let me know if it worked :) – Dani Akash Apr 17 '19 at 17:59
  • @DaniAkash I tried what you said and added in `plotOptions.series.point.events` the value `click: function(e) { eval("setTimeout(function() {console.log('bla');window.postMessage(JSON.stringify({'url': window.myUrl}));}, 200);") }` but it did not work, nothing showed in the console and the JSON did not get through. – fedorqui Apr 17 '19 at 20:17
  • 1
    @hackape `window.__REACT_WEB_VIEW_BRIDGE.postMessage` made it!! Two things: 1) if I say `if (Platform.OS === 'ios') { window.postMessage(data); } else { window.__REACT_WEB_VIEW_BRIDGE.postMessage(message_data); }` it does not work, but it does if I just say `window.postMessage(data);`. I know it is tangential, but sounds weird to me. 2) Post an answer so that I can award the bounty : ) – fedorqui Apr 17 '19 at 20:37
  • @fedorqui thanks! answer posted, glab it works :D. feel free to edit if I missed sth. – hackape Apr 18 '19 at 09:13

1 Answers1

4

The "click" event is fine. Problem is that, RN core's <WebView> implementation for Android is flawed at polyfilling window.postMessage(). This issue has been well discussed in this github issue.

So it seems the nature of problem, is RN has to polyfill the native window.postMessage for some reason, but the override didn't take place timely, causing window.postMessage calls still made to the native one.

One solution (for Android) proposed in that github issue, is simply stop using the native window.postMessage, and directly use the internal interface, which is the actual polyfill function.

// in place of `window.postMessage(data)`, use:
window.__REACT_WEB_VIEW_BRIDGE.postMessage(String(data))

One heads-up though, this API is not public and might be subject to change in future.

hackape
  • 18,643
  • 2
  • 29
  • 57