Files
chatwoot/app/javascript/sdk/IFrameHelper.js
T

379 lines
12 KiB
JavaScript

import Cookies from 'js-cookie';
import {
addClasses,
loadCSS,
removeClasses,
onLocationChangeListener,
} from './DOMHelpers';
import {
body,
widgetHolder,
createBubbleHolder,
createBubbleIcon,
bubbleSVG,
chatBubble,
closeBubble,
bubbleHolder,
onClickChatBubble,
onBubbleClick,
setBubbleText,
addUnreadClass,
removeUnreadClass,
} from './bubbleHelpers';
import { isWidgetColorLighter } from 'shared/helpers/colorHelper';
import { dispatchWindowEvent } from 'shared/helpers/CustomEventHelper';
import { CHATWOOT_ERROR, CHATWOOT_READY } from '../widget/constants/sdkEvents';
import { SET_USER_ERROR } from '../widget/constants/errorTypes';
import { getUserCookieName, setCookieWithDomain } from './cookieHelpers';
import {
getAlertAudio,
initOnEvents,
} from 'shared/helpers/AudioNotificationHelper';
import { isFlatWidgetStyle } from './settingsHelper';
import { popoutChatWindow } from '../widget/helpers/popoutHelper';
import addHours from 'date-fns/addHours';
const updateAuthCookie = (cookieContent, baseDomain = '') => {
console.log('Updating auth cookie:', { cookieContent, baseDomain });
setCookieWithDomain('cw_conversation', cookieContent, {
baseDomain,
});
};
const updateCampaignReadStatus = baseDomain => {
console.log('Updating campaign read status:', { baseDomain });
const expireBy = addHours(new Date(), 1);
setCookieWithDomain('cw_snooze_campaigns_till', Number(expireBy), {
expires: expireBy,
baseDomain,
});
};
export const IFrameHelper = {
getUrl({ baseUrl, websiteToken }) {
const url = `${baseUrl}/widget?website_token=${websiteToken}`;
console.log('Generated widget URL:', url);
return url;
},
createFrame: ({ baseUrl, websiteToken }) => {
console.log('Creating iframe with:', { baseUrl, websiteToken });
if (IFrameHelper.getAppFrame()) {
console.log('Iframe already exists.');
return;
}
loadCSS();
const iframe = document.createElement('iframe');
const cwCookie = Cookies.get('cw_conversation');
let widgetUrl = IFrameHelper.getUrl({ baseUrl, websiteToken });
if (cwCookie) {
widgetUrl = `${widgetUrl}&cw_conversation=${cwCookie}`;
console.log('Appending conversation cookie to widget URL:', widgetUrl);
}
iframe.src = widgetUrl;
iframe.allow =
'camera;microphone;fullscreen;display-capture;picture-in-picture;clipboard-write;';
iframe.id = 'chatwoot_live_chat_widget';
iframe.style.visibility = 'hidden';
let holderClassName = `woot-widget-holder woot--hide woot-elements--${window.$chatwoot.position}`;
if (window.$chatwoot.hideMessageBubble) {
holderClassName += ` woot-widget--without-bubble`;
}
if (isFlatWidgetStyle(window.$chatwoot.widgetStyle)) {
holderClassName += ` woot-widget-holder--flat`;
}
console.log('Holder class name:', holderClassName);
addClasses(widgetHolder, holderClassName);
widgetHolder.id = 'cw-widget-holder';
widgetHolder.appendChild(iframe);
body.appendChild(widgetHolder);
IFrameHelper.initPostMessageCommunication();
IFrameHelper.initWindowSizeListener();
IFrameHelper.preventDefaultScroll();
},
getAppFrame: () => {
const frame = document.getElementById('chatwoot_live_chat_widget');
console.log('Retrieved app frame:', frame);
return frame;
},
getBubbleHolder: () => {
const holder = document.getElementsByClassName('woot--bubble-holder');
console.log('Retrieved bubble holder:', holder);
return holder;
},
sendMessage: (key, value) => {
console.log('Sending message:', { key, value });
const element = IFrameHelper.getAppFrame();
element.contentWindow.postMessage(
`chatwoot-widget:${JSON.stringify({ event: key, ...value })}`,
'*'
);
},
initPostMessageCommunication: () => {
console.log('Initializing post message communication...');
window.onmessage = e => {
console.log('Message received:', e.data);
if (
typeof e.data !== 'string' ||
e.data.indexOf('chatwoot-widget:') !== 0
) {
return;
}
const message = JSON.parse(e.data.replace('chatwoot-widget:', ''));
console.log('Parsed message:', message);
if (typeof IFrameHelper.events[message.event] === 'function') {
IFrameHelper.events[message.event](message);
}
};
},
initWindowSizeListener: () => {
console.log('Initializing window size listener...');
window.addEventListener('resize', () => IFrameHelper.toggleCloseButton());
},
preventDefaultScroll: () => {
console.log('Adding scroll prevention on widget holder...');
widgetHolder.addEventListener('wheel', event => {
const deltaY = event.deltaY;
const contentHeight = widgetHolder.scrollHeight;
const visibleHeight = widgetHolder.offsetHeight;
const scrollTop = widgetHolder.scrollTop;
console.log('Scroll event:', {
deltaY,
contentHeight,
visibleHeight,
scrollTop,
});
if (
(scrollTop === 0 && deltaY < 0) ||
(visibleHeight + scrollTop === contentHeight && deltaY > 0)
) {
event.preventDefault();
}
});
},
setFrameHeightToFitContent: (extraHeight, isFixedHeight) => {
console.log('Setting iframe height:', { extraHeight, isFixedHeight });
const iframe = IFrameHelper.getAppFrame();
const updatedIframeHeight = isFixedHeight ? `${extraHeight}px` : '100%';
if (iframe)
iframe.setAttribute('style', `height: ${updatedIframeHeight} !important`);
},
setupAudioListeners: () => {
console.log('Setting up audio listeners...');
const { baseUrl = '' } = window.$chatwoot;
getAlertAudio(baseUrl, { type: 'widget', alertTone: 'ding' }).then(() =>
initOnEvents.forEach(event => {
document.removeEventListener(
event,
IFrameHelper.setupAudioListeners,
false
);
})
);
},
events: {
loaded: message => {
console.log('Widget loaded event:', message);
updateAuthCookie(message.config.authToken, window.$chatwoot.baseDomain);
window.$chatwoot.hasLoaded = true;
const campaignsSnoozedTill = Cookies.get('cw_snooze_campaigns_till');
console.log('Campaigns snoozed till:', campaignsSnoozedTill);
IFrameHelper.sendMessage('config-set', {
locale: window.$chatwoot.locale,
position: window.$chatwoot.position,
hideMessageBubble: window.$chatwoot.hideMessageBubble,
showPopoutButton: window.$chatwoot.showPopoutButton,
widgetStyle: window.$chatwoot.widgetStyle,
darkMode: window.$chatwoot.darkMode,
showUnreadMessagesDialog: window.$chatwoot.showUnreadMessagesDialog,
campaignsSnoozedTill,
});
IFrameHelper.onLoad({
widgetColor: message.config.channelConfig.widgetColor,
});
IFrameHelper.toggleCloseButton();
if (window.$chatwoot.user) {
console.log('Setting user on load:', window.$chatwoot.user);
IFrameHelper.sendMessage('set-user', window.$chatwoot.user);
}
window.playAudioAlert = () => {};
initOnEvents.forEach(e => {
document.addEventListener(e, IFrameHelper.setupAudioListeners, false);
});
if (!window.$chatwoot.resetTriggered) {
console.log('Dispatching CHATWOOT_READY event...');
dispatchWindowEvent({ eventName: CHATWOOT_READY });
}
},
error: ({ errorType, data }) => {
console.error('Widget error:', { errorType, data });
dispatchWindowEvent({ eventName: CHATWOOT_ERROR, data: data });
if (errorType === SET_USER_ERROR) {
console.log('Removing user cookie due to error.');
Cookies.remove(getUserCookieName());
}
},
onEvent({ eventIdentifier: eventName, data }) {
console.log('Custom event triggered:', { eventName, data });
dispatchWindowEvent({ eventName, data });
},
setBubbleLabel(message) {
console.log('Setting bubble label:', message);
setBubbleText(window.$chatwoot.launcherTitle || message.label);
},
setAuthCookie({ data: { widgetAuthToken } }) {
console.log('Setting auth cookie with token:', widgetAuthToken);
updateAuthCookie(widgetAuthToken, window.$chatwoot.baseDomain);
},
setCampaignReadOn() {
console.log('Setting campaign read status.');
updateCampaignReadStatus(window.$chatwoot.baseDomain);
},
toggleBubble: state => {
console.log('Toggling bubble state:', state);
let bubbleState = {};
if (state === 'open') {
bubbleState.toggleValue = true;
} else if (state === 'close') {
bubbleState.toggleValue = false;
}
onBubbleClick(bubbleState);
},
popoutChatWindow: ({ baseUrl, websiteToken, locale }) => {
console.log('Popping out chat window:', {
baseUrl,
websiteToken,
locale,
});
const cwCookie = Cookies.get('cw_conversation');
window.$chatwoot.toggle('close');
popoutChatWindow(baseUrl, websiteToken, locale, cwCookie);
},
closeWindow: () => {
console.log('Closing chat window.');
onBubbleClick({ toggleValue: false });
removeUnreadClass();
},
onBubbleToggle: isOpen => {
console.log('Bubble toggle event:', isOpen);
IFrameHelper.sendMessage('toggle-open', { isOpen });
if (isOpen) {
IFrameHelper.pushEvent('webwidget.triggered');
}
},
onLocationChange: ({ referrerURL, referrerHost }) => {
console.log('Location change detected:', { referrerURL, referrerHost });
IFrameHelper.sendMessage('change-url', {
referrerURL,
referrerHost,
});
},
updateIframeHeight: message => {
console.log('Updating iframe height:', message);
const { extraHeight = 0, isFixedHeight } = message;
IFrameHelper.setFrameHeightToFitContent(extraHeight, isFixedHeight);
},
setUnreadMode: () => {
console.log('Setting unread mode.');
addUnreadClass();
onBubbleClick({ toggleValue: true });
},
resetUnreadMode: () => {
console.log('Resetting unread mode.');
removeUnreadClass();
},
handleNotificationDot: event => {
console.log('Handling notification dot:', event);
if (window.$chatwoot.hideMessageBubble) {
return;
}
const bubbleElement = document.querySelector('.woot-widget-bubble');
if (
event.unreadMessageCount > 0 &&
!bubbleElement.classList.contains('unread-notification')
) {
addClasses(bubbleElement, 'unread-notification');
} else if (event.unreadMessageCount === 0) {
removeClasses(bubbleElement, 'unread-notification');
}
},
closeChat: () => {
console.log('Closing chat.');
onBubbleClick({ toggleValue: false });
},
playAudio: () => {
console.log('Playing audio alert.');
window.playAudioAlert();
},
},
pushEvent: eventName => {
console.log('Pushing event:', eventName);
IFrameHelper.sendMessage('push-event', { eventName });
},
onLoad: ({ widgetColor }) => {
console.log('On load with widget color:', widgetColor);
const iframe = IFrameHelper.getAppFrame();
iframe.style.visibility = '';
iframe.setAttribute('id', `chatwoot_live_chat_widget`);
if (IFrameHelper.getBubbleHolder().length) {
console.log('Bubble holder already exists.');
return;
}
createBubbleHolder(window.$chatwoot.hideMessageBubble);
onLocationChangeListener();
let className = 'woot-widget-bubble';
let closeBtnClassName = `woot-elements--${window.$chatwoot.position} woot-widget-bubble woot--close woot--hide`;
if (isFlatWidgetStyle(window.$chatwoot.widgetStyle)) {
className += ' woot-widget-bubble--flat';
closeBtnClassName += ' woot-widget-bubble--flat';
}
if (isWidgetColorLighter(widgetColor)) {
className += ' woot-widget-bubble-color--lighter';
closeBtnClassName += ' woot-widget-bubble-color--lighter';
}
const chatIcon = createBubbleIcon({
className,
path: bubbleSVG,
target: chatBubble,
});
addClasses(closeBubble, closeBtnClassName);
chatIcon.style.background = widgetColor;
closeBubble.style.background = widgetColor;
bubbleHolder.appendChild(chatIcon);
bubbleHolder.appendChild(closeBubble);
onClickChatBubble();
},
toggleCloseButton: () => {
const isMobile = window.matchMedia('(max-width: 668px)').matches;
console.log('Toggling close button for mobile:', isMobile);
IFrameHelper.sendMessage('toggle-close-button', { isMobile });
},
};