releasebird-javascript-sdk 1.0.72 → 1.0.73
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/.claude/settings.local.json +2 -1
- package/build/index.js +1 -1
- package/package.json +1 -1
- package/published/1.0.73/index.js +1 -0
- package/published/latest/index.js +1 -1
- package/src/RbirdWebsiteWidget.js +239 -0
- package/src/Styles.js +142 -0
|
@@ -40,6 +40,10 @@ export default class RbirdWebsiteWidget {
|
|
|
40
40
|
countNotificationsTimer = null;
|
|
41
41
|
countNotificationsDelay = 1000; // 1 second
|
|
42
42
|
|
|
43
|
+
// Message bubbles
|
|
44
|
+
messageBubblesContainer = null;
|
|
45
|
+
dismissedBubbles = new Set(); // Track dismissed bubbles by chatId
|
|
46
|
+
|
|
43
47
|
static getInstance() {
|
|
44
48
|
if (!this.instance) {
|
|
45
49
|
this.instance = new RbirdWebsiteWidget();
|
|
@@ -110,6 +114,9 @@ export default class RbirdWebsiteWidget {
|
|
|
110
114
|
// Disable scrolling on the main page
|
|
111
115
|
document.body.style.overflow = 'hidden';
|
|
112
116
|
|
|
117
|
+
// Hide message bubbles when widget is open
|
|
118
|
+
this.hideMessageBubbles();
|
|
119
|
+
|
|
113
120
|
this.registerListeners();
|
|
114
121
|
if (this.hideWidgetButton) {
|
|
115
122
|
this.hideWidgetButton.style.display = "none";
|
|
@@ -264,6 +271,10 @@ export default class RbirdWebsiteWidget {
|
|
|
264
271
|
|
|
265
272
|
this.websiteWidget.onclick = () => this.openWebsiteWidget();
|
|
266
273
|
this.unregisterListeners();
|
|
274
|
+
|
|
275
|
+
// Reload message bubbles when widget is closed
|
|
276
|
+
this.dismissedBubbles.clear(); // Reset dismissed bubbles
|
|
277
|
+
this.fetchUnreadMessages();
|
|
267
278
|
}
|
|
268
279
|
|
|
269
280
|
initButton() {
|
|
@@ -301,7 +312,9 @@ export default class RbirdWebsiteWidget {
|
|
|
301
312
|
|
|
302
313
|
if (e.data === 'newMessageArrived') {
|
|
303
314
|
this.countNotifications();
|
|
315
|
+
// Also refresh message bubbles when new message arrives
|
|
304
316
|
if (!RbirdUtils.hasClass(this.widgetContent, 'cta__modal--visible')) {
|
|
317
|
+
this.fetchUnreadMessages();
|
|
305
318
|
if (this.iframe) {
|
|
306
319
|
this.iframe.contentWindow?.postMessage({
|
|
307
320
|
type: 'showMessageTab',
|
|
@@ -347,6 +360,7 @@ export default class RbirdWebsiteWidget {
|
|
|
347
360
|
window.onmessage = (e) => this.handleEvent(e, this);
|
|
348
361
|
|
|
349
362
|
this.countNotifications();
|
|
363
|
+
this.fetchUnreadMessages();
|
|
350
364
|
}
|
|
351
365
|
|
|
352
366
|
updateIframe(iframeSrc) {
|
|
@@ -378,5 +392,230 @@ export default class RbirdWebsiteWidget {
|
|
|
378
392
|
document.addEventListener("keydown", this.escListener);
|
|
379
393
|
}
|
|
380
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Fetch unread messages and display as bubbles above the widget button
|
|
397
|
+
*/
|
|
398
|
+
fetchUnreadMessages() {
|
|
399
|
+
if (typeof window === 'undefined') return;
|
|
400
|
+
if (!RbirdSessionManager.getInstance().identify?.people && !RbirdSessionManager.getInstance().anonymousIdentifier) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const http = new XMLHttpRequest();
|
|
405
|
+
http.open("GET", `${API}/ewidget/unread/messages`);
|
|
406
|
+
http.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
|
407
|
+
http.setRequestHeader("apiKey", RbirdSessionManager.getInstance().apiKey);
|
|
408
|
+
if (RbirdSessionManager.getInstance().identify?.people) {
|
|
409
|
+
http.setRequestHeader("peopleId", RbirdSessionManager.getInstance().identify.people);
|
|
410
|
+
}
|
|
411
|
+
if (RbirdSessionManager.getInstance().anonymousIdentifier) {
|
|
412
|
+
http.setRequestHeader("ai", RbirdSessionManager.getInstance().anonymousIdentifier);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const that = this;
|
|
416
|
+
http.onerror = function () {
|
|
417
|
+
console.error('[RbirdWidget] Failed to fetch unread messages');
|
|
418
|
+
};
|
|
419
|
+
http.onreadystatechange = function () {
|
|
420
|
+
if (http.readyState === XMLHttpRequest.DONE) {
|
|
421
|
+
console.log('[RbirdWidget] Unread messages response:', http.status, http.responseText);
|
|
422
|
+
if (http.status === 200 || http.status === 201) {
|
|
423
|
+
try {
|
|
424
|
+
const messages = JSON.parse(http.responseText);
|
|
425
|
+
console.log('[RbirdWidget] Parsed messages:', messages);
|
|
426
|
+
that.renderMessageBubbles(messages);
|
|
427
|
+
} catch (e) {
|
|
428
|
+
console.error('[RbirdWidget] Error parsing unread messages:', e);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
http.send();
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Render message bubbles above the widget button
|
|
438
|
+
* @param {Array} messages - Array of unread messages (one per chat, max 5)
|
|
439
|
+
*/
|
|
440
|
+
renderMessageBubbles(messages) {
|
|
441
|
+
console.log('[RbirdWidget] renderMessageBubbles called with:', messages);
|
|
442
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') return;
|
|
443
|
+
|
|
444
|
+
// Don't show bubbles if widget is open
|
|
445
|
+
if (this.isOpen()) {
|
|
446
|
+
console.log('[RbirdWidget] Widget is open, hiding bubbles');
|
|
447
|
+
this.hideMessageBubbles();
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Filter out dismissed bubbles and limit to 5
|
|
452
|
+
const filteredMessages = messages
|
|
453
|
+
.filter(msg => !this.dismissedBubbles.has(msg.chatId))
|
|
454
|
+
.slice(0, 5);
|
|
455
|
+
|
|
456
|
+
console.log('[RbirdWidget] Filtered messages:', filteredMessages);
|
|
457
|
+
|
|
458
|
+
if (filteredMessages.length === 0) {
|
|
459
|
+
console.log('[RbirdWidget] No messages to display');
|
|
460
|
+
this.hideMessageBubbles();
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Create or get container
|
|
465
|
+
if (!this.messageBubblesContainer) {
|
|
466
|
+
this.messageBubblesContainer = document.createElement('div');
|
|
467
|
+
this.messageBubblesContainer.className = 'rbird-message-bubbles-container';
|
|
468
|
+
document.body.appendChild(this.messageBubblesContainer);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Clear existing bubbles
|
|
472
|
+
this.messageBubblesContainer.innerHTML = '';
|
|
473
|
+
|
|
474
|
+
// Render each bubble
|
|
475
|
+
filteredMessages.forEach(msg => {
|
|
476
|
+
const bubble = this.createMessageBubble(msg);
|
|
477
|
+
this.messageBubblesContainer.appendChild(bubble);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
this.messageBubblesContainer.style.display = 'flex';
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Create a single message bubble element
|
|
485
|
+
* @param {Object} msg - Message object with chatId, text, senderName, senderAvatar, timestamp
|
|
486
|
+
*/
|
|
487
|
+
createMessageBubble(msg) {
|
|
488
|
+
const bubble = document.createElement('div');
|
|
489
|
+
bubble.className = 'rbird-message-bubble';
|
|
490
|
+
bubble.setAttribute('data-chat-id', msg.chatId);
|
|
491
|
+
|
|
492
|
+
// Avatar
|
|
493
|
+
const avatarDiv = document.createElement('div');
|
|
494
|
+
avatarDiv.className = 'rbird-message-bubble-avatar';
|
|
495
|
+
if (msg.senderAvatar) {
|
|
496
|
+
const avatarImg = document.createElement('img');
|
|
497
|
+
avatarImg.src = msg.senderAvatar;
|
|
498
|
+
avatarImg.alt = msg.senderName || 'Avatar';
|
|
499
|
+
avatarDiv.appendChild(avatarImg);
|
|
500
|
+
} else {
|
|
501
|
+
// Show initials
|
|
502
|
+
const initials = (msg.senderName || 'U').charAt(0).toUpperCase();
|
|
503
|
+
avatarDiv.textContent = initials;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Content
|
|
507
|
+
const contentDiv = document.createElement('div');
|
|
508
|
+
contentDiv.className = 'rbird-message-bubble-content';
|
|
509
|
+
|
|
510
|
+
const textP = document.createElement('p');
|
|
511
|
+
textP.className = 'rbird-message-bubble-text';
|
|
512
|
+
textP.textContent = msg.text || '';
|
|
513
|
+
|
|
514
|
+
const metaDiv = document.createElement('div');
|
|
515
|
+
metaDiv.className = 'rbird-message-bubble-meta';
|
|
516
|
+
|
|
517
|
+
const senderSpan = document.createElement('span');
|
|
518
|
+
senderSpan.className = 'rbird-message-bubble-sender';
|
|
519
|
+
senderSpan.textContent = msg.senderName || 'Support';
|
|
520
|
+
|
|
521
|
+
const dotSpan = document.createElement('span');
|
|
522
|
+
dotSpan.textContent = '·';
|
|
523
|
+
|
|
524
|
+
const timeSpan = document.createElement('span');
|
|
525
|
+
timeSpan.className = 'rbird-message-bubble-time';
|
|
526
|
+
timeSpan.textContent = this.formatMessageTime(msg.timestamp);
|
|
527
|
+
|
|
528
|
+
metaDiv.appendChild(senderSpan);
|
|
529
|
+
metaDiv.appendChild(dotSpan);
|
|
530
|
+
metaDiv.appendChild(timeSpan);
|
|
531
|
+
|
|
532
|
+
contentDiv.appendChild(textP);
|
|
533
|
+
contentDiv.appendChild(metaDiv);
|
|
534
|
+
|
|
535
|
+
// Close button
|
|
536
|
+
const closeBtn = document.createElement('button');
|
|
537
|
+
closeBtn.className = 'rbird-message-bubble-close';
|
|
538
|
+
closeBtn.innerHTML = '×';
|
|
539
|
+
closeBtn.onclick = (e) => {
|
|
540
|
+
e.stopPropagation();
|
|
541
|
+
this.dismissMessageBubble(msg.chatId, bubble);
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
bubble.appendChild(avatarDiv);
|
|
545
|
+
bubble.appendChild(contentDiv);
|
|
546
|
+
bubble.appendChild(closeBtn);
|
|
547
|
+
|
|
548
|
+
// Click on bubble opens widget
|
|
549
|
+
bubble.onclick = () => {
|
|
550
|
+
this.hideMessageBubbles();
|
|
551
|
+
this.openWebsiteWidget();
|
|
552
|
+
// Send message to iframe to open the specific chat
|
|
553
|
+
if (this.iframe) {
|
|
554
|
+
this.iframe.contentWindow?.postMessage({
|
|
555
|
+
type: 'openChat',
|
|
556
|
+
chatId: msg.chatId
|
|
557
|
+
}, '*');
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
return bubble;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Format timestamp to relative time (e.g., "Just now", "5 min ago")
|
|
566
|
+
*/
|
|
567
|
+
formatMessageTime(timestamp) {
|
|
568
|
+
if (!timestamp) return '';
|
|
569
|
+
|
|
570
|
+
const now = new Date();
|
|
571
|
+
const msgDate = new Date(timestamp);
|
|
572
|
+
const diffMs = now - msgDate;
|
|
573
|
+
const diffSec = Math.floor(diffMs / 1000);
|
|
574
|
+
const diffMin = Math.floor(diffSec / 60);
|
|
575
|
+
const diffHour = Math.floor(diffMin / 60);
|
|
576
|
+
const diffDay = Math.floor(diffHour / 24);
|
|
577
|
+
|
|
578
|
+
if (diffSec < 60) {
|
|
579
|
+
return 'Just now';
|
|
580
|
+
} else if (diffMin < 60) {
|
|
581
|
+
return `${diffMin} min ago`;
|
|
582
|
+
} else if (diffHour < 24) {
|
|
583
|
+
return `${diffHour}h ago`;
|
|
584
|
+
} else if (diffDay === 1) {
|
|
585
|
+
return 'Yesterday';
|
|
586
|
+
} else if (diffDay < 7) {
|
|
587
|
+
return `${diffDay}d ago`;
|
|
588
|
+
} else {
|
|
589
|
+
return msgDate.toLocaleDateString();
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Dismiss a single message bubble
|
|
595
|
+
*/
|
|
596
|
+
dismissMessageBubble(chatId, bubbleElement) {
|
|
597
|
+
this.dismissedBubbles.add(chatId);
|
|
598
|
+
|
|
599
|
+
if (bubbleElement) {
|
|
600
|
+
bubbleElement.style.animation = 'rbird-bubble-slide-out 0.2s ease-in forwards';
|
|
601
|
+
setTimeout(() => {
|
|
602
|
+
bubbleElement.remove();
|
|
603
|
+
// Hide container if no more bubbles
|
|
604
|
+
if (this.messageBubblesContainer && this.messageBubblesContainer.children.length === 0) {
|
|
605
|
+
this.messageBubblesContainer.style.display = 'none';
|
|
606
|
+
}
|
|
607
|
+
}, 200);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Hide all message bubbles
|
|
613
|
+
*/
|
|
614
|
+
hideMessageBubbles() {
|
|
615
|
+
if (this.messageBubblesContainer) {
|
|
616
|
+
this.messageBubblesContainer.style.display = 'none';
|
|
617
|
+
this.messageBubblesContainer.innerHTML = '';
|
|
618
|
+
}
|
|
619
|
+
}
|
|
381
620
|
|
|
382
621
|
}
|
package/src/Styles.js
CHANGED
|
@@ -381,6 +381,148 @@ background: transparent
|
|
|
381
381
|
0% { transform: rotate(0deg); }
|
|
382
382
|
100% { transform: rotate(360deg); }
|
|
383
383
|
}
|
|
384
|
+
|
|
385
|
+
/* Message Bubbles - Unread messages preview */
|
|
386
|
+
.rbird-message-bubbles-container {
|
|
387
|
+
position: fixed;
|
|
388
|
+
bottom: ${spaceBottom + 70}px;
|
|
389
|
+
${launcherPosition === 'right' ? `right: ${spaceLeftRight}px;` : `left: ${spaceLeftRight}px;`}
|
|
390
|
+
z-index: 9999999;
|
|
391
|
+
display: flex;
|
|
392
|
+
flex-direction: column;
|
|
393
|
+
gap: 10px;
|
|
394
|
+
max-width: 320px;
|
|
395
|
+
pointer-events: none;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.rbird-message-bubble {
|
|
399
|
+
background: #ffffff;
|
|
400
|
+
border-radius: 12px;
|
|
401
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
402
|
+
padding: 12px 14px;
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: flex-start;
|
|
405
|
+
gap: 10px;
|
|
406
|
+
cursor: pointer;
|
|
407
|
+
pointer-events: auto;
|
|
408
|
+
animation: rbird-bubble-slide-in 0.3s ease-out;
|
|
409
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
410
|
+
position: relative;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.rbird-message-bubble:hover {
|
|
414
|
+
transform: translateY(-2px);
|
|
415
|
+
box-shadow: 0 6px 25px rgba(0, 0, 0, 0.2);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.rbird-message-bubble-avatar {
|
|
419
|
+
width: 36px;
|
|
420
|
+
height: 36px;
|
|
421
|
+
border-radius: 50%;
|
|
422
|
+
background-color: #e0e0e0;
|
|
423
|
+
flex-shrink: 0;
|
|
424
|
+
object-fit: cover;
|
|
425
|
+
display: flex;
|
|
426
|
+
align-items: center;
|
|
427
|
+
justify-content: center;
|
|
428
|
+
font-size: 14px;
|
|
429
|
+
font-weight: 600;
|
|
430
|
+
color: #666;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.rbird-message-bubble-avatar img {
|
|
434
|
+
width: 100%;
|
|
435
|
+
height: 100%;
|
|
436
|
+
border-radius: 50%;
|
|
437
|
+
object-fit: cover;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.rbird-message-bubble-content {
|
|
441
|
+
flex: 1;
|
|
442
|
+
min-width: 0;
|
|
443
|
+
overflow: hidden;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.rbird-message-bubble-text {
|
|
447
|
+
font-size: 14px;
|
|
448
|
+
line-height: 1.4;
|
|
449
|
+
color: #1a1a1a;
|
|
450
|
+
margin: 0 0 4px 0;
|
|
451
|
+
overflow: hidden;
|
|
452
|
+
text-overflow: ellipsis;
|
|
453
|
+
display: -webkit-box;
|
|
454
|
+
-webkit-line-clamp: 2;
|
|
455
|
+
-webkit-box-orient: vertical;
|
|
456
|
+
word-break: break-word;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.rbird-message-bubble-meta {
|
|
460
|
+
font-size: 12px;
|
|
461
|
+
color: #888;
|
|
462
|
+
display: flex;
|
|
463
|
+
align-items: center;
|
|
464
|
+
gap: 6px;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.rbird-message-bubble-sender {
|
|
468
|
+
font-weight: 500;
|
|
469
|
+
color: #666;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.rbird-message-bubble-time {
|
|
473
|
+
color: #999;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.rbird-message-bubble-close {
|
|
477
|
+
position: absolute;
|
|
478
|
+
top: 6px;
|
|
479
|
+
right: 6px;
|
|
480
|
+
width: 18px;
|
|
481
|
+
height: 18px;
|
|
482
|
+
border: none;
|
|
483
|
+
background: rgba(0, 0, 0, 0.1);
|
|
484
|
+
border-radius: 50%;
|
|
485
|
+
cursor: pointer;
|
|
486
|
+
display: flex;
|
|
487
|
+
align-items: center;
|
|
488
|
+
justify-content: center;
|
|
489
|
+
font-size: 12px;
|
|
490
|
+
color: #666;
|
|
491
|
+
opacity: 0;
|
|
492
|
+
transition: opacity 0.2s ease;
|
|
493
|
+
padding: 0;
|
|
494
|
+
line-height: 1;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.rbird-message-bubble:hover .rbird-message-bubble-close {
|
|
498
|
+
opacity: 1;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.rbird-message-bubble-close:hover {
|
|
502
|
+
background: rgba(0, 0, 0, 0.2);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
@keyframes rbird-bubble-slide-in {
|
|
506
|
+
from {
|
|
507
|
+
opacity: 0;
|
|
508
|
+
transform: translateY(20px) scale(0.95);
|
|
509
|
+
}
|
|
510
|
+
to {
|
|
511
|
+
opacity: 1;
|
|
512
|
+
transform: translateY(0) scale(1);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
@keyframes rbird-bubble-slide-out {
|
|
517
|
+
from {
|
|
518
|
+
opacity: 1;
|
|
519
|
+
transform: translateY(0) scale(1);
|
|
520
|
+
}
|
|
521
|
+
to {
|
|
522
|
+
opacity: 0;
|
|
523
|
+
transform: translateY(20px) scale(0.95);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
384
526
|
}`
|
|
385
527
|
|
|
386
528
|
const oldNode = document.querySelector(".rbird-styles");
|