Skip to content

Instantly share code, notes, and snippets.

@bryanthuan
Created January 2, 2021 07:26
Show Gist options
  • Save bryanthuan/a1329606aedc3ad7a14753b5ea5745d2 to your computer and use it in GitHub Desktop.
Save bryanthuan/a1329606aedc3ad7a14753b5ea5745d2 to your computer and use it in GitHub Desktop.

Revisions

  1. @tao tao created this gist Sep 15, 2019.
    250 changes: 250 additions & 0 deletions MessengerChat.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,250 @@
    import React, { Component } from 'react'
    import PropTypes from 'prop-types'

    /**
    * Utils
    */
    const removeElementByIds = ids => {
    ids.forEach(id => {
    const element = document.getElementById(id)
    if (element && element.parentNode) {
    element.parentNode.removeChild(element)
    }
    })
    }

    /**
    * Messenger Chat Plugin
    */
    class MessengerChat extends Component {
    static propTypes = {
    pageId: PropTypes.string.isRequired,
    // appId: PropTypes.string.isRequired,
    shouldShowDialog: PropTypes.bool,
    htmlRef: PropTypes.string,
    minimized: PropTypes.bool,
    themeColor: PropTypes.string,
    loggedInGreeting: PropTypes.string,
    loggedOutGreeting: PropTypes.string,
    greetingDialogDisplay: PropTypes.oneOf(['show', 'hide', 'fade']),
    greetingDialogDelay: PropTypes.number,
    autoLogAppEvents: PropTypes.bool,
    xfbml: PropTypes.bool,
    version: PropTypes.string,
    // language: PropTypes.string,
    onCustomerChatDialogShow: PropTypes.func,
    onCustomerChatDialogHide: PropTypes.func,
    }

    static defaultProps = {
    shouldShowDialog: false,
    htmlRef: undefined,
    minimized: undefined,
    themeColor: undefined,
    loggedInGreeting: undefined,
    loggedOutGreeting: undefined,
    greetingDialogDisplay: undefined,
    greetingDialogDelay: undefined,
    autoLogAppEvents: true,
    xfbml: true,
    version: '4.0',
    onCustomerChatDialogShow: undefined,
    onCustomerChatDialogHide: undefined,
    }

    constructor(props) {
    super(props)

    this.setFbAsyncInit = this.setFbAsyncInit.bind(this)
    this.reloadSDKAsynchronously = this.reloadSDKAsynchronously.bind(this)
    this.loadSDKAsynchronously = this.loadSDKAsynchronously.bind(this)
    this.createMarkup = this.createMarkup.bind(this)
    this.controlPlugin = this.controlPlugin.bind(this)
    this.subscribeEvents = this.subscribeEvents.bind(this)
    this.removeFacebookSDK = this.removeFacebookSDK.bind(this)

    this.state = {
    fbLoaded: false,
    shouldShowDialog: undefined,
    }
    }

    componentDidMount() {
    this.setFbAsyncInit()
    this.reloadSDKAsynchronously()
    }

    componentDidUpdate(prevProps) {
    if (
    prevProps.pageId !== this.props.pageId ||
    // prevProps.appId !== this.props.appId ||
    prevProps.shouldShowDialog !== this.props.shouldShowDialog ||
    prevProps.htmlRef !== this.props.htmlRef ||
    prevProps.minimized !== this.props.minimized ||
    prevProps.themeColor !== this.props.themeColor ||
    prevProps.loggedInGreeting !== this.props.loggedInGreeting ||
    prevProps.loggedOutGreeting !== this.props.loggedOutGreeting ||
    prevProps.greetingDialogDisplay !== this.props.greetingDialogDisplay ||
    prevProps.greetingDialogDelay !== this.props.greetingDialogDelay ||
    prevProps.autoLogAppEvents !== this.props.autoLogAppEvents ||
    prevProps.xfbml !== this.props.xfbml ||
    prevProps.version !== this.props.version ||
    prevProps.language !== this.props.language
    ) {
    this.setFbAsyncInit()
    this.reloadSDKAsynchronously()
    }
    }

    setFbAsyncInit() {
    const { autoLogAppEvents, xfbml, version } = this.props

    window.fbAsyncInit = () => {
    window.FB.init({
    // appId,
    autoLogAppEvents,
    xfbml,
    version: `v${version}`,
    })

    this.setState({ fbLoaded: true })
    }
    }

    loadSDKAsynchronously() {
    // const { language } = this.props
    /* eslint-disable */
    (function(d, s, id) {
    var js,
    fjs = d.getElementsByTagName(s)[0]
    if (d.getElementById(id)) {
    return;
    }
    js = d.createElement(s)
    js.id = id
    js.src = `https://connect.facebook.net/en_GB/sdk/xfbml.customerchat.js`
    fjs.parentNode.insertBefore(js, fjs)
    })(document, 'script', 'facebook-jssdk')
    /* eslint-enable */
    }

    removeFacebookSDK() {
    removeElementByIds(['facebook-jssdk', 'fb-root'])
    delete window.FB
    }

    reloadSDKAsynchronously() {
    this.removeFacebookSDK()
    this.loadSDKAsynchronously()
    }

    controlPlugin() {
    const { shouldShowDialog } = this.props

    if (shouldShowDialog) {
    window.FB.CustomerChat.showDialog()
    } else {
    window.FB.CustomerChat.hideDialog()
    }
    }

    subscribeEvents() {
    const { onCustomerChatDialogShow, onCustomerChatDialogHide } = this.props

    if (onCustomerChatDialogShow) {
    window.FB.Event.subscribe(
    'customerchat.dialogShow',
    onCustomerChatDialogShow
    )
    }

    if (onCustomerChatDialogHide) {
    window.FB.Event.subscribe(
    'customerchat.dialogHide',
    onCustomerChatDialogHide
    )
    }
    }

    createMarkup() {
    const {
    pageId,
    htmlRef,
    minimized,
    themeColor,
    loggedInGreeting,
    loggedOutGreeting,
    greetingDialogDisplay,
    greetingDialogDelay,
    } = this.props

    const refAttribute = htmlRef !== undefined ? `ref="${htmlRef}"` : ''

    const minimizedAttribute =
    minimized !== undefined ? `minimized="${minimized}"` : ''

    const themeColorAttribute =
    themeColor !== undefined ? `theme_color="${themeColor}"` : ''

    const loggedInGreetingAttribute =
    loggedInGreeting !== undefined
    ? `logged_in_greeting="${loggedInGreeting}"`
    : ''

    const loggedOutGreetingAttribute =
    loggedOutGreeting !== undefined
    ? `logged_out_greeting="${loggedOutGreeting}"`
    : ''

    const greetingDialogDisplayAttribute =
    greetingDialogDisplay !== undefined
    ? `greeting_dialog_display="${greetingDialogDisplay}"`
    : ''

    const greetingDialogDelayAttribute =
    greetingDialogDelay !== undefined
    ? `greeting_dialog_delay="${greetingDialogDelay}"`
    : ''

    return {
    __html: `<div
    class="fb-customerchat"
    page_id="${pageId}"
    ${refAttribute}
    ${minimizedAttribute}
    ${themeColorAttribute}
    ${loggedInGreetingAttribute}
    ${loggedOutGreetingAttribute}
    ${greetingDialogDisplayAttribute}
    ${greetingDialogDelayAttribute}
    ></div>`,
    }
    }

    render() {
    const { fbLoaded, shouldShowDialog } = this.state

    if (fbLoaded && shouldShowDialog !== this.props.shouldShowDialog) {
    document.addEventListener(
    'DOMNodeInserted',
    event => {
    const element = event.target
    if (
    element.className &&
    typeof element.className === 'string' &&
    element.className.includes('fb_dialog')
    ) {
    this.controlPlugin()
    }
    },
    false
    );
    this.subscribeEvents()
    }
    // Add a random key to rerender. Reference:
    // https://stackoverflow.com/questions/30242530/dangerouslysetinnerhtml-doesnt-update-during-render
    return <div key={Date()} dangerouslySetInnerHTML={this.createMarkup()} />
    }
    }

    export default MessengerChat
    35 changes: 35 additions & 0 deletions _app.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,35 @@
    import React from 'react'
    import App, { Container } from 'next/app'

    import MessengerChat from '../utils/MessengerChat'

    class MyApp extends App {
    // Only uncomment this method if you have blocking data requirements for
    // every single page in your application. This disables the ability to
    // perform automatic static optimization, causing every page in your app to
    // be server-side rendered.
    //
    // static async getInitialProps(appContext) {
    // // calls page's `getInitialProps` and fills `appProps.pageProps`
    // const appProps = await App.getInitialProps(appContext);
    //
    // return { ...appProps }
    // }

    render() {
    const { Component, pageProps } = this.props
    return (
    <Container>
    <Component {...pageProps} />

    <MessengerChat
    pageId="your-page-id"
    ref="fb-msgr"
    />

    </Container>
    )
    }
    }

    export default MyApp