feat: Add a popout option on webwidget (#1174)
* feat: Add a popout option on webwidget
This commit is contained in:
parent
ce13efd273
commit
45cd949c40
17 changed files with 347 additions and 127 deletions
|
@ -14,6 +14,7 @@ const runSDK = ({ baseUrl, websiteToken }) => {
|
|||
locale: chatwootSettings.locale,
|
||||
type: getBubbleView(chatwootSettings.type),
|
||||
launcherTitle: chatwootSettings.launcherTitle || '',
|
||||
showPopoutButton: chatwootSettings.showPopoutButton || false,
|
||||
|
||||
toggle() {
|
||||
IFrameHelper.events.toggleBubble();
|
||||
|
|
|
@ -99,6 +99,7 @@ export const IFrameHelper = {
|
|||
locale: window.$chatwoot.locale,
|
||||
position: window.$chatwoot.position,
|
||||
hideMessageBubble: window.$chatwoot.hideMessageBubble,
|
||||
showPopoutButton: window.$chatwoot.showPopoutButton,
|
||||
});
|
||||
IFrameHelper.onLoad({
|
||||
widgetColor: message.config.channelConfig.widgetColor,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
:unread-message-count="unreadMessageCount"
|
||||
:is-left-aligned="isLeftAligned"
|
||||
:hide-message-bubble="hideMessageBubble"
|
||||
:show-popout-button="showPopoutButton"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
@ -21,6 +22,7 @@ import { setHeader } from 'widget/helpers/axios';
|
|||
import { IFrameHelper } from 'widget/helpers/utils';
|
||||
|
||||
import Router from './views/Router';
|
||||
import { getLocale } from './helpers/urlParamsHelper';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
|
@ -33,6 +35,7 @@ export default {
|
|||
isMobile: false,
|
||||
hideMessageBubble: false,
|
||||
widgetPosition: 'right',
|
||||
showPopoutButton: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -49,74 +52,25 @@ export default {
|
|||
const isLeft = this.widgetPosition === 'left';
|
||||
return isLeft;
|
||||
},
|
||||
isIFrame() {
|
||||
return IFrameHelper.isIFrame();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const { websiteToken, locale } = window.chatwootWebChannel;
|
||||
this.setLocale(locale);
|
||||
|
||||
if (IFrameHelper.isIFrame()) {
|
||||
IFrameHelper.sendMessage({
|
||||
event: 'loaded',
|
||||
config: {
|
||||
authToken: window.authToken,
|
||||
channelConfig: window.chatwootWebChannel,
|
||||
},
|
||||
});
|
||||
if (this.isIFrame) {
|
||||
this.registerListeners();
|
||||
this.sendLoadedEvent();
|
||||
setHeader('X-Auth-Token', window.authToken);
|
||||
} else {
|
||||
setHeader('X-Auth-Token', window.authToken);
|
||||
this.fetchOldConversations();
|
||||
this.fetchAvailableAgents(websiteToken);
|
||||
this.setLocale(getLocale(window.location.search));
|
||||
}
|
||||
this.setWidgetColor(window.chatwootWebChannel);
|
||||
|
||||
window.addEventListener('message', e => {
|
||||
const wootPrefix = 'chatwoot-widget:';
|
||||
const isDataNotString = typeof e.data !== 'string';
|
||||
const isNotFromWoot = isDataNotString || e.data.indexOf(wootPrefix) !== 0;
|
||||
|
||||
if (isNotFromWoot) return;
|
||||
|
||||
const message = JSON.parse(e.data.replace(wootPrefix, ''));
|
||||
if (message.event === 'config-set') {
|
||||
this.setLocale(message.locale);
|
||||
this.setBubbleLabel();
|
||||
this.setPosition(message.position);
|
||||
this.fetchOldConversations().then(() => {
|
||||
this.setUnreadView();
|
||||
});
|
||||
this.fetchAvailableAgents(websiteToken);
|
||||
this.setHideMessageBubble(message.hideMessageBubble);
|
||||
} else if (message.event === 'widget-visible') {
|
||||
this.scrollConversationToBottom();
|
||||
} else if (message.event === 'set-current-url') {
|
||||
window.refererURL = message.refererURL;
|
||||
} else if (message.event === 'toggle-close-button') {
|
||||
this.isMobile = message.showClose;
|
||||
} else if (message.event === 'push-event') {
|
||||
this.createWidgetEvents(message);
|
||||
} else if (message.event === 'set-label') {
|
||||
this.$store.dispatch('conversationLabels/create', message.label);
|
||||
} else if (message.event === 'remove-label') {
|
||||
this.$store.dispatch('conversationLabels/destroy', message.label);
|
||||
} else if (message.event === 'set-user') {
|
||||
this.$store.dispatch('contacts/update', message);
|
||||
} else if (message.event === 'set-custom-attributes') {
|
||||
this.$store.dispatch(
|
||||
'contacts/setCustomAttributes',
|
||||
message.customAttributes
|
||||
);
|
||||
} else if (message.event === 'delete-custom-attribute') {
|
||||
this.$store.dispatch('contacts/setCustomAttributes', {
|
||||
[message.customAttribute]: null,
|
||||
});
|
||||
} else if (message.event === 'set-locale') {
|
||||
this.setLocale(message.locale);
|
||||
this.setBubbleLabel();
|
||||
} else if (message.event === 'set-unread-view') {
|
||||
this.showUnreadView = true;
|
||||
} else if (message.event === 'unset-unread-view') {
|
||||
this.showUnreadView = false;
|
||||
}
|
||||
});
|
||||
|
||||
this.$store.dispatch('conversationAttributes/get');
|
||||
this.setWidgetColor(window.chatwootWebChannel);
|
||||
this.registerUnreadEvents();
|
||||
},
|
||||
methods: {
|
||||
|
@ -147,15 +101,23 @@ export default {
|
|||
this.hideMessageBubble = !!hideBubble;
|
||||
},
|
||||
registerUnreadEvents() {
|
||||
bus.$on('on-agent-message-recieved', () => this.setUnreadView());
|
||||
bus.$on('on-agent-message-recieved', () => {
|
||||
if (!this.isIFrame) {
|
||||
this.setUserLastSeen();
|
||||
}
|
||||
this.setUnreadView();
|
||||
});
|
||||
bus.$on('on-unread-view-clicked', () => {
|
||||
this.unsetUnreadView();
|
||||
this.setUserLastSeen();
|
||||
});
|
||||
},
|
||||
setPopoutDisplay(showPopoutButton) {
|
||||
this.showPopoutButton = showPopoutButton;
|
||||
},
|
||||
setUnreadView() {
|
||||
const { unreadMessageCount } = this;
|
||||
if (IFrameHelper.isIFrame() && unreadMessageCount > 0) {
|
||||
if (this.isIFrame && unreadMessageCount > 0) {
|
||||
IFrameHelper.sendMessage({
|
||||
event: 'setUnreadMode',
|
||||
unreadMessageCount,
|
||||
|
@ -163,7 +125,7 @@ export default {
|
|||
}
|
||||
},
|
||||
unsetUnreadView() {
|
||||
if (IFrameHelper.isIFrame()) {
|
||||
if (this.isIFrame) {
|
||||
IFrameHelper.sendMessage({ event: 'resetUnreadMode' });
|
||||
}
|
||||
},
|
||||
|
@ -176,6 +138,63 @@ export default {
|
|||
this.setUserLastSeen();
|
||||
this.$store.dispatch('events/create', { name: eventName });
|
||||
},
|
||||
registerListeners() {
|
||||
const { websiteToken } = window.chatwootWebChannel;
|
||||
window.addEventListener('message', e => {
|
||||
if (!IFrameHelper.isAValidEvent(e)) {
|
||||
return;
|
||||
}
|
||||
const message = IFrameHelper.getMessage(e);
|
||||
if (message.event === 'config-set') {
|
||||
this.setLocale(message.locale);
|
||||
this.setBubbleLabel();
|
||||
this.setPosition(message.position);
|
||||
this.fetchOldConversations().then(() => this.setUnreadView());
|
||||
this.setPopoutDisplay(message.showPopoutButton);
|
||||
this.fetchAvailableAgents(websiteToken);
|
||||
this.setHideMessageBubble(message.hideMessageBubble);
|
||||
} else if (message.event === 'widget-visible') {
|
||||
this.scrollConversationToBottom();
|
||||
} else if (message.event === 'set-current-url') {
|
||||
window.refererURL = message.refererURL;
|
||||
} else if (message.event === 'toggle-close-button') {
|
||||
this.isMobile = message.showClose;
|
||||
} else if (message.event === 'push-event') {
|
||||
this.createWidgetEvents(message);
|
||||
} else if (message.event === 'set-label') {
|
||||
this.$store.dispatch('conversationLabels/create', message.label);
|
||||
} else if (message.event === 'remove-label') {
|
||||
this.$store.dispatch('conversationLabels/destroy', message.label);
|
||||
} else if (message.event === 'set-user') {
|
||||
this.$store.dispatch('contacts/update', message);
|
||||
} else if (message.event === 'set-custom-attributes') {
|
||||
this.$store.dispatch(
|
||||
'contacts/setCustomAttributes',
|
||||
message.customAttributes
|
||||
);
|
||||
} else if (message.event === 'delete-custom-attribute') {
|
||||
this.$store.dispatch('contacts/setCustomAttributes', {
|
||||
[message.customAttribute]: null,
|
||||
});
|
||||
} else if (message.event === 'set-locale') {
|
||||
this.setLocale(message.locale);
|
||||
this.setBubbleLabel();
|
||||
} else if (message.event === 'set-unread-view') {
|
||||
this.showUnreadView = true;
|
||||
} else if (message.event === 'unset-unread-view') {
|
||||
this.showUnreadView = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
sendLoadedEvent() {
|
||||
IFrameHelper.sendMessage({
|
||||
event: 'loaded',
|
||||
config: {
|
||||
authToken: window.authToken,
|
||||
channelConfig: window.chatwootWebChannel,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -58,4 +58,14 @@ $button-border-width: 1px;
|
|||
&.block {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.transparent {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
&.compact {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ html,
|
|||
body {
|
||||
font-family: $font-family;
|
||||
font-size: 10px;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
@ -18,36 +20,15 @@ body {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
width: $space-two;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
background-color: $color-heading;
|
||||
content: ' ';
|
||||
height: $space-normal;
|
||||
left: $space-small;
|
||||
position: absolute;
|
||||
top: $space-micro;
|
||||
width: 2px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
&::after {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
}
|
||||
|
||||
.is-mobile {
|
||||
.header-wrap {
|
||||
.actions {
|
||||
.close-button {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.new-window--button {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,16 +4,18 @@
|
|||
<img v-if="avatarUrl" :src="avatarUrl" alt="avatar" />
|
||||
<h2 class="title" v-html="title"></h2>
|
||||
</div>
|
||||
<span class="close-button" @click="closeWindow"></span>
|
||||
<header-actions :show-popout-button="showPopoutButton" />
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { IFrameHelper } from 'widget/helpers/utils';
|
||||
|
||||
import HeaderActions from './HeaderActions';
|
||||
export default {
|
||||
name: 'ChatHeader',
|
||||
components: {
|
||||
HeaderActions,
|
||||
},
|
||||
props: {
|
||||
avatarUrl: {
|
||||
type: String,
|
||||
|
@ -23,21 +25,16 @@ export default {
|
|||
type: String,
|
||||
default: '',
|
||||
},
|
||||
showPopoutButton: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
widgetColor: 'appConfig/getWidgetColor',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
closeWindow() {
|
||||
if (IFrameHelper.isIFrame()) {
|
||||
IFrameHelper.sendMessage({
|
||||
event: 'toggleBubble',
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -73,9 +70,5 @@ export default {
|
|||
width: 24px;
|
||||
margin-right: $space-small;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<template>
|
||||
<header class="header-expanded">
|
||||
<img v-if="avatarUrl" class="logo" :src="avatarUrl" />
|
||||
<span class="close close-button" @click="closeWindow"></span>
|
||||
<div class="header--row">
|
||||
<img v-if="avatarUrl" class="logo" :src="avatarUrl" />
|
||||
<header-actions :show-popout-button="showPopoutButton" />
|
||||
</div>
|
||||
<h2 class="title" v-html="introHeading"></h2>
|
||||
<p class="body" v-html="introBody"></p>
|
||||
</header>
|
||||
|
@ -9,10 +11,12 @@
|
|||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { IFrameHelper } from 'widget/helpers/utils';
|
||||
|
||||
import HeaderActions from './HeaderActions';
|
||||
export default {
|
||||
name: 'ChatHeaderExpanded',
|
||||
components: {
|
||||
HeaderActions,
|
||||
},
|
||||
props: {
|
||||
avatarUrl: {
|
||||
type: String,
|
||||
|
@ -26,21 +30,16 @@ export default {
|
|||
type: String,
|
||||
default: '',
|
||||
},
|
||||
showPopoutButton: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
widgetColor: 'appConfig/getWidgetColor',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
closeWindow() {
|
||||
if (IFrameHelper.isIFrame()) {
|
||||
IFrameHelper.sendMessage({
|
||||
event: 'toggleBubble',
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -59,12 +58,6 @@ export default {
|
|||
height: 56px;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: $space-medium;
|
||||
top: $space-medium;
|
||||
display: none;
|
||||
}
|
||||
.title {
|
||||
color: $color-heading;
|
||||
font-size: $font-size-mega;
|
||||
|
@ -79,4 +72,10 @@ export default {
|
|||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.header--row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
|
|
89
app/javascript/widget/components/HeaderActions.vue
Normal file
89
app/javascript/widget/components/HeaderActions.vue
Normal file
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<div v-if="isIframe" class="actions">
|
||||
<button
|
||||
v-if="showPopoutButton"
|
||||
class="button transparent compact new-window--button"
|
||||
@click="popoutWindow"
|
||||
>
|
||||
<span class="ion-android-open"></span>
|
||||
</button>
|
||||
<button class="button transparent compact close-button">
|
||||
<span class="ion-android-close" @click="closeWindow"></span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { IFrameHelper } from 'widget/helpers/utils';
|
||||
import { buildPopoutURL } from '../helpers/urlParamsHelper';
|
||||
import Vue from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'HeaderActions',
|
||||
props: {
|
||||
showPopoutButton: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isIframe() {
|
||||
return IFrameHelper.isIFrame();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
popoutWindow() {
|
||||
this.closeWindow();
|
||||
const {
|
||||
location: { origin },
|
||||
chatwootWebChannel: { websiteToken },
|
||||
authToken,
|
||||
} = window;
|
||||
|
||||
const popoutWindowURL = buildPopoutURL({
|
||||
origin,
|
||||
websiteToken,
|
||||
locale: Vue.config.lang,
|
||||
conversationCookie: authToken,
|
||||
});
|
||||
const popoutWindow = window.open(
|
||||
popoutWindowURL,
|
||||
`webwidget_session_${websiteToken}`,
|
||||
'resizable=off,width=400,height=600'
|
||||
);
|
||||
popoutWindow.focus();
|
||||
},
|
||||
closeWindow() {
|
||||
if (IFrameHelper.isIFrame()) {
|
||||
IFrameHelper.sendMessage({
|
||||
event: 'toggleBubble',
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
margin-left: $space-normal;
|
||||
}
|
||||
|
||||
span {
|
||||
color: $color-heading;
|
||||
font-size: $font-size-large;
|
||||
|
||||
&.ion-android-close {
|
||||
font-size: $font-size-big;
|
||||
}
|
||||
}
|
||||
|
||||
.close-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -12,3 +12,5 @@ export const MESSAGE_TYPE = {
|
|||
ACTIVITY: 2,
|
||||
TEMPLATE: 3,
|
||||
};
|
||||
|
||||
export const WOOT_PREFIX = 'chatwoot-widget:';
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import { buildSearchParamsWithLocale } from '../urlParamsHelper';
|
||||
import {
|
||||
buildSearchParamsWithLocale,
|
||||
getLocale,
|
||||
buildPopoutURL,
|
||||
} from '../urlParamsHelper';
|
||||
|
||||
jest.mock('vue', () => ({
|
||||
config: {
|
||||
|
@ -14,3 +18,29 @@ describe('#buildSearchParamsWithLocale', () => {
|
|||
expect(buildSearchParamsWithLocale('')).toEqual('?locale=el');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getLocale', () => {
|
||||
it('returns correct locale', () => {
|
||||
expect(getLocale('?test=1&cw_conv=2&locale=fr')).toEqual('fr');
|
||||
expect(getLocale('?test=1&locale=fr')).toEqual('fr');
|
||||
expect(getLocale('?test=1&cw_conv=2&website_token=3&locale=fr')).toEqual(
|
||||
'fr'
|
||||
);
|
||||
expect(getLocale('')).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#buildPopoutURL', () => {
|
||||
it('returns popout URL', () => {
|
||||
expect(
|
||||
buildPopoutURL({
|
||||
origin: 'https://chatwoot.com',
|
||||
conversationCookie: 'random-jwt-token',
|
||||
websiteToken: 'random-website-token',
|
||||
locale: 'ar',
|
||||
})
|
||||
).toEqual(
|
||||
'https://chatwoot.com/widget?cw_conversation=random-jwt-token&website_token=random-website-token&locale=ar'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
42
app/javascript/widget/helpers/specs/utils.spec.js
Normal file
42
app/javascript/widget/helpers/specs/utils.spec.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { IFrameHelper } from '../utils';
|
||||
|
||||
jest.mock('vue', () => ({
|
||||
config: {
|
||||
lang: 'el',
|
||||
},
|
||||
}));
|
||||
|
||||
describe('#IFrameHelper', () => {
|
||||
describe('#isAValidEvent', () => {
|
||||
it('returns if the event is valid', () => {
|
||||
expect(
|
||||
IFrameHelper.isAValidEvent({
|
||||
data:
|
||||
'chatwoot-widget:{"event":"config-set","locale":"fr","position":"left","hideMessageBubble":false,"showPopoutButton":true}',
|
||||
})
|
||||
).toEqual(true);
|
||||
expect(
|
||||
IFrameHelper.isAValidEvent({
|
||||
data:
|
||||
'{"event":"config-set","locale":"fr","position":"left","hideMessageBubble":false,"showPopoutButton":true}',
|
||||
})
|
||||
).toEqual(false);
|
||||
});
|
||||
});
|
||||
describe('#getMessage', () => {
|
||||
it('returns parsed message', () => {
|
||||
expect(
|
||||
IFrameHelper.getMessage({
|
||||
data:
|
||||
'chatwoot-widget:{"event":"config-set","locale":"fr","position":"left","hideMessageBubble":false,"showPopoutButton":true}',
|
||||
})
|
||||
).toEqual({
|
||||
event: 'config-set',
|
||||
locale: 'fr',
|
||||
position: 'left',
|
||||
hideMessageBubble: false,
|
||||
showPopoutButton: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,4 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
|
||||
export const buildSearchParamsWithLocale = search => {
|
||||
const locale = Vue.config.lang;
|
||||
if (search) {
|
||||
|
@ -8,3 +9,23 @@ export const buildSearchParamsWithLocale = search => {
|
|||
}
|
||||
return search;
|
||||
};
|
||||
|
||||
export const getLocale = (search = '') => {
|
||||
const searchParamKeyValuePairs = search.split('&');
|
||||
return searchParamKeyValuePairs.reduce((acc, keyValuePair) => {
|
||||
const [key, value] = keyValuePair.split('=');
|
||||
if (key === 'locale') {
|
||||
return value;
|
||||
}
|
||||
return acc;
|
||||
}, undefined);
|
||||
};
|
||||
|
||||
export const buildPopoutURL = ({
|
||||
origin,
|
||||
conversationCookie,
|
||||
websiteToken,
|
||||
locale,
|
||||
}) => {
|
||||
return `${origin}/widget?cw_conversation=${conversationCookie}&website_token=${websiteToken}&locale=${locale}`;
|
||||
};
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { WOOT_PREFIX } from './constants';
|
||||
|
||||
export const isEmptyObject = obj =>
|
||||
Object.keys(obj).length === 0 && obj.constructor === Object;
|
||||
|
||||
|
@ -16,4 +18,11 @@ export const IFrameHelper = {
|
|||
'*'
|
||||
);
|
||||
},
|
||||
isAValidEvent: e => {
|
||||
const isDataAString = typeof e.data === 'string';
|
||||
const isAValidWootEvent =
|
||||
isDataAString && e.data.indexOf(WOOT_PREFIX) === 0;
|
||||
return isAValidWootEvent;
|
||||
},
|
||||
getMessage: e => JSON.parse(e.data.replace(WOOT_PREFIX, '')),
|
||||
};
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
:intro-heading="introHeading"
|
||||
:intro-body="introBody"
|
||||
:avatar-url="channelConfig.avatarUrl"
|
||||
:show-popout-button="showPopoutButton"
|
||||
/>
|
||||
<ChatHeader
|
||||
v-else
|
||||
:title="channelConfig.websiteName"
|
||||
:avatar-url="channelConfig.avatarUrl"
|
||||
:show-popout-button="showPopoutButton"
|
||||
/>
|
||||
</div>
|
||||
<AvailableAgents v-if="showAvailableAgents" :agents="availableAgents" />
|
||||
|
@ -69,6 +71,10 @@ export default {
|
|||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
showPopoutButton: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isOpen() {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
:has-fetched="hasFetched"
|
||||
:conversation-attributes="conversationAttributes"
|
||||
:unread-message-count="unreadMessageCount"
|
||||
:show-popout-button="showPopoutButton"
|
||||
/>
|
||||
<unread
|
||||
v-else
|
||||
|
@ -81,6 +82,10 @@ export default {
|
|||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
showPopoutButton: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
window.chatwootSettings = {
|
||||
hideMessageBubble: false,
|
||||
position: 'left',
|
||||
locale: 'en',
|
||||
locale: 'fr',
|
||||
type: 'expanded_bubble',
|
||||
showPopoutButton: true,
|
||||
};
|
||||
|
||||
(function(d,t) {
|
||||
|
|
|
@ -46,6 +46,17 @@ window.chatwootSettings = {
|
|||
}
|
||||
```
|
||||
|
||||
### To enable popout window
|
||||
|
||||
Inorder to enable the popout window, add the following configuration to `chatwootSettings`. This option is disabled by default.
|
||||
|
||||
```js
|
||||
window.chatwootSettings = {
|
||||
// ...Other Config
|
||||
showPopoutButton: true,
|
||||
}
|
||||
```
|
||||
|
||||
### To trigger widget without displaying bubble
|
||||
|
||||
```js
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue