yelda-webchat 1.1.9 → 1.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yelda-webchat",
3
- "version": "1.1.9",
3
+ "version": "1.1.13",
4
4
  "description": "Website Chat Plugin",
5
5
  "main": "./dist/js/yeldaWebchatInjector.min.js",
6
6
  "license": "MIT",
@@ -30,5 +30,11 @@ module.exports = {
30
30
  ADD_BUBBLE_TEXT: 'addBubbleText',
31
31
  ADD_MINIMAL_NOTIFICATION_TEXT: 'addMinimalNotificationText'
32
32
  }
33
+ },
34
+ CHAT_BUBBLE_EXTERNAL_ENDPOINT: 'https://webchat.yelda.ai/webchat',
35
+ CHAT_BUBBLE_REQUEST_TIMEOUT: 5000,
36
+ APP_ENV_VALUES: {
37
+ STAGING: 'staging',
38
+ PRODUCTION: 'production'
33
39
  }
34
40
  }
@@ -3,33 +3,26 @@
3
3
 
4
4
  .yelda_assistant_img {
5
5
  cursor: pointer;
6
- opacity: 1;
7
6
  height: auto;
8
- transition-property: opacity, height;
9
- transition-duration: 1s, 1ms;
10
- transition-delay: 1.2s, 1.2s;
7
+ transition: opacity 0.6s ease-in-out 1.2s, height 1ms linear 1.2s;
11
8
  animation-name: appear;
12
9
  animation-duration: 0.8s;
13
10
  animation-delay: 1s;
14
11
  animation-timing-function: cubic-bezier(0.230, 1.000, 0.320, 1.000);
15
12
  animation-iteration-count: 1;
16
- animation-fill-mode: forwards;
13
+ animation-fill-mode: both;
17
14
  opacity: 0;
18
15
  overflow: hidden;
19
16
  }
20
17
 
21
18
  .yelda_container.y_active .yelda_assistant_img{
22
- opacity:0 ;
23
- height: 0;
24
- transition-property: opacity, height;
25
- transition-duration: 1ms, 1ms;
26
- transition-delay: 0, 0;
19
+ display:none;
27
20
  animation: none;
28
21
  }
29
22
 
23
+ /* activated : not the first time we see the bubble */
30
24
  .yelda_assistant_img.activated{
31
- transition-delay: 1s;
32
- /*animation-delay: 0s;*/
25
+ transition: opacity 0.6s ease-in-out 0.6s, height 1ms linear 0.6s;
33
26
  }
34
27
 
35
28
  .yelda_assistant_img.default {
@@ -65,6 +58,9 @@
65
58
  font-size: 35px !important;
66
59
  }
67
60
 
61
+
62
+
63
+
68
64
  .yelda_container {
69
65
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,
70
66
  'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
@@ -93,14 +89,12 @@
93
89
  }
94
90
 
95
91
  .yelda_iframe_container {
96
- width: 430px;
92
+ width: 0;
97
93
  height: 0;
98
94
  max-height: 600px;
99
95
  margin: 0;
100
96
  overflow: hidden;
101
- transition-property: opacity, transform, height;
102
- transition-duration: 1s, 1s, 1ms;
103
- transition-delay: 0s, 0s, 1s;
97
+ transition: opacity 0.6s linear 0s, transform 0.6s ease-in-out 0s, height 1ms linear 0.6s, width 1ms linear 0.6s;
104
98
  opacity: 0;
105
99
  transform: translateY(20px);
106
100
  }
@@ -111,10 +105,9 @@
111
105
  }
112
106
 
113
107
  .y_active .yelda_iframe_container{
108
+ width: 430px;
114
109
  height: calc(100vh - 170px);
115
- transition-property: opacity, transform, height;
116
- transition-duration: 1s, 1s, 1ms;
117
- transition-delay: 0s, 0s, 0s;
110
+ transition: opacity 0.6s linear 0s, transform 0.6s ease-in-out 0s, height 1ms linear 0s, width 1ms linear 0s;
118
111
  opacity: 1 !important;
119
112
  transform: translateY(0);
120
113
  }
@@ -142,13 +135,17 @@
142
135
  z-index: 2147483647;
143
136
  }
144
137
 
138
+ .y_active .yelda_iframe_container{
139
+ width: 100% !important;
140
+ }
141
+
145
142
  .yelda_iframe_container:not(.inner) {
146
143
  position: fixed;
147
144
  left: 0;
148
145
  top: 0;
149
146
  right: 0;
150
147
  bottom: 0;
151
- width: 100%;
148
+ width: 0;
152
149
  height: auto;
153
150
  max-height: none;
154
151
  border-radius: 0;
@@ -156,6 +153,8 @@
156
153
  padding: 0;
157
154
  }
158
155
 
156
+
157
+
159
158
  .yelda_iframe_container.inner {
160
159
  width: 100%;
161
160
  box-shadow: 0 5px 32px rgba(0,0,0,.16);
@@ -177,8 +176,6 @@
177
176
  box-shadow: #00000030 0 2px 8px 0;
178
177
  background-color: white;
179
178
  cursor: pointer;
180
- right: 0;
181
- bottom: 76px;
182
179
  max-width: 300px;
183
180
  text-align: left;
184
181
  font-size: 14px;
@@ -260,3 +257,4 @@ a.bubbleCloseButton:hover {
260
257
  opacity: 1;
261
258
  }
262
259
  }
260
+
@@ -282,15 +282,15 @@ class YeldaChat {
282
282
  /**
283
283
  * Add 'y_active' class to the webChatContainer if required
284
284
  *
285
- * To control the opacity of the iframe container (.yelda_iframe_container) and the assistant image (.yelda_assistant_img),
285
+ * To control the opacity/display of the iframe container (.yelda_iframe_container) and the assistant image (.yelda_assistant_img),
286
286
  * We add and remove the "y_active" CSS class to the webChatContainer (.yelda_container) which is always visible
287
287
  * By default
288
- * - yelda_iframe_container is hidden (opacity: 0;)
289
- * - assistant image is visible (opacity: 1 for yelda_assistant_img)
288
+ * - yelda_iframe_container is hidden (opacity: 0; height:0; width: 0; to not affect page interactions)
289
+ * - assistant image is hidden (opacity: 0), but immediatly animated by css to be displayed (opacity: 1 with animation-name: appear;) for yelda_assistant_img)
290
290
  *
291
291
  * When we add the class "y_active" to (webChatContainer)
292
292
  * - yelda_iframe_container is now visible (.y_active .yelda_iframe_container => opacity: 1)
293
- * - the assistant image is hidden (.yelda_container.y_active .yelda_assistant_img => opacity: 0
293
+ * - the assistant image is hidden (.yelda_container.y_active .yelda_assistant_img => display:none, no opacity manipulation because no need of animation)
294
294
  *
295
295
  * @param {Boolean} data.shouldBeOpened
296
296
  * @param {Boolean} data.isStartBtn
@@ -775,38 +775,108 @@ class YeldaChat {
775
775
  return isFound
776
776
  }
777
777
 
778
+ /**
779
+ * Get the app env from the assistant url
780
+ * @param {String} assistantUrl
781
+ * @returns {String} env
782
+ */
783
+ getAppEnv(assistantUrl) {
784
+ if (config.REGEX_HOST.STAGING.test(assistantUrl)) {
785
+ return config.APP_ENV_VALUES.STAGING
786
+ }
787
+
788
+ // By default, use the production env
789
+ return config.APP_ENV_VALUES.PRODUCTION
790
+ }
791
+
792
+ /**
793
+ * Get the chat bubble url base based on isFallback
794
+ * @param {Object} data { assistantUrl, assistantId, settingId }
795
+ * @param {Boolean} isFallback fallback means yelda endpoint
796
+ * @returns {String} url base
797
+ */
798
+ getChatBubbleUrlBase(data, isFallback) {
799
+ if (isFallback) {
800
+ return `${data.assistantUrl}/assistants`
801
+ }
802
+ return config.CHAT_BUBBLE_EXTERNAL_ENDPOINT
803
+ }
804
+
778
805
  /**
779
806
  * return getAssistantSettings endpoint URL from data
780
807
  * @param {Object} data { assistantUrl, assistantId, settingId }
808
+ * @param {Boolean} isFallback
781
809
  * @return {String} url
782
810
  */
783
- getAssistantSettingsUrl(data) {
811
+ getAssistantSettingsUrl(data, isFallback) {
812
+ const urlBase = this.getChatBubbleUrlBase(data, isFallback)
813
+ // The app env is required as query for the external chatbubble endpoint
814
+ // If the env was not explicitly set in data, guess it from the assistant url
815
+ const appEnv = data.env || this.getAppEnv(data.assistantUrl)
816
+ const envQuery = `env=${appEnv}`
784
817
  if(data.settingId) {
785
- return `${data.assistantUrl}/assistants/settings/${data.settingId}/chatBubble`
818
+ return `${urlBase}/settings/${data.settingId}/chatBubble?${envQuery}`
786
819
  }
787
820
 
788
- return `${data.assistantUrl}/assistants/${data.assistantId}/chatBubble/${data.locale}`
821
+ return `${urlBase}/${data.assistantId}/chatBubble/${data.locale}?${envQuery}`
789
822
  }
790
823
 
791
824
  /**
792
825
  * Update assistantImage with assistant settings from backend if any
793
826
  * @param {Object} data { assistantUrl, assistantId, settingId }
827
+ * @param {Object} options
828
+ * @param {Object} options.isFallback
829
+ * @param {Number} options.customTimeout in milliseconds
794
830
  * @return {Promise}
795
831
  */
796
- getAssistantSettings(data) {
832
+ getAssistantSettings(data, { isFallback = false, customTimeout = null } = {}) {
797
833
  return new Promise((resolve, reject) => {
798
834
  try {
799
835
  const xhr = new XMLHttpRequest()
800
- const url = this.getAssistantSettingsUrl(data)
801
836
 
837
+ // Get the url base depending on if we are doing the basic request (external chatbubble endpoint)
838
+ // or the fallback one (chatbubble endpoint in Yelda)
839
+ const url = this.getAssistantSettingsUrl(data, isFallback)
840
+
802
841
  xhr.open('GET', url)
842
+ if (customTimeout) {
843
+ xhr.timeout = customTimeout
844
+ }
803
845
  xhr.send()
804
846
 
805
847
  // Bind and call are necessary to pass the "this" to the callback function
806
- xhr.onreadystatechange = function() {
848
+ xhr.onreadystatechange = () => {
807
849
  if (xhr.readyState === 4) {
808
- const webchatSettings = xhr.responseText ? JSON.parse(xhr.responseText).data : null
809
- resolve(webchatSettings)
850
+ const responseText = xhr.responseText ? JSON.parse(xhr.responseText) : null
851
+ // Yelda endpoint returns { data: ...webchatSettings } (to be compatible with old versions of the chat-plugin)
852
+ // whereas the external endpoint returns { ...webchatSettings }
853
+ // We need to handle both format
854
+ const webchatSettings = responseText && (responseText.data || responseText)
855
+
856
+ if (webchatSettings) {
857
+ return resolve(webchatSettings)
858
+ }
859
+
860
+ // We did not received the expected webchatSettings
861
+ // If we are already in fallback mode, it means there is no webchat settings for the given init information
862
+ if (isFallback) {
863
+ return resolve(null)
864
+ }
865
+
866
+ // If the initial request do not return webchatSettings, it might be because they were not set in Redis yet
867
+ // So let's call the yelda endpoint (fallback) that will find the setting (if it exists) and set it in Redis
868
+ return resolve(this.getAssistantSettings(data, { isFallback: true }))
869
+ }
870
+ }
871
+
872
+ xhr.ontimeout = () => {
873
+ /*
874
+ It the initial request to the external chatbubble endpoint timed out, (maybe the endpoint is down)
875
+ let's fallback on the yelda endpoint
876
+ */
877
+ if (!isFallback) {
878
+ // Set isFallback to true to be sure to not end up in an infinite loop
879
+ return resolve(this.getAssistantSettings(data, { isFallback: true }))
810
880
  }
811
881
  }
812
882
  } catch (e) {
@@ -846,7 +916,7 @@ class YeldaChat {
846
916
 
847
917
  // Get assistant settings from backend
848
918
  try {
849
- this.webchatSettings = await this.getAssistantSettings(data)
919
+ this.webchatSettings = await this.getAssistantSettings(data, { customTimeout: config.CHAT_BUBBLE_REQUEST_TIMEOUT })
850
920
  // Webchat can be loaded thanks to a single settingId or group of data : {assistantId, assistantSlug, locale}
851
921
  // If only settingId is provided, then we will fill {assistantId, assistantSlug, locale} thanks to the assistant settings
852
922
  data = this.updateAssistantData(data, this.webchatSettings)