mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-11-04 13:07:55 +00:00 
			
		
		
		
	# Pull Request Template
## Description
This PR allows users to dynamically pass custom welcome and availability
messages, along with UI feature toggles, via `window.chatwootSettings`.
If any of the following settings are provided, the widget will use them;
otherwise, it falls back to default behavior.
**New options:**
```
window.chatwootSettings = {
  welcomeTitle: 'Need help?',                        // Custom widget title
  welcomeDescription: 'We’re here to support you.',        // Subtitle in the header
  availableMessage: 'We’re online and ready to chat!', // Shown when team is online
  unavailableMessage: 'We’re currently offline.',      // Shown when team is unavailable
  enableFileUpload: true,          // Enable file attachments
  enableEmojiPicker: true,         // Enable emoji picker in chat input
  enableEndConversation: true     // Allow users to end the conversation
}
```
Fixes
https://linear.app/chatwoot/issue/CW-4589/add-options-to-windowchatwootsettings
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
### Loom video
https://www.loom.com/share/413fc4aa59384366b071450bd19d1bf8?sid=ff30fb4c-267c-4beb-80ab-d6f583aa960d
## Checklist:
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
		
	
		
			
				
	
	
		
			222 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			222 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
import Cookies from 'js-cookie';
 | 
						|
import { IFrameHelper } from '../sdk/IFrameHelper';
 | 
						|
import {
 | 
						|
  getBubbleView,
 | 
						|
  getDarkMode,
 | 
						|
  getWidgetStyle,
 | 
						|
} from '../sdk/settingsHelper';
 | 
						|
import {
 | 
						|
  computeHashForUserData,
 | 
						|
  getUserCookieName,
 | 
						|
  hasUserKeys,
 | 
						|
} from '../sdk/cookieHelpers';
 | 
						|
import {
 | 
						|
  addClasses,
 | 
						|
  removeClasses,
 | 
						|
  restoreWidgetInDOM,
 | 
						|
} from '../sdk/DOMHelpers';
 | 
						|
import { setCookieWithDomain } from '../sdk/cookieHelpers';
 | 
						|
import { SDK_SET_BUBBLE_VISIBILITY } from 'shared/constants/sharedFrameEvents';
 | 
						|
 | 
						|
const runSDK = ({ baseUrl, websiteToken }) => {
 | 
						|
  if (window.$chatwoot) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // if this is a Rails Turbo app
 | 
						|
  document.addEventListener('turbo:before-render', event => {
 | 
						|
    // when morphing the page, this typically happens on reload like events
 | 
						|
    // say you update a "Customer" on a form and it reloads the page
 | 
						|
    // We have already added data-turbo-permananent to true. This
 | 
						|
    // will ensure that the widget it preserved
 | 
						|
    // Read more about morphing here: https://turbo.hotwired.dev/handbook/page_refreshes#morphing
 | 
						|
    // and peristing elements here: https://turbo.hotwired.dev/handbook/building#persisting-elements-across-page-loads
 | 
						|
    if (event.detail.renderMethod === 'morph') return;
 | 
						|
 | 
						|
    restoreWidgetInDOM(event.detail.newBody);
 | 
						|
  });
 | 
						|
 | 
						|
  if (window.Turbolinks) {
 | 
						|
    document.addEventListener('turbolinks:before-render', event => {
 | 
						|
      restoreWidgetInDOM(event.data.newBody);
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  // if this is an astro app
 | 
						|
  document.addEventListener('astro:before-swap', event =>
 | 
						|
    restoreWidgetInDOM(event.newDocument.body)
 | 
						|
  );
 | 
						|
 | 
						|
  const chatwootSettings = window.chatwootSettings || {};
 | 
						|
  let locale = chatwootSettings.locale;
 | 
						|
  let baseDomain = chatwootSettings.baseDomain;
 | 
						|
 | 
						|
  if (chatwootSettings.useBrowserLanguage) {
 | 
						|
    locale = window.navigator.language.replace('-', '_');
 | 
						|
  }
 | 
						|
 | 
						|
  window.$chatwoot = {
 | 
						|
    baseUrl,
 | 
						|
    baseDomain,
 | 
						|
    hasLoaded: false,
 | 
						|
    hideMessageBubble: chatwootSettings.hideMessageBubble || false,
 | 
						|
    isOpen: false,
 | 
						|
    position: chatwootSettings.position === 'left' ? 'left' : 'right',
 | 
						|
    websiteToken,
 | 
						|
    locale,
 | 
						|
    useBrowserLanguage: chatwootSettings.useBrowserLanguage || false,
 | 
						|
    type: getBubbleView(chatwootSettings.type),
 | 
						|
    launcherTitle: chatwootSettings.launcherTitle || '',
 | 
						|
    showPopoutButton: chatwootSettings.showPopoutButton || false,
 | 
						|
    showUnreadMessagesDialog: chatwootSettings.showUnreadMessagesDialog ?? true,
 | 
						|
    widgetStyle: getWidgetStyle(chatwootSettings.widgetStyle) || 'standard',
 | 
						|
    resetTriggered: false,
 | 
						|
    darkMode: getDarkMode(chatwootSettings.darkMode),
 | 
						|
    welcomeTitle: chatwootSettings.welcomeTitle || '',
 | 
						|
    welcomeDescription: chatwootSettings.welcomeDescription || '',
 | 
						|
    availableMessage: chatwootSettings.availableMessage || '',
 | 
						|
    unavailableMessage: chatwootSettings.unavailableMessage || '',
 | 
						|
    enableFileUpload: chatwootSettings.enableFileUpload ?? true,
 | 
						|
    enableEmojiPicker: chatwootSettings.enableEmojiPicker ?? true,
 | 
						|
    enableEndConversation: chatwootSettings.enableEndConversation ?? true,
 | 
						|
 | 
						|
    toggle(state) {
 | 
						|
      IFrameHelper.events.toggleBubble(state);
 | 
						|
    },
 | 
						|
 | 
						|
    toggleBubbleVisibility(visibility) {
 | 
						|
      let widgetElm = document.querySelector('.woot--bubble-holder');
 | 
						|
      let widgetHolder = document.querySelector('.woot-widget-holder');
 | 
						|
      if (visibility === 'hide') {
 | 
						|
        addClasses(widgetHolder, 'woot-widget--without-bubble');
 | 
						|
        addClasses(widgetElm, 'woot-hidden');
 | 
						|
        window.$chatwoot.hideMessageBubble = true;
 | 
						|
      } else if (visibility === 'show') {
 | 
						|
        removeClasses(widgetElm, 'woot-hidden');
 | 
						|
        removeClasses(widgetHolder, 'woot-widget--without-bubble');
 | 
						|
        window.$chatwoot.hideMessageBubble = false;
 | 
						|
      }
 | 
						|
      IFrameHelper.sendMessage(SDK_SET_BUBBLE_VISIBILITY, {
 | 
						|
        hideMessageBubble: window.$chatwoot.hideMessageBubble,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    popoutChatWindow() {
 | 
						|
      IFrameHelper.events.popoutChatWindow({
 | 
						|
        baseUrl: window.$chatwoot.baseUrl,
 | 
						|
        websiteToken: window.$chatwoot.websiteToken,
 | 
						|
        locale,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    setUser(identifier, user) {
 | 
						|
      if (typeof identifier !== 'string' && typeof identifier !== 'number') {
 | 
						|
        throw new Error('Identifier should be a string or a number');
 | 
						|
      }
 | 
						|
 | 
						|
      if (!hasUserKeys(user)) {
 | 
						|
        throw new Error(
 | 
						|
          'User object should have one of the keys [avatar_url, email, name]'
 | 
						|
        );
 | 
						|
      }
 | 
						|
 | 
						|
      const userCookieName = getUserCookieName();
 | 
						|
      const existingCookieValue = Cookies.get(userCookieName);
 | 
						|
      const hashToBeStored = computeHashForUserData({ identifier, user });
 | 
						|
      if (hashToBeStored === existingCookieValue) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      window.$chatwoot.identifier = identifier;
 | 
						|
      window.$chatwoot.user = user;
 | 
						|
      IFrameHelper.sendMessage('set-user', { identifier, user });
 | 
						|
 | 
						|
      setCookieWithDomain(userCookieName, hashToBeStored, {
 | 
						|
        baseDomain,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    setCustomAttributes(customAttributes = {}) {
 | 
						|
      if (!customAttributes || !Object.keys(customAttributes).length) {
 | 
						|
        throw new Error('Custom attributes should have atleast one key');
 | 
						|
      } else {
 | 
						|
        IFrameHelper.sendMessage('set-custom-attributes', { customAttributes });
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    deleteCustomAttribute(customAttribute = '') {
 | 
						|
      if (!customAttribute) {
 | 
						|
        throw new Error('Custom attribute is required');
 | 
						|
      } else {
 | 
						|
        IFrameHelper.sendMessage('delete-custom-attribute', {
 | 
						|
          customAttribute,
 | 
						|
        });
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    setConversationCustomAttributes(customAttributes = {}) {
 | 
						|
      if (!customAttributes || !Object.keys(customAttributes).length) {
 | 
						|
        throw new Error('Custom attributes should have atleast one key');
 | 
						|
      } else {
 | 
						|
        IFrameHelper.sendMessage('set-conversation-custom-attributes', {
 | 
						|
          customAttributes,
 | 
						|
        });
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    deleteConversationCustomAttribute(customAttribute = '') {
 | 
						|
      if (!customAttribute) {
 | 
						|
        throw new Error('Custom attribute is required');
 | 
						|
      } else {
 | 
						|
        IFrameHelper.sendMessage('delete-conversation-custom-attribute', {
 | 
						|
          customAttribute,
 | 
						|
        });
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    setLabel(label = '') {
 | 
						|
      IFrameHelper.sendMessage('set-label', { label });
 | 
						|
    },
 | 
						|
 | 
						|
    removeLabel(label = '') {
 | 
						|
      IFrameHelper.sendMessage('remove-label', { label });
 | 
						|
    },
 | 
						|
 | 
						|
    setLocale(localeToBeUsed = 'en') {
 | 
						|
      IFrameHelper.sendMessage('set-locale', { locale: localeToBeUsed });
 | 
						|
    },
 | 
						|
 | 
						|
    setColorScheme(darkMode = 'light') {
 | 
						|
      IFrameHelper.sendMessage('set-color-scheme', {
 | 
						|
        darkMode: getDarkMode(darkMode),
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    reset() {
 | 
						|
      if (window.$chatwoot.isOpen) {
 | 
						|
        IFrameHelper.events.toggleBubble();
 | 
						|
      }
 | 
						|
 | 
						|
      Cookies.remove('cw_conversation');
 | 
						|
      Cookies.remove(getUserCookieName());
 | 
						|
 | 
						|
      const iframe = IFrameHelper.getAppFrame();
 | 
						|
      iframe.src = IFrameHelper.getUrl({
 | 
						|
        baseUrl: window.$chatwoot.baseUrl,
 | 
						|
        websiteToken: window.$chatwoot.websiteToken,
 | 
						|
      });
 | 
						|
 | 
						|
      window.$chatwoot.resetTriggered = true;
 | 
						|
    },
 | 
						|
  };
 | 
						|
 | 
						|
  IFrameHelper.createFrame({
 | 
						|
    baseUrl,
 | 
						|
    websiteToken,
 | 
						|
  });
 | 
						|
};
 | 
						|
 | 
						|
window.chatwootSDK = {
 | 
						|
  run: runSDK,
 | 
						|
};
 |