aumera-on-screen-widget 0.0.15 → 0.0.17

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.
Files changed (2) hide show
  1. package/df-btn.js +208 -35
  2. package/package.json +1 -1
package/df-btn.js CHANGED
@@ -1,4 +1,4 @@
1
- (async function() {
1
+ (async function () {
2
2
  const wrapper = document.querySelector("#df-btn");
3
3
 
4
4
  // Helper function to get GraphQL endpoint based on environment
@@ -26,12 +26,27 @@
26
26
  return supportedLangs.includes(lang) ? lang : "de";
27
27
  }
28
28
 
29
+ // Helper function to resize Cloudinary images for optimized file size
30
+ function cloudinaryResize(url, width = 32, height = 32) {
31
+ const transform = `w_${width},h_${height},c_fill,f_auto,q_auto`;
32
+
33
+ // Insert transformation right after `/upload/`
34
+ return url.replace('/upload/', `/upload/${transform}/`);
35
+ }
36
+
29
37
  // Async function to fetch chatbot settings from GraphQL
30
38
  async function fetchChatbotSettings(orgId, env, language) {
31
39
  try {
32
40
  const graphqlUrl = getGraphQLUrl(env);
33
41
  console.log("[OSW Widget] Fetching settings from:", graphqlUrl);
34
- console.log("[OSW Widget] Request params - orgId:", orgId, "env:", env, "language:", language);
42
+ console.log(
43
+ "[OSW Widget] Request params - orgId:",
44
+ orgId,
45
+ "env:",
46
+ env,
47
+ "language:",
48
+ language
49
+ );
35
50
 
36
51
  const query = `
37
52
  query osw {
@@ -58,16 +73,26 @@
58
73
  method: "POST",
59
74
  headers: {
60
75
  "Content-Type": "application/json",
61
- "Accept-Language": language
76
+ "Accept-Language": language,
62
77
  },
63
- body: JSON.stringify({ query })
78
+ body: JSON.stringify({ query }),
64
79
  });
65
80
 
66
- console.log("[OSW Widget] Response status:", response.status, response.statusText);
81
+ console.log(
82
+ "[OSW Widget] Response status:",
83
+ response.status,
84
+ response.statusText
85
+ );
67
86
 
68
87
  if (!response.ok) {
69
- console.error("[OSW Widget] Failed to fetch - Status:", response.status, response.statusText);
70
- const errorText = await response.text().catch(() => "Unable to read error response");
88
+ console.error(
89
+ "[OSW Widget] Failed to fetch - Status:",
90
+ response.status,
91
+ response.statusText
92
+ );
93
+ const errorText = await response
94
+ .text()
95
+ .catch(() => "Unable to read error response");
71
96
  console.error("[OSW Widget] Error response:", errorText);
72
97
  return null;
73
98
  }
@@ -76,11 +101,16 @@
76
101
  console.log("[OSW Widget] GraphQL response:", result);
77
102
 
78
103
  if (result.data?.oswChatbotSettingsPublic?.success) {
79
- console.log("[OSW Widget] Settings retrieved successfully:", result.data.oswChatbotSettingsPublic.settings);
104
+ console.log(
105
+ "[OSW Widget] Settings retrieved successfully:",
106
+ result.data.oswChatbotSettingsPublic.settings
107
+ );
80
108
  return result.data.oswChatbotSettingsPublic.settings;
81
109
  }
82
110
 
83
- console.warn("[OSW Widget] GraphQL query succeeded but success=false or missing data");
111
+ console.warn(
112
+ "[OSW Widget] GraphQL query succeeded but success=false or missing data"
113
+ );
84
114
  return null;
85
115
  } catch (error) {
86
116
  console.error("[OSW Widget] Exception while fetching settings:", error);
@@ -101,7 +131,7 @@
101
131
  fontColor: "#3c4043",
102
132
  fontColorDark: "#FFFFFF",
103
133
  position: "right",
104
- animation: "message",
134
+ animation: "none",
105
135
  shakeDuration: 3000,
106
136
  shakeInterval: 1000,
107
137
  shakeAmplitude: 10,
@@ -119,18 +149,30 @@
119
149
  openText: wrapper.getAttribute("openText") || defaultConfig.openText,
120
150
  closeText: wrapper.getAttribute("closeText") || defaultConfig.closeText,
121
151
  background: wrapper.getAttribute("background") || defaultConfig.background,
122
- backgroundDark: wrapper.getAttribute("backgroundDark") || defaultConfig.backgroundDark,
152
+ backgroundDark:
153
+ wrapper.getAttribute("backgroundDark") || defaultConfig.backgroundDark,
123
154
  fontColor: wrapper.getAttribute("fontColor") || defaultConfig.fontColor,
124
- fontColorDark: wrapper.getAttribute("fontColorDark") || defaultConfig.fontColorDark,
155
+ fontColorDark:
156
+ wrapper.getAttribute("fontColorDark") || defaultConfig.fontColorDark,
125
157
  logo: wrapper.getAttribute("logo"),
126
158
  logoDark: wrapper.getAttribute("logoDark"),
127
159
  position: wrapper.getAttribute("position") || defaultConfig.position,
128
160
  animation: wrapper.getAttribute("animation") || defaultConfig.animation,
129
- shakeDuration: parseInt(wrapper.getAttribute("shakeDuration")) || defaultConfig.shakeDuration,
130
- shakeInterval: parseInt(wrapper.getAttribute("shakeInterval")) || defaultConfig.shakeInterval,
131
- shakeAmplitude: parseInt(wrapper.getAttribute("shakeAmplitude")) || defaultConfig.shakeAmplitude,
132
- shakeFrequency: parseInt(wrapper.getAttribute("shakeFrequency")) || defaultConfig.shakeFrequency,
133
- showNotification: wrapper.getAttribute("showNotification") === "true" || defaultConfig.showNotification,
161
+ shakeDuration:
162
+ parseInt(wrapper.getAttribute("shakeDuration")) ||
163
+ defaultConfig.shakeDuration,
164
+ shakeInterval:
165
+ parseInt(wrapper.getAttribute("shakeInterval")) ||
166
+ defaultConfig.shakeInterval,
167
+ shakeAmplitude:
168
+ parseInt(wrapper.getAttribute("shakeAmplitude")) ||
169
+ defaultConfig.shakeAmplitude,
170
+ shakeFrequency:
171
+ parseInt(wrapper.getAttribute("shakeFrequency")) ||
172
+ defaultConfig.shakeFrequency,
173
+ showNotification:
174
+ wrapper.getAttribute("showNotification") === "true" ||
175
+ defaultConfig.showNotification,
134
176
  };
135
177
 
136
178
  const origin = config.src.substring(0, config.src.lastIndexOf("/"));
@@ -142,15 +184,25 @@
142
184
 
143
185
  // Fetch settings from GraphQL and merge with config
144
186
  if (!config.orgId) {
145
- console.error("[OSW Widget] No organization ID provided! Widget will not be displayed.");
146
- console.error("[OSW Widget] Add orgId attribute to the script tag: <script id='df-btn' orgId='YOUR_ORG_ID' ...>");
187
+ console.error(
188
+ "[OSW Widget] No organization ID provided! Widget will not be displayed."
189
+ );
190
+ console.error(
191
+ "[OSW Widget] Add orgId attribute to the script tag: <script id='df-btn' orgId='YOUR_ORG_ID' ...>"
192
+ );
147
193
  return; // Exit if no orgId
148
194
  }
149
195
 
150
- const settings = await fetchChatbotSettings(config.orgId, config.env, detectedLang);
196
+ const settings = await fetchChatbotSettings(
197
+ config.orgId,
198
+ config.env,
199
+ detectedLang
200
+ );
151
201
 
152
202
  if (!settings) {
153
- console.warn("[OSW Widget] Failed to fetch chatbot settings. Using fallback configuration.");
203
+ console.warn(
204
+ "[OSW Widget] Failed to fetch chatbot settings. Using fallback configuration."
205
+ );
154
206
  console.warn("[OSW Widget] Check:");
155
207
  console.warn("[OSW Widget] 1. Network tab for CORS errors");
156
208
  console.warn("[OSW Widget] 2. Organization ID is correct");
@@ -165,17 +217,22 @@
165
217
  config.openText = settings.oswLabel || config.openText;
166
218
  config.position = settings.position || config.position;
167
219
  config.background = settings.oswColourBackgroundLight || config.background;
168
- config.backgroundDark = settings.oswColourBackgroundDark || config.backgroundDark;
220
+ config.backgroundDark =
221
+ settings.oswColourBackgroundDark || config.backgroundDark;
169
222
  config.fontColor = settings.oswColourFontLight || config.fontColor;
170
223
  config.fontColorDark = settings.oswColourFontDark || config.fontColorDark;
171
- config.showNotification = settings.showNotification ?? config.showNotification;
224
+ config.showNotification =
225
+ settings.showNotification ?? config.showNotification;
172
226
  config.hintMessage = settings.oswHintMessage || "Click to check messages";
173
227
 
174
228
  // Use chatbotMask as logo if available
175
229
  if (settings.chatbotMask) {
176
- config.logo = settings.chatbotMask;
177
- config.logoDark = settings.chatbotMask;
178
- console.log("[OSW Widget] Using chatbotMask as logo:", settings.chatbotMask);
230
+ config.logo = cloudinaryResize(settings.chatbotMask, 64, 64);
231
+ config.logoDark = cloudinaryResize(settings.chatbotMask, 64, 64);
232
+ console.log(
233
+ "[OSW Widget] Using chatbotMask as logo:",
234
+ settings.chatbotMask
235
+ );
179
236
  }
180
237
  }
181
238
 
@@ -185,7 +242,7 @@
185
242
  openText: config.openText,
186
243
  position: config.position,
187
244
  showNotification: config.showNotification,
188
- logo: config.logo ? "Custom logo" : "Default logo"
245
+ logo: config.logo ? "Custom logo" : "Default logo",
189
246
  });
190
247
  const style = document.createElement("style");
191
248
  style.innerHTML = `
@@ -215,8 +272,8 @@
215
272
  position: absolute;
216
273
  top: -45px;
217
274
  ${config.position === "left" ? "left: 0;" : "right: 0;"}
218
- background: #202124;
219
- color: white;
275
+ background: ${config.background || "#FEFFFF"};
276
+ color: ${config.fontColor || "#3c4043"};
220
277
  padding: 8px 16px;
221
278
  border-radius: 8px;
222
279
  font-size: 13px;
@@ -225,7 +282,10 @@
225
282
  transition: all 0.3s ease;
226
283
  white-space: nowrap;
227
284
  box-shadow: 0 2px 6px rgba(0,0,0,0.15);
228
- pointer-events: none;
285
+ pointer-events: auto;
286
+ display: flex;
287
+ align-items: center;
288
+ gap: 8px;
229
289
  }
230
290
 
231
291
  .df-notification:after {
@@ -237,7 +297,7 @@
237
297
  height: 0;
238
298
  border-left: 6px solid transparent;
239
299
  border-right: 6px solid transparent;
240
- border-top: 6px solid #202124;
300
+ border-top: 6px solid ${config.background || "#FEFFFF"};
241
301
  }
242
302
 
243
303
  .df-notification.show {
@@ -245,6 +305,26 @@
245
305
  transform: translateY(0);
246
306
  }
247
307
 
308
+ .df-notification-close {
309
+ cursor: pointer;
310
+ width: 16px;
311
+ height: 16px;
312
+ display: flex;
313
+ align-items: center;
314
+ justify-content: center;
315
+ flex-shrink: 0;
316
+ opacity: 0.7;
317
+ transition: opacity 0.2s ease;
318
+ }
319
+
320
+ .df-notification-close:hover {
321
+ opacity: 1;
322
+ }
323
+
324
+ .df-notification-text {
325
+ flex: 1;
326
+ }
327
+
248
328
  .df-btn-text {
249
329
  min-width: 56px;
250
330
  color: ${config.fontColor || "#3c4043"};
@@ -268,7 +348,9 @@
268
348
  background-position: center;
269
349
  background-repeat: no-repeat;
270
350
  background-size: cover;
271
- background-image: url('${config.logo || origin + "/assets/chat-agent.jpg"}');
351
+ background-image: url('${
352
+ config.logo || origin + "/assets/chat-agent.jpg"
353
+ }');
272
354
  border-radius: 50%;
273
355
  width: 32px;
274
356
  height: 32px;
@@ -415,12 +497,25 @@
415
497
  }
416
498
 
417
499
  .df-btn-text:before {
418
- background-image: url('${config.logoDark || config.logo || origin + "/assets/chat-agent.jpg"}')
500
+ background-image: url('${
501
+ config.logoDark ||
502
+ config.logo ||
503
+ origin + "/assets/chat-agent.jpg"
504
+ }')
419
505
  }
420
506
 
421
507
  .df-btn:not(.df-closed) > .df-btn-text:before {
422
508
  background-image: url('${origin}/assets/close_dark.svg')
423
509
  }
510
+
511
+ .df-notification {
512
+ background: ${config.backgroundDark || "#171717"};
513
+ color: ${config.fontColorDark || "#FFFFFF"};
514
+ }
515
+
516
+ .df-notification:after {
517
+ border-top: 6px solid ${config.backgroundDark || "#171717"};
518
+ }
424
519
  }
425
520
 
426
521
  @keyframes shake {
@@ -462,7 +557,16 @@
462
557
  <button class="df-btn df-closed" onclick="dfToggle()">
463
558
  ${
464
559
  config.showNotification
465
- ? `<div class="df-notification">${config.hintMessage || "Click to check messages"}</div>`
560
+ ? `<div class="df-notification">
561
+ <span class="df-notification-text">${
562
+ config.hintMessage || "Click to check messages"
563
+ }</span>
564
+ <div class="df-notification-close" onclick="dismissNotification(event)">
565
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
566
+ <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
567
+ </svg>
568
+ </div>
569
+ </div>`
466
570
  : ""
467
571
  }
468
572
  <div class="df-btn-header">
@@ -475,13 +579,60 @@
475
579
  `;
476
580
 
477
581
  // Insert button into DOM (works in async context unlike document.write)
478
- document.body.insertAdjacentHTML('beforeend', buttonHTML);
582
+ document.body.insertAdjacentHTML("beforeend", buttonHTML);
479
583
 
480
584
  let dfToggled = false;
481
585
  let inactivityTimer = null;
482
586
  let shakeInterval = null;
483
587
 
588
+ // Check if notification was dismissed
589
+ const NOTIFICATION_DISMISSED_KEY = `osw-notification-dismissed-${config.orgId}`;
590
+
591
+ function isNotificationDismissed() {
592
+ try {
593
+ return localStorage.getItem(NOTIFICATION_DISMISSED_KEY) === "true";
594
+ } catch (e) {
595
+ return false;
596
+ }
597
+ }
598
+
599
+ function dismissNotification(event) {
600
+ event.stopPropagation();
601
+ event.preventDefault();
602
+
603
+ const notification = document.querySelector(".df-notification");
604
+ if (notification) {
605
+ notification.classList.remove("show");
606
+ }
607
+
608
+ // Stop any ongoing shake animation
609
+ const button = document.querySelector(".df-btn");
610
+ if (button) {
611
+ button.classList.remove("shake");
612
+ }
613
+
614
+ // Clear timers
615
+ clearInactivityAndShake();
616
+
617
+ // Store dismissed state
618
+ try {
619
+ localStorage.setItem(NOTIFICATION_DISMISSED_KEY, "true");
620
+ } catch (e) {
621
+ console.warn(
622
+ "[OSW Widget] Could not save notification dismissed state:",
623
+ e
624
+ );
625
+ }
626
+ }
627
+
628
+ window.dismissNotification = dismissNotification;
629
+
484
630
  const startInactivityTimer = () => {
631
+ // Don't start timer if notification was dismissed
632
+ if (isNotificationDismissed()) {
633
+ return;
634
+ }
635
+
485
636
  // Only start inactivity timer if button is closed
486
637
  if (!dfToggled) {
487
638
  inactivityTimer = setTimeout(() => {
@@ -663,6 +814,28 @@
663
814
  btn.addEventListener("click", () => {
664
815
  // Only toggle if chat is closed (popover mode)
665
816
  if (btn.classList.contains("df-closed")) {
817
+ // Dismiss notification when opening chat
818
+ const notification = document.querySelector(".df-notification");
819
+ if (notification && notification.classList.contains("show")) {
820
+ notification.classList.remove("show");
821
+ }
822
+
823
+ // Stop any ongoing shake animation
824
+ btn.classList.remove("shake");
825
+
826
+ // Clear timers
827
+ clearInactivityAndShake();
828
+
829
+ // Store dismissed state
830
+ try {
831
+ localStorage.setItem(NOTIFICATION_DISMISSED_KEY, "true");
832
+ } catch (e) {
833
+ console.warn(
834
+ "[OSW Widget] Could not save notification dismissed state:",
835
+ e
836
+ );
837
+ }
838
+
666
839
  const isMobile = window.innerWidth <= 768;
667
840
 
668
841
  if (isMobile) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aumera-on-screen-widget",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "A lightweight, customizable chat widget for websites",
5
5
  "main": "df-btn.js",
6
6
  "scripts": {