Skip to content

LiveChat Widget Integration

Shailesh Baldaniya edited this page Sep 21, 2021 · 3 revisions

Installation Script

https://docs.rocket.chat/guides/omnichannel/livechat-widget-installation

This script loads livechat widget in web page

<!-- Start of Rocket.Chat Livechat Script -->
	<script type="text/javascript">
	(function(w, d, s, u) {
		w.RocketChat = function(c) { w.RocketChat._.push(c) }; w.RocketChat._ = []; w.RocketChat.url = u; w.RocketChat.parentURL = w.location.href;
		var h = d.getElementsByTagName(s)[0], j = d.createElement(s);
		j.async = true; j.src = 'http://localhost:3000/livechat/rocketchat-livechat.min.js?_=201903270000';
		h.parentNode.insertBefore(j, h);
	})(window, document, 'script', 'http://localhost:3000/livechat');
	</script>

Livechat Widget API

Livechat Widget API is used for initializing widget with configuration(theme, customFields, department, visitor name/email etc) and for registering callbacks for some events like onChatStarted, onChatEnded, onChatMinimized, onChatMaximized, etc.

This Widget API is used inside Installation script as described in https://developer.rocket.chat/reference/api/livechat-api

Integrate Widget in Web

To integrate widget in web, just need to add installation script in the html file.

<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>

<h1>This is a Heading</h1>
<p>This is a paragraph.</p>

<!-- Start of Rocket.Chat Livechat Script -->
	<script type="text/javascript">
	(function(w, d, s, u) {
		w.RocketChat = function(c) { w.RocketChat._.push(c) }; w.RocketChat._ = []; w.RocketChat.url = u; w.RocketChat.parentURL = w.location.href;
		var h = d.getElementsByTagName(s)[0], j = d.createElement(s);
		j.async = true; j.src = 'http://localhost:3000/livechat/rocketchat-livechat.min.js?_=201903270000';
		h.parentNode.insertBefore(j, h);
	})(window, document, 'script', 'http://localhost:3000/livechat');
</script>

</body>
</html>

We can use LiveChat Widget API in the script as below inside RocketChat() function. API documentation: https://developer.rocket.chat/reference/api/livechat-api

For testing,

  1. Create an HTML file with the below code. Modify server info i.e. http://localhost:3000/livechat. (Or directly get this from Omnichannel Installation Section)
  2. Open the HTML file in the web browser. It should load Livechat Widget on the web page.
  3. If there is a CORS issue, we need to deploy this HTML file on the same domain. In the code add this file in public folder in the server repo. i.e. Rocket.Chat/public/livechat.html. And it should be accessible using https://<server>/livechat.html
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>

<h1>This is a Heading</h1>
<p>This is a paragraph.</p>

<!-- Start of Rocket.Chat Livechat Script -->
<script type="text/javascript">
	(function (w, d, s, u) {
		w.RocketChat = function (c) { w.RocketChat._.push(c) }; w.RocketChat._ = []; w.RocketChat.url = u;
		var h = d.getElementsByTagName(s)[0], j = d.createElement(s);
		j.async = true; j.src = 'http://localhost:3000/livechat/rocketchat-livechat.min.js?_=201903270000';
		h.parentNode.insertBefore(j, h);
	})(window, document, 'script', 'http://localhost:3000/livechat');

	RocketChat(function () {
		const appName = "MyViasat";
		const token = "MyViasatToken";

		this.setCustomField('appName', appName);
		this.setCustomField('token', token);

		this.onChatStarted(() => {
			console.log("onChatStarted fn")
		});
	});
 </script>

</body>
</html>

Integrate Widget in Mobile

To integrate widget in mobile, need to inject installation javascript in the web web view.

import React, { useContext, createContext, useEffect, useState } from 'react';
import { Platform } from 'react-native';
import { useQuery } from '@apollo/client';
import { GET_CONFIGURATIONS } from 'shared/queries/configurations';
import { GET_ACCOUNT_INFO_QUERY } from '../queries/profile';
import AppColors from 'shared/styles/colors';
import { useToken } from './token';
import packageJson from 'shared/myversion.json';
/*
 * Documentation for the RocketChat livechat API: https://docs.rocket.chat/api/livechat-api#usage
 */
interface CustomFields {
  name: string;
  value: string;
  disableOverwrite?: boolean;
}

const isWeb = Platform.OS === 'web';

const APP_NAME = Platform.select({
  android: 'MyViasatMobileAND',
  ios: 'MyViasatMobileIOS',
  web: 'MyViasatWebAmer',
  default: 'MyViasatWebAmer',
});

const SCRIPT_ID = 'rocketchat-script';
let webView: any = null;

const theme = {
  color: AppColors.darkGray32424E,
  fontColor: AppColors.white,
  iconColor: AppColors.white,
};

const uninitalizedRocketChatFn = (): void => {
  console.error('RocketChat did not load properly');
};

const Context = createContext({
  maximizeWidget: uninitalizedRocketChatFn,
  restartMobileWidget: uninitalizedRocketChatFn,
  setCustomFields: (fields: CustomFields[]) => {
    console.error('RocketChat did not load properly');
  },
  hideWidget: uninitalizedRocketChatFn,
  showWidget: uninitalizedRocketChatFn,
  isRocketChatEnabled: false,
  loadRocketChatMobile: (webViewRef: any) => {
    console.error('RocketChat did not load properly');
  },
});

const runRocketChatMethods = (stringFunction: string) => {
  if (isWeb) {
    Function(stringFunction)();
  } else {
    //validating this so it doesn't throw a random error, moving back and forward screens
    if (webView && webView.current) webView.current.injectJavaScript(stringFunction);
  }
};

const loadRocketChatScript = (
  hostUrl: string,
  livechatUrl: string,
  setIsScriptLoaded: React.Dispatch<React.SetStateAction<boolean>>,
) => {
  const w: any = window;
  const existingScript = document.getElementById(SCRIPT_ID);

  if (!existingScript) {
    return new Promise(function (resolve, reject) {
      //@ts-ignore
      w.RocketChat = params => w.RocketChat._.push(params);
      w.RocketChat._ = [];
      w.RocketChat.url = `${livechatUrl}`;
      const tag = document.createElement('script');
      tag.async = true;
      tag.src = `${hostUrl}`;
      tag.id = SCRIPT_ID;
      tag.onload = resolve;
      tag.onerror = reject;
      const scriptElement = document.getElementsByTagName('script')[0];
      scriptElement.parentNode?.insertBefore(tag, scriptElement);
    })
      .then(() => {
        setDefaultsOnLoad(setIsScriptLoaded);
      })
      .catch(err => {
        console.error(err);
      });
  }
};

const loadRocketChatMobileScript = (hostUrl: string, livechatUrl: string, webViewRef: any) => {
  const injectRocketChatScript = `(function () {
    var existingScript = document.getElementById("${SCRIPT_ID}");
    if (!existingScript) {
      window.RocketChat = function (c) { window.RocketChat._.push(c) }; 
      window.RocketChat._ = []; 
      window.RocketChat.url = "${livechatUrl}";
      var tag = document.createElement('script');
      tag.async = true; 
      tag.src = "${hostUrl}";
      tag.id = "${SCRIPT_ID}";
      var head = document.getElementsByTagName("head")[0];  
      head.append(tag); 
    }
   })();
   `;

  webViewRef.current.injectJavaScript(injectRocketChatScript);
  webView = webViewRef;
};

const setDefaultsOnLoad = (setIsScriptLoaded: React.Dispatch<React.SetStateAction<boolean>>) => {
  if (isWeb) {
    hideWidget();
  } else {
    restartMobileWidget();
  }
  setTheme();
  setCustomFields([{ name: 'appName', value: APP_NAME }]);
  setIsScriptLoaded(true);
};

// Kept the methods as string then parse it as function (see runRocketChatMethods) for web since they are using the exact same thing.
const rocketChatMethods = {
  restartMobileWidgetString: `
    RocketChat(function () {
      this.minimizeWidget();
      this.maximizeWidget();
    });`,
  maximizeWidgetString: `
    RocketChat(function () {
      this.maximizeWidget();
    });`,
  showWidget: `
    RocketChat(function(){
      this.showWidget();
    });`,
  setCustomFields: (fields: CustomFields[]) =>
    `RocketChat(function(){
      ${fields
        .map(
          ({ name, value, disableOverwrite }) =>
            `this.setCustomField("${name}", "${value}", ${disableOverwrite ? false : true});`,
        )
        .join('')}
    });`,
  setTheme: `
    RocketChat(function(){
      this.setTheme(${JSON.stringify(theme)});
    });`,
  hideWidget: `
    RocketChat(function(){
      this.hideWidget();
    });`,
  setGuestName: (name: string) => `RocketChat(function(){
      this.setGuestName("${name}");
    });`,
  setGuestEmail: (email: string) => `RocketChat(function(){
      this.setGuestEmail("${email}");
    });`,
};
const maximizeWidget = () => runRocketChatMethods(rocketChatMethods.maximizeWidgetString);

const restartMobileWidget = () => runRocketChatMethods(rocketChatMethods.restartMobileWidgetString);

const setCustomFields = (fields: CustomFields[]) => runRocketChatMethods(rocketChatMethods.setCustomFields(fields));

const setTheme = () => runRocketChatMethods(rocketChatMethods.setTheme);

const hideWidget = () => runRocketChatMethods(rocketChatMethods.hideWidget);

const showWidget = () => runRocketChatMethods(rocketChatMethods.showWidget);

const setGuestName = (name: string) => runRocketChatMethods(rocketChatMethods.setGuestName(name));

const setGuestEmail = (email: string) => runRocketChatMethods(rocketChatMethods.setGuestEmail(email));

const getEmail = (contactMethods: any[]): string =>
  contactMethods.find(contactMethod => contactMethod.__typename.toLowerCase() === 'email')?.email ?? '';

export const RocketChatProvider = ({ children }: { children: React.ReactNode }): JSX.Element | null => {
  // Will not take any scope other than global(at least from what I can tell).
  // Mobile can only be passed a function to access the global scope there.
  const { token } = useToken();
  const [isScriptLoaded, setIsScriptLoaded] = useState<boolean>(false);

  useEffect(() => {
    if (isScriptLoaded && token?.accessToken) setCustomFields([{ name: 'token', value: token?.accessToken ?? '' }]);
  }, [token?.accessToken, isScriptLoaded]);

  const { data, loading, error } = useQuery(GET_CONFIGURATIONS, {
    variables: { input: { device: Platform.OS, version: packageJson.version } },
  });

  const { data: accountInfoData, loading: accountInfoLoading } = useQuery(GET_ACCOUNT_INFO_QUERY);

  if (
    !error &&
    !loading &&
    data &&
    !accountInfoLoading &&
    accountInfoData &&
    Platform.OS === 'web' &&
    data.getConfigurations.rocketChat.isEnabled
  ) {
    loadRocketChatScript(
      data.getConfigurations.rocketChat.hostUrl,
      data.getConfigurations.rocketChat.livechatUrl,
      setIsScriptLoaded,
    );
    setGuestName(accountInfoData.getAccountInfo.fullName);
    setGuestEmail(getEmail(accountInfoData.getAccountInfo.contactMethods));
  }

  const loadRocketChatMobile = (webViewRef: any) => {
    loadRocketChatMobileScript(
      data?.getConfigurations.rocketChat.hostUrl,
      data?.getConfigurations.rocketChat.livechatUrl,
      webViewRef,
    );
    setDefaultsOnLoad(setIsScriptLoaded);
    setGuestName(accountInfoData.getAccountInfo.fullName);
    setGuestEmail(getEmail(accountInfoData.getAccountInfo.contactMethods));
  };

  const isRocketChatEnabled = data?.getConfigurations.rocketChat.isEnabled;
  return (
    <Context.Provider
      value={{
        maximizeWidget,
        restartMobileWidget,
        setCustomFields,
        hideWidget,
        showWidget,
        isRocketChatEnabled,
        loadRocketChatMobile,
      }}
    >
      {children}
    </Context.Provider>
  );
};

export const useRocketChatContext = () => useContext(Context);
Clone this wiki locally