WolfieZero
WolfieZero

Reputation: 387

React Native WebView JavaScript issue on build

I've got a React Native application for Android that has live chat support (using Intercom). To access Intercom I'm using WebView with injectedJavaScript to show the UI. It works fine on my dev build, but when I do a release build it complains it cannot find window.Intercom (I get the same relative issue if I remove the window.)

Here's the code I'm trying to run

IntercomContainer.js

// =============================================================================
// Components >> IntercomContainer
// =============================================================================
// @flow

// Import
// =============================================================================

import * as React from 'react';
import { View, WebView } from 'react-native';
import Spinner from 'react-native-loading-spinner-overlay';

import styles from './styles';

// Content
// =============================================================================

type State = {
    isLoading: boolean,
};

type Props = {
    appId: string,
}

// Render
// =============================================================================

export default class IntercomContainer extends React.Component<Props, State> {

    props: Props = {
        appId: '',
    };

    state: State = {
        isLoading: true,
    }

    setState: Function;

    injectedJS = (appId: string) => {
        return `
            try {

                window.Intercom('boot', {
                    app_id: '${appId}',
                }); 

                window.Intercom('show');
                window.Intercom('onShow', function() { 
                    document.getElementById('message').innerHTML = '';
                    setTimeout(() => {
                        document.getElementById('message').innerHTML = 'Click on the chat button in the bottom-right to open chat...';
                    }, 1000)
                });

            } catch(e) {
                alert('Intercom failed to load: ' + e.message);
            }
        `;
    }

    onLoadEnd = () => {
        this.setState({
            isLoading: false,
        });
    }

    render(){
        const { appId } = this.props;
        const { isLoading } = this.state;

        return (
            <View style={styles.container}>
                <Spinner visible={isLoading} />
                <WebView
                    injectedJavaScript={this.injectedJS(appId)}
                    source={require('./IntercomWebView.html')}
                    onLoadEnd={this.onLoadEnd}
                    javaScriptEnabled={true}
                    style={styles.webView}
                />
            </View>
        );
    }
}

IntercomWebView.html

<!DOCTYPE html>
<head>
    <script>
        // intercom JS library
        var APP_ID = '';
        (function(){
            debugger;
            console.log("Executing function main...");
            var w=window;
            var ic=w.Intercom;
            if (typeof ic === "function") {
                ic('reattach_activator');
                ic('update',intercomSettings);
            } else {
                var d=document;
                var i= function() {
                    i.c(arguments)
                };
                i.q=[];
                i.c=function(args){
                    i.q.push(args)
                };
                w.Intercom=i;

                function l(){
                    debugger;
                    console.log("Executing function l...");
                    var s=d.createElement('script');
                    s.type='text/javascript';
                    s.async=true;
                    s.src='https://widget.intercom.io/widget/' + APP_ID;
                    var x=d.getElementsByTagName('script')[0];
                    x.parentNode.insertBefore(s,x);
                }

                if(w.attachEvent){
                    w.attachEvent('onload',l);
                }else{
                    w.addEventListener('load',l,false);
                }
            }
        })();
    </script>
    <style>
        main {
            align-items: center;
            background-color: #fefefe;
            color: #999;
            display: flex;
            font-family: sans-serif;
            height: 80vh;
            justify-content: center;
            text-align: center;
        }
    </style>
</head>
<body>
    <main id="message">
        loading...
    </main>
</body>
</html>

Thanks!

Upvotes: 0

Views: 1161

Answers (2)

Alex
Alex

Reputation: 2053

This seems to be an issue as referred to above with the delay, but also a cookies problem.

I managed to get around this with the following, similar to the original question;

const injectedJavaScript = `
  var APP_ID = "YOUR_APP_ID";
  // Wait for a "time" before trying to execute
  setTimeout(function() {
    try {
      window.Intercom('boot', {
        app_id: APP_ID,
        email: 'an_email_@an_address.com',
        user_id: 'unique_id'
      });
      // i want to show by default, when ready
      window.Intercom('show');
      // then i am doing some stuff to show the "instruction" to reopen by the icon if they close
      var instruction = document.getElementById("instruction");
      window.Intercom('onHide', function() {
        instruction.classList.add("show");
      })
    } catch(e) {
      alert('Intercom failed to load: ' + e.message);
    }
  }, 500)
`

My WebView looks like this;

return (
  <View flex={1}>
    <WebView
      javaScriptEnabled
      scrollEnabled={false}
      bounces={false}
      originWhitelist={['*']}
      injectedJavaScript={injectedJavaScript}
      source={{
        html: `
          <head>
            <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0">
            <style>
              p#instruction {
                opacity: 0;
                font-family: 'Rubik', sans-serif;
                text-align: center;
                font-size: 14px;
                position: absolute;
                top: 50%;
                left: 0;
                margin-top: -8px;
                width: 100%;
                -webkit-transition: opacity 0.5s linear;
                -moz-transition: opacity 0.5s linear;
                -o-transition: opacity 0.5s linear;
                -ms-transition: opacity 0.5s linear;
                transition: opacity 0.5s linear;
              }
              p#instruction.show {
                opacity: 1;
              }

            </style>
          </head>
          <body>
            <p id="instruction">Click the icon to open chat</p>
            <script>
              var APP_ID = "YOUR_APP_ID";

              (function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',w.intercomSettings);}else{var d=document;var i=function(){i.c(arguments);};i.q=[];i.c=function(args){i.q.push(args);};w.Intercom=i;var l=function(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='https://widget.intercom.io/widget/' + APP_ID;var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);};if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();
            </script>
          </body>
        `,
        baseUrl: 'https://a-base-url.co.uk', // This part is important! This solved the cookie issue for me
      }}
      useWebKit
      onLoad={() => console.warn('do something on load')}
    />
  </View>
)

Upvotes: 0

Andrei Lesnitsky
Andrei Lesnitsky

Reputation: 1078

Most likely the root cause of your issue is the same as postMessage bug. Your code which uses Intercom object is being loaded before javascript code which initilizes this object. As a workaround you can call this code in setTimeout with some magic value, or implement more "neat" solution where you'll defer actual calls of Intercom object until it is initialized

Upvotes: 1

Related Questions