Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Twitter widget inside WebView in React Native

I'm trying to load a Twitter widget inside a WebView in my React Native app, but it seems that my injected Javascript is not working for some reason.

What I'm doing is loading Twitter script asynchronously (function taken from here), then executing twttr.widgets.load() function when script is loaded in order to draw the widget.

Is it possible to do it, or am I trying an impossible with default Webview component?

Here is my code:

render() {
    let utils = ' \
      function loadScript(src, callback) { \
        var s, r, t; \
        r = false; \
        s = document.createElement("script"); \
        s.type = "text/javascript"; \
        s.src = src; \
        s.onload = s.onreadystatechange = function() { \
          if ( !r && (!this.readyState || this.readyState == "complete") ) { \
            r = true; \
            callback(); \
          } \
        }; \
        t = document.getElementsByTagName("script")[0]; \
        t.parentNode.insertBefore(s, t); \
      } \
    ';

    let twitter = ' \
      loadScript("//platform.twitter.com/widgets.js", function () { \
        twttr.widgets.load(); \
      }); \
    ';

    let JS = utils + twitter;

    let source = '<blockquote class="twitter-tweet" data-lang="es"><p lang="en" dir="ltr">8 TED Talks to inspire projects with kids: <a href="https://twitter.com/TEDTalks/status/758116657638309896">https://twitter.com/TEDTalks/status/758116657638309896</a> <a href="https://twitter.com/TEDTalks/status/758116657638309896">pic.twitter.com/HMmYAeP7Km</a></p>&mdash; TED Talks (@TEDTalks) <a href="https://twitter.com/TEDTalks/status/758116657638309896">27 de julio de 2016</a></blockquote>';

    return (
      <WebView
        source={{html: source}}
        javaScriptEnabled={true}
        injectedJavascript={ JS }
      />
    );
}
like image 420
Fran Verona Avatar asked Feb 07 '23 12:02

Fran Verona


2 Answers

It seems like loading scripts inside a WebView is not working (I don't know why). So to make it work, I need to load Twitter's script using script tag and concatenating it to my HTML code.

render() {

    let JS = '<script type="text/javascript" src="https://platform.twitter.com/widgets.js"></script>';

    let source = JS + '<blockquote class="twitter-tweet" data-lang="es"><p lang="en" dir="ltr">8 TED Talks to inspire projects with kids: <a href="https://twitter.com/TEDTalks/status/758116657638309896">https://twitter.com/TEDTalks/status/758116657638309896</a> <a href="https://twitter.com/TEDTalks/status/758116657638309896">pic.twitter.com/HMmYAeP7Km</a></p>&mdash; TED Talks (@TEDTalks) <a href="https://twitter.com/TEDTalks/status/758116657638309896">27 de julio de 2016</a></blockquote>';

    return (
      <WebView
        source={{html: source}}
        javaScriptEnabled={true}
      />
    );
}
like image 70
Fran Verona Avatar answered Feb 10 '23 23:02

Fran Verona


I hacked it together like this and it mostly works, although height isn't variable which isn't ideal:

import React from "react"
import ReactNative from "react-native"

const { View, WebView, StyleSheet, ActivityIndicator } = ReactNative

export default class EmbeddedTweet extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      loading: true,
      embedHtml: null,
    }
  }

  componentDidMount() {
    this.setupEmbed()
  }

  setupEmbed() {
    let tweetUrl =
      "https://publish.twitter.com/oembed?url=" + encodeURIComponent(this.props.url)
    fetch(tweetUrl, { method: "GET", headers: { Accepts: "application/json" } }).then(
      resp => {
        resp.json().then(json => {
          let html = json.html
          this.setState({
            loading: false,
            embedHtml: html,
          })
        })
      }
    )
  }

  renderLoading() {
    if (this.state.loading) {
      return (
        <View style={styles.loadingWrap}>
          <ActivityIndicator />
        </View>
      )
    }
  }

  renderEmbed() {
    if (this.state.embedHtml) {
      let html = `<!DOCTYPE html>\
        <html>\
          <head>\
            <meta charset="utf-8">\
            <meta name="viewport" content="width=device-width, initial-scale=1.0">\
            </head>\
            <body>\
              ${this.state.embedHtml}\
            </body>\
        </html>`
      return (
        <View style={styles.webviewWrap}>
          <WebView source={{ html: html }} style={styles.webview} />
        </View>
      )
    }
  }
  
  render() {
    return (
      <View
        style={[styles.container, { height: this.props.height || 300 }, this.props.style]}
      >
        {this.renderLoading()}
        {this.renderEmbed()}
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  loadingWrap: {
    flex: 1,
    backgroundColor: "#999",
    justifyContent: "center",
    alignItems: "center",
  },
  webviewWrap: {
    flex: 1,
    borderWidth: 1,
    borderRadius: 4,
    borderColor: "#999",
  },
  webview: {
    flex: 1,
  },
})
like image 42
Udi Avatar answered Feb 10 '23 22:02

Udi