chore: Use API to load widget data

This commit is contained in:
Pranav Raj S 2021-07-20 18:20:45 +05:30
parent af73383c23
commit ea9e78a3f3
16 changed files with 174 additions and 128 deletions

View file

@ -3,7 +3,6 @@ import Vuelidate from 'vuelidate';
import VueI18n from 'vue-i18n';
import store from '../widget/store';
import App from '../widget/App.vue';
import ActionCableConnector from '../widget/helpers/actionCable';
import i18n from '../widget/i18n';
Vue.use(VueI18n);
@ -25,9 +24,4 @@ window.onload = () => {
i18n: i18nConfig,
render: h => h(App),
}).$mount('#app');
window.actionCable = new ActionCableConnector(
window.WOOT_WIDGET,
window.chatwootPubsubToken
);
};

View file

@ -19,6 +19,7 @@ import Router from './views/Router';
import { getLocale } from './helpers/urlParamsHelper';
import { BUS_EVENTS } from 'shared/constants/busEvents';
import { isEmptyObject } from 'widget/helpers/utils';
import ActionCableConnector from './helpers/actionCable';
export default {
name: 'App',
@ -38,11 +39,15 @@ export default {
},
computed: {
...mapGetters({
activeCampaign: 'campaign/getActiveCampaign',
authToken: 'appConfig/getAuthToken',
campaigns: 'campaign/getCampaigns',
contact: 'appConfig/getContact',
hasFetched: 'agent/getHasFetched',
isWidgetConfigured: 'appConfig/getWidgetConfigured',
messageCount: 'conversation/getMessageCount',
unreadMessageCount: 'conversation/getUnreadMessageCount',
campaigns: 'campaign/getCampaigns',
activeCampaign: 'campaign/getActiveCampaign',
webChannelConfig: 'appConfig/getWebChannelConfig',
}),
isLeftAligned() {
const isLeft = this.widgetPosition === 'left';
@ -59,31 +64,42 @@ export default {
activeCampaign() {
this.setCampaignView();
},
isWidgetConfigured() {
const { pubsubToken } = this.contact || {};
window.chatwootPubsubToken = pubsubToken;
window.actionCable = new ActionCableConnector(
window.WOOT_WIDGET,
window.chatwootPubsubToken
);
const { websiteToken, locale } = this.webChannelConfig;
this.setLocale(locale);
if (this.isIFrame) {
this.registerListeners();
this.sendLoadedEvent();
setHeader('X-Auth-Token', this.authToken);
} else {
setHeader('X-Auth-Token', this.authToken);
this.fetchOldConversations();
this.fetchAvailableAgents(websiteToken);
this.setLocale(getLocale(window.location.search));
}
if (this.isRNWebView) {
this.registerListeners();
this.sendRNWebViewLoadedEvent();
}
this.$store.dispatch('conversationAttributes/getAttributes');
this.registerUnreadEvents();
this.registerCampaignEvents();
},
},
mounted() {
const { websiteToken, locale } = window.chatwootWebChannel;
this.setLocale(locale);
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));
}
if (this.isRNWebView) {
this.registerListeners();
this.sendRNWebViewLoadedEvent();
}
this.$store.dispatch('conversationAttributes/getAttributes');
this.setWidgetColor(window.chatwootWebChannel);
this.registerUnreadEvents();
this.registerCampaignEvents();
const url = new URL(window.location.href);
this.$store.dispatch('appConfig/fetchAppConfig', {
token: url.searchParams.get('cw_conversation'),
websiteToken: url.searchParams.get('website_token'),
});
},
methods: {
...mapActions('appConfig', ['setWidgetColor']),
...mapActions('conversation', ['fetchOldConversations', 'setUserLastSeen']),
...mapActions('campaign', ['initCampaigns', 'executeCampaign']),
...mapActions('agent', ['fetchAvailableAgents']),
@ -98,7 +114,7 @@ export default {
});
},
setLocale(locale) {
const { enabledLanguages } = window.chatwootWebChannel;
const { enabledLanguages } = this.webChannelConfig;
if (enabledLanguages.some(lang => lang.iso_639_1_code === locale)) {
this.$root.$i18n.locale = locale;
}
@ -124,7 +140,7 @@ export default {
},
registerCampaignEvents() {
bus.$on('on-campaign-view-clicked', campaignId => {
const { websiteToken } = window.chatwootWebChannel;
const { websiteToken } = this.webChannelConfig;
this.showCampaignView = false;
this.showUnreadView = false;
this.unsetUnreadView();
@ -143,9 +159,7 @@ export default {
!this.isWebWidgetTriggered;
if (this.isIFrame && isCampaignReadyToExecute) {
this.showCampaignView = true;
IFrameHelper.sendMessage({
event: 'setCampaignMode',
});
IFrameHelper.sendMessage({ event: 'setCampaignMode' });
}
},
setUnreadView() {
@ -176,7 +190,7 @@ export default {
this.$store.dispatch('events/create', { name: eventName });
},
registerListeners() {
const { websiteToken } = window.chatwootWebChannel;
const { websiteToken } = this.webChannelConfig;
window.addEventListener('message', e => {
if (!IFrameHelper.isAValidEvent(e)) {
return;
@ -233,8 +247,8 @@ export default {
IFrameHelper.sendMessage({
event: 'loaded',
config: {
authToken: window.authToken,
channelConfig: window.chatwootWebChannel,
authToken: this.authToken,
channelConfig: this.webChannelConfig,
},
});
},
@ -242,8 +256,8 @@ export default {
RNHelper.sendMessage({
event: 'loaded',
config: {
authToken: window.authToken,
channelConfig: window.chatwootWebChannel,
authToken: this.authToken,
channelConfig: this.webChannelConfig,
},
});
},

View file

@ -0,0 +1,12 @@
import { API } from 'widget/helpers/axios';
export const fetchAppConfigAPI = async ({ token, websiteToken }) => {
return API({
method: 'post',
url: '/api/v1/widget/config',
data: {
website_token: websiteToken,
},
headers: { 'X-Auth-Token': token },
});
};

View file

@ -28,9 +28,7 @@ export default {
};
},
computed: {
...mapGetters({
globalConfig: 'globalConfig/get',
}),
...mapGetters({ globalConfig: 'appConfig/getGlobalConfig' }),
brandRedirectURL() {
const baseURL = `${this.globalConfig.widgetBrandURL}?utm_source=widget_branding`;
if (this.referrerHost) {
@ -46,8 +44,6 @@ export default {
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
@import '~widget/assets/scss/variables.scss';

View file

@ -53,12 +53,13 @@ export default {
widgetColor: 'appConfig/getWidgetColor',
getConversationSize: 'conversation/getConversationSize',
currentUser: 'contacts/getCurrentUser',
webChannelConfig: 'appConfig/getWebChannelConfig',
}),
textColor() {
return getContrastingTextColor(this.widgetColor);
},
hideReplyBox() {
const { csatSurveyEnabled } = window.chatwootWebChannel;
const { csatSurveyEnabled } = this.webChannelConfig;
const { status } = this.conversationAttributes;
return csatSurveyEnabled && status === 'resolved';
},

View file

@ -57,9 +57,7 @@ export default {
},
},
computed: {
...mapGetters({
widgetColor: 'appConfig/getWidgetColor',
}),
...mapGetters({ widgetColor: 'appConfig/getWidgetColor' }),
isOnline() {
const { workingHoursEnabled } = this.channelConfig;
const anyAgentOnline = this.availableAgents.length > 0;

View file

@ -39,9 +39,7 @@ export default {
},
},
computed: {
...mapGetters({
widgetColor: 'appConfig/getWidgetColor',
}),
...mapGetters({ widgetColor: 'appConfig/getWidgetColor' }),
},
};
</script>

View file

@ -18,6 +18,7 @@
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import { IFrameHelper, RNHelper } from 'widget/helpers/utils';
import { buildPopoutURL } from '../helpers/urlParamsHelper';
@ -39,16 +40,21 @@ export default {
showHeaderActions() {
return this.isIframe || this.isRNWebView;
},
...mapGetters({
authToken: 'appConfig/getAuthToken',
webChannelConfig: 'appConfig/getWebChannelConfig',
}),
},
methods: {
popoutWindow() {
this.closeWindow();
const {
location: { origin },
chatwootWebChannel: { websiteToken },
webChannelConfig: { websiteToken },
authToken,
} = this;
const {
location: { origin },
} = window;
const popoutWindowURL = buildPopoutURL({
origin,
websiteToken,

View file

@ -1,13 +1,15 @@
import compareAsc from 'date-fns/compareAsc';
import { buildDateFromTime } from 'shared/helpers/DateHelper';
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters({ webChannelConfig: 'appConfig/getWebChannelConfig' }),
channelConfig() {
return window.chatwootWebChannel;
return this.webChannelConfig;
},
replyTime() {
return window.chatwootWebChannel.replyTime;
return this.webChannelConfig.replyTime;
},
replyTimeStatus() {
switch (this.replyTime) {

View file

@ -1,16 +1,16 @@
import { mapGetters } from 'vuex';
export default {
computed: {
useInboxAvatarForBot() {
return window.chatwootWidgetDefaults.useInboxAvatarForBot;
},
...mapGetters({ webChannelConfig: 'appConfig/getWebChannelConfig' }),
hasAConnectedAgentBot() {
return !!window.chatwootWebChannel.hasAConnectedAgentBot;
return !!this.webChannelConfig.hasAConnectedAgentBot;
},
inboxAvatarUrl() {
return window.chatwootWebChannel.avatarUrl;
return this.webChannelConfig.avatarUrl;
},
channelConfig() {
return window.chatwootWebChannel;
return this.webChannelConfig;
},
hasEmojiPickerEnabled() {
return this.channelConfig.enabledFeatures.includes('emoji_picker');
@ -19,12 +19,12 @@ export default {
return this.channelConfig.enabledFeatures.includes('attachments');
},
preChatFormEnabled() {
return window.chatwootWebChannel.preChatFormEnabled;
return this.webChannelConfig.preChatFormEnabled;
},
preChatFormOptions() {
let requireEmail = false;
let preChatMessage = '';
const options = window.chatwootWebChannel.preChatFormOptions || {};
const options = this.webChannelConfig.preChatFormOptions || {};
if (!this.isOnNewConversation) {
requireEmail = options.require_email;
preChatMessage = options.pre_chat_message;

View file

@ -1,24 +0,0 @@
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home,
},
// {
// path: '/about',
// name: 'about',
// // route level code-splitting
// // this generates a separate chunk (about.[hash].js) for this route
// // which is lazy-loaded when the route is visited.
// component: () =>
// import(/* webpackChunkName: "about" */ './views/About.vue'),
// },
],
});

View file

@ -7,7 +7,6 @@ import conversation from 'widget/store/modules/conversation';
import conversationAttributes from 'widget/store/modules/conversationAttributes';
import conversationLabels from 'widget/store/modules/conversationLabels';
import events from 'widget/store/modules/events';
import globalConfig from 'shared/store/globalConfig';
import message from 'widget/store/modules/message';
import campaign from 'widget/store/modules/campaign';
@ -21,7 +20,6 @@ export default new Vuex.Store({
conversationAttributes,
conversationLabels,
events,
globalConfig,
message,
campaign,
},

View file

@ -1,22 +1,96 @@
import { SET_WIDGET_COLOR } from '../types';
import { fetchAppConfigAPI } from '../../api/appConfig';
import { SET_APP_CONFIG, SET_APP_CONFIG_UI_FLAGS } from '../types';
const state = {
widgetColor: '',
webChannelConfig: {
enabledFeatures: [],
widgetColor: '',
},
authToken: '',
contact: {
pubsubToken: '',
},
globalConfig: {
brandName: '',
logoThumbnail: '',
widgetBrandURL: '',
},
uiFlags: {
isFetching: false,
isWidgetConfigured: false,
},
};
const getters = {
getWidgetColor: $state => $state.widgetColor,
getWidgetColor: $state => $state.webChannelConfig.widgetColor,
getWebChannelConfig: $state => $state.webChannelConfig,
getWidgetConfigured: $state => $state.uiFlags.isWidgetConfigured,
getGlobalConfig: $state => $state.globalConfig,
getAuthToken: $state => $state.authToken,
getContact: $state => $state.contact || {},
};
const actions = {
setWidgetColor({ commit }, data) {
commit(SET_WIDGET_COLOR, data);
setAppConfig({ commit }, data) {
commit(SET_APP_CONFIG, data);
},
async fetchAppConfig({ commit }, params) {
commit(SET_APP_CONFIG_UI_FLAGS, { isFetching: true });
try {
const { data } = await fetchAppConfigAPI(params);
commit(SET_APP_CONFIG, data);
commit(SET_APP_CONFIG_UI_FLAGS, { isWidgetConfigured: true });
} catch (error) {
// Ignore error
} finally {
commit(SET_APP_CONFIG_UI_FLAGS, { isFetching: false });
}
},
};
const mutations = {
[SET_WIDGET_COLOR]($state, data) {
$state.widgetColor = data.widgetColor;
[SET_APP_CONFIG]($state, data) {
const {
chatwoot_website_channel: webChannelConfig,
chatwoot_widget_defaults: widgetDefaults,
global_config: globalConfig,
} = data;
$state.webChannelConfig = {
avatarUrl: webChannelConfig.avatar_url,
csatSurveyEnabled: webChannelConfig.csat_survey_enabled,
enabledFeatures: webChannelConfig.enabled_features,
enabledLanguages: webChannelConfig.enabled_languages,
hasAConnectedAgentBot: webChannelConfig.has_a_connected_agent_bot,
locale: webChannelConfig.locale,
outOfOfficeMessage: webChannelConfig.out_of_office_message,
preChatFormEnabled: webChannelConfig.pre_chat_form_enabled,
preChatFormOptions: webChannelConfig.pre_chat_form_options,
replyTime: webChannelConfig.reply_time,
useInboxAvatarForBot: widgetDefaults.use_inbox_avatar_for_bot,
utcOffset: webChannelConfig.utc_off_set,
websiteName: webChannelConfig.website_name,
websiteToken: webChannelConfig.website_token,
welcomeTagline: webChannelConfig.welcome_tagline,
welcomeTitle: webChannelConfig.welcome_title,
widgetColor: webChannelConfig.widget_color,
workingHours: webChannelConfig.working_hours,
workingHoursEnabled: webChannelConfig.working_hours_enabled,
};
$state.contact = {
pubsubToken: data.contact.pubsub_token,
};
$state.authToken = data.auth_token;
$state.globalConfig = {
brandName: globalConfig.BRAND_NAME,
logoThumbnail: globalConfig.LOGO_THUMBNAIL,
widgetBrandURL: globalConfig.WIDGET_BRAND_URL,
};
},
[SET_APP_CONFIG_UI_FLAGS]($state, uiFlags) {
$state.uiFlags = {
...$state.uiFlags,
...uiFlags,
};
},
};

View file

@ -1,4 +1,5 @@
export const SET_WIDGET_COLOR = 'SET_WIDGET_COLOR';
export const SET_APP_CONFIG = 'SET_APP_CONFIG';
export const SET_APP_CONFIG_UI_FLAGS = 'SET_APP_CONFIG_UI_FLAGS';
export const SET_CONVERSATION_ATTRIBUTES = 'SET_CONVERSATION_ATTRIBUTES';
export const UPDATE_CONVERSATION_ATTRIBUTES = 'UPDATE_CONVERSATION_ATTRIBUTES';
export const CLEAR_CONVERSATION_ATTRIBUTES = 'CLEAR_CONVERSATION_ATTRIBUTES';

View file

@ -121,14 +121,18 @@ export default {
@import '~widget/assets/scss/variables';
.unread-wrap {
width: 100%;
height: 100%;
background: transparent;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
height: 100%;
justify-content: flex-end;
overflow: hidden;
width: 100%;
@media only screen and (max-device-width: 667px) {
padding: $space-normal;
}
.unread-messages {
padding-bottom: $space-small;

View file

@ -4,34 +4,6 @@
<title><%= @global_config['INSTALLATION_NAME'] %></title>
<%= csrf_meta_tags %>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
<script>
window.chatwootWebChannel = {
avatarUrl: '<%= @web_widget.inbox.avatar_url %>',
hasAConnectedAgentBot: '<%= @web_widget.inbox.agent_bot&.name %>',
locale: '<%= @web_widget.account.locale %>',
websiteName: '<%= @web_widget.inbox.name %>',
websiteToken: '<%= @web_widget.website_token %>',
welcomeTagline: '<%= @web_widget.welcome_tagline %>',
welcomeTitle: '<%= @web_widget.welcome_title %>',
widgetColor: '<%= @web_widget.widget_color %>',
enabledFeatures: <%= @web_widget.selected_feature_flags.to_json.html_safe %>,
enabledLanguages: <%= available_locales_with_name.to_json.html_safe %>,
replyTime: '<%= @web_widget.reply_time %>',
preChatFormEnabled: <%= @web_widget.pre_chat_form_enabled %>,
preChatFormOptions: <%= @web_widget.pre_chat_form_options.to_json.html_safe %>,
workingHoursEnabled: <%= @web_widget.inbox.working_hours_enabled %>,
csatSurveyEnabled: <%= @web_widget.inbox.csat_survey_enabled %>,
workingHours: <%= @web_widget.inbox.working_hours.to_json.html_safe %>,
outOfOfficeMessage: <%= @web_widget.inbox.out_of_office_message.to_json.html_safe %>,
utcOffset: '<%= ActiveSupport::TimeZone[@web_widget.inbox.timezone].formatted_offset %>'
}
window.chatwootWidgetDefaults = {
useInboxAvatarForBot: <%= ActiveModel::Type::Boolean.new.cast(ENV.fetch('USE_INBOX_AVATAR_FOR_BOT', false)) %>,
}
window.chatwootPubsubToken = '<%= @contact.pubsub_token %>'
window.authToken = '<%= @token %>'
window.globalConfig = <%= raw @global_config.to_json %>
</script>
<%= javascript_pack_tag 'widget' %>
<%= stylesheet_pack_tag 'widget' %>
</head>